USDs

USDs

  • ERC20 contract

  • USDs is a stable coin which generates auto-yield natively.

  • USDs is a rebasing token with two modes of accounting for users:

    • Rebasing wallets

      • Users holding tokens in their EOA are by default in this category and are eligible for auto-yield via a rebasing mechanism.

      • Any contract opted-in for rebase also comes in this category.

      • Balance for this category is tracked via a credit system, as described below :

      • creditsPerToken is tracked at a global level and is updated when doing a rebase.

      • creditBalance is tracked at an account level and is updated via token transfer, mint, redemption of USDs

    • Non-Rebasing wallets

      • For Non-Rebasing wallets this token acts as a normal ERC20 token and tracks the balance of the wallet as usual.

  • Rebasing Mechanism

    • Users mint USDs using approved list of collaterals

    • These collaterals accumulated in the USDs vault are then deployed to various yield on-chain earning opportunities.

    • Yield is harvested periodically and is used to buyback USDs from the market of which 50% is used as revenue for veSPA holders and rest of it goes for USDs auto yield.

    • While doing a rebase the x amount of USDs is partially burnt (without changing the overall total supply) and the creditsPerToken value is adjusted such that the burnt amount is proportionally distributed across all rebasing wallets.

Contract Documentation

Inherits: ERC20PermitUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable, IUSDs

Author: Sperax Foundation

ERC20 compatible contract for USDs supporting the rebase feature. This ERC20 token represents USDs on the Arbitrum (L2) network. Note that the invariant holds that the sum of balanceOf(x) for all x is not greater than totalSupply(). This is a consequence of the rebasing design. Integrations with USDs should be aware of this feature.

Inspired by OUSD: https://github.com/OriginProtocol/origindollar/blob/master/contracts/contracts/token/OUSD.sol

State Variables

MAX_SUPPLY

uint256 private constant MAX_SUPPLY = ~uint128(0);

_totalSupply

uint256 internal _totalSupply;

_deprecated_vars

uint256[4] private _deprecated_vars;

_allowances

mapping(address => mapping(address => uint256)) private _allowances;

vault

address public vault;

_creditBalances

mapping(address => uint256) private _creditBalances;

_deprecated_rebasingCredits

uint256 private _deprecated_rebasingCredits;

rebasingCreditsPerToken

uint256 public rebasingCreditsPerToken;

nonRebasingSupply

uint256 public nonRebasingSupply;

nonRebasingCreditsPerToken

mapping(address => uint256) public nonRebasingCreditsPerToken;

rebaseState

mapping(address => RebaseOptions) public rebaseState;

_deprecated_gatewayAddr

address[2] private _deprecated_gatewayAddr;

_deprecated_isUpgraded

mapping(address => bool) private _deprecated_isUpgraded;

paused

bool public paused;

Functions

onlyVault

Verifies that the caller is the Savings Manager contract.

modifier onlyVault();

constructor

constructor();

initialize

Initializes the contract with the provided name, symbol, and vault address.

function initialize(string memory _nameArg, string memory _symbolArg, address _vaultAddress) external initializer;

Parameters

mint

Mints new USDs tokens, increasing totalSupply.

function mint(address _account, uint256 _amount) external override onlyVault nonReentrant;

Parameters

burn

Burns tokens, decreasing totalSupply.

function burn(uint256 _amount) external override nonReentrant;

Parameters

rebaseOptIn

Voluntary opt-in for rebase.

Useful for smart-contract wallets.

function rebaseOptIn() external;

rebaseOptOut

Voluntary opt-out from rebase.

function rebaseOptOut() external;

rebaseOptIn

Adds _account to the rebasing account list.

function rebaseOptIn(address _account) external onlyOwner;

Parameters

rebaseOptOut

Adds _account to the non-rebasing account list.

function rebaseOptOut(address _account) external onlyOwner;

Parameters

rebase

The rebase function. Modifies the supply without minting new tokens. This uses a change in the exchange rate between "credits" and USDs tokens to change balances.

function rebase(uint256 _rebaseAmt) external override onlyVault nonReentrant;

Parameters

updateVault

Change the vault address.

function updateVault(address _newVault) external onlyOwner;

Parameters

pauseSwitch

Called by the owner to pause or unpause the contract.

function pauseSwitch(bool _pause) external onlyOwner;

Parameters

transfer

Transfer tokens to a specified address.

function transfer(address _to, uint256 _value) public override returns (bool);

Parameters

Returns

transferFrom

Transfer tokens from one address to another.

function transferFrom(address _from, address _to, uint256 _value) public override returns (bool);

Parameters

Returns

approve

Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. This method is included for ERC20 compatibility.

increaseAllowance and decreaseAllowance should be used instead. Changing an allowance with this method brings the risk that someone may transfer both the old and the new allowance - if they are both greater than zero - if a transfer transaction is mined before the later approve() call is mined.

function approve(address _spender, uint256 _value) public override returns (bool);

Parameters

Returns

increaseAllowance

Increase the amount of tokens that an owner has allowed a _spender to spend. This method should be used instead of approve() to avoid the double approval vulnerability described above.

function increaseAllowance(address _spender, uint256 _addedValue) public override returns (bool);

Parameters

Returns

decreaseAllowance

Decrease the amount of tokens that an owner has allowed a _spender to spend.

function decreaseAllowance(address _spender, uint256 _subtractedValue) public override returns (bool);

Parameters

Returns

totalSupply

Check the current total supply of USDs.

function totalSupply() public view override(ERC20Upgradeable, IUSDs) returns (uint256);

Returns

balanceOf

Gets the USDs balance of the specified address.

function balanceOf(address _account) public view override returns (uint256);

Parameters

Returns

creditsBalanceOf

Gets the credits balance of the specified address.

function creditsBalanceOf(address _account) public view returns (uint256, uint256);

Parameters

Returns

allowance

Function to check the amount of tokens that an owner has allowed a spender.

function allowance(address _owner, address _spender) public view override returns (uint256);

Parameters

Returns

_mint

Creates _amount tokens and assigns them to _account, increasing the total supply.

Emits a {Transfer} event with from set to the zero address.

Requirements - to cannot be the zero address.

function _mint(address _account, uint256 _amount) internal override;

Parameters

_burn

Destroys _amount tokens from _account, reducing the total supply.

Emits a {Transfer} event with to set to the zero address.

  • Requirements:

  • _account cannot be the zero address.

  • _account must have at least _amount tokens.*

function _burn(address _account, uint256 _amount) internal override;

Parameters

_executeTransfer

For non-rebasing accounts credit amount = _amount

Update the count of non-rebasing credits in response to a transfer

function _executeTransfer(address _from, address _to, uint256 _value) private;

Parameters

_rebaseOptIn

Add a contract address to the non-rebasing exception list. I.e., the address's balance will be part of rebases so the account will be exposed to upside and downside.

function _rebaseOptIn(address _account) private;

Parameters

_rebaseOptOut

Remove a contract address from the non-rebasing exception list.

function _rebaseOptOut(address _account) private;

_isNonRebasingAccount

Is an account using rebasing accounting or non-rebasing accounting? Also, ensure contracts are non-rebasing if they have not opted in.

function _isNonRebasingAccount(address _account) private returns (bool);

Parameters

_ensureNonRebasingMigration

Ensures internal account for rebasing and non-rebasing credits and supply is updated following the deployment of frozen yield change.

function _ensureNonRebasingMigration(address _account) private;

Parameters

_balanceOf

Calculates the balance of the account.

Function assumes the _account is already upgraded.

function _balanceOf(address _account) private view returns (uint256);

Parameters

_creditsPerToken

Get the credits per token for an account. Returns a fixed amount if the account is non-rebasing.

function _creditsPerToken(address _account) private view returns (uint256);

Parameters

_isNotPaused

Validates if the contract is not paused.

function _isNotPaused() private view;

Events

TotalSupplyUpdated

event TotalSupplyUpdated(uint256 totalSupply, uint256 rebasingCredits, uint256 rebasingCreditsPerToken);

Paused

event Paused(bool isPaused);

VaultUpdated

event VaultUpdated(address newVault);

RebaseOptIn

event RebaseOptIn(address indexed account);

RebaseOptOut

event RebaseOptOut(address indexed account);

Errors

CallerNotVault

error CallerNotVault(address caller);

ContractPaused

error ContractPaused();

IsAlreadyRebasingAccount

error IsAlreadyRebasingAccount(address account);

IsAlreadyNonRebasingAccount

error IsAlreadyNonRebasingAccount(address account);

CannotIncreaseZeroSupply

error CannotIncreaseZeroSupply();

InvalidRebase

error InvalidRebase();

TransferToZeroAddr

error TransferToZeroAddr();

TransferGreaterThanBal

error TransferGreaterThanBal(uint256 val, uint256 bal);

MintToZeroAddr

error MintToZeroAddr();

MaxSupplyReached

error MaxSupplyReached(uint256 totalSupply);

Enums

RebaseOptions

enum RebaseOptions {
    NotSet,
    OptOut,
    OptIn
}

Last updated