ERC-3643: T-REX Compliance
ERC-3643 (Token for Regulated EXchanges, or T-REX) is the standard for permissioned security tokens and real-world asset (RWA) tokenization. It enforces compliance rules at the token contract level — every transfer is checked against an on-chain compliance registry before execution. Tenzro integrates ERC-3643 with the TDIP identity system, allowing compliance checks to reference verified identity claims (KYC, accreditation, jurisdiction) directly from decentralized identities rather than requiring a separate off-chain compliance database.
How It Works
When a transfer is attempted on an ERC-3643 token, the token contract calls can_transfer() on the ComplianceRegistry before allowing execution. The registry evaluates a configurable set of compliance rules against the sender and recipient identities. If any rule fails, the transfer is rejected with a specific reason code.
Transfer Request
│
▼
┌──────────────────────┐
│ Token Contract │
│ (ERC-3643) │
│ transfer(to, amount)│
└──────────┬───────────┘
│ can_transfer(from, to, amount)?
▼
┌──────────────────────────────────────────┐
│ ComplianceRegistry │
│ │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │KYC Check│ │Accred. │ │Country │ │
│ │ Tier≥2 │ │Check │ │Restrict│ │
│ └────┬────┘ └────┬─────┘ └───┬────┘ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼─────┐ ┌───▼────┐ │
│ │Freeze │ │Amount │ │Holding │ │
│ │Check │ │Limit │ │Period │ │
│ └────┬────┘ └────┬─────┘ └───┬────┘ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼─────┐ ┌───▼────┐ │
│ │Max │ │Whitelist │ │Custom │ │
│ │Holders │ │Check │ │Rules │ │
│ └─────────┘ └──────────┘ └────────┘ │
│ │
│ TDIP Identity Resolution │
│ did:tenzro:human:{uuid} → claims │
└──────────────────────────────────────────┘
│
▼
✓ Transfer Allowed OR ✗ ComplianceViolationCompliance Checks
The ComplianceRegistry supports the following built-in compliance rules. Each rule can be enabled or disabled per token, and custom rules can be added through the plugin interface.
| Rule | Check | Failure Code |
|---|---|---|
| KYC Tier | Both sender and recipient must have TDIP identity with KYC tier at or above the configured minimum (default: Enhanced / tier 2) | INSUFFICIENT_KYC |
| Accredited Investor | Recipient must hold a valid AccreditedInvestor credential issued by a trusted issuer | NOT_ACCREDITED |
| Country Restriction | Sender and recipient country claims must not be in the blocked jurisdictions list | RESTRICTED_COUNTRY |
| Freeze | Neither sender nor recipient address is frozen by the token issuer | ADDRESS_FROZEN |
| Amount Limit | Transfer amount does not exceed the per-transaction maximum | EXCEEDS_AMOUNT_LIMIT |
| Holding Period | Sender has held the tokens for at least the configured minimum duration (e.g., 12 months for Reg D securities) | HOLDING_PERIOD_NOT_MET |
| Max Holders | Transferring to a new holder does not exceed the configured maximum holder count (e.g., 2000 for Reg D 506(c)) | MAX_HOLDERS_REACHED |
| Whitelist | Both sender and recipient are on the token's transfer whitelist | NOT_WHITELISTED |
Identity Claims and Trusted Issuers
ERC-3643 compliance on Tenzro uses TDIP verifiable credentials as the source of truth for identity claims. Each claim topic maps to a credential type stored on the identity. The compliance registry only trusts credentials issued by addresses in the trusted issuers list.
| Claim Topic | TDIP Credential Type | Description |
|---|---|---|
1 | KycAttestation | KYC verification level (Unverified, Basic, Enhanced, Full) |
2 | AccreditedInvestor | SEC Reg D accredited investor status |
3 | CountryOfResidence | ISO 3166-1 alpha-2 country code |
4 | QualifiedInvestor | MiFID II qualified/professional investor classification |
5 | InstitutionalInvestor | Institutional classification (fund, bank, insurance) |
6 | AmlScreening | Anti-money laundering screening clearance |
use tenzro_vm::compliance::{
ComplianceRegistry, ComplianceConfig, ComplianceRule,
TrustedIssuer, ClaimTopic,
};
use tenzro_identity::IdentityRegistry;
// Create compliance registry with TDIP identity integration
let identity_registry = IdentityRegistry::new();
let compliance = ComplianceRegistry::new(identity_registry.clone())?;
// Configure compliance rules for a security token
let config = ComplianceConfig::new()
.with_rule(ComplianceRule::KycTier { min_tier: 2 }) // Enhanced KYC
.with_rule(ComplianceRule::AccreditedInvestor) // Reg D
.with_rule(ComplianceRule::CountryRestriction {
blocked: vec!["US".into(), "KP".into(), "IR".into()], // Restricted jurisdictions
})
.with_rule(ComplianceRule::HoldingPeriod {
min_duration_days: 365, // 12-month lock-up
})
.with_rule(ComplianceRule::MaxHolders { limit: 2000 }) // Reg D 506(c)
.with_rule(ComplianceRule::AmountLimit {
max_per_tx: 1_000_000 * 10u128.pow(18), // 1M tokens per tx
})
.with_rule(ComplianceRule::Whitelist); // Only whitelisted addresses
// Register trusted issuers for identity claims
compliance.add_trusted_issuer(TrustedIssuer {
address: kyc_provider_address,
claim_topics: vec![ClaimTopic::Kyc, ClaimTopic::Aml],
name: "VerifyKYC Inc.".to_string(),
})?;
compliance.add_trusted_issuer(TrustedIssuer {
address: accreditation_provider_address,
claim_topics: vec![ClaimTopic::AccreditedInvestor, ClaimTopic::QualifiedInvestor],
name: "SecureAccred LLC".to_string(),
})?;
// Apply configuration to a token
compliance.configure_token(security_token_address, config)?;Transfer Compliance Check
// The can_transfer() check runs automatically before every transfer.
// It can also be called externally to pre-validate a transfer.
let result = compliance.can_transfer(
security_token_address,
sender_address,
recipient_address,
transfer_amount,
)?;
match result {
ComplianceResult::Allowed => {
println!("Transfer permitted — all compliance checks passed");
}
ComplianceResult::Denied { violations } => {
for violation in &violations {
match violation {
ComplianceViolation::InsufficientKyc { address, required, actual } => {
println!("{} has KYC tier {} but tier {} required",
address, actual, required);
}
ComplianceViolation::NotAccredited { address } => {
println!("{} missing AccreditedInvestor credential", address);
}
ComplianceViolation::RestrictedCountry { address, country } => {
println!("{} located in restricted jurisdiction: {}", address, country);
}
ComplianceViolation::AddressFrozen { address } => {
println!("{} is frozen by token issuer", address);
}
ComplianceViolation::HoldingPeriodNotMet { required_days, held_days } => {
println!("Must hold for {} days, only held {} days",
required_days, held_days);
}
ComplianceViolation::MaxHoldersReached { limit } => {
println!("Token already has {} holders (max)", limit);
}
_ => {}
}
}
}
}Issuer Controls
Token issuers have administrative controls to manage compliance and address permissions:
| Action | Description | CLI Command |
|---|---|---|
| Freeze Address | Block all transfers from/to an address | tenzro compliance freeze |
| Unfreeze Address | Re-enable transfers for a frozen address | tenzro compliance unfreeze |
| Add to Whitelist | Add an address to the transfer whitelist | tenzro compliance whitelist-add |
| Remove from Whitelist | Remove an address from the whitelist | tenzro compliance whitelist-remove |
| Force Transfer | Issuer-initiated transfer (recovery, court orders) | tenzro compliance force-transfer |
| Pause Token | Halt all transfers globally | tenzro compliance pause |
| Update Rules | Modify compliance configuration | tenzro compliance update-rules |
CLI Usage
# Create a compliant security token
tenzro token create --name "Real Estate Fund I" --symbol REF1 \
--decimals 18 --supply 10000000 --compliance erc3643
# Configure compliance rules
tenzro compliance update-rules --token <token-address> \
--kyc-tier 2 \
--require-accreditation \
--blocked-countries US,KP,IR \
--holding-period 365 \
--max-holders 2000 \
--max-per-tx 1000000
# Add trusted claim issuers
tenzro compliance add-issuer --token <token-address> \
--issuer 0xkyc_provider...abc \
--claims kyc,aml \
--name "VerifyKYC Inc."
# Whitelist an address
tenzro compliance whitelist-add --token <token-address> \
--address 0xinvestor...def
# Check if a transfer would be allowed (dry run)
tenzro compliance check-transfer --token <token-address> \
--from 0xsender...abc --to 0xrecipient...def --amount 50000
# Freeze an address
tenzro compliance freeze --token <token-address> \
--address 0xsuspicious...ghi --reason "pending investigation"
# View compliance status for a token
tenzro compliance info --token <token-address>
# List all frozen addresses
tenzro compliance frozen --token <token-address>Use Cases
Real-World Asset Tokenization
Tokenize real estate, commodities, or fund shares with built-in transfer restrictions. Configure KYC requirements, investor accreditation checks, and jurisdiction-based restrictions to comply with securities regulations in each target market.
Regulated Securities (Reg D / Reg S)
Issue security tokens that enforce SEC Regulation D requirements: accredited investor verification, 12-month holding periods, and 2,000 holder limits. Reg S tokens can restrict US investors while allowing unrestricted transfers in other jurisdictions.
Carbon Credits and ESG Assets
Tokenize verified carbon credits with compliance checks ensuring only certified entities can hold and trade them. The holding period rule prevents speculative short-term trading, and the whitelist ensures only verified offset buyers participate.
Enterprise Bond Issuance
Issue corporate bonds on-chain with compliance rules that enforce institutional investor requirements, minimum investment amounts, and jurisdiction restrictions. Integrates with Canton DAML for enterprise settlement workflows.
TDIP Integration Flow
The compliance check resolves wallet addresses to TDIP identities and reads verifiable credentials. This flow is fully on-chain and requires no external API calls during transfer execution:
- Transfer initiated:
token.transfer(recipient, amount) - Token contract calls
ComplianceRegistry.can_transfer(from, to, amount) - Registry resolves
fromandtoaddresses to TDIP DIDs via theWalletBinder - For each DID, the registry reads verifiable credentials from the
IdentityRegistry - Credentials are verified: issuer is trusted, signature is valid, not expired
- Each compliance rule is evaluated against the resolved claims
- If all rules pass, transfer proceeds. Otherwise,
ComplianceViolationis returned.
Credential caching: The compliance registry caches resolved credentials for a configurable TTL (default: 300 seconds) to avoid repeated identity lookups during high-throughput transfer periods. The cache is invalidated when a credential is revoked or a DID status changes.