This tutorial introduces assorted K-of-N multi-signer authentication operations and supplements content from the following tutorials:
Your First Transaction
Your First Coin
Your First Move Module
Try out the above tutorials (which include dependency installations) before moving on to multisig operations.
Step 1: Pick an SDK
Install your preferred SDK from the below list:
TypeScript SDK
Step 2: Run the example
Clone the endless-ts-sdk repo and build it:
git clone https://github.com/endless-labs/endless-ts-sdk.git
cd endless-ts-sdk
pnpm install
pnpm build
Navigate to the Typescript examples directory:
cd examples/endless
Install the necessary dependencies:
pnpm install
pnpm run bindings
Step 3: Generate accounts and fund them
First, we will generate accounts for Alice, Bob, and Chad and fund them:
Fresh accounts are generated for each example run, but the output should resemble:
Fund alice: version 633280
Fund bob: version 633291
=== Account addresses ===
Alice: 0x91c381ec582ee96f96841a8f71b13a9feea83f52441e15a9e8b9d2bcf2ebbbc9
Bob: 0xd92dffdee6345ed5a0b3e3fe72b972dd90ba200bb478693dba4288998bc8343d
Chad: 0x820a4393e98c207b920a098c251c72435b5830b5b938bdb1aee77f83713d359c
=== Authentication keys ===
Alice: 0x91c381ec582ee96f96841a8f71b13a9feea83f52441e15a9e8b9d2bcf2ebbbc9
Bob: 0xd92dffdee6345ed5a0b3e3fe72b972dd90ba200bb478693dba4288998bc8343d
Chad: 0x820a4393e98c207b920a098c251c72435b5830b5b938bdb1aee77f83713d359c
For each user, at this moment, the account address and authentication key are identical.
Step 4: Add Bob's authentication key into Alice's authentication key(list)
Next, Alice adds Bob's authentication key to her list of authentication keys. This involves submitting a transaction that invokes the `add_authentication_key` function within the account module of the system contract. Both Alice and Bob must sign this transaction.
let txn = await endless.transaction.build.multiAgent({
sender: alice.accountAddress,
data: {
function: "0x1::account::add_authentication_key",
functionArguments: []
},
secondarySignerAddresses: [bob.accountAddress],
})
let aliceAuth = alice.signTransactionWithAuthenticator(txn)
let bobAuth = bob.signTransactionWithAuthenticator(txn)
pending = await endless.transaction.submit.multiAgent({
transaction: txn,
senderAuthenticator: aliceAuth,
additionalSignersAuthenticators: [bobAuth],
})
tx_response = await endless.waitForTransaction({ transactionHash: pending.hash })
console.log(`\nAdd Bob's authkey into Alice authkey list: version ${tx_response.version}`)
After add authentication key transaction, bob's authentication key is added into Alice's authentication
Alice authentication_key:
0x91c381ec582ee96f96841a8f71b13a9feea83f52441e15a9e8b9d2bcf2ebbbc9,
0xd92dffdee6345ed5a0b3e3fe72b972dd90ba200bb478693dba4288998bc8343d
Bob controlled accounts:
0x91c381ec582ee96f96841a8f71b13a9feea83f52441e15a9e8b9d2bcf2ebbbc9
Step 6: Send coins from Alice to Chad, signed by Bob
We now build an EDS transfer transaction, sender is Alice's account, receiver is Chad, transaction is signed by Bob.
let transferEDSRawTransaction = await endless.transaction.build.simple({
sender: alice.accountAddress,
data: {
function: "0x1::endless_account::transfer",
functionArguments: [chad.accountAddress, 1000]
},
})
let multiAuthKeyAccount = new MultiAuthKeyAccount({ sender: alice.accountAddress, signers: [bob] })
pending = await endless.signAndSubmitTransaction({
transaction: transferEDSRawTransaction,
signer: multiAuthKeyAccount,
})
tx_response = await endless.waitForTransaction({ transactionHash: pending.hash })
console.log(`\ntransfer EDS from Alice to Chad with Bob auth: version ${tx_response.version}`)
Step 7: remove Bob's authentication key from Alice's authentication key list
We invoke 0x1::account::remove_authentication_key to remove Bob's authenticaion key, restore Alice's authentication key as default. This transaction also must be signed by both Alice and Bob.
Step 8: Check if Alice's authentication key is restored to default
Alice authentication_key:
0x91c381ec582ee96f96841a8f71b13a9feea83f52441e15a9e8b9d2bcf2ebbbc9
Bob controlled accounts:
None
Step 9: Batch add Bob&Chad authkey into Alice's authentication key list, but set Threashold to 2
By adding Bob and Chad auth key to Alice's auth key list, either Bob or Chad can sign transaction on Alice behalf. A required signature count of 2 (Threshold = 2) is enforced for transactions originating from Alice. Verification will fail if fewer than two signatures are present (e.g., only Alice's signature, or only one of Bob's or Chad's)
Step 10: transfer transaction, signed only by Bob, will fail
transferEDSRawTransaction = await endless.transaction.build.simple({
sender: alice.accountAddress,
data: {
function: "0x1::endless_account::transfer",
functionArguments: [chad.accountAddress, 1000]
},
})
multiAuthKeyAccount = new MultiAuthKeyAccount({ sender: alice.accountAddress, signers: [bob] })
try {
pending = await endless.signAndSubmitTransaction({
transaction: transferEDSRawTransaction,
signer: multiAuthKeyAccount,
})
await endless.waitForTransaction({ transactionHash: pending.hash });
} catch (error) {
console.log("\nFailed to transfer EDS from Alice to Chad with only by Bob auth, cause `Auth Threshold` is 2")
}
Step 11: transfer transaction, signed by both Bob and Chad, will success
multiAuthKeyAccount = new MultiAuthKeyAccount({ sender: alice.accountAddress, signers: [bob, alice] })
pending = await endless.signAndSubmitTransaction({
transaction: transferEDSRawTransaction,
signer: multiAuthKeyAccount,
})
tx_response = await endless.waitForTransaction({ transactionHash: pending.hash })
console.log(`\ntransfer EDS from Alice to Chad with Alice and Bob auth, version ${tx_response.version}`)
The above examples showcase the Endless multi-signature feature, demonstrating how to manage Authentication Keys and generate transaction signatures. This multi-signature implementation, combined with Keyless functionality, offers an emergency escape route.
For example, if a Keyless account (A) is locked due to Web2 service issues, and its authentication key list includes another wallet (AA), then AA can rescue the funds by initiating a transaction on A's behalf, avoiding assets locked.