Home of Stellarcarbon's Soroban smart contracts
This repository uses the recommended structure for a Soroban project:
.
├── contracts
│ └── sink_carbon
│ ├── src
│ │ ├── tests/
│ │ ├── contract.rs
│ │ ├── errors.rs
│ │ ├── lib.rs
│ │ ├── storage_types.rs
│ │ └── utils.rs
│ ├── Cargo.toml
│ └── README.md
├── Cargo.toml
└── README.md
If you haven't worked on Soroban contracts before, you'll need to set up a local development environment. Start with the excellent Soroban documentation. We've reused some snippets here, under the Apache-2.0 license.
Run cargo test to run the test suite.
cargo testOr, to display backtraces when there are failures:
RUST_BACKTRACE=1 cargo testYou should see output similar to:
running 16 tests
test tests::test_sink_carbon::test_quantize_to_kg ... ok
test tests::test_sink_carbon::test_funder_balance_too_low ... ok
test tests::test_sink_carbon::test_sink_carbon_separate_recipient ... ok
test tests::test_sink_carbon::test_funder_account_or_trustline_missing ... ok
...
We use mutation testing
to identify code that is poorly tested.
Run cargo mutants to execute the test suite with mutants of the contract code.
Contract code that can be mutated without having an effect on the test outcomes tends to indicate
that this line isn't yet being tested properly.
First install cargo-mutants globally:
cargo install cargo-mutantsThen, you should be able to:
cargo mutants --profile=mutantsIf such lack of test coverage is found, you should see output similar to:
Found 33 mutants to test
ok Unmutated baseline in 15.7s build + 0.4s test
INFO Auto-set test timeout to 20s
MISSED contracts/sink_carbon/src/storage_types.rs:4:51: replace * with + in 0.8s build + 0.4s test
MISSED contracts/sink_carbon/src/storage_types.rs:18:5: replace extend_instance_ttl with () in 0.8s build + 0.4s test
MISSED contracts/sink_carbon/src/storage_types.rs:5:71: replace - with / in 0.8s build + 0.4s test
33 mutants tested in 52s: 3 missed, 28 caught, 2 unviable
Cargo-mutants can be slow to complete with a vanilla cargo build setup. See the guide on improving performance to speed up these runs.
To build a smart contract to deploy or run, use the stellar contract build command.
stellar contract buildIf you get an error like can't find crate for 'core', it means you didn't install the wasm32 target
during the Soroban setup.
You can fix it by running rustup target add wasm32v1-none.
Use stellar contract optimize to further minimize the size of the .wasm.
First, re-install stellar-cli with the opt feature:
cargo install --locked stellar-cli --features optThen build an optimized .wasm file:
stellar contract optimize --wasm target/wasm32v1-none/release/sink_carbon.wasmThis will optimize and output a new sink_carbon.optimized.wasm file in the same location as the input .wasm.
The release process is incorporated into CI/CD using the soroban-build-workflow. To deploy the latest release to testnet, first download it from GitHub. Deploying a locally built WASM can break the attestations that are generated by the workflow.
Upload the contract WASM bytes to the network with stellar-cli:
stellar contract upload \
--network testnet \
--source <SOURCE_ACCOUNT> \
--wasm ~/Downloads/sink-carbon_v0.4.0.wasmThe (stdout) output is the WASM hash:
ℹ️ Simulating install transaction…
ℹ️ Signing transaction: d8de6f2a24633ab73d0d37bcc0c4a4c75d5dbbd281d837ec82daad49771b6cec
🌎 Submitting install transaction…
0f82a7cacf4085a42905b9a54767ecc06d1132ee03ffef7018fbfb15f32be217
The contract configuration is very important. The CarbonSINK issuer must be set as the contract admin,
because it's going to delegate its own admin privileges to the SinkContract. There is a recovery
mechanism that can set the CarbonSINK SAC admin, which expects the contract admin to be the CarbonSINK
issuer.
PREV_SINK=$(stellar contract alias show sink --network testnet) # only applies for upgrades
CARBON_ISSUER="GDT5XM5C5STQZS5R3F4CEGKJWKDVWBIWBEV4TIYV5MDVVMKA775T4OKY"
CSINK_ISSUER="GBO66IRGFZE7UP7MAM5H5IBMZLTM64XE6YNOL4KSL2BFVH7JW6AEKZHO"
CARBON_SAC="$(stellar contract id asset --network testnet --asset CARBON:$CARBON_ISSUER)"
CSINK_SAC="$(stellar contract id asset --network testnet --asset CarbonSINK:$CSINK_ISSUER)"
stellar contract deploy \
--wasm-hash <HASH> \
--network testnet \
--source <SOURCE_ACCOUNT> \
--alias sink \
-- \
--admin $CSINK_ISSUER \
--carbon_id $CARBON_SAC \
--carbonsink_id $CSINK_SACThe (stdout) output is the contract address of the instance you've just deployed:
ℹ️ Contract alias 'sink' references CAVS7HEUNFCMOW6DC7EBY7J6HNFJ5JJ7LV4H7RPUC6V5QO5OMS7AQLD5 on network 'Test SDF Network ; September 2015'
ℹ️ Using wasm hash 0f82a7cacf4085a42905b9a54767ecc06d1132ee03ffef7018fbfb15f32be217
ℹ️ Simulating deploy transaction…
ℹ️ Transaction hash is 824b0ec0402b31ae5d1ca3db4cbff72ac098f6036a6e0af6df436995fc771c11
🔗 https://stellar.expert/explorer/testnet/tx/824b0ec0402b31ae5d1ca3db4cbff72ac098f6036a6e0af6df436995fc771c11
ℹ️ Signing transaction: 824b0ec0402b31ae5d1ca3db4cbff72ac098f6036a6e0af6df436995fc771c11
🌎 Submitting deploy transaction…
🔗 https://stellar.expert/explorer/testnet/contract/CBDWJLGQPU3DOYCMVPYF56QFC7ISSC633QSJUSBJIXM6RJBTGDZVA27P
✅ Deployed!
⚠️ Overwriting existing alias "sink" that currently links to contract ID: CAVS7HEUNFCMOW6DC7EBY7J6HNFJ5JJ7LV4H7RPUC6V5QO5OMS7AQLD5
CBDWJLGQPU3DOYCMVPYF56QFC7ISSC633QSJUSBJIXM6RJBTGDZVA27P
Within stellar-cli, the contract is now also available under the sink alias.
Finally, we need to make the contract address the CarbonSINK SAC admin. Configuring the contracts this way,
achieves that the CarbonSINK SAC can automatically authorize the SAC sub-calls within sink_carbon.
# Reset CarbonSINK SAC admin to its own issuer account
if [ -n "$PREV_SINK" ]; then
stellar contract invoke \
--network testnet \
--source <CSINK_ISSUER_SECRET> \
--id $PREV_SINK \
-- \
reset_admin
fi
# Set the new SinkContract as the CarbonSINK SAC admin
stellar contract invoke \
--network testnet \
--source <CSINK_ISSUER_SECRET> \
--id $CSINK_SAC \
-- \
set_admin \
--new_admin $(stellar contract alias show sink --network testnet)When successful, the output shows a set_admin event:
Contract alias 'sink' references CBDWJLGQPU3DOYCMVPYF56QFC7ISSC633QSJUSBJIXM6RJBTGDZVA27P
on network 'Test SDF Network ; September 2015'
ℹ️ Signing transaction: d4a213b34b787da798c508a5f149b812bd8b04a09011fc4701698bb8cb4933a7
📅 CCUQDX22YTF72Q2F5C4HZSWVMBFTPTLIYXOC3BSNTBSZVJWKMMNUOWXH - Event:
[
{"symbol":"set_admin"},
{"address":"GBO66IRGFZE7UP7MAM5H5IBMZLTM64XE6YNOL4KSL2BFVH7JW6AEKZHO"},
{"string":"CarbonSINK:GBO66IRGFZE7UP7MAM5H5IBMZLTM64XE6YNOL4KSL2BFVH7JW6AEKZHO"}
] =
{"address":"CBDWJLGQPU3DOYCMVPYF56QFC7ISSC633QSJUSBJIXM6RJBTGDZVA27P"}
If the new contract replaces a previously deployed contract, set its successor to complete the upgrade process:
stellar contract invoke \
--network testnet \
--source <CSINK_ISSUER_SECRET> \
--id $PREV_SINK \
-- \
set_contract_successor
--successor $(stellar contract alias show sink --network testnet)We use Retroshades to emit events instead of native Soroban events. Deploy a slimmed-down version of the sink carbon contract to Mercury with:
./deploy-retroshades.shYou'll need to have your own Mercury account and API key to do this. The retroshade will listen to the invocations of the "sink" alias from the stellar-cli by default.