Skip to main content
The cross contract call example demonstrates how to call a contract from another contract. Open in Codespaces Open in Codeanywhere
In this example there are two contracts that are compiled separately, deployed separately, and then tested together. There are a variety of ways to develop and test contracts with dependencies on other contracts, and the Soroban SDK and tooling is still building out the tools to support these workflows. Feedback appreciated here.

Run the Example

First go through the Setup process to get your development environment configured, then clone the v23.0.0 tag of soroban-examples repository:
Or, skip the development environment setup and open this example in GitHub Codespaces or Code Anywhere. To run the tests for the example, first build Contract A (the contract to be called) and then run cargo test from Contract B’s directory. Build Contract A by navigating to the cross_contract/contract_a directory and use the stellar contract build build command:
When Contract A has been built, navigate to the cross_contract/contract_b directory, and use cargo test.
You should see the output:

Code

cross_contract/contract_a/src/lib.rs
cross_contract/contract_b/src/lib.rs
Ref: https://github.com/stellar/soroban-examples/tree/v23.0.0/cross_contract

How it Works

Cross contract calls are made by invoking another contract by its contract ID. Contracts to invoke can be imported into your contract with the use of contractimport!(file = "..."). The import will code generate:
  • A ContractClient type that can be used to invoke functions on the contract.
  • Any types in the contract that were annotated with #[contracttype].
The contractimport! macro will generate the types in the module it is used, so it’s a good idea to use the macro inside a mod { ... } block, or inside its own file, so that the names of generated types don’t collide with names of types in your own contract.
Open the files above to follow along.

Contract A: The Contract to be Called

The contract to be called is Contract A. It is a simple contract that accepts x and y parameters, adds them together and returns the result.
cross_contract/contract_a/src/lib.rs
The contract uses the checked_add method to ensure that there is no overflow, and if there is overflow, panics rather than returning an overflowed value. Rust’s primitive integer types all have checked operations available as functions with the prefix checked_.

Contract B: The Contract doing the Calling

The contract that does the calling is Contract B. It accepts a contract ID that it will call, as well as the same parameters to pass through. In many contracts the contract to call might have been stored as contract data and be retrieved, but in this simple example it is being passed in as a parameter each time. The contract imports Contract A into the contract_a module. The contract_a::Client is constructed pointing at the contract ID passed in. The client is used to execute the add function with the x and y parameters on Contract A.
cross_contract/contract_b/src/lib.rs

Tests

Open the cross_contract/contract_b/src/test.rs file to follow along.
cross_contract/contract_b/src/test.rs
In any test the first thing that is always required is an Env, which is the Soroban environment that the contract will run in.
Contract A is registered with the environment using the imported Wasm.
Contract B is registered with the environment using the contract type and the contract instance is compiled into the Rust binary.
All public functions within an impl block that is annotated with the #[contractimpl] attribute have a corresponding function generated in a generated client type. The client type will be named the same as the contract type with Client appended. For example, in our contract the contract type is ContractB, and the client is named ContractBClient. The client can be constructed and used in the same way that client generated for Contract A can be.
The client is used to invoke the add_with function on Contract B. Contract B will invoke Contract A, and the result will be returned.
The test asserts that the result that is returned is as we expect.

Build the Contracts

To build the contract into a .wasm file, use the stellar contract build command. Both contract_call/contract_a and contract_call/contract_b must be built, with contract_a being built first.
Both .wasm files should be found in both contract target directories after building both contracts:

Run the Contract

If you have stellar-cli installed, you can invoke contract functions. Both contracts must be deployed.
Invoke Contract B’s add_with function, passing in values for x and y (e.g. as 5 and 7), and then pass in the contract ID or alias of Contract A.
The following output should occur using the code above.
Contract B’s add_with function invoked Contract A’s add function to do the addition.