Skip to main content

Source code

View RWAToken contract source code
RWAToken is not a plain ERC-20 used only for bookkeeping. From a system perspective, it serves two roles:
  • As the on-chain tokenized representation of RWA units, tracking balances, transfers, and burns.
  • As the primary-sale entrypoint, receiving USDT during a sale window and minting tokens to buyers according to predefined rules.
This contract targets controlled issuance for RWA use cases. The design emphasizes “issuable, restrictable, recoverable, and upgradable”.Core responsibilities include:
  • Binding token metadata, custody wallet, USDT address, and the compliance module during initialization.
  • Managing supply under a fixed hard cap and minting the reserved tranche to custodyWallet at deployment time.
  • Handling primary sales via mint(). Before paying USDT, a buyer must pass off-chain compliance authorization verification via a signature.
  • Integrating with ComplianceManage on transfer paths to restrict blacklisted addresses.
  • Providing administrative capabilities for operations and compliance handling, such as forced transfers, token recovery, and transfer lock updates.
RWAToken depends on the following external components:
  • USDT: the payment asset for the primary sale.
  • ComplianceManage: the external compliance service contract responsible for blacklist checks, signature verification, and related controls.
  • ArtStarERC1967Proxy: the proxy contract that holds state for the upgradeable deployment.
With Hardhat Ignition, the implementation contract is deployed first, then the proxy is created and initialized by passing the ABI-encoded initialize() calldata. After that, the proxy address is registered in ComplianceManage under verifiedTokens.
The contract keeps the standard token interface, while layering additional business restrictions on transfer and mint paths. It uses SafeERC20 to wrap transferFrom and transfer calls for more robust interactions and reduced integration risk.
The contract state is organized around three core invariants:
  • TOTAL_SUPPLY_CAP = 10,000,000 * 1e18
  • RESERVED_AMOUNT = 2,000,000 * 1e18
  • SALE_CAP = TOTAL_SUPPLY_CAP - RESERVED_AMOUNT
The 2,000,000 reserved tokens are minted to custodyWallet during initialization, leaving 8,000,000 as the maximum available amount for primary sales.Other key state variables:
  • sold: total amount sold via primary sales.
  • priceUSDT / minPurchaseUSDT / maxPurchaseUSDT: pricing and per-purchase bounds.
  • saleActive / saleStartTime / saleEndTime: sale switch and time window.
  • unlockTime: transfer unlock time; 0 means no lock.
  • assetInfo: underlying asset metadata URI, valuation, and the last valuation update timestamp.
initialize() takes the token name and symbol, initial owner, custodyWallet, USDT address, and ComplianceManage address.The flow includes:
  1. Calling an internal init function.
  2. Validating that external dependency addresses are non-zero and persisting references.
  3. Minting RESERVED_AMOUNT directly to custodyWallet.
The address exposed to users on-chain is the proxy address.
A typical primary subscription flow looks like this:
1

Operational configuration

The owner sets price and purchase bounds, configures the sale window, and calls startSale().
2

Buyer subscription

The buyer submits amountUSDT, a signature, and an expiration timestamp.
3

Compliance verification

The contract checks the sale window, per-transaction limits, and the hard cap, then verifies the buyer’s authorization via ComplianceManage.
4

Payment and minting

After verification, USDT is transferred from the buyer to custodyWallet. The contract updates state and mints tokens to the buyer.
RWAToken delegates compliance authorization to ComplianceManage. Token logic and compliance logic are separated, but primary-sale availability depends on the external compliance contract’s configuration and state.
By overriding _update(), the contract centralizes multiple constraints at the state transition entrypoint:
  • Pause constraint: when paused, both regular transfers and primary sales (mint) are rejected.
  • Lockup constraint: when unlockTime != 0 and the unlock time has not been reached, regular transfers are rejected.
  • Blacklist constraint: when the sender or recipient is blacklisted in ComplianceManage, regular transfers are rejected.
The owner holds significant operational privileges, including:
  • Configuring price, purchase bounds, sale window, and sale switch.
  • Setting or clearing the transfer lock time (including emergency unlock).
  • Pausing and unpausing the full token system.
  • Forced transfers forcedTransfer(), burning burn(), recovery recoverTokens(), and related controls.
  • Updating underlying asset information via updateAssetInfo().
These capabilities improve operational flexibility, but also indicate explicit governance and compliance intervention points. This is not a fully decentralized, permissionless free-float asset.
assetInfo stores the underlying asset’s metadataURI and valuation data. Valuation accuracy depends entirely on owner updates.Recovery-focused functions reflect real-world operations:
  • recoverTokens(): recover any ERC-20 mistakenly sent to the contract.
  • recoverUnsoldTokens(): after the sale ends, mint the remaining sale capacity to the custody wallet in one action.
  • forcedTransfer(): forcibly move tokens for compliance or dispute-resolution scenarios.
Upgrade notes
  • The proxy address remains unchanged; state is stored in the proxy.
  • Only the owner can pass _authorizeUpgrade().
  • New implementations must preserve storage layout compatibility.
Audit focus areas
  • Compliance dependency: correct ComplianceManage configuration is critical.
  • Concentrated privileges: high-privilege operations require strong key management.
  • Price conversion: priceUSDT precision and conversion rules must be strictly consistent.