Aptos 開發心得分享

多簽是甚麼:

一個Account 由多個使用者進行管理,而每個使用者都至少握有一把private key。

 

建立多簽帳戶:

1.新增多簽帳戶時就要確定參與多簽的人數。

2.以及要幾把私鑰才可以交易成功。

3.產生Private key and Public Key -> 產生 Auth Key -> Address
 


 

情況範例:

1.每把鑰匙的權重一樣

假設有A,B,C,D,E五把鑰匙,他們共管理一個帳戶,只需要三把就可以通過交易。

 

2.A的權重比別較多,其餘每把鑰匙的權重一樣

假設這個多簽帳戶是由A,A,B,C,D,這五把鑰匙組成的,只需要三把就可以通過交易。

(但因為A這把鑰匙的權重大,只需要他簽一次,隨便其他人在簽一次就可)

 

應用在哪:

1.多裝置驗證

2.鑰匙備份

3.管理帳務

4.管理NFT的發放

 

程式開發流程:

 

沒有多簽帳戶:

1.新建多簽帳戶,確定多少人要參與加簽

2.要幾把鑰匙才算通過 (確定需要多少人簽核這筆交易)

3.1 建立多簽帳戶

3.2 進行faucet

4.紀錄多簽賬戶的Address、PublicKey 

(以後在發起交易、簽核交易會用到)

 

發起交易:

1.輸入要參與交易的帳戶的地址和privateKey

2.輸入目標地址

3.輸入金額

4.加簽交易

5.發起交易

 

建立多簽帳戶:

步驟1:引入套件、物件

import { AptosClient, AptosAccount, FaucetClient, BCS, TransactionBuilderMultiEd25519, TxnBuilderTypes } from "aptos";

 

步驟2:設定連接節點、鏈的資訊

const NODE_URL = process.env.APTOS_NODE_URL || "https://fullnode.devnet.aptoslabs.com";
const FAUCET_URL = process.env.APTOS_FAUCET_URL || "https://faucet.devnet.aptoslabs.com";

 

步驟3:初始化本地端(客戶端)連結鏈的物件,利用這物件跟鏈取得資料

const client = new AptosClient(NODE_URL);

 

步驟4:初始化水龍頭物件

const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL);

 

步驟5:創建要參與加簽的帳戶

const account1 = new AptosAccount();
const account2 = new AptosAccount();
const account3 = new AptosAccount();

筆記:

如果是現有帳戶,可以輸入privatekey、address

 

步驟6:建立多簽帳戶、設定多少private才能通過交易,這邊會產出public key

const multiSigPublicKey = new TxnBuilderTypes.MultiEd25519PublicKey(
  [
    new TxnBuilderTypes.Ed25519PublicKey(account1.signingKey.publicKey),
    new TxnBuilderTypes.Ed25519PublicKey(account2.signingKey.publicKey),
    new TxnBuilderTypes.Ed25519PublicKey(account3.signingKey.publicKey),
  ],
  // Threshold
  2,
);

 

步驟7:公鑰產出Authentication key

const authKey = TxnBuilderTypes.AuthenticationKey.fromMultiEd25519PublicKey(multiSigPublicKey);

 

步驟8:導出帳戶Address

const mutisigAccountAddress = authKey.derivedAddress();

 

步驟9:水管匯入APT Token

await faucetClient.fundAccount(mutisigAccountAddress, 100_000_000);

筆記:

如果Account 要記錄在鏈上,就必須水管匯入APT 0個也可以

 

步驟10:查看帳戶資源

let resources = await client.getAccountResources(mutisigAccountAddress);
let accountResource = resources.find((r) => r.type === aptosCoinStore);
let balance = parseInt((accountResource?.data as any).coin.value);
assert(balance === 100_000_000);
console.log(`multisig account coins: ${balance}. Should be 100000000!`);

 

發起多簽交易:

步驟1:建立傳送的帳號地址

const account4 = new AptosAccount();
// Creates a receiver account and fund the account with 0 AptosCoin
await faucetClient.fundAccount(account4.address(), 0);
resources = await client.getAccountResources(account4.address());
accountResource = resources.find((r) => r.type === aptosCoinStore);
balance = parseInt((accountResource?.data as any).coin.value);
assert(balance === 0);
console.log(`account4 coins: ${balance}. Should be 0!`);

 

步驟2:取得要交易的Token,交易的Module

const token = new TxnBuilderTypes.TypeTagStruct(TxnBuilderTypes.StructTag.fromString("0x1::aptos_coin::AptosCoin"));
const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
// Fully qualified module name, `AccountAddress::ModuleName`
"0x1::coin",
// Module function
"transfer",
// The coin type to transfer
[token],
// Arguments for function `transfer`: receiver account address and amount to transfer
[BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(account4.address())), BCS.bcsSerializeUint64(123)],
),
);

 

步驟3:取得帳戶交易到哪一個編號和在哪一個鏈進行交易

const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
client.getAccount(mutisigAccountAddress),
client.getChainId(),
]);

 

步驟4:組成交易資料

const rawTxn = new TxnBuilderTypes.RawTransaction(
// Transaction sender account address
TxnBuilderTypes.AccountAddress.fromHex(mutisigAccountAddress),
BigInt(sequenceNumber),
entryFunctionPayload,
// Max gas unit to spend
BigInt(10000),
// Gas price per unit
BigInt(100),
// Expiration timestamp. Transaction is discarded if it is not executed within 10 seconds from now.
BigInt(Math.floor(Date.now() / 1000) + 10),
new TxnBuilderTypes.ChainId(chainId),
);

 

步驟5:新增物件,設定要用那些Private Key進行加簽

const txnBuilder = new TransactionBuilderMultiEd25519((signingMessage: TxnBuilderTypes.SigningMessage) => {
const sigHexStr1 = account1.signBuffer(signingMessage);
const sigHexStr3 = account3.signBuffer(signingMessage);
const bitmap = TxnBuilderTypes.MultiEd25519Signature.createBitmap([0, 2]);
const muliEd25519Sig = new TxnBuilderTypes.MultiEd25519Signature(
[
new TxnBuilderTypes.Ed25519Signature(sigHexStr1.toUint8Array()),
new TxnBuilderTypes.Ed25519Signature(sigHexStr3.toUint8Array()),
],
bitmap,
);
return muliEd25519Sig;
}, multiSigPublicKey);

 

步驟6:加簽交易

const bcsTxn = txnBuilder.sign(rawTxn);

 

步驟7:提交加簽資料做驗證

const transactionRes = await client.submitSignedBCSTransaction(bcsTxn);

 

步驟8:發送交易

await client.waitForTransaction(transactionRes.hash);

 

步驟9:查看多簽帳戶Resource

resources = await client.getAccountResources(mutisigAccountAddress);
accountResource = resources.find((r) => r.type === aptosCoinStore);
balance = parseInt((accountResource?.data as any).coin.value);
console.log(`multisig account coins: ${balance}.`);

 

步驟10:查看目標帳戶

resources = await client.getAccountResources(account4.address());
accountResource = resources.find((r) => r.type === aptosCoinStore);
balance = parseInt((accountResource?.data as any).coin.value);
assert(balance === 123);
console.log(`account4 coins: ${balance}. Should be 123!`);