cosmjs
cosmjs copied to clipboard
Failed to execute multisig admin transaction using custom type URL
Hi,
I'm trying to find a way to execute an admin transaction(custom transaction) using a custom type URL, you can find the logic I'm using to sign and broadcast an adimn transaction with a single account, which works fine. When I try to broadcast using the multisig functionality I am getting the following error:
Broadcasting transaction failed with code 4 (codespace: sdk). Log: signature verification failed; please verify account number (42), sequence (10) and chain-id (network-testnet-private): unauthorized
Can you spot anything obvious, wrong with the signAdminTransaction & BroadcastTx methods?
-The below implemetation works fine, however it is not using the multisig sign logic:
// Custom transaction
const handleAdminTransaction = async () => {
const proposalTypePath =
"/custom.amino.MsgAdminSpendCommunityPool";
const myAddress = "address1";
const receiverAddress = "address2";
const myRegistry = new Registry(defaultRegistryTypes);
myRegistry.register(
proposalTypePath,
MsgAdminSpendCommunityPool as GeneratedType
);
window.keplr.enable(process.env.REACT_APP_CHAIN_ID);
const offlineSigner = window.getOfflineSigner(
process.env.REACT_APP_CHAIN_ID
);
window.keplr.defaultOptions = {
sign: {
preferNoSetMemo: true,
preferNoSetFee: true,
disableBalanceCheck: true,
},
};
const client = await SigningStargateClient.connectWithSigner(
process.env.REACT_APP_RPC!,
offlineSigner,
{ registry: myRegistry }
);
const message = {
typeUrl: proposalTypePath,
value: MsgAdminSpendCommunityPool.fromPartial({
initiator: myAddress,
toAddress: receiverAddress,
coins: [{ denom: "acudos", amount: "10000000000000000000" }],
}),
};
const fee = {
amount: [
{
denom: "denom",
amount: "120000",
},
],
gas: "200000",
};
const response = await client.signAndBroadcast(
myAddress,
[message],
fee,
"Admin Spend Community Pool"
);
};
- The below implementaition throws the following error when trying to broadcast:
Broadcasting transaction failed with code 4 (codespace: sdk). Log: signature verification failed; please verify account number (42), sequence (10) and chain-id (network-testnet-private): unauthorized
const signAdminTransaction = async (id: number) => {
const transaction = transactionData.filter((td: any) => td.id === id);
const signers = transaction[0].hasSigned.filter((signer: any) =>
multiSigAccount.owners.includes(signer)
);
if (
signers.includes(keplrAccount) ||
!multiSigAccount.owners.includes(keplrAccount)
) {
return;
}
const proposalTypePath =
"/custom.amino.MsgAdminSpendCommunityPool";
const myRegistry = new Registry(defaultRegistryTypes);
myRegistry.register(
proposalTypePath,
MsgAdminSpendCommunityPool as GeneratedType
);
const offlineSigner = window.getOfflineSigner(
process.env.REACT_APP_CHAIN_ID
);
window.keplr.defaultOptions = {
sign: {
preferNoSetMemo: true,
preferNoSetFee: true,
disableBalanceCheck: true,
},
};
const signingClient = await SigningStargateClient.connectWithSigner(
process.env.REACT_APP_RPC!,
offlineSigner,
{ registry: myRegistry }
);
const signerData = {
accountNumber: transaction[0].accountNumber,
sequence: transaction[0].sequence,
chainId: process.env.REACT_APP_CHAIN_ID!,
};
await window.keplr.enable(process.env.REACT_APP_CHAIN_ID);
const walletAccount = await window.keplr.getKey(
process.env.REACT_APP_CHAIN_ID
);
const msgSend = MsgAdminSpendCommunityPool.fromPartial({
initiator: transaction[0].msgs[0].value.fromAddress,
toAddress: transaction[0].msgs[0].value.toAddress,
coins: [
{
denom: CosmosNetworkConfig.CURRENCY_DENOM,
amount: transaction[0].msgs[0].value.amount[0].amount,
},
],
});
const msg = [
{
typeUrl: proposalTypePath,
value: msgSend,
},
];
const msgFee = {
amount: [
{
denom: CosmosNetworkConfig.CURRENCY_DENOM,
amount: "0",
},
],
gas: "180000",
};
const msgMemo = "Admin Spend Community Pool";
const { signatures, bodyBytes } = await signingClient.sign(
walletAccount.bech32Address,
msg,
msgFee,
msgMemo,
signerData
);
const bases64EncodedSignature = encode(signatures[0]);
const bases64EncodedBodyBytes = encode(bodyBytes);
const signature = {
transactionId: transaction[0].id,
bodyBytes: bases64EncodedBodyBytes,
signature: bases64EncodedSignature,
address: walletAccount.bech32Address,
};
await createSignature(signature.transactionId, signature);
const { data } = await getTransactions(multiSigAccount.address);
const newTd = data.map((td: any) => {
return {
...td,
hasSigned:
td.id === id
? [...td.hasSigned, walletAccount.bech32Address]
: td.hasSigned,
sequence: td.id === id ? signerData.sequence : td.sequence,
};
});
setTransactionData(newTd);
const updatedTransaction = newTd.filter((td: any) => td.id === id);
await updateTransaction(id, updatedTransaction[0]);
};
const broadcastTx = async (id: number) => {
try {
const { data: response } = await getMultisig(multiSigAccount.address);
const { pubkey } = response;
const { data: sigs } = await getSignature(id);
const multisigBalance = await getMultisigBalance(multiSigAccount.address);
const signatures = new Map();
const [transactionToBroadcast] = transactionData.filter(
(trans: any) => trans.id === id
);
const [transactionToReject] = transactionData.filter(
(trans: any) =>
trans.sequence === transactionToBroadcast.sequence && trans.id !== id
);
const transactionAmount = new BigNumber(
transactionToBroadcast.msgs[0].value.amount[0].amount
)
.dividedBy(CosmosNetworkConfig.CURRENCY_1_CUDO)
.toString(10);
const bodyBytes = decode(sigs[0].bodyBytes);
const signedTx = makeMultisignedTx(
{
type: pubkey.type,
value: {
threshold: pubkey.value.threshold,
pubkeys: pubkey.value.pubkeys,
},
},
transactionToBroadcast.sequence,
transactionToBroadcast.fee,
bodyBytes,
signatures
);
const broadcaster = await StargateClient.connect(
process.env.REACT_APP_RPC!
);
console.log("broadcasting...", broadcaster);
const result = await broadcaster.broadcastTx(
Uint8Array.from(TxRaw.encode(signedTx).finish())
);
const updateWalletBalance = await getMultisigBalance(
multiSigAccount.address
);
setMultiSigAccount({
...multiSigAccount,
balance: updateWalletBalance,
});
const updatedTransactionToBroadcast = {
...transactionToBroadcast,
hash: result.transactionHash,
executionTime: new Date(),
status: TransactionStatus.SUCCESSFUL,
};
await updateTransaction(id, updatedTransactionToBroadcast);
if (transactionToReject) {
const updatedTransactionToReject = {
...transactionToReject,
status: TransactionStatus.REJECTED,
};
await updateTransaction(
updatedTransactionToReject.id,
updatedTransactionToReject
);
}
const { data } = await getTransactions(multiSigAccount.address);
setTransactionData(data);
setNotificationMessage("Success!");
} else {
setErrorMessage("Account balance is insufficient");
}
} catch (e) {
setErrorMessage(e.message);
const [transactionToBroadcast] = transactionData.filter(
(trans: any) => trans.id === id
);
};
Thank you!
How do you define MsgAdminSpendCommunityPool? I didn't see your specific import type