use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use tap_agent::agent::{Agent, TapAgent};
use tap_agent::agent_key_manager::AgentKeyManagerBuilder;
use tap_agent::config::AgentConfig;
use tap_agent::error::Result;
use tap_agent::key_manager::{Secret, SecretMaterial, SecretType};
use tap_caip::AssetId;
use tap_msg::message::{AddAgents, Authorize, Reject, Settle, Transfer};
use tap_msg::{Agent as TapAgent_, Party};
fn main() -> Result<()> {
tokio_test::block_on(async {
println!("=== Multi-Agent TAIP-3 Transfer Flow with TAIP-4 Authorization ===\n");
let (originator_vasp, originator_vasp_did) = create_agent(
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo",
"nWGxne/9WmC6hEr+BQh+uDpW6n7dZsN4c4C9rFfIz3Yh",
)
.await?;
let (originator_wallet, originator_wallet_did) = create_agent(
"did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k",
"8zYZK2vvsAyVYpNpnYzTnUPjBuWdWpYmPpQmwErV9XQg",
"8zYZK2vvsAyVYpNpnYzTnUPjBuWdWpYmPpQmwErV9XQg",
)
.await?;
let (originator_wallet_api, originator_wallet_api_did) = create_agent(
"did:key:z6MkgYAFirTGpAaHxfQrJxSUVNBsrGZEXEqnawEUCPnVKVXJ",
"CnEDU0Jxr6Jx9XH+61JrGK8Bz1xm0xwLOqVDjd+5FVM",
"CnEDU0Jxr6Jx9XH+61JrGK8Bz1xm0xwLOqVDjd+5FVM",
)
.await?;
let (beneficiary_vasp, beneficiary_vasp_did) = create_agent(
"did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
"B12NYF8RrR3h41TDCTJojY59usg3mbtbjnFs7Eud1Y6u",
"B12NYF8RrR3h41TDCTJojY59usg3mbtbjnFs7Eud1Y6u",
)
.await?;
let (beneficiary_wallet, beneficiary_wallet_did) = create_agent(
"did:key:z6MkrJVkLHBdQQS5y2CnXAHJcgBWMVv7V5aukAtQyBx4qJA4",
"5TVS4YKJxmqVVQUM7xbVsYiFrCbdwgLLdu6QB98q3a4",
"5TVS4YKJxmqVVQUM7xbVsYiFrCbdwgLLdu6QB98q3a4",
)
.await?;
let (beneficiary_wallet_api, beneficiary_wallet_api_did) = create_agent(
"did:key:z6MkqyYXcBQH7dJyXWrTrEKJNEjXnkajQ2xjGPEgBsqVRmVS",
"D9WbJ5H9sTNXLVLYARpVSXhwrLGJUHNn6vJUUXFqYj4",
"D9WbJ5H9sTNXLVLYARpVSXhwrLGJUHNn6vJUUXFqYj4",
)
.await?;
println!("Created agents with DIDs:");
println!(" Originator VASP: {}", originator_vasp_did);
println!(" Originator Wallet: {}", originator_wallet_did);
println!(" Originator Wallet API: {}", originator_wallet_api_did);
println!(" Beneficiary VASP: {}", beneficiary_vasp_did);
println!(" Beneficiary Wallet: {}", beneficiary_wallet_did);
println!(" Beneficiary Wallet API: {}\n", beneficiary_wallet_api_did);
println!("Step 1: Originator VASP creates a transfer request");
let asset =
match AssetId::from_str("eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f") {
Ok(asset) => asset,
Err(e) => {
println!("Error parsing asset ID: {}", e);
return Err(tap_agent::error::Error::Validation(format!(
"Invalid asset ID: {}",
e
)));
}
};
let transfer_id = uuid::Uuid::new_v4();
let originator_party = "did:pkh:eip155:1:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; let beneficiary_party = "did:pkh:eip155:1:0x71C7656EC7ab88b098defB751B7401B5f6d8976F";
let transfer = Transfer {
transaction_id: Some(uuid::Uuid::new_v4().to_string()),
asset,
originator: Some(Party::new(originator_party)),
beneficiary: Some(Party::new(beneficiary_party)),
amount: "100.0".to_string(),
agents: vec![
TapAgent_::new(&originator_vasp_did, "originatorVASP", originator_party),
TapAgent_::new(&originator_wallet_did, "originatorWallet", originator_party),
TapAgent_::new(
&originator_wallet_api_did,
"originatorWalletAPI",
originator_party,
),
TapAgent_::new(&beneficiary_vasp_did, "beneficiaryVASP", beneficiary_party),
],
settlement_id: None,
expiry: None,
transaction_value: None,
memo: Some("Multi-agent transfer example with dynamic agent addition".to_string()),
connection_id: None,
metadata: HashMap::new(),
};
println!("Transfer details:");
println!(" Asset: {}", transfer.asset);
println!(" Amount: {}", transfer.amount);
println!(
" From: {} (party)",
transfer
.originator
.as_ref()
.map(|o| o.id.as_str())
.unwrap_or("unknown")
);
println!(
" To: {} (party)",
transfer.beneficiary.as_ref().unwrap().id
);
println!(" Initial agents:");
println!(" - {} (originator VASP)", originator_vasp_did);
println!(" - {} (originator wallet)", originator_wallet_did);
println!(
" - {} (originator wallet API)",
originator_wallet_api_did
);
println!(" - {} (beneficiary VASP)", beneficiary_vasp_did);
println!();
println!("Step 2: Sending transfer to beneficiary VASP");
let (packed_transfer_vasp, _delivery_results) = originator_vasp
.send_message(&transfer, vec![&beneficiary_vasp_did], false)
.await?;
println!("Transfer sent to beneficiary VASP\n");
println!("Step 3: Beneficiary VASP receives the transfer");
let plain_message = beneficiary_vasp
.receive_message(&packed_transfer_vasp)
.await?;
let received_transfer_vasp: Transfer = serde_json::from_value(plain_message.body)?;
println!("Transfer received by beneficiary VASP");
println!(" Transfer ID: {}", transfer_id);
println!(
" Initial agents count: {}\n",
received_transfer_vasp.agents.len()
);
println!("Step 4: Beneficiary VASP adds their wallet and wallet API as agents");
let add_agents = AddAgents {
transaction_id: transfer_id.to_string(),
agents: vec![
TapAgent_::new(
&beneficiary_wallet_did,
"beneficiaryWallet",
beneficiary_party,
),
TapAgent_::new(
&beneficiary_wallet_api_did,
"beneficiaryWalletAPI",
beneficiary_party,
),
],
};
let (packed_add_agents, _delivery_results) = beneficiary_vasp
.send_message(&add_agents, vec![&originator_vasp_did], false)
.await?;
println!("Beneficiary VASP sends AddAgents message to add wallet and wallet API");
println!(
" Added agents: {} (beneficiary wallet), {} (beneficiary wallet API)\n",
beneficiary_wallet_did, beneficiary_wallet_api_did
);
println!("Step 5: Originator VASP receives the AddAgents message");
let plain_message = originator_vasp.receive_message(&packed_add_agents).await?;
let received_add_agents: AddAgents = serde_json::from_value(plain_message.body)?;
println!("Originator VASP received AddAgents message:");
println!(" Transfer ID: {}", received_add_agents.transaction_id);
println!(" Added agents count: {}", received_add_agents.agents.len());
println!(
" Added agents: {} ({}), {} ({})\n",
received_add_agents.agents[0].id,
received_add_agents.agents[0]
.role
.as_deref()
.unwrap_or("unknown"),
received_add_agents.agents[1].id,
received_add_agents.agents[1]
.role
.as_deref()
.unwrap_or("unknown")
);
println!(
"Step 6: Beneficiary VASP initially rejects the transfer due to compliance concerns"
);
let reject = Reject {
transaction_id: transfer_id.to_string(),
reason: Some("compliance.policy: Additional beneficiary information required. Please provide additional beneficiary information to comply with regulations".to_string()),
};
let (packed_reject, _delivery_results) = beneficiary_vasp
.send_message(&reject, vec![&originator_vasp_did], false)
.await?;
let plain_message = originator_vasp.receive_message(&packed_reject).await?;
let received_reject: Reject = serde_json::from_value(plain_message.body)?;
println!("Originator VASP received rejection:");
println!(" Transfer ID: {}", received_reject.transaction_id);
println!(" Reason: {:?}", received_reject.reason);
println!();
println!(
"Step 7: After resolving compliance concerns, beneficiary VASP authorizes the transfer"
);
let _settlement_address = "eip155:1:0x742d35Cc6634C0532925a3b844Bc454e4438f44e";
let authorize = Authorize {
transaction_id: transfer_id.to_string(),
settlement_address: None,
expiry: None,
};
let (packed_authorize_vasp, _delivery_results) = beneficiary_vasp
.send_message(&authorize, vec![&originator_vasp_did], false)
.await?;
let plain_message = originator_vasp
.receive_message(&packed_authorize_vasp)
.await?;
let received_authorize: Authorize = serde_json::from_value(plain_message.body)?;
println!("Originator VASP received authorization:");
println!(" Transfer ID: {}", received_authorize.transaction_id);
println!("Step 8: Beneficiary wallet also authorizes the transfer");
let authorize_wallet = Authorize {
transaction_id: transfer_id.to_string(),
settlement_address: None,
expiry: None,
};
let (packed_authorize_wallet, _delivery_results) = beneficiary_wallet
.send_message(&authorize_wallet, vec![&originator_wallet_did], false)
.await?;
let plain_message = originator_wallet
.receive_message(&packed_authorize_wallet)
.await?;
let received_authorize_wallet: Authorize = serde_json::from_value(plain_message.body)?;
println!("Originator wallet received authorization:");
println!(
" Transfer ID: {}",
received_authorize_wallet.transaction_id
);
println!("Step 9: Wallet APIs exchange technical information for settlement");
let api_note = format!(
"API technical details: callback_url=https://api.wallet.example/callbacks/{}, nonce={}",
transfer_id,
uuid::Uuid::new_v4()
);
println!(" - {}", api_note);
let api_authorize = Authorize {
transaction_id: transfer_id.to_string(),
settlement_address: None,
expiry: None,
};
let (packed_api_authorize, _delivery_results) = originator_wallet_api
.send_message(&api_authorize, vec![&beneficiary_wallet_api_did], false)
.await?;
let plain_message = beneficiary_wallet_api
.receive_message(&packed_api_authorize)
.await?;
let received_api_authorize: Authorize = serde_json::from_value(plain_message.body)?;
println!("Beneficiary wallet API received technical details:");
println!(" Transfer ID: {}", received_api_authorize.transaction_id);
println!("Step 10: Originator wallet initiates settlement");
let settlement_id =
"eip155:1:tx/0x3edb98c24d46d148eb926c714f4fbaa117c47b0c0821f38bfce9763604457c33";
let settle = Settle {
transaction_id: transfer_id.to_string(),
settlement_id: Some(settlement_id.to_string()),
amount: Some(transfer.amount.clone()),
};
let (packed_settle_vasp, _delivery_results1) = originator_wallet
.send_message(&settle, vec![&beneficiary_vasp_did], false)
.await?;
let (packed_settle_wallet, _delivery_results2) = originator_wallet
.send_message(&settle, vec![&beneficiary_wallet_did], false)
.await?;
println!("Settlement sent to beneficiary VASP and wallet");
println!(" Transaction ID: {}\n", settlement_id);
println!("Step 11: Wallet APIs confirm settlement details");
let api_settlement_id =
"eip155:1:tx/0x3edb98c24d46d148eb926c714f4fbaa117c47b0c0821f38bfce9763604457c33";
let api_settle = Settle {
transaction_id: transfer_id.to_string(),
settlement_id: Some(api_settlement_id.to_string()),
amount: Some(transfer.amount.clone()),
};
let (packed_api_settle, _delivery_results) = originator_wallet_api
.send_message(&api_settle, vec![&beneficiary_wallet_api_did], false)
.await?;
let plain_message = beneficiary_wallet_api
.receive_message(&packed_api_settle)
.await?;
let received_api_settle: Settle = serde_json::from_value(plain_message.body)?;
println!("Beneficiary wallet API received settlement confirmation:");
println!(" Transfer ID: {}", received_api_settle.transaction_id);
println!(" Settlement ID: {:?}", received_api_settle.settlement_id);
if let Some(amount) = &received_api_settle.amount {
println!(" Amount: {}\n", amount);
}
println!("Step 12: Beneficiaries receive settlement confirmation");
let plain_message = beneficiary_vasp
.receive_message(&packed_settle_vasp)
.await?;
let received_settle_vasp: Settle = serde_json::from_value(plain_message.body)?;
let plain_message_wallet = beneficiary_wallet
.receive_message(&packed_settle_wallet)
.await?;
let _received_settle_wallet: Settle = serde_json::from_value(plain_message_wallet.body)?;
println!("Settlement received by beneficiary VASP and wallet:");
println!(" Transfer ID: {}", received_settle_vasp.transaction_id);
println!(" Settlement ID: {:?}", received_settle_vasp.settlement_id);
if let Some(amount) = &received_settle_vasp.amount {
println!(" Amount: {}", amount);
}
println!("\n=== Multi-agent transfer flow with dynamic agent addition completed successfully ===");
Ok(())
})
}
async fn create_agent(
did: &str,
public_key: &str,
private_key: &str,
) -> Result<(Arc<TapAgent>, String)> {
let agent_config = AgentConfig::new(did.to_string());
let mut builder = AgentKeyManagerBuilder::new();
let secret = Secret {
id: format!("{}#keys-1", did),
type_: SecretType::JsonWebKey2020,
secret_material: SecretMaterial::JWK {
private_key_jwk: serde_json::json!({
"kty": "OKP",
"crv": "Ed25519",
"x": public_key,
"d": private_key
}),
},
};
builder = builder.add_secret(did.to_string(), secret);
let key_manager = builder.build()?;
let agent = TapAgent::new(agent_config, Arc::new(key_manager));
Ok((Arc::new(agent), did.to_string()))
}