convert test-stablecoin to SAC - #71
Conversation
There was a problem hiding this comment.
Pull request overview
This PR repurposes the test-stablecoin Soroban crate from a standalone token contract into a test-only SAC (Stellar Asset Contract) administration wrapper that manages onboarding and compliance holds by toggling SAC authorization on a classic underlying asset.
Changes:
- Replaces the prior token implementation with
TestSacAdminContract, adding onboarding/block-list state tracking, role-gated admin actions, and wrapper-specific events. - Adds a README documenting the wrapper’s state model, public test API, and deliberate omissions.
- Updates the deploy script to deploy a classic asset SAC and transfer SAC admin to the wrapper; removes now-unneeded Rust dependencies.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| soroban/test-stablecoin/src/lib.rs | Replaces the token contract with a SAC admin wrapper and adds extensive integration tests. |
| soroban/test-stablecoin/README.md | Documents the wrapper’s test-only purpose, state model, and API. |
| soroban/test-stablecoin/Cargo.toml | Removes old token/access deps and adds crate description. |
| soroban/scripts/deploy.sh | Deploys classic asset SAC + wrapper and transfers SAC admin to the wrapper. |
| soroban/Cargo.lock | Removes lock entries for deleted dependencies. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| fn repeated_block_is_silent_noop() { | ||
| let s = setup(); | ||
| let user = Address::generate(&s.env); | ||
| s.contract.block_user(&user, &s.blocker); | ||
|
|
||
| s.contract.block_user(&user, &s.blocker); | ||
|
|
||
| client.block_user(&user, &blocker); | ||
| client.approve(&user, &spender, &100i128, &1000u32); | ||
| assert!(wrapper_events(&s).is_empty()); | ||
| } |
| fn wrapper_events(s: &Setup) -> std::vec::Vec<(Address, soroban_sdk::Vec<Val>, Val)> { | ||
| s.env | ||
| .events() | ||
| .all() | ||
| .iter() | ||
| .filter(|(address, _, _)| address == &s.contract.address) | ||
| .collect() | ||
| } |
patrickdappollonio
left a comment
There was a problem hiding this comment.
There are some comments from the bot but I'm not sure how important they are, considering this is testing only
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
soroban/test-stablecoin/src/lib.rs:480
repeated_block_is_silent_noopcurrently asserts that no wrapper events exist after callingblock_usertwice, but the firstblock_usercall should emitUserBlocked. This makes the test fail (or hides regressions) instead of verifying that the second call is a no-op.
s.contract.block_user(&user, &s.blocker);
s.contract.block_user(&user, &s.blocker);
assert!(wrapper_events(&s).is_empty());
| --source-account "$ISSUER_KEY" \ | ||
| --network "$NETWORK" \ | ||
| --id "$SAC_CONTRACT_ID" \ | ||
| -- \ | ||
| --name "\"${TOKEN_NAME}\"" \ | ||
| --symbol "\"${TOKEN_SYMBOL}\"" \ | ||
| --admin "$ADMIN_ADDRESS" \ | ||
| --manager "$MANAGER_ADDRESS" \ | ||
| --blocker "$BLOCKER_ADDRESS" \ | ||
| --initial_supply "$INITIAL_SUPPLY") | ||
|
|
||
| echo "Contract deployed: $CONTRACT_ID" | ||
|
|
||
| echo "" | ||
| echo "=== Deployment complete ===" | ||
| echo " Network: $NETWORK" | ||
| echo " Contract: $CONTRACT_ID" | ||
| echo " Admin: $ADMIN_ADDRESS" | ||
| echo " Manager: $MANAGER_ADDRESS" | ||
| echo " Blocker: $BLOCKER_ADDRESS" | ||
| echo " Token: $TOKEN_NAME ($TOKEN_SYMBOL)" | ||
| echo " Supply: $INITIAL_SUPPLY (6 decimals = 1,000 tokens)" | ||
| set_admin --new_admin "$WRAPPER_CONTRACT_ID" |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
soroban/test-stablecoin/src/lib.rs:497
- This test asserts that no wrapper events were emitted, but the first
block_usercall should publishUserBlocked. The intent seems to be verifying the second call is a no-op (no additional event), so the assertion should compare the event count before/after the repeated call.
s.contract.block_user(&user, &s.blocker);
s.contract.block_user(&user, &s.blocker);
assert!(wrapper_events(&s).is_empty());
| struct Setup { | ||
| env: Env, | ||
| contract: TestSacAdminContractClient<'static>, | ||
| sac: TokenClient<'static>, | ||
| sac_admin: StellarAssetClient<'static>, |
Yeah, I'm not worried about it for this particular use case. |
This PR updates the
test-stablecoinasset to instead function as a SAC (Stellar Asset Contract) with a soroban auth wrapper. It uses a classic stellar asset as the underlying token.This contract should only be used for testing purposes.