GM Index
Brief explanations of all Index functions
The core contract stores the Indexes 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:
Owner Functions
pause
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
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
Sets the nominal base fee for deposits and withdrawals. Must be called by the current owner.
Parameters:
setTaxbase
Sets the base tax for deposits and withdrawals. Must be called by the current owner.
Parameters:
updateFeeRecipient
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:
updateProtocolFeeRatio
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:
addToken
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 to be added has an active vault, the token cannot be added again to the Index. This is done 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 any number of tokens (up to the uint256
max allowed value) via the deposit
and withdraw
functions.
Parameters:
updateOracle
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:
setTokenWeight
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:
removeTreasuryVault
Removes the token 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:
recoverUnsupported
Prevents the drain of currently tracked tokens. It is added as a safety measure in case GMX airdrops any rewards. Only the current owner can call this function. The owner inputs the IERC20-wrapped token that has been airdropped 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:
Public Functions
deposit
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. As previously mentioned, 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 fee percentage (or basis points) is multiplied by the mintAmount
, then divided by the FEE_DIVISOR
to get the total feeAmount
owed. The feeAmount
is subtracted from the mintAmount
, and an amountOut
is determined. If the amountOut
is less than the _minAmountOut
, the function will revert.
The protocolFeePayment
is calculated by multiplying the feeAmount
by the protocolFeeRatio
, then dividing by the FEE_DIVISOR
.
The tokens are now finally deposited into the token bank, and an event with the token’s name and amount is emitted.
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).
Parameters:
Returns:
withdraw
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 fee percentage (or basis points) is multiplied by the withdrawAmount
, then divided by the FEE_DIVISOR
to get the total feeAmount
owed. This is paid out of the withdrawAmount
of GM tokens. Thus, the feeAmount
is subtracted from the withdrawAmount
, and an amountOut
to the _recipient
is determined. 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:
Returns:
Public View Functions
calculateFee
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:
getTargetAmount
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:
Returns:
totalControlledValue
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:
Returns:
controlledValue
Description: 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:
Returns:
getPrice
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:
Returns:
bankBalance
Returns the amount of tokens deposited in the token’s bank for a given Index asset.
Parameters:
Returns:
targetBalance
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:
Returns:
Internal Functions
_isDepositToken
Returns true if the asset passed in has a non-zero token weight, which, under normal conditions, means it is a deposit token.
Parameters:
Returns:
_depositToBank
Deposits tokens into their respective token bank contract.
Parameters:
Last updated