cosmjs
cosmjs copied to clipboard
Q: How to simulate MultisignedTx ?
I'm trying to simulate the multi-signature to get the gas fee.I changed signerInfos
to refer to the multisignature and simulate.
const request = SimulateRequest.fromPartial({
tx: Tx.fromPartial({
authInfo: AuthInfo.fromPartial({
fee: Fee.fromPartial({}),
signerInfos: [
{
publicKey: multisigPubkey,
sequence: long.fromNumber(signingInstruction.sequence, true),
modeInfo: {
multi: {
bitarray: makeCompactBitArray([false, false, true, true, false]),
modeInfos: [0, 1, 2, 3, 4].map((_) => ({ single: { mode: signing.SignMode.SIGN_MODE_LEGACY_AMINO_JSON } })),
},
},
},
],
}),
body: TxBody.fromPartial({
messages: signingInstruction.msgs,
memo: signingInstruction.memo,
}),
signatures: [new Uint8Array()],
}),
txBytes: undefined,
});
console.log('msgs : ', signingInstruction.msgs);
const response = await client.forceGetQueryClient().tx.request(request);
console.log(' response : ', response);
response : {
key: Uint8Array(0) [],
value: Uint8Array(0) [],
proof: undefined,
height: 365607,
code: 18,
index: 0,
log: 'unable to resolve type URL : tx parse error: invalid request'
}
Seems like messages: signingInstruction.msgs,
is wrong. TxBody.messages
is an array of Any
, so the messages need to go through the Registry
for encoding.
Thanks for your help, here it's the signingInstruction.msgs. works fine on single address transactions
[
{
"typeUrl": "/cosmos.bank.v1beta1.MsgSend",
"value": {
"fromAddress": "cosmos14txtjx9yx8zjxhu6s2jpa8xsx6auz3rhq7zhus", // multi address
"toAddress": "cosmos19rvl6ja9h0erq9dc2xxfdzypc739ej8k5esnhg",
"amount": [
{
"amount": "1234",
"denom": "uphoton"
}
]
}
}
]
How do you construct multisigPubkey
? Maybe this is where the type URL of the inner Any is an issue.
const { createMultisigThresholdPubkey, pubkeyToAddress, encodeSecp256k1Pubkey } = require('@cosmjs/amino');
const { fromHex } = require('@cosmjs/encoding');
async function main() {
const threshold = 2;
const prefix = "cosmos";
var pubkeys = [
"02f4147da97162a214dbe25828ee4c4acc4dc721cd0c15b2761b43ed0292ed82b5",
"0377155e520059d3b85c6afc5c617b7eb519afadd0360f1ef03aff3f7e3f5438dd",
"02f44bce3eecd274e7aa24ec975388d12905dfc670a99b16e1d968e6ab5f69b266"
].map(key => encodeSecp256k1Pubkey(fromHex(key)));
const multisigThresholdPubkey = createMultisigThresholdPubkey(pubkeys, threshold, true);
const multisigAddress = pubkeyToAddress(multisigThresholdPubkey, prefix);
console.log('multisigThresholdPubkey : ', JSON.stringify(multisigThresholdPubkey));
console.log('multisigAddress : ', multisigAddress);
}
main().catch(console.error);
output:
multisigThresholdPubkey : {"type":"tendermint/PubKeyMultisigThreshold","value":{"threshold":"2","pubkeys":[{"type":"tendermint/PubKeySecp256k1","value":"AvQUfalxYqIU2+JYKO5MSsxNxyHNDBWydhtD7QKS7YK1"},{"type":"tendermint/PubKeySecp256k1","value":"A3cVXlIAWdO4XGr8XGF7frUZr63QNg8e8Dr/P34/VDjd"},{"type":"tendermint/PubKeySecp256k1","value":"AvRLzj7s0nTnqiTsl1OI0SkF38ZwqZsW4dlo5qtfabJm"}]}}
multisigAddress : cosmos1mca888pm39ld9zjnaagrjcjmtm27w0tzzaydct
SDK Version: 0.29.5
Call the client.forceGetQueryClient().tx.simulate
Error: Query failed with (6): rpc error: code = Unknown desc = invalid from address: empty address string is not allowed: invalid address [/go/pkg/mod/cosmossdk.io/[email protected]/errors.go:153] With gas wanted: '0' and gas used: '6663' : unknown request
at QueryClient.queryAbci (~/workpalce/blockchain-notes/node_modules/.pnpm/@[email protected]/node_modules/@cosmjs/stargate/build/queryclient/queryclient.js:135:19)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Object.request (~/workpalce/blockchain-notes/node_modules/.pnpm/@[email protected]/node_modules/@cosmjs/stargate/build/queryclient/utils.js:35:30)
at async Object.simulate (~/workpalce/blockchain-notes/node_modules/.pnpm/@[email protected]/node_modules/@cosmjs/stargate/build/modules/tx/queries.js:48:34)
at async main (~/workpalce/blockchain-notes/cosmos/tx/multisig/xxx.js:137:28
require('dotenv').config();
const { strict: assert } = require('assert');
const { toHex } = require('@cosmjs/encoding');
const { coins, createMultisigThresholdPubkey, encodeSecp256k1Pubkey, pubkeyToAddress, makeCosmoshubPath } = require('@cosmjs/amino');
const { assertIsDeliverTxSuccess, SigningStargateClient, StargateClient, makeMultisignedTx } = require('@cosmjs/stargate');
const { Secp256k1HdWallet } = require('@cosmjs/amino');
const { TxRaw } = require("cosmjs-types/cosmos/tx/v1beta1/tx");
async function getMnemonicPubKeyAndAddress(mnemonic, prefix) {
const wallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const [account] = await wallet.getAccounts();
const secp256k1PubKey = encodeSecp256k1Pubkey(account.pubkey);
const address = pubkeyToAddress(secp256k1PubKey, prefix);
return { wallet, secp256k1PubKey, pubkey: toHex(account.pubkey), address };
}
async function signInstruction(mnemonic, instruction) {
const wallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const [account] = await wallet.getAccounts();
const address = account.address;
const offlineSigningClient = await SigningStargateClient.offline(wallet);
const pubkey = encodeSecp256k1Pubkey(account.pubkey);
const signerData = {
accountNumber: instruction.accountNumber,
sequence: instruction.sequence,
chainId: instruction.chainId,
};
const { bodyBytes: bb, signatures } = await offlineSigningClient.sign(
address,
instruction.msgs,
instruction.fee,
instruction.memo,
signerData,
);
return [pubkey, signatures[0], bb];
}
// https://github.com/cosmos/cosmjs/blob/c192fc9b95ef97e4afbf7f5b94f8e8194ae428a6/packages/stargate/src/multisignature.spec.ts#L175
async function main() {
const rpcEndpoint = '';
const threshold = 2;
const prefix = 'iaa';
const denom = 'uiris';
const alice = '';
const bob = '';
const carol = '';
const mnemonics = [alice, bob, carol];
const client = await StargateClient.connect(rpcEndpoint);
const multisigAccountAddress = 'iaa18y4kun6wupgly9kja8awhnqpjhxt6hlj348923';
const receipt = 'iaa18y4kun6wupgly9kja8awhnqpjhxt6hlj348923';
const keys = await Promise.all(mnemonics.map((mnemonic) => getMnemonicPubKeyAndAddress(mnemonic, prefix)));
const secp256k1PubKeys = keys.map((item) => item.secp256k1PubKey);
var multisigPubkey = createMultisigThresholdPubkey(secp256k1PubKeys, threshold, true);
const multisigAddress = pubkeyToAddress(multisigPubkey, prefix);
console.log('multisigAddress : ', multisigAddress);
// account
const accountOnChain = await client.getAccount(multisigAddress);
assert(accountOnChain, 'Account does not exist on chain');
console.log('accountOnChain : ', accountOnChain);
// balance
const balance = await client.getBalance(multisigAddress, 'uiris');
console.log('balance', balance);
const msgSend = {
fromAddress: multisigAddress,
toAddress: receipt,
amount: coins(2000, denom),
};
const msg = {
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
value: msgSend,
};
const gasLimit = 200000;
const fee = {
amount: coins(2000, denom),
gas: gasLimit.toString(),
};
const chainId = await client.getChainId();
const memo = 'multisig memo';
// On the composer's machine signing instructions are created.
const signingInstruction = {
accountNumber: accountOnChain.accountNumber,
sequence: accountOnChain.sequence,
chainId,
msgs: [msg],
fee,
memo,
};
const [
[pubkey0, signature0, bodyBytes], //
[pubkey1, signature1], //
[pubkey2, signature2] //
] = await Promise.all(
[AARON, PHCC, PENG].map(async (mnemonic) => signInstruction(mnemonic, signingInstruction, rpcEndpoint)));
const address0 = pubkeyToAddress(pubkey0, prefix);
const address1 = pubkeyToAddress(pubkey1, prefix);
const address2 = pubkeyToAddress(pubkey2, prefix);
var multisigPubkey = createMultisigThresholdPubkey([pubkey0, pubkey1, pubkey2], threshold, true);
assert.strictEqual(multisigAccountAddress, pubkeyToAddress(multisigPubkey, prefix), 'should be equal');
const signedTx = makeMultisignedTx(
multisigPubkey,
signingInstruction.sequence,
signingInstruction.fee,
bodyBytes,
new Map([
[address0, signature0],
[address1, signature1],
// [address2, signature2],
])
);
// const queryClient = client.forceGetQueryClient();
// const simulateResponse = await queryClient.tx.simulate(signingInstruction.msgs, signingInstruction.memo, multisigPubkey, signingInstruction.sequence);
// console.log("simulateResponse : ", simulateResponse);
// return;
// Error: Query failed with (6): rpc error: code = Unknown desc = invalid from address: empty address string is not allowed: invalid address [/go/pkg/mod/cosmossdk.io/[email protected]/errors.go:153] With gas wanted: '0' and gas used: '6663' : unknown request
const tx = TxRaw.encode(signedTx).finish();
const result = await client.broadcastTx(tx);
assertIsDeliverTxSuccess(result);
const { transactionHash } = result;
console.log('tx : ', `https://www.mintscan.io/iris/txs/${transactionHash}`);
}
main().catch(console.error);