Ripple USD (RLUSD) is an ERC-20 compliant token. The ERC-20 design includes standard imported functions from OpenZeppelin, and specific functions written by Ripple Engineers.
The OpenZeppelin library was chosen because:
-
OpenZeppelin are renowned smart contract auditors and open-source contributors.
-
The OpenZeppelin contracts are built with security, upgradeability, and modularity in mind.
-
OpenZeppelin contracts are completely standard compliant.
The deployed smart contracts enable minting, burning, global and individual freezing, clawback, and future upgrades to the ERC-20 contract. Permissions are controlled by a central Role Admin account, which is managed by Ripple internally.
The Ripple USD token provides the following enhancements beyond the standard ERC-20 features:
-
Individual Freeze/Unfreeze: A mechanism to pause/unpause activity on an individual account. A frozen account cannot call the transfer(to, value), transferFrom(from, to, value), allowance(owner, spender), and
approve(spender, value)
functions, and is unable to receive Ripple USD stablecoin. -
Global Freeze/Unfreeze: A safety measure that enacts a pause/unpause on all accounts. When a global freeze is enabled, the transfer(to, value), transferFrom(from, to, value), decreaseAllowance(spender, value) and
approve(spender, value)
functions will fail for all accounts. -
Clawback: A forced
burn(value)
function, which does not require a signature from the owner of the account, and is instead signed for by the account with theClawbacker
role.
Ripple USD supports a number of roles which control how accounts interact with the token on-chain:
-
Minter
: Gives an account the ability to issue stablecoin. -
Burner
: Gives an account the ability to burn stablecoin. -
Pauser
: Gives an account the ability to create an individual or global freeze/unfreeze. -
Clawbacker
: Gives an account the ability to force burn stablecoin from any account. -
Upgrader
: Gives an account the ability to point the proxy at a new ERC-20, effectively upgrading the contract.
Ripple is the only entity given the ability to mint and burn Ripple USD stablecoin via the Minter
and Burner
roles.
Only Ripple's issuer account can call the mint()
function to issue new stablecoin upon a distribution request from an onboarded customer.
Multiple internal Ripple accounts can call the burn()
function to ensure an efficient operational process. Users can submit a redemption request to a Ripple owned redemption account. Once the request is received and processed, Ripple will burn the amount of stablecoin in the request.
This iterative process ensures Ripple USD is always fully collateralized.
Security of internal accounts is incredibly important. For this reason, Ripple has chosen to use on-chain multi-signature safeguards for all internal accounts. Since this is supported natively on the XRP Ledger, Ripple chose to expand this functionality to Ethereum by introducing a custom MultiSign
contract.
The MultiSign
contract requires the creation of a predetermined list of known signers (accounts). The contract verifies that the transaction bytes provided are signed by the correct accounts, and then forwards the request function to the ERC-20 contract to execute the transaction.
Important
This functionality is not required by any community developer, but provides assurance that only Ripple groups will be able to increase and decrease the amount of stablecoin in circulation.
Events are emitted when the state of the stablecoin contract changes.
-
SignersChanged(address, address[], uint8[], uint256)
: The event is emitted when thesetSigners
method on an account'sMultiSign
contract is called. The values here are the accountaddress
for which the signers were changed, signer addresses, their weights, and the quorum. -
Transfer(address, address, uint256)
: Emitted when value is moved from one account to another. -
Paused(address)
: Emitted whenpause()
is called, triggering a Global Freeze with the address that called the method. -
Unpaused(address)
: Emitted whenunpause()
is called, triggering a Global Unfreeze with the address that called the method. -
AccountPaused(address)
: Emitted whenpauseAccount(address)
is called. Theaddress
represents the account that was frozen. -
AccountUnpaused(address)
: Emitted whenunpauseAccount(address)
is called. Theaddress
represents the account that was unfrozen.
The Ripple USD token uses the UUPS proxy pattern to go from one implementation to another. This pattern suggests that the proxy contract holds the ERC-20 implementation contract address and delegates all calls to it. The logic to control the upgrade itself is found in the ERC-20 implementation contract, and the ability to upgrade is given to accounts with the Upgrader
role.
The UUPSUpgradeable contract is a dependency of the ERC-20 contract which gives the token access to upgradeToAndCall(newImplementation, data). When an authorized address with the Upgrader
role calls this function, Ripple can safely migrate over to the new implementation. Because the upgrade process has no impact on storage, the state of balances, current transactions, and granted roles is preserved.