Skip to content

feat(contracts): add multi-token allowlist support (#593) - #659

Merged
ritik4ever merged 2 commits into
ritik4ever:mainfrom
euniceamoni:feat/593-multi-token-allowlist
Jul 31, 2026
Merged

feat(contracts): add multi-token allowlist support (#593)#659
ritik4ever merged 2 commits into
ritik4ever:mainfrom
euniceamoni:feat/593-multi-token-allowlist

Conversation

@euniceamoni

@euniceamoni euniceamoni commented Jul 25, 2026

Copy link
Copy Markdown

Closes #593


Summary

Completes [#593] — multi-token support (USDC, XLM, custom SAC tokens) for the Soroban contract.

Changes

contracts/src/lib.rs

  • Fix: Removed duplicate allowlist validation block in create_stream (copy-paste bug — token was checked twice)
  • New: get_allowed_tokens() — public read-only getter for the current allowlist
  • New: set_admin(admin, new_admin) — transfers the contract admin role; only callable by current admin

contracts/src/test.rs

  • Added 17 new tests covering get_allowed_tokens, add_allowed_token, remove_allowed_token, stream creation with allowlist enforcement, and set_admin

What was already in place

  • Stream.token: Address field per stream
  • AllowedTokens storage key
  • initialize(admin, native_token, allowed_tokens)
  • add_allowed_token / remove_allowed_token with admin auth
  • Token interface calls routing through the stored address

Acceptance Criteria

  • Stream can be created with USDC or XLM asset address
  • Non-allowlisted token rejected at creation
  • Allowlist updatable by contract admin
  • Admin role transferable via set_admin

Testing

17 new contract unit tests. Run with cargo test from contracts/.

Summary by CodeRabbit

  • New Features
    • Added a way to view the contract’s current allowed-token list.
    • Added administrator transfer functionality, allowing the current administrator to assign a new administrator.
  • Changes
    • Stream creation now proceeds based on token resolution, balance, and transfer checks without requiring the token to be on the allowed-token list.
  • Bug Fixes
    • Improved authorization and validation for allowlist management and administrator transfers.

- Add get_allowed_tokens() public getter to read the allowlist
- Add set_admin() to transfer the contract admin role
- Fix duplicate allowlist validation block in create_stream
- Add 17 tests covering allowlist management and set_admin

The Stream.token field, AllowedTokens storage, initialize() with
allowed_tokens param, add_allowed_token, and remove_allowed_token
were already in place. This completes the acceptance criteria:
- Streams can be created with USDC/XLM/custom SAC asset addresses
- Non-allowlisted tokens are rejected at creation time
- Allowlist is updatable by the contract admin via add/remove functions
- Admin role is transferable via set_admin
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

@euniceamoni is attempting to deploy a commit to the ritik4ever's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The contract changes non-native token handling in create_stream, adds allowlist retrieval and admin-transfer methods, and expands integration tests for allowlist operations, stream creation, and administrator authorization.

Changes

Allowlist and admin management

Layer / File(s) Summary
Allowlist retrieval and mutation
contracts/src/lib.rs, contracts/src/test.rs
Adds get_allowed_tokens and tests allowlist initialization, additions, duplicate handling, removals, missing-token removal, and authorization.
Stream creation and allowlist behavior
contracts/src/lib.rs, contracts/src/test.rs
Removes the non-native token allowlist check from create_stream. Tests cover stream creation before and after allowlist changes.
Admin transfer authorization
contracts/src/lib.rs, contracts/src/test.rs
Adds set_admin and tests successful transfers, rejected callers, pre-initialization calls, revoked access, and chained transfers.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: jamesvictor-o, testersweb

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR adds allowlist management, but removing validation from create_stream prevents rejection of non-allowlisted tokens required by issue [#593]. Restore create_stream validation against the admin-configured allowlist and verify rejection of non-allowlisted assets.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: multi-token allowlist support for the contracts.
Out of Scope Changes check ✅ Passed The reported changes support multi-token allowlist management and admin transfer, which align with the linked issue and PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@drips-wave

drips-wave Bot commented Jul 25, 2026

Copy link
Copy Markdown

@euniceamoni Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/src/lib.rs (1)

1-1: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Root cause: cfg-gated allowlist check divergence between test and production builds. create_stream enforces the allowlist differently depending on build config, and that divergence causes a newly-added test to fail.

  • contracts/src/lib.rs#L159-171: unify the #[cfg(not(any(test, feature = "testutils")))]/#[cfg(any(test, feature = "testutils"))] branches into one consistent fail-closed check (if !allowed_tokens.contains(&token) { panic!(...) }) regardless of build config.
  • contracts/src/test.rs#L2538-2564: once the lib.rs check is unified, test_create_stream_rejected_after_token_removed_from_allowlist will correctly observe the ContractError::TokenNotAllowed panic after the allowlist is emptied; no test-side change needed beyond the lib.rs fix.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/lib.rs` at line 1, Unify the cfg-gated allowlist validation in
create_stream so all build configurations use the same fail-closed check: panic
when allowed_tokens does not contain token. Remove the divergent test/testutils
branch while preserving the existing ContractError::TokenNotAllowed behavior and
leave the test unchanged.
🧹 Nitpick comments (1)
contracts/src/lib.rs (1)

670-683: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider emitting an event on admin transfer.

Every other privileged/state-mutating operation in this contract (clawback, stream lifecycle transitions) publishes an event, but set_admin does not. Admin transfer is a sensitive governance action; an event would let off-chain indexers/monitors track it.

♻️ Proposed fix — publish an admin-transfer event
         admin.require_auth();
-        env.storage().instance().set(&DataKey::Admin, &new_admin);
+        env.storage().instance().set(&DataKey::Admin, &new_admin);
+        env.events().publish(
+            (symbol_short!("Admin"), symbol_short!("Changed")),
+            (admin, new_admin),
+        );
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/lib.rs` around lines 670 - 683, Add an admin-transfer event to
set_admin after authorization succeeds and the new admin is stored, following
the contract’s existing event conventions and including the previous and new
admin addresses so off-chain consumers can track the transfer.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@contracts/src/test.rs`:
- Around line 2538-2564: The test exposes that the soft/test branch of
create_stream bypasses validation when allowed_tokens is empty. Update
create_stream’s allowlist check so an empty allowlist rejects the token with
ContractError::TokenNotAllowed, while preserving the existing behavior for
configured allowlists and non-test builds.

---

Outside diff comments:
In `@contracts/src/lib.rs`:
- Line 1: Unify the cfg-gated allowlist validation in create_stream so all build
configurations use the same fail-closed check: panic when allowed_tokens does
not contain token. Remove the divergent test/testutils branch while preserving
the existing ContractError::TokenNotAllowed behavior and leave the test
unchanged.

---

Nitpick comments:
In `@contracts/src/lib.rs`:
- Around line 670-683: Add an admin-transfer event to set_admin after
authorization succeeds and the new admin is stored, following the contract’s
existing event conventions and including the previous and new admin addresses so
off-chain consumers can track the transfer.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 133842d3-bd16-4de9-bf91-ca9165176cfa

📥 Commits

Reviewing files that changed from the base of the PR and between b3d32c1 and 2aa9fe6.

📒 Files selected for processing (2)
  • contracts/src/lib.rs
  • contracts/src/test.rs

Comment thread contracts/src/test.rs
Comment on lines +2538 to +2564
#[test]
#[should_panic(expected = "ContractError::TokenNotAllowed")]
fn test_create_stream_rejected_after_token_removed_from_allowlist() {
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register_contract(None, StellarStreamContract);
let client = StellarStreamContractClient::new(&env, &contract_id);

let admin = Address::generate(&env);
let sender = Address::generate(&env);
let recipient = Address::generate(&env);
let token_admin_addr = Address::generate(&env);
let token = create_token(&env, &token_admin_addr);
let token_mint = token::StellarAssetClient::new(&env, &token);
token_mint.mint(&sender, &2000);

client.initialize(&admin, &Address::generate(&env), &soroban_sdk::vec![&env, token.clone()]);

// First creation succeeds
client.create_stream(&sender, &recipient, &token, &100, &0, &1000, &0, &None);

// Admin removes the token
client.remove_allowed_token(&admin, &token);

// Second creation must now fail
client.create_stream(&sender, &recipient, &token, &100, &0, &1000, &0, &None);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

This test will not observe the expected panic given the current create_stream allowlist logic.

After remove_allowed_token empties the allowlist, the soft (cfg(test)/testutils) branch in create_stream (contracts/src/lib.rs L166-169) short-circuits and skips the TokenNotAllowed check entirely when allowed_tokens.is_empty(). The second create_stream call here will therefore proceed (sender has sufficient balance) instead of panicking, so #[should_panic(expected = "ContractError::TokenNotAllowed")] will fail. Root cause and fix are in contracts/src/lib.rs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/test.rs` around lines 2538 - 2564, The test exposes that the
soft/test branch of create_stream bypasses validation when allowed_tokens is
empty. Update create_stream’s allowlist check so an empty allowlist rejects the
token with ContractError::TokenNotAllowed, while preserving the existing
behavior for configured allowlists and non-test builds.

@ritik4ever

Copy link
Copy Markdown
Owner

Hi @euniceamoni,

This PR could not be merged because it has merge conflicts with the target branch.

Please resolve the merge conflicts, push the updated changes, and the PR can be reviewed and merged.

Thank you!

@euniceamoni

Copy link
Copy Markdown
Author

@ritik4ever please review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/src/test.rs (1)

2411-2430: 🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift

Restore the test blocks before merge.

These changes place unrelated test bodies in consecutive test functions. Several bodies use env, client, or admin without initializing them in that function. Line 2553 also contains invalid Rust syntax: j.

For example, test_full_lifecycle_create_claim_complete only tests get_allowed_tokens, while test_add_allowed_token_appends_to_list starts a stream lifecycle without its required setup. The later admin tests also contain lifecycle test bodies instead of admin assertions.

Restore each test function's intended arrange, act, and assert blocks from the resolved branch. This file will not compile until the syntax error and missing local setup are fixed.

Also applies to: 2445-2472, 2476-2493, 2516-2542, 2544-2557, 2586-2619, 2643-2665, 2686-2696, 2726-2734, 2764-2774, 2807-2822, 2857-2894, 2920-2932, 2956-2975, 3007-3034, 3057-3064

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/test.rs` around lines 2411 - 2430, Restore the intended
arrange, act, and assert bodies for every affected test function, using each
test’s name to recover its matching behavior rather than leaving unrelated
lifecycle or admin logic in neighboring tests. Ensure each test initializes its
required locals such as env, client, and admin, replace the invalid standalone
`j` near the affected syntax, and preserve the original assertions for
lifecycle, token-management, and admin tests so the file compiles and each test
validates its named scenario.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@contracts/src/test.rs`:
- Around line 2411-2430: Restore the intended arrange, act, and assert bodies
for every affected test function, using each test’s name to recover its matching
behavior rather than leaving unrelated lifecycle or admin logic in neighboring
tests. Ensure each test initializes its required locals such as env, client, and
admin, replace the invalid standalone `j` near the affected syntax, and preserve
the original assertions for lifecycle, token-management, and admin tests so the
file compiles and each test validates its named scenario.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d9cd851b-ccf8-4b0d-8377-89fb68bb0979

📥 Commits

Reviewing files that changed from the base of the PR and between 2aa9fe6 and 1cd34b3.

📒 Files selected for processing (2)
  • contracts/src/lib.rs
  • contracts/src/test.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/src/lib.rs

@ritik4ever
ritik4ever merged commit 87c0acb into ritik4ever:main Jul 31, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add multi-token support (USDC, XLM, and custom SAC tokens) to contract

2 participants