Skip to content

RWA: country compliance modules#650

Open
pasevin wants to merge 8 commits intoOpenZeppelin:mainfrom
pasevin:feat/rwa-country-standalone
Open

RWA: country compliance modules#650
pasevin wants to merge 8 commits intoOpenZeppelin:mainfrom
pasevin:feat/rwa-country-standalone

Conversation

@pasevin
Copy link
Copy Markdown
Contributor

@pasevin pasevin commented Mar 23, 2026

Country Compliance Modules

This PR introduces two country-based compliance modules for RWA token management:

Country Allow

Only recipients whose identity has at least one country code present in the allowlist may receive tokens. Administrators can configure which countries are allowed per token, either individually or in batch.

Country Restrict

Recipients whose identity has a country code on the restriction list are blocked from receiving tokens. Administrators can configure which countries are restricted per token, either individually or in batch.

Transplant the reviewed country compliance modules and example crates onto
upstream/main so they can ship as an independent PR without the old stack.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 23, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d737ce9d-913d-4b50-802e-620737de844b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR introduces two new RWA compliance modules—CountryAllow and CountryRestrict—with corresponding Soroban contract examples. Both modules validate token transfers and mints against Identity Registry Storage country data: one enforces an allowlist, the other a blocklist. The implementation includes trait definitions, storage modules, example contracts, and documentation.

Changes

Cohort / File(s) Summary
Workspace & Crate Configuration
Cargo.toml, examples/rwa-country-allow/Cargo.toml, examples/rwa-country-restrict/Cargo.toml
Added two new example crates to workspace members and created matching Cargo.toml manifests with cdylib/rlib library targets and workspace dependency resolution.
CountryAllow Module (Core)
packages/tokens/src/rwa/compliance/modules/country_allow/mod.rs, packages/tokens/src/rwa/compliance/modules/country_allow/storage.rs
Implemented CountryAllow trait with country allowlist validation logic. Validates transfers/mints by querying IRS for recipient country data and checking membership in per-token allowlists. Includes storage helpers for (token, country) key-value pairs and publishes CountryAllowed/CountryUnallowed events.
CountryRestrict Module (Core)
packages/tokens/src/rwa/compliance/modules/country_restrict/mod.rs, packages/tokens/src/rwa/compliance/modules/country_restrict/storage.rs
Implemented CountryRestrict trait with country restriction list validation logic. Blocks transfers/mints when recipient's IRS country data matches any restricted country. Mirror implementation to CountryAllow with inverted logic. Publishes CountryRestricted/CountryUnrestricted events and includes matching storage helpers.
CountryAllow Example Contract
examples/rwa-country-allow/src/lib.rs, examples/rwa-country-allow/README.md
Created concrete example contract implementing CountryAllow trait with admin/compliance authorization gating, IRS configuration, allowlist management (single and batch updates), and one-time compliance address handoff. Delegates to trait helpers for persistence.
CountryRestrict Example Contract
examples/rwa-country-restrict/src/lib.rs, examples/rwa-country-restrict/README.md
Created concrete example contract implementing CountryRestrict trait with identical authorization and lifecycle patterns to CountryAllow example but enforcing restriction blocklist logic. Mirrors structure for consistency.
Module Registry
packages/tokens/src/rwa/compliance/modules/mod.rs
Exported two new public submodules (country_allow and country_restrict) in compliance modules index.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • RWA: examples #614: Adjusts root Cargo.toml workspace membership to include RWA example crates, directly overlapping with workspace configuration changes in this PR.
  • Rwa reorg #620: Restructures the packages/tokens/src/rwa/compliance/modules/ module namespace that this PR extends with concrete country_allow and country_restrict implementations.
  • feat(rwa): add compliance module base architecture #607: Establishes foundational RWA compliance module infrastructure (helpers, errors, TTL constants) that both trait implementations in this PR build upon.

Suggested labels

Small

Suggested reviewers

  • brozorec
  • ozgunozerk

Poem

🐰✨ Allowlists and blocklists, oh what a sight!
Countries dancing left and right,
With IRS checks and compliance grace,
Two modules find their rightful place!
The rabbit hops with RWA delight! 🚀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding country-based compliance modules (country_allow and country_restrict) for RWA tokens, which is the primary focus of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description provides a clear summary of the two country-based compliance modules being added, with references to the T-REX source implementations, but it lacks detail on tests, documentation, and deployment verification.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
packages/tokens/src/rwa/compliance/modules/country_restrict/storage.rs (1)

19-28: Optional: Consider extracting a shared storage helper.

The country_allow and country_restrict storage modules share nearly identical logic (read with TTL extension, set with TTL, remove). If more country-based modules are anticipated, a generic helper could reduce duplication. However, keeping them separate is reasonable for clarity and independent evolution.

Also applies to: 37-41, 50-54

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/tokens/src/rwa/compliance/modules/country_restrict/storage.rs`
around lines 19 - 28, Extract the repeated TTL-aware storage operations into a
shared helper to avoid duplication across the country_allow and country_restrict
modules: create a small utility (e.g., functions like get_with_ttl_extend,
set_with_ttl, remove_with_ttl) that accepts the Env reference, a storage key
type (used by CountryRestrictStorageKey and the country_allow key), TTL
threshold and extend amounts, and a closure/type param for the value; then
replace the body of is_country_restricted (and the analogous functions at the
other locations) to call get_with_ttl_extend instead of duplicating
e.storage().persistent().get(...).inspect(...).unwrap_or_default(), and use
set_with_ttl/remove_with_ttl for writes and deletes so all TTL logic is
centralized and consistent.
examples/rwa-country-restrict/src/lib.rs (1)

3-3: Remove unused String import.

The String type is imported but never used in this file.

Suggested fix
-use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String, Vec};
+use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, Vec};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/rwa-country-restrict/src/lib.rs` at line 3, The import list from
soroban_sdk includes an unused symbol `String`; remove `String` from the `use
soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String, Vec};`
statement so the import becomes only the actually used symbols (`contract`,
`contractimpl`, `contracttype`, `Address`, `Env`, `Vec`), eliminating the unused
`String` import and any related compiler warnings.
examples/rwa-country-allow/src/lib.rs (1)

3-3: Remove unused String import.

The String type is imported but never used in this file.

Suggested fix
-use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String, Vec};
+use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, Vec};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/rwa-country-allow/src/lib.rs` at line 3, Remove the unused String
import from the module-level use statement: in the use declaration that
currently lists contract, contractimpl, contracttype, Address, Env, String, Vec
remove the String identifier so only used symbols remain; ensure no other code
references String in this file before committing the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@examples/rwa-country-allow/src/lib.rs`:
- Line 3: Remove the unused String import from the module-level use statement:
in the use declaration that currently lists contract, contractimpl,
contracttype, Address, Env, String, Vec remove the String identifier so only
used symbols remain; ensure no other code references String in this file before
committing the change.

In `@examples/rwa-country-restrict/src/lib.rs`:
- Line 3: The import list from soroban_sdk includes an unused symbol `String`;
remove `String` from the `use soroban_sdk::{contract, contractimpl,
contracttype, Address, Env, String, Vec};` statement so the import becomes only
the actually used symbols (`contract`, `contractimpl`, `contracttype`,
`Address`, `Env`, `Vec`), eliminating the unused `String` import and any related
compiler warnings.

In `@packages/tokens/src/rwa/compliance/modules/country_restrict/storage.rs`:
- Around line 19-28: Extract the repeated TTL-aware storage operations into a
shared helper to avoid duplication across the country_allow and country_restrict
modules: create a small utility (e.g., functions like get_with_ttl_extend,
set_with_ttl, remove_with_ttl) that accepts the Env reference, a storage key
type (used by CountryRestrictStorageKey and the country_allow key), TTL
threshold and extend amounts, and a closure/type param for the value; then
replace the body of is_country_restricted (and the analogous functions at the
other locations) to call get_with_ttl_extend instead of duplicating
e.storage().persistent().get(...).inspect(...).unwrap_or_default(), and use
set_with_ttl/remove_with_ttl for writes and deletes so all TTL logic is
centralized and consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a63ad065-ba2e-4f2b-b4b6-cecc0c0c99a3

📥 Commits

Reviewing files that changed from the base of the PR and between ff17d24 and 30c56ef.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • Cargo.toml
  • examples/rwa-country-allow/Cargo.toml
  • examples/rwa-country-allow/README.md
  • examples/rwa-country-allow/src/lib.rs
  • examples/rwa-country-restrict/Cargo.toml
  • examples/rwa-country-restrict/README.md
  • examples/rwa-country-restrict/src/lib.rs
  • packages/tokens/src/rwa/compliance/modules/country_allow/mod.rs
  • packages/tokens/src/rwa/compliance/modules/country_allow/storage.rs
  • packages/tokens/src/rwa/compliance/modules/country_restrict/mod.rs
  • packages/tokens/src/rwa/compliance/modules/country_restrict/storage.rs
  • packages/tokens/src/rwa/compliance/modules/mod.rs

Align the country compliance modules with the repository's preferred test
layout by extracting inline test modules into dedicated test.rs files.
Comment thread examples/rwa-country-allow/src/lib.rs Outdated
Comment thread examples/rwa-country-allow/src/lib.rs Outdated
}

#[contractimpl(contracttrait)]
impl CountryAllow for CountryAllowContract {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think all the implementations below can be skipped with default implementations. The only problem may be require_module_admin_or_compliance_auth, but I think we can workaround that. In fact, that limitation itself requires a bit discussion imo

Comment thread examples/rwa-country-allow/README.md Outdated
Copy link
Copy Markdown
Collaborator

@ozgunozerk ozgunozerk left a comment

Choose a reason for hiding this comment

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

examples are not done in the light of our other examples. The structure is missing, there is only lib file, and an unwanted readme.
There isn't contract, nor tests.

pasevin added 5 commits April 10, 2026 12:35
Move country_allow and country_restrict module logic into public free
functions in storage.rs. Remove per-module contracttrait definitions
from mod.rs. Restructure examples into canonical lib.rs / contract.rs
layout. Delete example READMEs.
@pasevin
Copy link
Copy Markdown
Contributor Author

pasevin commented Apr 10, 2026

examples are not done in the light of our other examples. The structure is missing, there is only lib file, and an unwanted readme. There isn't contract, nor tests.

Thanks, I updated this to be closer to the structure of the other examples.

The example now has the usual split, the extra README is gone, and I added local tests for both country examples so they’re easier to review and a bit more complete.

I left the repeated methods as-is for now instead of trying to refactor that part in this PR. That felt like a bigger cleanup than I wanted to mix into what was meant to stay a structural-only pass. If you want, I can take that on next in a separate follow-up.

Align the country standalone examples and module docs with the repo's
compliance-module conventions.

Add example-local coverage for the admin/compliance auth boundary so the
reviewed structure is verified without changing module behavior.
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.

2 participants