Unitas Architecture

ERC20Token.sol: ERC20 contract + (Blacklist + AccessControl + Pausable)

Unitas.sol: Core contract, this contract is primarily used for exchanging tokens, managing reserve assets.

InsurancePool.sol: The funds in our first phase InsurancePool are all contributed by Unitas. So this contract is just a simple vault. The auction module will be supported in the second phase.

SwapFunctions.sol: This is an abstract contract called SwapFunctions that implements the ISwapFunctions interface. It defines several functions for calculating swapping results, including calculating fees, converting amounts, and validating fees. These functions are implemented using the MathUpgradeable library from OpenZeppelin.

TokenManager.sol: This contract is responsible for managing currency pairs that are open for swapping, and setting parameters such as swapping fees and reserve ratio thresholds.

** Deflationary token is not supported.

  • TokenType.Asset: USDT (Phase1), USDC (TBD).

  • TokenType.Stable: USD1, USD91, USD971, and USD84. All Unitas stablecoins decimal is 18.

TypeTokens.sol: This is an abstract contract called TypeTokens that implements the ITypeTokens interface. It includes mappings for storing token types and token addresses, and events for adding or removing tokens from the pool. The contract also defines two modifiers for checking if a token is already in the pool or not.

TokenPairs.sol: This abstract contract implements the ITokenPairs interface. It is used to manage pairs, including the enumerable functionality.

PoolBalances.sol: This is an abstract contract includes some generic functions that are used to manage pool assets. It includes mappings for storing token balances and token portfolios, and events are emitted when portfolios change.

XOracle.sol: This contract is an oracle that provides price data for various assets. It allows addresses with the “FEEDER_ROLE” to update the prices of assets and anyone to view the current and previous prices. The “GUARDIAN_ROLE” is a role with higher permissions than the “FEEDER_ROLE” and is used for administrative purposes such as adding or removing feeders.

UnitasProxy.sol: Transparent Proxy

UnitasProxyAdmin.sol: ProxyAdmin

TimelockController.sol (OpenZeppelin Contracts): The TimelockController is configured with a time delay of 24/48 hours. This means that any proposed transactions or function calls through the TimelockController must be scheduled in advance and will only be executed after a waiting period of 24/48 hours. (Only calls from the GOVERNOR_ROLE are allowed.)

  • We will monitor the execution of Timelock functions on-chain and send notifications to the Unitas Telegram channel or any official Unitas channel, informing everyone about the operation.

Role management:

  • MINTER_ROLE (Unitas contract)

  • TIMELOCK_ROLE (OZ Contract)

    • TIMELOCK’s admin == (GOVERNOR_ROLE)

  • FEEDER_ROLE (EOA)

  • GOVERNOR_ROLE (Multisig wallet)

  • GUARDIAN_ROLE (Multisig wallet)

  • PORTFOLIO_ROLE (Multisig wallet)

  • WITHDRAWER_ROLE (Unitas contract)

  • ProxyAdmin (OZ Contract)

  • ProxyAdmin’s admin (GUARDIAN_ROLE)

Multisig wallet - Gnosis Safe

  • GUARDIAN_ROLE 2 of 3

  • PORTFOLIO_ROLE 2 of 3

  • GOVERNOR_ROLE (DAO) 4 of 7

    • Phase 1: 2 of 3

    • Phase2: 4 of 7

we will use the GOVERNOR multi-signature wallet as the SurplusPool in phase1. All protocol transaction fees will be sent to the surplus pool.

In phase 1, as the Insurance Pool does not yet support the auction module, the Unitas Foundation will act as an Insurance Provider (IP) for staking/unstaking USDT. This can be done through the depositCollateral/withdrawCollateral functions, using only the Guardian (multi-signature wallet).

The sendPortfolio function perform a timelock for 24 hours to transfer a portion of the funds to the portfolio wallet (multi-signature wallet) for DeFi staking investments.

Contract Functions

Unitas

Inherits Initializable, PausableUpgradeable, AccessControlUpgradeable, ReentrancyGuardUpgradeable, PoolBalances and SwapFunctions. The contract is upgradeable.

State Variables

  • usd1: Gets the address of USD1

  • oracle: Gets the address of oracle

  • surplusPool: Gets the address of surplus pool (treasury)

  • insurancePool: Gets the address of insurance pool

  • tokenManager: Gets the address of token manager

Modifiers

  • onlyTimelock: This modifier is used to restrict access to a function to only the address that has the TIMELOCK_ROLE. If the msg.sender does not have the TIMELOCK_ROLE, the function will revert with the NotTimelock error.

  • onlyGuardian: This modifier is used to restrict access to a function to only the address that has the GUARDIAN_ROLE. If the msg.sender does not have the GUARDIAN_ROLE, the function will revert with the NotGuardian error.

  • onlyPortfolio: This modifier is used to restrict access to a function based on a specific address having the PORTFOLIO_ROLE. The address of the portfolio is passed as an argument to the modifier. If the provided account does not have the PORTFOLIO_ROLE, the function will revert with the NotPortfolio error.

Public or External Functions

  • initialize: Initializes the states when deploying the proxy contract, e.g., access control, dependent contract addresses. (initializer restriction)

  • swap: Swaps tokens between USD-Pegs, USD1 or Unitized Local Stablcoins. Users can choose to input the amount to be spent or the amount to be received. The function will check oracle price is in the tolerance range, checks the reserve ratio is sufficient when there has a limitation. It will update the reserve of the token when the user spends or redeems USD-Pegs, and it will send fee to the surplus pool

  • estimateSwapResult: Estimates the swap result for quoting. It will return the amount to spent, the amount to receive, the fee, the numerator of fee ratio, and the price

  • getReserve: Gets current reserve of the token

  • getReserveStatus: Gets the reserve status, total reserves denominated in USD1, total collaterals denominated in USD1, total liabilities denominated in USD1, and the reserve ratio

  • getPortfolio: Gets the portfolio state of the token

Timelock Functions

  • setOracle: Updates the address of oracle

  • setSurplusPool: Updates the address of surplus pool

  • setInsurancePool: Updates the address of insurance pool

  • setTokenManager: Updates the address of token manager

  • sendPortfolio: Transfers the tokens from the pool to the portfolio manager

Guardian Functions

  • pause: Disables swapping

  • unpause: Enables swapping

Portfolio Functions

  • receivePortfolio: Transfers the tokens from the portfolio manager to the pool

Internal or Private Functions

  • _setOracle: Updates the address of oracle

  • _setSurplusPool: Updates the address of surplus pool

  • _setInsurancePool: Updates the address of insurance pool

  • _setTokenManager: Updates the address of token manager

  • _setReserve: Updates the reserve of the token

  • _swapIn: Transfers USD-Pegs to the pool, or burns Unitas stablecoins from the user

  • _swapOut: Transfers USD-Pegs or mints Unitas stablecoins to the user

  • _getSwapResult: Gets the swap result

  • _getReserveStatus: Calculates the reserve ratio by reserves and liabilities, returns the reserve status and the reserve ratio

  • _getTotalReservesAndCollaterals: Gets total reserves and total collaterals denominated in USD1

  • _getTotalLiabilities: Gets total liabilities denominated in USD1

  • _getPriceQuoteToken: Gets the quote currency based on two token address parameters

  • _checkPrice: Reverts when the price tolerance range of the token is unset, or the price is not in the tolerance range

  • _checkReserveRatio: When the reserve ratio threshold of the swap type is positive, checks the reserve ratio is greater than the threshold

InsurancePool

Inherits AccessControl and ReentrancyGuard and PoolBalances.

Modifiers

  • onlyGuardian: This modifier is used to restrict access to a function to only the address that has the GUARDIAN_ROLE. If the msg.sender does not have the GUARDIAN_ROLE, the function will revert with the NotGuardian error.

  • onlyGuardianOrWithdrawer: This modifier allows access to a function for addresses that have either the GUARDIAN_ROLE or the WITHDRAWER_ROLE. If the msg.sender does not have either of these roles, the function will revert with the NotWithdrawer error.

  • onlyTimelock: This modifier restricts access to a function to only the address that has the TIMELOCK_ROLE. If the msg.sender does not have the TIMELOCK_ROLE, the function will revert with the NotTimelock error.

  • onlyPortfolio: This modifier restricts access to a function based on a specific address having the PORTFOLIO_ROLE. The address of the portfolio is passed as an argument to the modifier. If the provided account does not have the PORTFOLIO_ROLE, the function will revert with the NotPortfolio error.

Public or External Functions

  • getCollateral: Gets current collateral of the token

  • getPortfolio: Gets the portfolio state of the token

Guardian Functions

  • depositCollateral: Puts the collateral for a given token to the pool

Guardian or Withdrawer Functions

  • withdrawCollateral: Withdraws the collateral of the token from the pool. The guardian can withdraw the collateral, and Unitas contract (withdrawer) will withdraw the collateral when the reserve is insufficient to redeem

Timelock Functions

  • sendPortfolio: Transfers the tokens from the pool to the portfolio manager

Portfolio Functions

  • receivePortfolio: Transfers the tokens from the portfolio manager to the pool

TokenManager

Inherits AccessControl, TypeTokens, TokenPairs.

State Variables

  • RESERVE_RATIO_BASE: Returns 1018. The denominator of reserve ratio and threshold that have 18 decimal places

  • SWAP_FEE_BASE: Returns 106. The denominator of swapping fee that has 6 decimal places

  • _maxPriceTolerance: This is a mapping that associates an address of a token with its corresponding maximum price tolerance. It is used to store the maximum price tolerance for each token.

  • _minPriceTolerance: This is a mapping that associates an address of a token with its corresponding minimum price tolerance. It is used to store the minimum price tolerance for each token.

  • _pair: This is a mapping that associates a pair hash with a PairConfig object. The PairConfig contains two token addresses, fee numerators and reserve ratio thresholds

  • usd1: Gets the address of USD1

Modifiers

  • onlyTimelock: This modifier is used to restrict access to a function to only the address that has the TIMELOCK_ROLE. If the msg.sender does not have the TIMELOCK_ROLE, the function will revert with the NotTimelock error.

Public or External Functions

  • listPairsByIndexAndCount: Gets an array of PairConfig, supporting pagination

  • getPriceTolerance: Gets the price tolerance range of the token

  • getTokenType: Gets the type of the token. There has two types of tokens, Asset (USD-Pegs) for calculating reserves, Stable (USD1, USD91 and USD971) for calculating liabilities

  • getPair: Gets the pair setting by the two token addresses. Reverts if the pair does not exist

  • pairByIndex: Gets the pair setting by the index of pair hashes

Timelock Functions

  • setUSD1: Updates the address of USD1, and adds it to the pool as stablecoin

  • setMinMaxPriceTolerance: Updates the price tolerance range of the token

  • addTokensAndPairs: Adds the tokens and the pairs to the pool. The input arrays can be empty, and the update is performed only when any array has values

  • removeTokensAndPairs: Removes the tokens and the pairs from the pool. The input arrays can be empty, and the update is performed only when the tokens array, or the two arrays of pair token addresses have values

  • updatePairs: Updates the pair settings based on the input array of PairConfig. Reverts if any PairConfig is invalid or not in the pool

Internal or Private Functions

  • _setUSD1: Updates the address of USD1, and adds it to the pool as stablecoin

  • _setMinMaxPriceTolerance: Updates the price tolerance range of the token

  • _addTokensAndPairs: Adds the tokens and the pairs to the pool. Since _addPair checks whether the token is already in the pool, this function adds the tokens before the pairs

  • _removeToken: Removes the token from the pool. The pairs associated with the token must be removed first

  • _addPair: Adds the pair to the pool. Reverts if the PairConfig is invalid, two tokens are not in the pool, or the pair is already in the pool

  • _updatePair: Updates the pair setting by PairConfig. Reverts if the PairConfig is invalid, two tokens are not in the pool, or the pair is not in the pool

  • _removePair: Removes the pair and the PairConfig, reverts if the pair doesn’t exist

  • _checkPairParameters: Checks whether the parameters of the PairConfig are valid. The two tokens must be added to the pool before adding the pair. It validates the two token addresses are not the same, and one of the tokens must be USD1. It also validates the fee numerators and the reserve ratio thresholds

  • _isTokenTypeValid: Returns true when the token type is Asset or Stable

  • _checkSwapFeeNumerator: Checks if the input of the swapping fee numerator is valid. A valid swapping fee numerator must be zero or less than the denominator. which has 6 decimal places

  • _checkReserveRatioThreshold: Checks if the input of the reserve ratio threshold is valid. A valid threshold must be zero, or greater than or equal to the denominator, which has 18 decimals places

TypeTokens

State Variables

  • _tokenType: This mapping associates a token address with its corresponding token type. It allows you to map a specific token address to a numeric token type, represented as an uint8 value.

  • _typeTokens: This mapping associates a token type with a set of addresses. It uses an EnumerableSetUpgradeable.AddressSet data structure to store the set of addresses associated with a particular token type. This mapping allows you to group token addresses based on their token types.

Modifiers

  • tokenInPool: This modifier is used to ensure that a given token is already present in the pool. It checks the condition using the isTokenInPool function. If the token is not found in the pool, the function will revert with the TokenNotInPool error.

  • tokenNotInPool: This modifier is used to ensure that a given token is not already present in the pool. It also checks the condition using the isTokenInPool function. If the token is found in the pool, the function will revert with the TokenAlreadyInPool error.

Public or External Functions

  • listTokensByIndexAndCount: Gets token addresses of the token type, supporting pagination

  • isTokenInPool: Returns true when token is in the pool

  • tokenLength: Gets the token count of the token type

  • tokenByIndex: Gets the token address for a given token type and index

Internal or Private Functions

  • _addToken: Adds a token to the pool for a given token address and token type

  • _removeToken: Removes the token from the pool

TokenPairs

State Variables

  • _pairTokens: This mapping is used to map the token address to the set of addresses, where the key and each element in the set represents a pair of tokens

  • _pairHashes: The set of pair hashes that is used to determine whether a pair exists in the pool

Public or External Functions

  • listPairTokensByIndexAndCount: Gets an array of token addresses that are paired with the specified token, supporting pagination

  • isPairInPool: Returns true when the pair is in the pool

  • pairTokenLength: Gets the total number of tokens that are paired with the specified token

  • pairTokenByIndex: Gets the paired token address by the specified token and index

  • pairLength: Gets the total number of pairs

  • getPairHash: Sorts the two token addresses in ascending order, then returns the hash

Internal or Private Functions

  • _addPairByTokens: Adds a pair to the pool by the two tokens addresses that are sorted in ascending order. Reverts if the pair already exists

  • _removePairByTokens: Removes the pair from the pool by the two tokens addresses. Reverts if the pair does not exist

  • _checkPairExists: Returns the hash of the pair by the two token addresses. Reverts if the pair does not exist

  • _getPairHash: Returns the hash of the two token addresses

  • _sortTokens: Sorts the two token addresses in ascending order

PoolBalances

State Variables

  • _balance: This mapping is used to associate an address with the balance of a token. It allows you to store and retrieve the balance of a specific token for a given address.

  • _portfolio: This mapping is used to associate an address with the portfolio of a token. It allows you to store and retrieve the portfolio value of a specific token for a given address.

Internal or Private Functions

  • _setBalance: Updates the balance state of the token

  • _setPortfolio: Updates the portfolio state of the token

  • _receivePortfolio: Transfers the balance of the token from the sender to the pool

  • _sendPortfolio: Transfers the balance of the token from the pool to the portfolio manager.

  • _getBalance: Gets the balance state of the token

  • _getPortfolio: Gets the portfolio state of the token

  • _checkAmountPositive: Reverts if the amount is not greater than zero

SwapFunctions

Internal or Private Functions

  • _calculateSwapResult: Calculates the swap result by SwapRequest. It will return the amount to spent, the amount to receive, and the fee

  • _calculateSwapResultByAmountIn: Calculates the swap result when the amount type is In.

  • _calculateSwapResultByAmountOut: Calculates the swap result when the amount type is Out.

  • _validateFeeFraction: Checks the fee fraction is valid

  • _getFeeByAmountWithFee: Calculates the fee based on the amount including the fee

  • _getFeeByAmountWithoutFee: Calculates the fee based on the amount excluding the fee

  • _convert: Converts the amount of one token to another token based on the price, and allows selecting the rounding mode

  • _convertByFromPrice: Converts the amount of source token to target token when the price is based on source token

  • _convertByToPrice: Converts the amount of source token to target token when the price is based on target token

XOracle

Inherits AccessControl.

State Variables

  • prices: The mapping prices is defined with the key type address and the value type IOracle.Price.

Public or External Functions

  • putPrice: This function allows a caller with the “FEEDER_ROLE” to update the price for a specific asset. It checks if the provided timestamp is newer than the previous one and updates the price accordingly.

  • updatePrices: This function enables a caller with the “FEEDER_ROLE” to update prices for multiple assets in a single transaction. It iterates through an array of NewPrice structs and calls the putPrice function for each entry.

  • getPrice: A view function that retrieves the timestamp, previous timestamp, current price, and previous price for a given asset.

  • getLatestPrice: A view function that returns only the latest price for a given asset.

  • getStalenessThreshold: Gets the price staleness threshold, returns STALENESS_DEFAULT_THRESHOLD if it is zero.

  • decimals: A pure function that returns the number of decimal places for the price data, which is hardcoded to 18.

Last updated