diff --git a/docs/build/guides/conventions/workspace.mdx b/docs/build/guides/conventions/workspace.mdx index 3c24ff3af..8b4110bdd 100644 --- a/docs/build/guides/conventions/workspace.mdx +++ b/docs/build/guides/conventions/workspace.mdx @@ -1,7 +1,7 @@ --- -title: Organize contracts with workspace +title: Workspaces hide_table_of_contents: true -description: Organize your contracts using Cargo workspaces +description: Organize contracts using Cargo workspaces --- ### Initializing workspace project @@ -10,10 +10,7 @@ Using Cargo's workspace [feature](https://doc.rust-lang.org/book/ch14-03-cargo-w It's very simple to get started using the cli: -```bash -mkdir soroban-project -cd soroban-project -PROJECT_ROOT=. +``` stellar contract init . --name add_contract ``` @@ -21,7 +18,7 @@ Running this command will create a root project directory (`soroban-project`) an Adding one more contract template to the project can be done using the same command: -```bash +``` stellar contract init . --name main_contract ``` @@ -47,32 +44,25 @@ $ tree └── README.md ``` -Running `contract init` command created a sample contracts subdirectories (located in `./contracts), each contains: +Running `stellar contract init` command created a sample contracts subdirectories (located in `./contracts), each contains: 1. Cargo.toml file with all (minimal) necessary dependencies 2. `Makefile` to easily build contract using `make` command 3. `src` directory with an example hello-world contract and a simple test. -We can now build our project's contracts with +We can now build our workspace's contracts with -```bash +``` stellar contract build ``` -command and check the build directory: - -```bash -$ ls target/wasm32-unknown-unknown/release/ | grep wasm -add_contract.wasm -main_contract.wasm -``` +command and check the build directory `target/wasm32-unknown-unknown/release/` ### Cross-contract call between workspaces With the given project structure, cross-contract calls can be easily made. Starting with modifying the sample hello world contract into an add contract: -```rust -// contracts/add_contract/src/lib.rs +```rust title="contracts/add_contract/src/lib.rs" #![no_std] use soroban_sdk::{contract, contractimpl}; @@ -89,7 +79,7 @@ impl ContractAdd { The `ContractAdd` can now be referenced and used from another contracts in the same workspace using [`contractimport`](https://docs.rs/soroban-sdk/latest/soroban_sdk/macro.contractimport.html) macros. Note that the `contractimport` requires relative path of the compiled wasm file, so first it's required to recompile the contract with: -```bash +``` stellar contract build ``` @@ -101,14 +91,13 @@ $PROJECT_ROOT/target/wasm32-unknown-unknown/release/ In order to reference this wasm file from `contracts/main_contract/src/lib.rs`, we need to first construct the path to the project root: `../..` (relative to second contract's Cargo.toml file location) followed by path to the wasm file: -```bash +``` ../../../target/wasm32-unknown-unknown/release/add_contract.wasm ``` It can now be used in the rust code: -```rust -// contracts/main_contract/src/lib.rs +```rust title="contracts/main_contract/src/lib.rs" #![no_std] use soroban_sdk::{contract, contractimpl, Address, Env}; @@ -135,8 +124,7 @@ mod test; Here, main contract will invoke `ContractAdd`'s `add` function to calculate the sum of 2 numbers. It's a good idea to update tests for our main contract as well: -```rust -// contracts/main_contract/src/test.rs +```rust title="contracts/main_contract/src/test.rs" #![cfg(test)] use crate::{contract_add, ContractMain, ContractMainClient}; @@ -161,18 +149,16 @@ fn test_adding_cross_contract() { } ``` -Contracts can now be re-complied: +Contracts can now be re-complied running the following command from the project root: -```bash -cd $PROJECT_ROOT +``` stellar contract build ``` -And check that the test is working correctly: +And check that the test is working correctly, running tests in `contracts/main_contract`: -```bash -$ cd contracts/main_contract -$ cargo test +``` +cargo test running 1 test test test::test_adding_cross_contract ... ok @@ -194,10 +180,23 @@ ADD_CONTRACT=`stellar contract deploy --network testnet --wasm target/wasm32-unk MAIN_CONTRACT=`stellar contract deploy --network testnet --wasm target/wasm32-unknown-unknown/release/main_contract.wasm` ``` -And finally call the main contract: +This contracts can also be referenced using aliases after running following command: ```bash -$ stellar contract invoke --id $MAIN_CONTRACT --network testnet -- add_with --contract $ADD_CONTRACT --x 9 --y 10 +stellar contract alias add --id $ADD_CONTRACT add_contract +stellar contract alias add --id MAIN_CONTRACT main_contract +``` + +And finally call the main contract: + +:::info + +Referencing contract arguments by aliases is not supported in the latest stable version yet, but it's still possible to use a corresponding environmental variable instead + +::: + +``` +$ stellar contract invoke --id main_contract --network testnet -- add_with --contract add_contract --x 9 --y 10 ℹ️ Send skipped because simulation identified as read-only. Send by rerunning with `--send=yes`. 19 ``` @@ -206,16 +205,14 @@ $ stellar contract invoke --id $MAIN_CONTRACT --network testnet -- add_with --co As the next step, we can abstract away add contract and allow it to have multiple implementations. Main contract will in turn use the contract interface that is not bound to its implementation. -```bash -cd $PROJECT_ROOT +``` stellar contract init . --name adder_interface stellar contract init . --name add_extra_contract ``` First, let's create an interface and change our existing implementation to use this interface: -```rust -// contracts/adder_interface/src/lib.rs +```rust title="contracts/adder_interface/src/lib.rs" #![no_std] use soroban_sdk::contractclient; @@ -228,8 +225,7 @@ pub trait ContractAInterface { To use the interface definition our workspace members will now have an `adder_interface` as a dependency: -```toml -# $PROJECT_PATH/Cargo.toml +```toml title="./Cargo.toml" # <...> [workspace.dependencies] soroban-sdk = "21.0.0" @@ -237,8 +233,7 @@ adder-interface = { path = "contracts/adder_interface" } # <...> ``` -```toml -# $PROJECT_PATH/contracts/add_contract/Cargo.toml +```toml title="./contracts/add_contract/Cargo.toml" # <...> [dependencies] soroban-sdk = { workspace = true } @@ -246,8 +241,7 @@ adder-interface = {workspace = true} # <...> ``` -```toml -# $PROJECT_PATH/contracts/main_contract/Cargo.toml +```toml title="./contracts/main_contract/Cargo.toml" # <...> [dependencies] soroban-sdk = { workspace = true } @@ -255,8 +249,7 @@ adder-interface = {workspace = true} # <...> ``` -```toml -# $PROJECT_PATH/contracts/add_extra_contract/Cargo.toml +```toml title="./contracts/add_extra_contract/Cargo.toml" # <...> [dependencies] soroban-sdk = { workspace = true } @@ -266,16 +259,14 @@ adder-interface = {workspace = true} And change lib type of `adder_interface` crate: -```toml -# $PROJECT_PATH/contracts/adder_interface/Cargo.toml +```toml title="./contracts/adder_interface/Cargo.toml" # <...> [lib] crate-type = ["rlib"] # <...> ``` -```rust -# $PROJECT_PATH/contracts/adder_interface/src/lib.rs +```rust title="./contracts/adder_interface/src/lib.rs" #![no_std] use soroban_sdk::contractclient; @@ -287,8 +278,7 @@ pub trait Adder { ``` -```rust -// contracts/add_contract/src/lib.rs +```rust title="contracts/add_contract/src/lib.rs" #![no_std] use soroban_sdk::{contract, contractimpl}; use adder_interface::Adder; @@ -305,8 +295,7 @@ impl Adder for ContractAdd { ``` -```rust -// contracts/main_contract/src/lib.rs +```rust title="contracts/main_contract/src/lib.rs" #![no_std] use soroban_sdk::{contract, contractimpl, Address, Env}; @@ -328,8 +317,7 @@ mod test; And update test imports: -```rust -// contracts/main_contract/src/test.rs +```rust title="contracts/main_contract/src/test.rs" #![cfg(test)] use crate::{ContractMain, ContractMainClient}; @@ -345,8 +333,7 @@ mod contract_add { As the final step we can create an alternative `Adder` implementation that adds an extra 1: -```rust -// contracts/add_extra_contract/src/lib.rs +```rust title="contracts/add_extra_contract/src/lib.rs" #![no_std] use soroban_sdk::{contract, contractimpl}; use adder_interface::Adder; @@ -366,18 +353,18 @@ We can now deploy this contracts and test the new behavior: ```bash stellar contract build -ADD_CONTRACT=`stellar contract deploy --network testnet --wasm target/wasm32-unknown-unknown/release/add_contract.wasm` -WRONG_MATH_CONTRACT=`stellar contract deploy --network testnet --wasm target/wasm32-unknown-unknown/release/add_extra_contract.wasm` -MAIN_CONTRACT=`stellar contract deploy --network testnet --wasm target/wasm32-unknown-unknown/release/main_contract.wasm` +stellar contract alias add --id `stellar contract deploy --network testnet --wasm target/wasm32-unknown-unknown/release/add_contract.wasm` add_contract +stellar contract alias add --id `stellar contract deploy --network testnet --wasm target/wasm32-unknown-unknown/release/add_extra_contract.wasm` wrong_math_contract +stellar contract alias add --id `stellar contract deploy --network testnet --wasm target/wasm32-unknown-unknown/release/main_contract.wasm` main_contract ``` Now let's try to do sum 2 unsigned integers causing an overflow: -```bash -$ stellar contract invoke --id $MAIN_CONTRACT --network testnet -- add_with --contract $ADD_CONTRACT --x 2 --y 2 +``` +$ stellar contract invoke --id main_contract --network testnet -- add_with --contract add_contract --x 2 --y 2 ℹ️ Send skipped because simulation identified as read-only. Send by rerunning with `--send=yes`. 4 -$ stellar contract invoke --id $MAIN_CONTRACT --network testnet -- add_with --contract $WRONG_MATH_CONTRACT --x 2 --y 2 +$ stellar contract invoke --id main_contract --network testnet -- add_with --contract wrong_math_contract --x 2 --y 2 ℹ️ Send skipped because simulation identified as read-only. Send by rerunning with `--send=yes`. 5 ```