Multi-Agent Transactions
Multi-agent transactions allow multiple accounts to participate in the logic of a Move contract. This is useful when a smart contract requires authorization from more than one party, such as atomic swaps, multi-party escrow, or any operation that needs to access resources from several accounts simultaneously.
When to Use Multi-Agent Transactions
Section titled “When to Use Multi-Agent Transactions”Multi-agent transactions are appropriate when:
- Atomic swaps — Two parties exchange assets in a single, atomic transaction where both must agree.
- Multi-party escrow — An escrow contract requires signatures from both the depositor and the arbiter to release funds.
- Shared resource access — A Move function needs
&signerreferences from multiple accounts to read or modify their resources.
How to Build a Multi-Agent Transaction
Section titled “How to Build a Multi-Agent Transaction”-
Build the entry function payload.
Create the payload for a Move function that accepts multiple signers.
use aptos_sdk::types::EntryFunctionPayload;let payload = EntryFunctionPayload::new("0x<address>::<module>::<function>".parse()?,vec![],vec![// Function arguments go here],); -
Build the raw transaction with
TransactionBuilder.Construct a raw transaction from the primary sender.
use aptos_sdk::transaction_builder::TransactionBuilder;let raw_txn = TransactionBuilder::new(payload, aptos.get_chain_id().await?).sender(alice.address()).sequence_number(aptos.get_sequence_number(alice.address()).await?).max_gas_amount(10_000).gas_unit_price(100).expiration_timestamp_secs(aptos.get_latest_ledger_info().await?.timestamp() + 60,).build(); -
Create a
MultiAgentRawTransactionwith secondary signer addresses.Wrap the raw transaction to declare which additional accounts must co-sign.
use aptos_sdk::types::MultiAgentRawTransaction;let multi_agent_txn = MultiAgentRawTransaction::new(raw_txn,vec![bob.address()], // Secondary signer addresses); -
Sign the transaction with all parties.
Use
sign_multi_agent_transactionto produce a signed transaction that includes signatures from the primary sender and all secondary signers. Each secondary signer is passed as a trait reference (&dyn Account).let signed_txn = aptos.sign_multi_agent_transaction(&multi_agent_txn,&alice, // Primary signer&[&bob as &dyn Account], // Secondary signers)?; -
Submit the transaction and wait for confirmation.
let result = aptos.submit_and_wait(signed_txn).await?;let success = result.data.get("success").and_then(|v| v.as_bool()).unwrap_or(false);println!("Transaction success: {}", success);
Common Errors
Section titled “Common Errors”NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH— The number of signers provided does not match the number of&signerparameters in the Move function. Verify that your entry function expects exactly the number of signers you are providing (primary + secondary).INVALID_AUTH_KEY— One of the secondary signer addresses does not match the account that signed the transaction. Ensure the addresses inMultiAgentRawTransaction::newmatch the actual signing accounts.
Full Working Example
Section titled “Full Working Example”/// This example demonstrates a multi-agent transaction where two accounts/// (Alice and Bob) both sign a single transaction.////// Note: You must deploy a Move module with a multi-signer entry function/// for this example to work. Replace the function reference below with/// your deployed contract.use aptos_sdk::{Aptos, AptosConfig};use aptos_sdk::account::{Account, Ed25519Account};use aptos_sdk::types::{EntryFunctionPayload, MultiAgentRawTransaction};use aptos_sdk::transaction_builder::TransactionBuilder;
#[tokio::main]async fn main() -> anyhow::Result<()> { // Connect to testnet let aptos = Aptos::new(AptosConfig::testnet())?;
// Generate and fund accounts let alice = Ed25519Account::generate(); let bob = Ed25519Account::generate(); aptos.fund_account(alice.address(), 100_000_000).await?; aptos.fund_account(bob.address(), 100_000_000).await?;
println!("Alice: {}", alice.address()); println!("Bob: {}", bob.address());
// 1. Build the payload for a multi-signer function let payload = EntryFunctionPayload::new( // Replace with your multi-agent Move function "0x<address>::<module>::<function>".parse()?, vec![], vec![], );
// 2. Build the raw transaction let raw_txn = TransactionBuilder::new(payload, aptos.get_chain_id().await?) .sender(alice.address()) .sequence_number(aptos.get_sequence_number(alice.address()).await?) .max_gas_amount(10_000) .gas_unit_price(100) .expiration_timestamp_secs( aptos.get_latest_ledger_info().await?.timestamp() + 60, ) .build();
// 3. Create the multi-agent raw transaction let multi_agent_txn = MultiAgentRawTransaction::new( raw_txn, vec![bob.address()], );
// 4. Sign with both accounts let signed_txn = aptos.sign_multi_agent_transaction( &multi_agent_txn, &alice, &[&bob as &dyn Account], )?;
// 5. Submit and wait for confirmation let result = aptos.submit_and_wait(signed_txn).await?; let success = result .data .get("success") .and_then(|v| v.as_bool()) .unwrap_or(false); println!("Transaction success: {}", success);
Ok(())}