Skip to main content

truthlinked_sdk/
call.rs

1//! Cross-contract call utilities for contract composability.
2//!
3//! This module provides functions to invoke other contracts from within your contract,
4//! enabling composability and modular contract design.
5//!
6//! # Features
7//!
8//! - **Legacy calls**: Basic cross-contract invocation without value transfer
9//! - **Value transfer**: Call contracts while transferring native tokens
10//! - **Return data**: Capture and process return values from called contracts
11//!
12//! # Example
13//!
14//! ```ignore
15//! use truthlinked_sdk::call;
16//!
17//! // Call another contract
18//! let token_contract = [0x12; 32];
19//! let calldata = encode_transfer_call(recipient, amount);
20//! let result = call::call_contract(&token_contract, &calldata)?;
21//!
22//! // Call with value transfer
23//! let result = call::call_contract_with_value(
24//!     &contract_id,
25//!     &calldata,
26//!     1000, // Transfer 1000 base units
27//! )?;
28//! ```
29
30extern crate alloc;
31
32use alloc::vec;
33use alloc::vec::Vec;
34
35use crate::env;
36use crate::error::Result;
37
38/// Type alias for 32-byte account identifiers.
39///
40/// Account IDs in TruthLinked are 32-byte arrays, typically BLAKE3 hashes
41/// of public keys or deterministic contract addresses.
42pub type AccountId = [u8; 32];
43
44/// Invokes another contract without transferring value.
45///
46/// This is the legacy cross-contract call mechanism. The called contract
47/// executes with the same caller context (the original transaction sender).
48///
49/// # Arguments
50///
51/// * `contract_id` - The 32-byte address of the contract to call
52/// * `calldata` - Encoded function selector and arguments
53///
54/// # Returns
55///
56/// * `Ok(Vec<u8>)` - The return data from the called contract
57/// * `Err(Error)` - If the call fails or the contract doesn't exist
58///
59/// # Gas
60///
61/// The called contract shares the remaining gas budget of the current execution.
62///
63/// # Example
64///
65/// ```ignore
66/// use truthlinked_sdk::call;
67///
68/// let token_contract = [0x12; 32];
69/// let calldata = encode_balance_of(account);
70/// let balance_bytes = call::call_contract(&token_contract, &calldata)?;
71/// let balance = decode_u128(&balance_bytes);
72/// ```
73pub fn call_contract(contract_id: &AccountId, calldata: &[u8]) -> Result<Vec<u8>> {
74    let mut out = vec![0u8; env::MAX_RETURN_DATA_SIZE];
75    let len = env::call_contract_legacy(contract_id, calldata, &mut out)?;
76    out.truncate(len.min(out.len()));
77    Ok(out)
78}
79
80/// Invokes another contract while transferring native tokens.
81///
82/// This is the v2 cross-contract call mechanism that supports value transfer.
83/// The specified amount of native tokens is transferred from the calling contract
84/// to the called contract before execution.
85///
86/// # Arguments
87///
88/// * `contract_id` - The 32-byte address of the contract to call
89/// * `calldata` - Encoded function selector and arguments
90/// * `value` - Amount of native tokens to transfer (in base units)
91///
92/// # Returns
93///
94/// * `Ok(Vec<u8>)` - The return data from the called contract
95/// * `Err(Error)` - If the call fails, contract doesn't exist, or insufficient balance
96///
97/// # Errors
98///
99/// This function will fail if:
100/// - The calling contract has insufficient balance
101/// - The called contract doesn't exist
102/// - The called contract reverts
103/// - Gas is exhausted
104///
105/// # Example
106///
107/// ```ignore
108/// use truthlinked_sdk::call;
109///
110/// // Call a payable function with 1000 tokens
111/// let result = call::call_contract_with_value(
112///     &contract_id,
113///     &calldata,
114///     1000,
115/// )?;
116/// ```
117pub fn call_contract_with_value(
118    contract_id: &AccountId,
119    calldata: &[u8],
120    value: u128,
121) -> Result<Vec<u8>> {
122    let mut out = vec![0u8; env::MAX_RETURN_DATA_SIZE];
123    let len = env::call_contract_v2_with_value(contract_id, calldata, value, &mut out)?;
124    out.truncate(len.min(out.len()));
125    Ok(out)
126}