如何自助恢復多簽資產?
Ownbit 多簽資產的安全性與可用性並不依賴於 Ownbit 伺服器。透過 Ownbit 多簽恢復工具(該工具可在無網路環境下運行,因此即使 Ownbit 伺服器永久宕機,您仍可自助恢復多簽資產),您始終可以相對輕鬆地、隨時隨地完成多簽資產的自助恢復。本文以 2-3 多簽為例,參與方為 A、B、C 三人。
1. 恢復 BTC 多簽資產
a. 第一步,透過區塊瀏覽器查詢該多簽地址上的可用 UTXO。每種幣別都有對應的瀏覽器,例如 BTC 可使用:https://www.blockchain.com/btc/address/3CkvfWhYd6behUYnrA5rMQJoyr6oLGM6Ed 進行查詢。顯示該地址已接收且尚未花費的交易即為可用 UTXO。請分別記錄該交易的 Hash、該地址在輸出欄位中的位置(第一個位置為 0,第二個為 1,依此類推)以及對應金額,即:txHash、index 和 satoshi。
b. 第二步,構建 BTC 的 input/output JSON,示例如下:
{
"inputs": [{
"segwit": "D",
"txHash": "757ef70941ea9170cc918097630a7aa7e4c644e3f8d73a15ff1e98d321bf5b62",
"index": 1,
"satoshis": 13400
}, {
"segwit": "D",
"txHash": "637dfeda367f051d34228badfef09726f0893653fe8baebbe6e150ea68e756f3",
"index": 2,
"satoshis": 456400
}],
"outputs": [{
"amount": 10000,
"address": "bc1q2dzu8fcrr66yq9n8q5u6qhwleslqystahdw923" //receiver address
}, {
"address": "bc1q8dl3vmrqmcs4xpyn884upnedlj2t6uewnfdnp034wqfjghpm5ghqupl9v9", //change address
"amount": 458144
}]
}
以 bc1 開頭的多簽地址需要提供 segwit = D 參數,而以 3 開頭的多簽地址則無需該參數。input/output 的總金額差值即為本筆交易的礦工費。
c. 第三步,準備 Pubkeys。Pubkeys 是所有多簽參與方的公鑰所組成的陣列。您可以在多簽錢包的詳情頁中查看每位參與者的 pubkey,如下圖所示:

將所有參與者的 pubkey 以冒號(:)分隔並串接起來,即為 Pubkeys,示例如下:
02810260c48b2b4e78dd70ae59b7a8a8858cfdba204ea28896ff7a4de20e0c2514:032f7e05649fbf0278076ab1e936998eddc81417131ed55b07d42fa2aea43c25f4
pubkey 也可以透過助記詞推導而得,邏輯如下:
助記詞(mnemonic)
↓
Seed(BIP39)
↓
主密鑰(BIP32)
↓
BIP44 路徑:m/44'/0'/0'/0
↓
擴展公鑰(xpub)
↓
Index 0 的子地址公鑰(m/44'/0'/0'/0/0 的 pubkey)
d. 第四步,分別在 A、B 使用者的手機上開啟:多簽恢復工具(位置:首頁選單 - 區塊鏈工具 - 多簽恢復工具)。多簽恢復工具可在無網路環境下運行,因此即使 Ownbit 伺服器永久宕機,您也能自助恢復多簽資產。如下圖所示:
選擇幣種:BTC,進入 BTC 多簽工具頁面,如下圖所示:
參數詳解如下:
| 參數名稱 | 參數內容詳解 |
|---|---|
| Network | 選擇網路,例如 BTC 的 bc1 地址或 3 開頭地址,LTC、DOGE 等其他鏈也可選擇 |
| Input Output JSON | 第一步中產生的 Input/Output JSON |
| Pubkeys | 第三步中產生的 Pubkeys,以冒號 : 分隔 |
| Min Singers | 最少簽名人數,例如 2-3 多簽中,最少簽名人數為 2 |
BTC 多簽的參數說明。
e. 第五步,填入相應內容,點擊按鈕簽名,即可獲得簽名結果。

提示:根據 input 的筆數,會對應生成相同數量的簽名結果,例如有 2 筆 input 則會生成 2 個簽名。
f. 第六步,此時即可組裝交易,生成 finalHex。
const bitcoin = require('bitcoinjs-lib');
const ecc = require('tiny-secp256k1');
bitcoin.initEccLib(ecc);
// 簽名(已從使用者 A+B 獲得)
const sigs = {
input0: [
'3044022062d213c22e36d9929de57deb1e5b6649757d28779a63f55d635b0124a9f45e7702205a6c8818a9db1580eadfb39286a399e2508a4fb807963aba8597983183b59dcc01',
'3044022041fa6e561e5906e03148b540d47f9bcf77d9b3505fcc15b0b0c51843cb359052022069ffd710386f5f2c968ce5e39715b8a0f3a9667f7486ab6cec08a2afd2bc874901'
],
input1: [
'3045022100f1fe8473f8125313e01615ef0db9b6b76dc5328ad2ace90905ce77948a2dbc3f02203a5fd49dfe3a0317cd73cace688e62de001b496894863560a580f1693db166d201',
'3045022100ee8dea3f7a8c1333c4a0e8a2e59206cb495bd7ecbbfec7122dea32cb90a7f9210220117a280d11d45c71b90047b5a430e2ec0ab18fe24064c911ac61926fee0eb53601'
]
};
// 建立 2-of-2 多簽腳本
const pubkey1 = Buffer.from('02810260c48b2b4e78dd70ae59b7a8a8858cfdba204ea28896ff7a4de20e0c2514', 'hex');
const pubkey2 = Buffer.from('032f7e05649fbf0278076ab1e936998eddc81417131ed55b07d42fa2aea43c25f4', 'hex');
const multisig = bitcoin.payments.p2ms({ m: 2, pubkeys: [pubkey1, pubkey2] });
const p2wsh = bitcoin.payments.p2wsh({ redeem: multisig });
const tx = new bitcoin.Transaction();
// 新增 input 0
tx.addInput(
Buffer.from('757ef70941ea9170cc918097630a7aa7e4c644e3f8d73a15ff1e98d321bf5b62', 'hex').reverse(),
1,
0xffffffff
);
// 新增 input 1
tx.addInput(
Buffer.from('637dfeda367f051d34228badfef09726f0893653fe8baebbe6e150ea68e756f3', 'hex').reverse(),
2,
0xffffffff
);
// 新增 output 0
tx.addOutput(
bitcoin.address.toOutputScript('bc1q2dzu8fcrr66yq9n8q5u6qhwleslqystahdw923', bitcoin.networks.bitcoin),
10000
);
// 新增 output 1
tx.addOutput(
bitcoin.address.toOutputScript('bc1q8dl3vmrqmcs4xpyn884upnedlj2t6uewnfdnp034wqfjghpm5ghqupl9v9', bitcoin.networks.bitcoin),
458144
);
// 為每個 input 設置 witness
[sigs.input0, sigs.input1].forEach((sigList, index) => {
tx.setWitness(index, [
Buffer.alloc(0),
Buffer.from(sigList[0], 'hex'),
Buffer.from(sigList[1], 'hex'),
p2wsh.redeem.output
]);
});
// 輸出交易 hex
const finalHex = tx.toHex();
console.log('Final Transaction Hex:\n', finalHex);
g. 第七步,將 finalHex 廣播至網路,即可花費多簽資產,簡單示例如下:
curl -X POST https://mempool.space/api/tx \
-H "Content-Type: text/plain" \
-d '<hex>'
提示:LTC、DOGE 等其他 UTXO 幣種與 BTC 類似,但組建 input/output JSON 的參數略有不同,以下是 LTC 多簽的 JSON 範例:
{
"inputs": [{
"vout": 1,
"satoshis": 94960,
"txid": "b830d41437f2f1cb3c5303c6626cc9762979bf88bd9bcfbb5a356150e86ef0ad"
}],
"outputs": [{
"address": "LeDXT3evSeAQyCwa2b4ohjS9YxcV9dx7AC",
"amount": 10000
}, {
"address": "MHosP5QAgWrqLGrEvYDRQVDxDohSoUsZA4",
"amount": 79920
}]
}
LTC 等其他 UTXO 幣種使用 txid、vout 參數(而非 BTC 的 txHash、index 參數)。
2. 恢復 ETH/ERC20 多簽資產
a. 第一步,使用者 A 開啟「多簽恢復工具」,選擇幣種:ETH,進入 ETH 多簽工具頁面,如下圖所示:
請根據您要恢復的資產類型填寫每個欄位,詳細說明如下:
| 參數名稱 | 參數內容詳解 |
|---|---|
| Network | 根據您的多簽選擇相應的網路:Ethereum、BNB Smart Chain、Base 等等 |
| MS Address | 您要恢復資產的多簽地址 |
| Destination | 您希望將資產轉入的目標地址 |
| Erc20 Contract Address | 若發送 ETH 請填寫:0x0 |
| Decimals or ErcType | 若發送 ETH 請填寫:18 |
| Amount | 填寫您要發送的金額,例如:1.2(表示發送 1.2 ETH) |
| Spend Nonce | 填寫目前多簽地址的 spend nonce,下面會說明如何獲取 Spend Nonce |
ETH 發送參數說明。
b. 第二步,獲取 Spend Nonce:Spend Nonce 是您的多簽地址累計發送交易的次數(包含失敗的交易)。您可以透過以下兩種方式獲得 Spend Nonce:1. 前往區塊瀏覽器查詢您多簽地址的所有交易並手動計數。2. 通過 RPC 調用獲取,如下所示:
% curl -X POST https://mainnet.infura.io/v3/your_api_key \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"method":"eth_call",
"params":[
{
"to": "0xD0BF699Fb29810bB58b5F6EDb482227b529Dfc5a",
"data": "0xc6a2a9f1"
},
"latest"
],
"id":1
}'
{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000016"}
獲得的 result 是十六進位的,需要轉換為十進位,例如例子中的 0016 轉為:22。
發送 ERC20 時請依下列格式填寫參數:
| 參數名稱 | 參數內容詳解 |
|---|---|
| Network | 根據您的多簽選擇相應網路:Ethereum、BNB Smart Chain、Base 等等 |
| MS Address | 要恢復資產的多簽地址 |
| Destination | 您要將資產發送至哪個地址 |
| Erc20 Contract Address | 對應的 ERC20 合約地址,例如發送 USDT 時填寫:0xdac17f95...3d831ec7 |
| Decimals or ErcType | 每個 ERC20 代幣都有自己的 decimals,例如 USDT 是 6,可在 etherscan.io 查詢 |
| Amount | 填寫欲發送的金額,例如填寫:1.2(代表發送 1.2 USDT) |
| Spend Nonce | 請參見前述說明 |
ERC20 發送參數說明。
發送 NFT(ERC721/ERC1155)時請依下列格式填寫參數:
| 參數名稱 | 參數內容詳解 |
|---|---|
| Network | 根據您的多簽選擇相應網路:Ethereum、BNB Smart Chain、Base 等等 |
| MS Address | 要恢復資產的多簽地址 |
| Destination | 您要將資產發送至哪個地址 |
| Erc20 Contract Address | 填寫 contractAddress_tokenId,中間以「_」分隔,例如:0xfcad2...009f7b_0x0000...00b9e |
| Decimals or ErcType | ERC721 請填寫 1,ERC1155 請填寫 2 |
| Amount | 填寫欲發送的數量,例如填寫:1 |
| Spend Nonce | 請參見前述說明 |
ERC721/ERC1155 發送參數說明。
c. 第三步,填寫完畢後點擊簽名按鈕,即可獲得簽名(r、s、v),如下圖所示:

d. 第四步,在使用者 B 的手機上重複相同流程以取得 B 的簽名結果(r、s、v),請注意,B 所填內容必須與 A 完全一致。此時,您已擁有兩組有效簽名(r、s、v),可開始調用多簽合約以花費資產。
接下來將透過 spend 方法發起多簽花費,方法結構如下:
//destination is the receiver address for sending ether, or an ERC20 contract address for sending ERC20.
//value is the wei to be trasnferred to the destination, when sending ERC20 value should be 0.
//data for transfer ether: 0x
//data for transfer erc20 example: 0xa9059cbb000000000000000000000000ac6342a7efb995d63cc91db49f6023e95873d25000000000000000000000000000000000000000000000000000000000000003e8
//data for transfer erc721 example: 0x42842e0e00000000000000000000000097b65ad59c8c96f2dd786751e6279a1a6d34a4810000000000000000000000006cb33e7179860d24635c66850f1f6a5d4f8eee6d0000000000000000000000000000000000000000000000000000000000042134
//data can contain any data to be executed.
function spend(address destination, uint256 value, uint8[] memory vs, bytes32[] memory rs, bytes32[] memory ss, bytes calldata data) external {
require(destination != address(this), "Not allow sending to yourself");
require(_validSignature(destination, value, vs, rs, ss, data), "invalid signatures");
spendNonce = spendNonce + 1;
//transfer tokens from this contract to the destination address
(bool sent,) = destination.call{value: value}(data);
if (sent) {
emit Spent(destination, value);
}
}
e. 第五步,根據已經擁有的兩組(r、s、v),您可以構建出 spend 方法所需的 calldata,以下是示例程式碼:
const Web3 = require("web3");
const web3 = new Web3();
const abi = [{
"name": "spend",
"type": "function",
"inputs": [
{ "name": "destination", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "vs", "type": "uint8[]" },
{ "name": "rs", "type": "bytes32[]" },
{ "name": "ss", "type": "bytes32[]" },
{ "name": "data", "type": "bytes" }
]
}];
const contract = new web3.eth.Contract(abi);
const calldata = contract.methods.spend(
"0xdac17f958d2ee523a2206206994597c13d831ec7", //destination
"0", //ether-value
[1, 1], //vs
[
"0xaca5025bd6d2fcd3308aa3589ea7aa419ef67914275ce7be80558584232554f9",
"0x9028a9e0fee249f874d747c0c082b1b9cdde12505cea47a2e8cc19b567c3fea8" //rs
],
[
"0x3a12af0ee1354b709b5312db6976150fc7ebc74f6f94404005593290c6322e26",
"0x310e7534a5a0a7e71566c14462c10cf2022c966a390d0edd144895c0c032d599" //ss
],
"0xa9059cbb000000000000000000000000e36b7026178f17a52f85b47280cc069840c8fa840000000000000000000000000000000000000000000000000000000077359400" //inner-calldata
).encodeABI();
console.log(calldata);
發送 ERC20 spend 的 calldata 構建
發送 ETH 的 spend calldata 構建方式類似,只需將 ether-value 改為相應的 value,並將 inner-calldata 設為 0x0 即可。
f. 第六步,生成 calldata 後,您可以發送任意一筆 ETH 交易,將 calldata 設為該交易的 hexdata,即可廣播交易,使多簽花費生效。
註:其他 EVM 網路(如:BSC、BASE)多簽恢復方式與 ETH 完全一致。
3. 恢復 TRX/TRC20 多簽資產
a. 第一步,生成要發送交易的 txJson,以下為程式碼範例:
const TronWeb = require('tronweb');
const tronWeb = new TronWeb({
fullHost: 'https://api.trongrid.io'
});
(async () => {
const from = 'TXeDpnE...';
const to = 'TQwbMLM...';
const contract = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'; // USDT TRC20
const amount = 2000000000; // 2000 USDT (6 decimals)
const feeLimit = 15000000;
const functionSelector = 'transfer(address,uint256)';
const parameter = [
{ type: 'address', value: to },
{ type: 'uint256', value: amount }
];
const options = {
feeLimit,
callValue: 0,
shouldPollResponse: false
};
const tx = await tronWeb.transactionBuilder.triggerSmartContract(
contract,
functionSelector,
options,
parameter,
from
);
const txJson = JSON.stringify(tx.transaction);
console.log(txJson);
})();
發送 TRC20 交易的 txJson 生成
const TronWeb = require('tronweb');
const tronWeb = new TronWeb({
fullHost: 'https://api.trongrid.io'
});
(async () => {
const from = 'TXeDpnE...';
const to = 'TQwbMLM...';
const amount = 2_000_000; // 2 TRX
const tx = await tronWeb.transactionBuilder.sendTrx(to, amount, from);
const txJson = JSON.stringify(tx);
console.log(txJson);
})();
發送 TRX 交易的 txJson 生成
提示:預設生成的 txJson 的 expiration 時間較短,建議適當延長 expiration 時間,以避免在廣播交易時已失效。
b. 第二步,分別在 A、B 使用者的手機上打開:多簽恢復工具 - TRX,輸入 txJson,並點擊按鈕簽名,獲得各自的簽名結果。如下圖所示:

c. 第三步,將獲得的簽名加入至 txJson 的 signature 欄位中,即可生成完整的 txJson,用於廣播至 TRON 網路。如下示例所示:
{
"txID": "4c214a020101e1775dd04036f2040caee7d78d5c2bbabf7fdc0318ddc2d9899f",
"raw_data": {
"contract": [...],
"ref_block_bytes": "...",
...
},
"raw_data_hex": "...",
"visible": false,
"signature": [
"ad7373b902f185f9a33c1c13b248f4bf5be003dff9d907fa4683aa54ad73f2c3186b57b32f03e477d909bb286d38993896e8a186c5c39c7e5d9f900eb3cabc271c",
"cf310bb59e38e7edd00d1e6ab6ac216b3ab80ba6ce2db140456744cce6b7ed610d599330669ca98ddf05e4211d02c54ff0e033cd28f82680ba8d50b9baf03b831b"
]
}
4. 第四步,將完整的 txJson 廣播至 TRON 網路,即可使多簽花費生效。
4. 恢复 SOL/SPL 多签资产
a. 通过多签助记词生成各参与方的 SOL 地址和签名所需私钥(生成路径:m/44'/501'/0'/0')。
b. 在 多签钱包 -> 多签页面 -> 菜单“多签详情”中获得 /multisig data/ 值(这是在调用合约过程中要用到的 multisig data account。
c. 当你同时拥有多签地址(multisigPda)、multisig data account,以及各参与方的 SOL 地址和签名私钥,就可以根据 Ownbit 多签源码进行调用以花费多签资产,多签源代码地址:https://github.com/bitbill/ownbit-multisig-contracts。
d. 一个完整的调用流程:先由任意参与方发送调用 create_transaction 指令,然后再由其他参与方调用 approve 指令。当达到足够人数时,再由任意一方发送调用 execute_transaction 指令。