跳到主要内容

多重签名质押交易

多重签名质押交易允许多个签名者共同管理质押操作,包括委托、取消委托、重新委托和提取奖励。本文档介绍如何使用多重签名账户执行质押相关的操作。

基本概念

质押操作类型

  • 委托 (Delegate): 将 ATOM 委托给验证者
  • 取消委托 (Undelegate): 取消对验证者的委托
  • 重新委托 (Redelegate): 将委托从一个验证者转移到另一个
  • 提取奖励 (Withdraw Rewards): 提取质押奖励

环境配置

设置助记词

# .env 文件
STAKING_MNEMONIC="your 12 or 24 word mnemonic phrase"

初始化环境

require('dotenv').config();

const mnemonic = process.env.STAKING_MNEMONIC;
const validatorAddress = 'cosmosvaloper17qukqafj25wn8gdjwzlmlcm9xl00rxxe2lxujd';
const threshold = 2;
const prefix = 'cosmos';
const rpcEndpoint = 'https://rpc.testnet.cosmos.network:443';
const client = await StargateClient.connect(rpcEndpoint);

多重签名账户查询

查询账户信息

const multisigAccountAddress = 'cosmos14txtjx9yx8zjxhu6s2jpa8xsx6auz3rhq7zhus';

// 查询多重签名账户
const accountOnChain = await client.getAccount(multisigAccountAddress);
assert(accountOnChain, 'Account does not exist on chain');

console.log('Account address:', accountOnChain.address);
console.log('Account pubkey:', accountOnChain.pubkey);
console.log('Account number:', accountOnChain.accountNumber);
console.log('Account sequence:', accountOnChain.sequence);

// 解码公钥信息
console.log('Decoded pubkeys:', accountOnChain.pubkey.value.pubkeys.map((key) =>
toHex(encodeAminoPubkey(key))
));

质押消息构建

委托消息

const { MsgDelegate } = require('cosmjs-types/cosmos/staking/v1beta1/tx');

const amount = {
denom: 'uphoton',
amount: '1234567',
};

const delegateMsg = {
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: MsgDelegate.fromPartial({
delegatorAddress: multisigAccountAddress,
validatorAddress: validatorAddress,
amount: amount,
}),
};

取消委托消息

const { MsgUndelegate } = require('cosmjs-types/cosmos/staking/v1beta1/tx');

const unDelegateMsg = {
typeUrl: "/cosmos.staking.v1beta1.MsgUndelegate",
value: MsgUndelegate.fromPartial({
delegatorAddress: multisigAccountAddress,
validatorAddress: validatorAddress,
amount: amount,
}),
};

重新委托消息

const { MsgBeginRedelegate } = require('cosmjs-types/cosmos/staking/v1beta1/tx');

const redelegateMsg = {
typeUrl: "/cosmos.staking.v1beta1.MsgBeginRedelegate",
value: MsgBeginRedelegate.fromPartial({
validatorSrcAddress: validatorAddress,
validatorDstAddress: validatorAddress, // 可以是不同的验证者
delegatorAddress: multisigAccountAddress,
amount: amount,
}),
};

提取奖励消息

const { MsgWithdrawDelegatorReward } = require('cosmjs-types/cosmos/distribution/v1beta1/tx');

const withdrawMsg = {
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward',
value: MsgWithdrawDelegatorReward.fromPartial({
validatorAddress: validatorAddress,
delegatorAddress: multisigAccountAddress,
}),
};

费用和参数设置

设置费用

const gasLimit = 200000;
const fee = {
amount: coins(2000, 'uphoton'),
gas: gasLimit.toString(),
};

const memo = 'Use your power wisely';

签名指令创建

创建签名指令

const signingInstruction = {
accountNumber: accountOnChain.accountNumber,
sequence: accountOnChain.sequence,
chainId: await client.getChainId(),
msgs: [msg], // 选择上述消息类型之一
fee: fee,
memo: memo,
};

并行签名收集

收集多个签名

const [
[pubkey0, signature0, bodyBytes],
[pubkey1, signature1],
[pubkey2, signature2],
[pubkey3, signature3],
[pubkey4, signature4]
] = await Promise.all(
[0, 1, 2, 3, 4].map(async (i) => {
// 签名环境
const wallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {
hdPaths: [makeCosmoshubPath(i)],
});
const [account] = await wallet.getAccounts();
const pubkey = encodeSecp256k1Pubkey(account.pubkey);
const address = account.address;

const signingClient = await SigningStargateClient.offline(wallet);
const signerData = {
accountNumber: signingInstruction.accountNumber,
sequence: signingInstruction.sequence,
chainId: signingInstruction.chainId,
};

const { bodyBytes: bb, signatures } = await signingClient.sign(
address,
signingInstruction.msgs,
signingInstruction.fee,
signingInstruction.memo,
signerData,
);

return [pubkey, signatures[0], bb];
})
);

console.log('Public key 0:', pubkey0);
console.log('Public key 1:', pubkey1);
console.log('Public key 2:', pubkey2);

多重签名交易组装

创建多重签名公钥

const multisigPubkey = createMultisigThresholdPubkey(
[pubkey0, pubkey1, pubkey2, pubkey3, pubkey4],
threshold
);

// 验证地址一致性
assert.strictEqual(
multisigAccountAddress,
pubkeyToAddress(multisigPubkey, prefix),
'should be equal'
);

生成地址映射

const address0 = pubkeyToAddress(pubkey0, prefix);
const address1 = pubkeyToAddress(pubkey1, prefix);
const address2 = pubkeyToAddress(pubkey2, prefix);
const address3 = pubkeyToAddress(pubkey3, prefix);
const address4 = pubkeyToAddress(pubkey4, prefix);

创建多重签名交易

const signedTx = makeMultisignedTx(
multisigPubkey,
signingInstruction.sequence,
signingInstruction.fee,
bodyBytes,
new Map([
[address0, signature0],
[address1, signature1],
// [address2, signature2], // 可选签名
// [address3, signature3], // 可选签名
// [address4, signature4], // 可选签名
])
);

交易广播

广播质押交易

// 确保签名有效
const broadcaster = await StargateClient.connect(rpcEndpoint);
const result = await broadcaster.broadcastTx(
Uint8Array.from(TxRaw.encode(signedTx).finish())
);

console.log('Broadcast result:', result);
assertIsBroadcastTxSuccess(result);

质押操作示例

执行委托操作

async function executeDelegate() {
// 使用委托消息
const msg = delegateMsg;

// 创建签名指令
const signingInstruction = {
accountNumber: accountOnChain.accountNumber,
sequence: accountOnChain.sequence,
chainId: await client.getChainId(),
msgs: [msg],
fee: fee,
memo: memo,
};

// 执行签名和广播流程
// ... (上述签名和广播代码)
}

执行取消委托操作

async function executeUndelegate() {
// 使用取消委托消息
const msg = unDelegateMsg;

// 创建签名指令
const signingInstruction = {
accountNumber: accountOnChain.accountNumber,
sequence: accountOnChain.sequence,
chainId: await client.getChainId(),
msgs: [msg],
fee: fee,
memo: memo,
};

// 执行签名和广播流程
// ... (上述签名和广播代码)
}

完整示例

查看 examples/cosmos/multisig/staking.js 文件获取完整的多重签名质押交易示例代码。

注意事项

  • 质押操作需要足够的余额支付费用
  • 取消委托后需要等待解绑期
  • 重新委托可以避免解绑期
  • 定期提取质押奖励
  • 选择可靠的验证者
  • 在生产环境中使用硬件钱包
  • 定期验证多重签名配置
  • 备份所有参与者的助记词