Skip to content

Commit

Permalink
Address PR comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Ifropc committed Dec 3, 2024
1 parent 60172b4 commit 39b88ef
Showing 1 changed file with 51 additions and 64 deletions.
115 changes: 51 additions & 64 deletions docs/build/guides/conventions/workspace.mdx
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -10,18 +10,15 @@ 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
```

Running this command will create a root project directory (`soroban-project`) and then initialize a project workspace with a single contract named `add_contract`.

Adding one more contract template to the project can be done using the same command:

```bash
```
stellar contract init . --name main_contract
```

Expand All @@ -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};

Expand All @@ -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
```

Expand All @@ -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};
Expand All @@ -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};
Expand All @@ -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
Expand All @@ -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
```
Expand All @@ -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;
Expand All @@ -228,35 +225,31 @@ 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"
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 }
adder-interface = {workspace = true}
# <...>
```

```toml
# $PROJECT_PATH/contracts/main_contract/Cargo.toml
```toml title="./contracts/main_contract/Cargo.toml"
# <...>
[dependencies]
soroban-sdk = { workspace = true }
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 }
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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};
Expand All @@ -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};
Expand All @@ -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;
Expand All @@ -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
```

0 comments on commit 39b88ef

Please sign in to comment.