Skip to content

Commit

Permalink
feat: injective integration
Browse files Browse the repository at this point in the history
  • Loading branch information
hedi-edelbloute committed Sep 5, 2023
1 parent f848f10 commit b781699
Show file tree
Hide file tree
Showing 20 changed files with 1,356 additions and 102 deletions.
1 change: 1 addition & 0 deletions apps/cli/src/live-common-setup-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ setSupportedCurrencies([
"stacks",
"telos_evm",
"coreum",
"injective",
]);

for (const k in process.env) setEnvUnsafe(k as EnvName, process.env[k]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ setSupportedCurrencies([
"stacks",
"telos_evm",
"coreum",
"injective",
]);
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const StepChooseCurrency = ({ currency, setCurrency }: StepProps) => {
const baseGoerli = useFeature("currencyBaseGoerli");
const klaytn = useFeature("currencyKlaytn");
const mock = useEnv("MOCK");
const injective = useFeature("currencyInjective");

const featureFlaggedCurrencies = useMemo(
(): Partial<Record<CryptoCurrencyId, Feature<unknown> | null>> => ({
Expand Down Expand Up @@ -103,6 +104,7 @@ const StepChooseCurrency = ({ currency, setCurrency }: StepProps) => {
base,
base_goerli: baseGoerli,
klaytn,
injective,
}),
[
axelar,
Expand Down Expand Up @@ -138,6 +140,7 @@ const StepChooseCurrency = ({ currency, setCurrency }: StepProps) => {
base,
baseGoerli,
klaytn,
injective,
],
);

Expand Down
1 change: 1 addition & 0 deletions apps/ledger-live-mobile/src/live-common-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ setSupportedCurrencies([
"stacks",
"telos_evm",
"coreum",
"injective",
]);

if (Config.VERBOSE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default function AddAccountsSelectCrypto({ navigation, route }: Props) {
const baseGoerli = useFeature("currencyBaseGoerli");
const klaytn = useFeature("currencyKlaytn");
const mock = useEnv("MOCK");
const injective = useFeature("currencyInjective");
const featureFlaggedCurrencies = useMemo(
(): Partial<Record<CryptoCurrencyId, Feature<unknown> | null>> => ({
axelar,
Expand Down Expand Up @@ -123,6 +124,7 @@ export default function AddAccountsSelectCrypto({ navigation, route }: Props) {
base,
base_goerli: baseGoerli,
klaytn,
injective,
}),
[
axelar,
Expand Down Expand Up @@ -158,6 +160,7 @@ export default function AddAccountsSelectCrypto({ navigation, route }: Props) {
base,
baseGoerli,
klaytn,
injective,
],
);

Expand Down
5 changes: 3 additions & 2 deletions libs/ledger-live-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@
"@celo/wallet-base": "^3.0.1",
"@celo/wallet-ledger": "^3.0.1",
"@cosmjs/amino": "^0.28.4",
"@cosmjs/crypto": "^0.26.5",
"@cosmjs/crypto": "^0.31.0",
"@cosmjs/launchpad": "^0.26.5",
"@cosmjs/ledger-amino": "^0.26.5",
"@cosmjs/ledger-amino": "^0.31.0",
"@cosmjs/proto-signing": "^0.26.5",
"@cosmjs/stargate": "^0.26.5",
"@crypto-org-chain/chain-jslib": "1.1.2",
Expand Down Expand Up @@ -193,6 +193,7 @@
"@xstate/react": "^1.6.3",
"@zondax/cbor": "v8.1.0-zondax-no-bigint",
"@zondax/izari-filecoin": "^1.2.0",
"@zondax/ledger-cosmos-js": "^3.0.3",
"@zondax/ledger-filecoin": "^0.11.2",
"@zondax/ledger-icp": "^0.7.0",
"@zondax/ledger-stacks": "^1.0.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ setSupportedCurrencies([
"stacks",
"telos_evm",
"coreum",
"injective",
]);

for (const k in process.env) setEnvUnsafe(k as EnvName, process.env[k]);
Expand Down
4 changes: 4 additions & 0 deletions libs/ledger-live-common/src/config/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ const defaultConfig = {
lcd: "https://full-node.mainnet-1.coreum.dev:1317",
minGasPrice: 0.1,
},
injective: {
lcd: "https://injective-api.polkachu.com",
minGasPrice: 0.02,
},
} as { [currency: string]: CosmosCurrencyConfig },
},
};
Expand Down
32 changes: 23 additions & 9 deletions libs/ledger-live-common/src/families/cosmos/api/Cosmos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,14 @@ export class CosmosAPI {
}
};

getAccount = async (address: string): Promise<{ accountNumber: number; sequence: number }> => {
const response = {
getAccount = async (
address: string,
): Promise<{ accountNumber: number; sequence: number; pubKeyType: string; pubKey: string }> => {
const accountData = {
accountNumber: 0,
sequence: 0,
pubKeyType: "/cosmos.crypto.secp256k1.PubKey",
pubKey: "",
};

try {
Expand All @@ -72,16 +76,26 @@ export class CosmosAPI {
url: `${this.defaultEndpoint}/cosmos/auth/${this.version}/accounts/${address}`,
});

if (data.account.account_number) {
response.accountNumber = parseInt(data.account.account_number);
const srcAccount =
data.account.base_account != null ? data.account.base_account : data.account;

if (srcAccount.account_number) {
accountData.accountNumber = parseInt(srcAccount.account_number);
}

if (srcAccount.sequence) {
accountData.sequence = parseInt(srcAccount.sequence);
}

if (data.account.sequence) {
response.sequence = parseInt(data.account.sequence);
if (srcAccount.pub_key) {
accountData.pubKey = srcAccount.pub_key.key;
accountData.pubKeyType = srcAccount.pub_key["@type"];
}
// eslint-disable-next-line no-empty
} catch (e) {}
return response;
} catch (e) {
console.log("Could not fetch account info, using default values instead");
}

return accountData;
};

getChainId = async (): Promise<string> => {
Expand Down
20 changes: 20 additions & 0 deletions libs/ledger-live-common/src/families/cosmos/chain/Injective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import CosmosBase from "./cosmosBase";

class Injective extends CosmosBase {
stakingDocUrl: string;
unbondingPeriod: number;
validatorPrefix: string;
prefix: string;
// Provided by coin config
lcd!: string;
ledgerValidator!: string;
constructor() {
super();
this.stakingDocUrl = "https://support.ledger.com/hc/en-us/articles/9604085095965?support=true";
this.unbondingPeriod = 21;
this.prefix = "inj";
this.validatorPrefix = `${this.prefix}valoper`;
}
}

export default Injective;
4 changes: 4 additions & 0 deletions libs/ledger-live-common/src/families/cosmos/chain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Stride from "./Stride";
import Umee from "./Umee";
import BinanceBeaconChain from "./BinanceBeaconChain";
import Coreum from "./Coreum";
import Injective from "./Injective";

const cosmosChainParams: { [key: string]: CosmosBase } = {};
export default function cryptoFactory(currencyId: string): CosmosBase {
Expand Down Expand Up @@ -66,6 +67,9 @@ export default function cryptoFactory(currencyId: string): CosmosBase {
case "coreum":
cosmosChainParams[currencyId] = new Coreum();
break;
case "injective":
cosmosChainParams[currencyId] = new Injective();
break;
default:
throw new Error(`${currencyId} is not supported`);
}
Expand Down
23 changes: 23 additions & 0 deletions libs/ledger-live-common/src/families/cosmos/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,26 @@ export const getMainMessage = (messages: CosmosMessage[]): CosmosMessage => {
.sort((a, b) => messagePriorities.indexOf(a.type) - messagePriorities.indexOf(b.type));
return sortedTypes[0];
};

const order = unordered =>
Object.keys(unordered)
.sort()
.reduce((obj, key) => {
obj[key] = unordered[key];
return obj;
}, {});

export const sortObjectKeysDeeply = object => {
for (let [key, value] of Object.entries(object)) {
if (Array.isArray(value)) {
const newArray = new Array();
for (const element of value) {
newArray.push(sortObjectKeysDeeply(element));
}
object[key] = newArray;
} else if (typeof value === "object") {
object[key] = sortObjectKeysDeeply(value);
}
}
return order(object);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Coin } from "@keplr-wallet/proto-types/cosmos/base/v1beta1/coin";
import BigNumber from "bignumber.js";
import { EncodeObject, GeneratedType, makeAuthInfoBytes, Registry } from "@cosmjs/proto-signing";
import { CosmosAPI } from "./api/Cosmos";
import cryptoFactory from "./chain/chain";

type ProtoMsg = {
typeUrl: string;
Expand Down Expand Up @@ -424,7 +425,7 @@ export const postBuildTransaction = async (
signResponse: AminoSignResponse,
protoMsgs: Array<ProtoMsg>,
): Promise<Uint8Array> => {
const signed_tx_bytes = TxRaw.encode({
const signedTx = TxRaw.encode({
bodyBytes: TxBody.encode(
TxBody.fromPartial({
messages: protoMsgs,
Expand All @@ -438,7 +439,7 @@ export const postBuildTransaction = async (
signerInfos: [
{
publicKey: {
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
typeUrl: signResponse.signature.pub_key.type,
value: PubKey.encode({
key: Buffer.from(signResponse.signature.pub_key.value, "base64"),
}).finish(),
Expand All @@ -462,7 +463,7 @@ export const postBuildTransaction = async (
signatures: [Buffer.from(signResponse.signature.signature, "base64")],
}).finish();

return signed_tx_bytes;
return signedTx;
};

export const postBuildUnsignedPayloadTransaction = async (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ export const getEstimatedFees = async (
account: CosmosAccount,
transaction: Transaction,
): Promise<{ estimatedFees: BigNumber; estimatedGas: BigNumber }> => {
const cosmosCurrency = cryptoFactory(account.currency.id);
let estimatedGas = new BigNumber(cosmosCurrency.defaultGas);
const chainInstance = cryptoFactory(account.currency.id);
let estimatedGas = new BigNumber(chainInstance.defaultGas);

const cosmosAPI = new CosmosAPI(account.currency.id);
const unsignedPayload: { typeUrl: string; value: any }[] = await buildUnsignedPayloadTransaction(
Expand All @@ -61,12 +61,13 @@ export const getEstimatedFees = async (

if (unsignedPayload && unsignedPayload.length > 0) {
const signature = new Uint8Array(Buffer.from(account.seedIdentifier, "hex"));
const { pubKeyType } = await cosmosAPI.getAccount(account.freshAddress);

// see https://github.com/cosmos/cosmjs/blob/main/packages/proto-signing/src/pubkey.spec.ts
const prefix = new Uint8Array([10, 33]);

const pubkey = {
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
typeUrl: pubKeyType,
value: new Uint8Array([...prefix, ...signature]),
};

Expand All @@ -90,7 +91,7 @@ export const getEstimatedFees = async (
}

const estimatedFees = estimatedGas
.times(cosmosCurrency.minGasPrice)
.times(chainInstance.minGasPrice)
.integerValue(BigNumber.ROUND_CEIL);

return { estimatedFees, estimatedGas };
Expand Down
43 changes: 34 additions & 9 deletions libs/ledger-live-common/src/families/cosmos/js-signOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { LedgerSigner } from "@cosmjs/ledger-amino";
import { stringToPath } from "@cosmjs/crypto";
import { buildTransaction, postBuildTransaction } from "./js-buildTransaction";
import BigNumber from "bignumber.js";
import { makeSignDoc } from "@cosmjs/launchpad";

import { AminoSignResponse, makeSignDoc, StdSignDoc } from "@cosmjs/launchpad";
import type { Operation, OperationType, SignOperationFnSignature } from "@ledgerhq/types-live";
import { CosmosAPI } from "./api/Cosmos";
import cryptoFactory from "./chain/chain";
import { sortObjectKeysDeeply } from "./helpers";
import { HdPath, Secp256k1Signature } from "@cosmjs/crypto";
import { CosmosApp } from "@zondax/ledger-cosmos-js";

const signOperation: SignOperationFnSignature<Transaction> = ({ account, deviceId, transaction }) =>
withDevice(deviceId)(
Expand All @@ -20,7 +22,10 @@ const signOperation: SignOperationFnSignature<Transaction> = ({ account, deviceI

async function main() {
const cosmosAPI = new CosmosAPI(account.currency.id);
const { accountNumber, sequence } = await cosmosAPI.getAccount(account.freshAddress);
const { accountNumber, sequence, pubKeyType } = await cosmosAPI.getAccount(
account.freshAddress,
);
const chainInstance = cryptoFactory(account.currency.id);
o.next({ type: "device-signature-requested" });
const { aminoMsgs, protoMsgs } = await buildTransaction(account, transaction);
if (!transaction.gas) {
Expand All @@ -42,20 +47,39 @@ const signOperation: SignOperationFnSignature<Transaction> = ({ account, deviceI
// Cosmos Nano App sign data in Amino way only, not Protobuf.
// This is a legacy outdated standard and a long-term blocking point.
const chainId = await cosmosAPI.getChainId();
const signDoc = makeSignDoc(
let signDoc = makeSignDoc(
aminoMsgs,
feeToEncode,
chainId,
transaction.memo || "",
accountNumber.toString(),
sequence.toString(),
);
const ledgerSigner = new LedgerSigner(transport, {
hdPaths: [stringToPath("m/" + account.freshAddressPath)],
prefix: cryptoFactory(account.currency.id).prefix,
});
signDoc = sortObjectKeysDeeply(signDoc) as StdSignDoc;
const tx = Buffer.from(JSON.stringify(signDoc), "utf-8");
const app = new CosmosApp(transport);
const path = account.freshAddressPath.split("/").map(p => parseInt(p.replace("'", "")));

const resp_add = await app.getAddressAndPubKey(path, chainInstance.prefix);

const signResponseApp =
path[1] === 60
? await app.sign(path, tx, chainInstance.prefix)
: await app.sign(path, tx);

const signResponse: AminoSignResponse = {
signed: signDoc,
signature: {
pub_key: {
value: Buffer.from(resp_add.compressed_pk).toString("base64"),
type: pubKeyType,
},
signature: Buffer.from(
Secp256k1Signature.fromDer(signResponseApp.signature).toFixedLength(),
).toString("base64"),
},
};

const signResponse = await ledgerSigner.signAmino(account.freshAddress, signDoc);
const tx_bytes = await postBuildTransaction(signResponse, protoMsgs);
const signed = Buffer.from(tx_bytes).toString("hex");

Expand Down Expand Up @@ -128,6 +152,7 @@ const signOperation: SignOperationFnSignature<Transaction> = ({ account, deviceI
signedOperation: {
operation,
signature: signed,
expirationDate: undefined,
},
});
}
Expand Down
3 changes: 3 additions & 0 deletions libs/ledger-live-common/src/featureFlags/defaultFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ export const defaultFeatures = {
currencyCoreum: {
enabled: false,
},
currencyInjective: {
enabled: true,
},
currencyPolygonZkEvm: {
enabled: false,
},
Expand Down
Loading

0 comments on commit b781699

Please sign in to comment.