GM Index

Brief explanations of all Index functions

The core contract stores the Index's parameters, token data, fee data, and is the router for all deposits and withdrawals. It inherits from Open Zeppelin's ERC-20, Ownable, and Pausable contracts. The protocol and token mechanisms are set according to the following functions:

Constructor

At contract creation, the constructor sets the contract as an ERC20 token and transfers ownership to the msg.sender. It also stores the deployer-provided _protocolFeeRecipient address parameter.

Parameters:

Name
Type
Description

_protocolFeeRecipient

address

The address that receives a portion of protocol revenue

Owner Functions

pause

function pause() external onlyOwner()

Pauses functions with whenNotPaused modifier in case of emergency or unforeseen events. Must be called by the current owner. The current functions that are pausable are deposit and withdraw. This is the standard implementation of the Open Zeppelin Pausable contract.

unpause

function unpause() external onlyOwner()

Resumes operation of paused functions with whenNotPaused modifier to restore the Index to normal operation. Must be called by the current owner. The current functions that are unpausable are deposit and withdraw. This is the standard implementation of the Open Zeppelin Pausable contract.

setFeeBase

function setFeeBase(uint256 _feeBase) external onlyOwner

Sets the nominal base fee for deposits and withdrawals that happen when the particular GM token is at its target Index weighting. Must be called by the current owner. The Fee Base cannot be more than 250 basis points (2.5%). An event with the new fee is emitted.

Parameters:

Name
Type
Description

_feeBase

uint256

The base fee applied to each deposit and withdrawal

setTaxBase

function setTaxBase(uint256 _taxBase) external onlyOwner

Sets the base tax for deposits and withdrawals that take the particular GM token away from its target Index weighting. Must be called by the current owner. The Tax Base cannot be more than 250 basis points (2.5%). An event with the new tax is emitted.

Parameters:

Name
Type
Description

_taxBase

uint256

The base tax applied depending on if the deposit/withdrawal brings the token bank's amount further away from the targetBalance than it originally was before the transfer

setMinFee

function setMinFee(uint256 _minFee) external onlyOwner

Sets the minimum fee for deposits and withdrawals. Must be called by the current owner. The Tax Base cannot be more than 100 basis points (1%). An event with the new fee is emitted.

Parameters:

Name
Type
Description

_minFee

uint256

The minimum fee for deposits and withdrawals

updateFeeRecipient

function updateFeeRecipient(address _protocolFeeRecipient) external onlyOwner

Updates the address to receive a portion of protocol revenue and emits an event with the new address. Must be called by the current owner.

Parameters:

Name
Type
Description

_protocolFeeRecipient

address

The address that receives a portion of protocol revenue

updateProtocolFeeRatio

function updateProtocolFeeRatio(uint256 _protocolFeeRatio) external onlyOwner

Sets/updates the ratio of fees to be provided as protocol revenue and emits an event with the new ratio. Must be called by the current owner. The fee ratio must be lower than the FEE_DIVISOR, immutably set to 100% (10,000) at contract creation.

Parameters:

Name
Type
Description

_protocolFeeRatio

uint256

The ratio (percentage) of each fee that goes to the protocolFeeRecipient as revenue

addToken

function addToken(bytes32 name, IERC20 _token, uint256 _weight, ITokenOracle oracle, IBank bank)
        external
        onlyOwner

Adds an individual GMX market token to the Index, so that users are able to deposit/withdraw the token. Tokens can only be added by the current owner. After the addition and ensuing deposits, the Index will begin to incorporate the asset’s value, amount changes, and fees generated from the inflows and outflows.

Each token’s data is stored in the Vault struct, which is accessed via the vaults mapping. If a token has an active vault, the token cannot be added again to the Index. This is checked by requiring that the token vault’s active boolean is not set to true.

The token is also added to the treasuryVaults bytes32 array, which enables the retrieval by name of each individual token currently in the Index.

The token _weight function parameter is added to the totalTokenWeights storage variable.

Lastly, this function stores the token’s already deployed Bank contract (see the Bank Contract section for details) in the token’s Vault data struct. The Index then approves the bank contract to spend up to the uint256 max allowed value of tokens via the deposit and withdraw functions.

Parameters:

Name
Type
Description

name

bytes32

Name of the asset to be added

_token

IERC20

The IERC20 interface type of the token

_weight

uint256

The weight that the token will be assigned in relation to the total Index’s token weight

oracle

ITokenOracle

The ITokenOracle interface type of the token’s oracle

bank

IBank

The IBank interface type of the token’s bank

updateOracle

function updateOracle(bytes32 name, ITokenOracle oracle) external onlyOwner 

Updates the oracle variable for a given token inside its vaults struct and emits an event with the token name and address of the new oracle. Oracles can only be updated by the current owner. If the token’s vault is not active, the function reverts.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

oracle

ITokenOracle

The ITokenOracle interface type of the token’s oracle

setTokenWeight

function setTokenWeight(bytes32 name, uint256 _weight) external onlyOwner 

Sets or updates the weight of the given token and emits an event with the new weight. Weights can only be set or updated by the current owner. The function call updates the totalTokenWeights variable with the new weight. The given token’s vault must be active.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

_weight

uint256

The weight that the token will be assigned in relation to the total Index’s token weight

removeTreasuryVault

function removeTreasuryVault(bytes32 name) external onlyOwner 

Removes the token's position in the treasuryVaults bytes32 array, sets the token vault’s active Boolean to false, and emits an event. Vaults can only be removed by the current owner. Being able to delete deprecated tokens is a safety measure if the list ever becomes too large.

This function call will revert if the token’s bank still has funds, if the token still has a non-zero weight in the Index, or if the token cannot be found in the treasuryVaults array.

Parameters:

Name
Type
Description

name

uint256

The name of the asset to be removed from the treasury’s vaults

recoverUnsupported

function recoverUnsupported(IERC20 _token, uint256 _amount, address _to) external onlyOwner 

It is added in case of airdrops or tokens accidentally sent to the Index. Only the current owner can call this function. The owner inputs the IERC20-wrapped token that has been airdropped or sent into the contract. The function loops through the treasuryVaults array, and if any position matches the input token, then the function reverts. If the entire array has been searched and no match has been found, the tokens are transferred to the _to address that was also entered as an argument.

Parameters:

Name
Type
Description

_token

IERC20

The IERC20 interface type of the given token

_amount

uint256

The amount of tokens to be removed and sent to _to

_to

address

The address that unsupported tokens will be sent to

Public Functions

deposit

function deposit(bytes32 name, address _recipient, uint256 _amount, uint256 _minAmountOut) external whenNotPaused returns(uint256) 

Deposits an amount of eligible GM tokens to the Index, which mints a calculated amount of GMI tokens, subtracts a deposit fee, and sends the rest of the GMI to the depositor. Anyone can call this function. If the amount of deposited tokens is zero or the token is not assigned a weight in the Index, then this function will revert. This function is pausable.

In order to determine the amount of GMI to mint, the following values are calculated/retrieved: totalControlledValue of the Index, the depositTokenValue of the user’s deposit, the current totalSupply of GMI tokens, the deposited token’s present _bankBalance, and the token’s _targetBalance.

The very first deposit mints an amount of GMI tokens equal to the deposit’s current value. After the first deposit, the mintAmount is in proportion to the value of the GM tokens deposited versus the total value of the Index.

The fee percentage is calculated by using the FeeUtils.sol library (see Fees section for further info), which compares the token’s before and after bank balances to the targetBalance.

The feeAmount, amountOut, and protocolFeePayment are calculated as follows:

uint256 feeAmount = (mintAmount * fee) / FEE_DIVISOR;
uint256 amountOut = mintAmount - feeAmount;
uint256 protocolFeePayment = (feeAmount * protocolFeeRatio) / FEE_DIVISOR;

If the amountOut is less than the _minAmountOut, the function will revert.

The GM asset tokens are now finally deposited into the token bank. The protocolFeePayment in GMI tokens is minted and sent to the protocolFeeRecipient, and the amountOut in GMI tokens is minted and sent to the _recipient address (generally, this is the caller of the function). An event with the token recipient address, token’s name, and amount is emitted.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

_recipient

address

The address to send newly minted GMI tokens to

_amount

uint256

The amount of deposit tokens

_minAmountOut

uint256

The minimum amount of GMI tokens to be received

Returns:

Name
Type
Description

amountOut

uint256

The amount of GMI tokens sent to _recipient, usually the caller

withdraw

function withdraw(bytes32 name, uint256 _amount, address _recipient, uint256 _minAmountOut) external whenNotPaused returns(uint256)

Withdraws an amount of GM tokens from the Index by burning GMI tokens of the user. Anyone can call this function. If the amount of deposited tokens is zero or the _recipient address is the zero address, then this function will revert. As previously mentioned, this function is pausable.

In order to determine the amount of GM tokens that can be withdrawn, the following values are calculated/retrieved: totalControlledValue of the Index, the current price of the token, the withdrawValue equivalent to the value of the user’s GMI tokens, the current totalSupply of GMI tokens, the to-be-withdrawn token’s present _bankBalance, and the token’s _targetBalance.

The withdrawal value is calculated by multiplying the amount of GMI tokens to be burned by the total current value of the Index, then dividing by the total GMI supply. From this, the withdrawAmount of the token can be derived.

The fee percentage is calculated by using the FeeUtils.sol library (see Fees section for further info), which compares the token’s before and after bank balances to the targetBalance.

The feeAmount, amountOut, and protocolFeePayment are calculated as follows:

uint256 feeAmount = (withdrawAmount * fee) / FEE_DIVISOR;
uint256 amountOut = withdrawAmount - feeAmount;
uint256 protocolFeePayment = (feeAmount * protocolFeeRatio) / FEE_DIVISOR;

If the amountOut is less than the _minAmountOut, the function will revert.

The user’s _amount of GMI tokens are then burned, and the amountOut of GM tokens are sent to the _recipient. If feeAmount is non-zero, then it is transferred to the protocolFeeRecipient from the GM token’s bank.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

_amount

uint256

The amount of deposit tokens

_recipient

address

The address to send newly minted GMI tokens to

_minAmountOut

uint256

The amount of GMI tokens sent to _recipient, usually the caller

Returns:

Name
Type
Description

amountOut

uint256

The amount of GM tokens calculated and sent to _recipient

Public View Functions

calculateFee

function calculateFee(bytes32 name, uint256 _amount, bool isWithdraw) public view returns(uint256)

Calculates the fee basis points or percentage for a hypothetical transaction of a certain amount of a given token. The fee percentage is calculated by using the FeeUtils.sol library (see Fees section for further info), which compares the token’s before and after transaction bank balances to the targetBalance.

In order to calculate the total feeAmount, the return value of getFeeBasisPoints needs to be multipled by a mintAmount or withdrawAmount, then divided by the FEE_DIVISOR.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

_amount

uint256

The amount of the token to be transferred that the caller wants to calculate the fee for

isWithdraw

Boolean

Set to true if the caller wants to calculate the fee for a withdrawal of the input amount of specific tokens. Set to false for deposits.

getTargetAmount

function getTargetAmount(bytes32 name) public view returns (uint256) 

Gets the current target amount of a given Index token, based on the Index’s current weighting of the token. If the total supply of GMI is zero, this function returns zero.

The weight is retrieved from the token’s vault data struct, multiplied by the total supply of GMI tokens, then divided by the total token weight of the Index.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

Returns:

Name
Type
Description

unnamed

uint256

The target amount of the Index token

totalControlledValue

function totalControlledValue(bool roundUp) public view returns (uint256 totalValue)

Loops through the treasuryVaults bytes32 array and calls the controlledValue helper function for each token (while passing the rounding Boolean through to the helper function) and adds the returned answer to the totalValue variable.

Parameters:

Name
Type
Description

roundUp

Boolean

This argument is passed through to the controlledValue function

Returns:

Name
Type
Description

totalValue

uint256

The total value of supported tokens that are in the Index (e.g. the total value deposited across all active token banks)

controlledValue

function controlledValue(bytes32 name, bool roundUp) public view returns (uint256) 

Calculates the value of a given Index token by multiplying the current bank Balance by the current price (rounded up, converted to 18 decimals), which is retrieved from the token’s oracle in the getPrice() helper function (passing the rounding Boolean one final time through to the oracle helper function).

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

roundUp

Boolean

This argument is passed through to the getPrice() function

Returns:

Name
Type
Description

totalValue

uint256

The total value of the given GM token that are in the Index (e.g. deposited in the token's bank)

getPrice

function getPrice(bytes32 name, bool roundUp) public view returns (uint256)

Calls getPrice() on the token’s stored oracle contract and retrieves the price, which is then rounded up if the roundUp parameter is true and down if the roundUp parameter is false.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

roundUp

Boolean

Rounds price up if true, down if false.

Returns:

Name
Type
Description

price

uint256

The rounded price from the token’s oracle

bankBalance

function bankBalance(bytes32 name) public view returns (uint256 balance)

Returns the amount of tokens deposited in the token’s bank for a given Index asset.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

Returns:

Name
Type
Description

balance

uint256

The current token balance of the given token’s bank

targetBalance

function targetBalance(bytes32 name, bool withdrawal, uint256 tokenValue, bool roundUp) public view returns (uint256 balance)

Calculates and returns the ideal balance of the given token by dividing the token’s targetValue (with token decimals factor) by the current price returned by the token’s oracle. The targetValue is obtained by multiplying the total Index value (after a potential deposit or withdrawal, rounded in a direction determined upstream if called by deposit() or withdraw()) by the ratio of the token’s weight to the totalTokenWeights of the Index.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

withdrawal

Boolean

This argument is normally passed in from the controlledValue() function and controls whether the tokenValue will be subtracted from or added to the totalControlledValue

tokenValue

uint256

The value of the token to be deposited or withdrawn

roundUp

Boolean

This argument is passed through to the totalControlledValue function

Returns:

Name
Type
Description

balance

uint256

The given token’s target balance that puts it in equilibrium with its set weight in the Index

Internal Functions

_isDepositToken

function _isDepositToken(bytes32 _name) internal view returns (bool)

Returns true if the asset passed in has a non-zero token weight, which, under normal conditions, means it is a deposit token.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

Returns:

Name
Type
Description

unnamed

Boolean

Resolves to true if the token has non-zero weight, false otherwise

_depositToBank

function _depositToBank(bytes32 name, uint256 amount) internal 

Deposits tokens into their respective token bank contract.

Parameters:

Name
Type
Description

name

bytes32

Identifier of the token

amount

uint256

The amount of tokens to deposit

Last updated