Nested Contracts
Soroban Contract in Contract Authorization
#![no_std]
pub mod contract_a {
use soroban_sdk::{
auth::{ContractContext, InvokerContractAuthEntry, SubContractInvocation},
contract, contractimpl, vec, Env, Address, Symbol, IntoVal, log
};
use crate::contract_b::ContractBClient;
#[contract]
pub struct ContractA;
#[contractimpl]
impl ContractA {
pub fn call_b(env: Env, contract_b_address: Address, contract_c_address: Address) {
/// @dev should remove logs before deploying smart contracts
log!(&env, "Contract B address: {}, Contract C address: {}", contract_b_address, contract_c_address);
env.authorize_as_current_contract(vec![
&env,
InvokerContractAuthEntry::Contract(SubContractInvocation {
context: ContractContext {
contract: contract_c_address.clone(),
fn_name: Symbol::new(&env, "authorized_fn_c"),
args: (env.current_contract_address(),).into_val(&env),
},
sub_invocations: vec![&env],
}),
]);
}
}
}
pub mod contract_b {
use soroban_sdk::{contract, contractimpl, Env, Address, log};
use crate::contract_c::ContractCClient;
#[contract]
pub struct ContractB;
#[contractimpl]
impl ContractB {
pub fn authorized_fn_b(env: Env, authorizer: Address, contract_c_address: Address) {
log!(&env, "Authorizer: {}, Contract C address: {}", authorizer, contract_c_address);
authorizer.require_auth();
let client = ContractCClient::new(&env, &contract_c_address);
client.authorized_fn_c(&authorizer);
}
}
}
pub mod contract_c {
use soroban_sdk::{contract, contractimpl, Env, Address, log};
#[contract]
pub struct ContractC;
#[contractimpl]
impl ContractC {
pub fn authorized_fn_c(_env: Env, authorizer: Address) {
/// @dev should remove logs before deploying smart contracts
log!(&env, "Authorizer: {}", authorizer);
authorizer.require_auth();
}
}
}
Explanation
#![no_std]
This attribute prevents linking to the standard library, making the code lighter and more efficient for Soroban contracts. It's big so we save on size.
use soroban_sdk::{contract, contractimpl, Env, log}
Imports stuffs from the Soroban SDK. Env
is basic Soroban type, we need it because we can't use the Rust standard library.
#[contract]
Marks the struct as a Soroban smart contract. Soroban smart contracts are defined as Rust structs.
#[contractimpl]
Marks the implementation block as containing contract methods and transforms it to code that Soroban can evaluate directly
ContractA
- Calls the
authorized_fn_b
function inContractB
. - Authorizes itself as the current contract using
authorize_as_current_contract
. - The authorization includes a
SubContractInvocation
forContractC
. TheSubContractInvocation
structure is used to define the details of a subcontract call. TheSubContractInvocation
specifies the contract address, function name (authorized_fn_c
), and arguments (the current contract's address). - This allows
ContractA
to callauthorized_fn_c
inContractC
on behalf of itself.
ContractB
- The
authorized_fn_b
function requires the caller to have authorization. - It creates a client for
ContractC
and callsauthorized_fn_c
inContractC
, passing the authorizer's address.
ContractC
- The
authorized_fn_c
function requires the caller to have authorization. - It simply checks the caller's authorization and doesn't perform any additional actions.
Run in Playground
Loading playground...