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);