icns-frontend/wallets/cosmos.ts

224 lines
5.6 KiB
TypeScript
Raw Normal View History

2022-12-12 06:19:24 +00:00
import { Wallet } from "./types";
import { BaseAccount } from "@keplr-wallet/cosmos";
import Axios from "axios";
import { StdSignDoc } from "@keplr-wallet/types";
import { sortObjectByKey } from "@keplr-wallet/common";
import { Buffer } from "buffer/";
import {
AuthInfo,
Fee,
2022-12-12 06:53:41 +00:00
SignerInfo,
2022-12-12 06:19:24 +00:00
TxBody,
TxRaw,
} from "@keplr-wallet/proto-types/cosmos/tx/v1beta1/tx";
import { Any } from "@keplr-wallet/proto-types/google/protobuf/any";
import { PubKey } from "@keplr-wallet/proto-types/cosmos/crypto/secp256k1/keys";
import { SignMode } from "@keplr-wallet/proto-types/cosmos/tx/signing/v1beta1/signing";
import { MsgExecuteContract } from "@keplr-wallet/proto-types/cosmwasm/wasm/v1/tx";
export async function sendMsgs(
wallet: Wallet,
chainInfo: {
readonly chainId: string;
readonly rest: string;
},
sender: string,
msgs: {
amino: {
readonly type: string;
readonly value: any;
}[];
proto: Any[];
},
fee: {
readonly amount: readonly {
readonly amount: string;
readonly denom: string;
}[];
readonly gas: string;
},
memo: string = "",
): Promise<Uint8Array> {
const account = await BaseAccount.fetchFromRest(
Axios.create({
baseURL: chainInfo.rest,
}),
sender,
true,
);
const signDocRaw: StdSignDoc = {
chain_id: chainInfo.chainId,
account_number: account.getAccountNumber().toString(),
sequence: account.getSequence().toString(),
fee,
msgs: msgs.amino,
memo: memo,
};
const signDoc = sortObjectByKey(signDocRaw);
const signResponse = await wallet.signAmino(
chainInfo.chainId,
sender,
signDoc,
);
const signedTx = TxRaw.encode({
bodyBytes: TxBody.encode(
TxBody.fromPartial({
// XXX: I don't know why typing error occurs.
// TODO: Solve typing problem.
messages: msgs.proto as any,
memo: signResponse.signed.memo,
}),
).finish(),
authInfoBytes: AuthInfo.encode({
signerInfos: [
{
publicKey: {
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: PubKey.encode({
key: Buffer.from(signResponse.signature.pub_key.value, "base64"),
}).finish(),
},
modeInfo: {
single: {
mode: SignMode.SIGN_MODE_LEGACY_AMINO_JSON,
},
multi: undefined,
},
sequence: signResponse.signed.sequence,
},
],
fee: Fee.fromPartial({
// XXX: I don't know why typing error occurs.
// TODO: Solve typing problem.
// amount: signResponse.signed.fee.amount as {
// amount: string;
// denom: string;
// }[],
amount: signResponse.signed.fee.amount as any,
gasLimit: signResponse.signed.fee.gas,
}),
}).finish(),
signatures: [Buffer.from(signResponse.signature.signature, "base64")],
}).finish();
return wallet.broadcastTxSync(chainInfo.chainId, signedTx);
}
2022-12-12 06:53:41 +00:00
export async function simulateMsgs(
chainInfo: {
readonly chainId: string;
readonly rest: string;
},
sender: string,
msgs: {
proto: Any[];
},
fee: {
readonly amount: readonly {
readonly amount: string;
readonly denom: string;
}[];
},
memo: string = "",
): Promise<{
gasUsed: number;
}> {
const lcdInstance = Axios.create({
baseURL: chainInfo.rest,
});
const account = await BaseAccount.fetchFromRest(lcdInstance, sender, true);
const unsignedTx = TxRaw.encode({
bodyBytes: TxBody.encode(
TxBody.fromPartial({
// XXX: I don't know why typing error occurs.
// TODO: Solve typing problem.
messages: msgs.proto as any,
memo: memo,
}),
).finish(),
authInfoBytes: AuthInfo.encode({
signerInfos: [
SignerInfo.fromPartial({
modeInfo: {
single: {
mode: SignMode.SIGN_MODE_LEGACY_AMINO_JSON,
},
multi: undefined,
},
sequence: account.getSequence().toString(),
}),
],
fee: Fee.fromPartial({
// XXX: I don't know why typing error occurs.
// TODO: Solve typing problem.
// amount: fee.amount as {
// amount: string;
// denom: string;
// }[],
amount: fee.amount as any,
}),
}).finish(),
// Because of the validation of tx itself, the signature must exist.
// However, since they do not actually verify the signature, it is okay to use any value.
signatures: [new Uint8Array(64)],
}).finish();
const result = await lcdInstance.post("/cosmos/tx/v1beta1/simulate", {
tx_bytes: Buffer.from(unsignedTx).toString("base64"),
});
const gasUsed = parseInt(result.data.gas_info.gas_used);
if (Number.isNaN(gasUsed)) {
throw new Error(`Invalid integer gas: ${result.data.gas_info.gas_used}`);
}
return {
gasUsed,
};
}
2022-12-12 06:19:24 +00:00
export function makeCosmwasmExecMsg(
sender: string,
contractAddress: string,
// eslint-disable-next-line @typescript-eslint/ban-types
obj: object,
funds: { readonly amount: string; readonly denom: string }[],
): {
amino: {
readonly type: string;
readonly value: any;
};
proto: Any;
} {
const amino = {
type: "wasm/MsgExecuteContract",
value: {
sender,
contract: contractAddress,
msg: obj,
funds,
},
};
const proto = {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.encode({
sender: amino.value.sender,
contract: amino.value.contract,
msg: Buffer.from(JSON.stringify(amino.value.msg)),
funds: amino.value.funds,
}).finish(),
};
return {
amino,
proto,
};
}