Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions contracts/fee/FeeDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ contract FeeDistributor is ReentrancyGuard, RoleModule {
wnt = _wnt;
}

// @dev withdraw the specified 'amount' of native token from this contract to 'receiver'
// @param receiver the receiver of the native token
// @param amount the amount of native token to withdraw
function withdrawNativeToken(address receiver, uint256 amount) external onlyTimelockAdmin {
FeeDistributorUtils.withdrawNativeToken(dataStore, receiver, amount);
}

// @dev withdraw the specified 'amount' of `token` from this contract to `receiver`
// @param token the token to withdraw
// @param amount the amount to withdraw
// @param receiver the address to withdraw to
function withdrawToken(address token, address receiver, uint256 amount) external onlyTimelockAdmin {
FeeDistributorUtils.withdrawToken(dataStore, token, receiver, amount);
}

// @dev initiate the weekly fee distribution process
//
// The fee distribution process relies on the premise that this function is executed synchronously
Expand Down Expand Up @@ -205,6 +220,8 @@ contract FeeDistributor is ReentrancyGuard, RoleModule {

uint256 requiredGmxAmount = Precision.mulDiv(totalFeeAmountGmx, stakedGmxCurrentChain, totalStakedGmx);
uint256 totalGmxBridgedOut;
EventUtils.EventLogData memory eventData;
eventData.uintItems.initItems(3);
// validate that the this chain has sufficient GMX to distribute fees
if (feeAmountGmxCurrentChain >= requiredGmxAmount) {
// only attempt to bridge to other chains if this chain has a surplus of GMX
Expand All @@ -230,15 +247,17 @@ contract FeeDistributor is ReentrancyGuard, RoleModule {
}
_setUint(Keys2.feeDistributorFeeAmountGmxKey(block.chainid), newFeeAmountGmxCurrentChain);
}
_setDistributionState(uint256(DistributionState.BridgingCompleted));
uint256 distributionState = uint256(DistributionState.BridgingCompleted);
_setDistributionState(distributionState);
_setUintItem(eventData, 0, "distributionState", distributionState);
} else {
_setDistributionState(uint256(DistributionState.ReadDataReceived));
uint256 distributionState = uint256(DistributionState.ReadDataReceived);
_setDistributionState(distributionState);
_setUintItem(eventData, 0, "distributionState", distributionState);
}

EventUtils.EventLogData memory eventData;
eventData.uintItems.initItems(2);
_setUintItem(eventData, 0, "feeAmountGmxCurrentChain", feeAmountGmxCurrentChain);
_setUintItem(eventData, 1, "totalGmxBridgedOut", totalGmxBridgedOut);
_setUintItem(eventData, 1, "feeAmountGmxCurrentChain", feeAmountGmxCurrentChain);
_setUintItem(eventData, 2, "totalGmxBridgedOut", totalGmxBridgedOut);
eventData.bytesItems.initItems(1);
eventData.bytesItems.setItem(0, "receivedData", abi.encode(receivedData));
_emitFeeDistributionEvent(eventData, "FeeDistributionDataReceived");
Expand Down Expand Up @@ -341,16 +360,16 @@ contract FeeDistributor is ReentrancyGuard, RoleModule {
_emitFeeDistributionEvent(eventData, "FeeDistributionCompleted");
}

// @dev deposit the calculated referral rewards in the ClaimVault for the specified accounts
// @param token the token in which the referral rewards will be sent
// @dev deposit the calculated referral rewards into the ClaimVault for the specified accounts
// @param token the token in which the referral rewards will be deposited
// @param distributionId the distribution id
// @param params array of referral rewards deposit parameters
function depositReferralRewards(
address token,
uint256 distributionId,
ClaimUtils.DepositParam[] calldata params
) external nonReentrant onlyFeeDistributionKeeper {
// validate the distribution state and that the accounts and amounts arrays are valid lengths
// validate the distribution state
_validateDistributionState(DistributionState.None);

uint256 tokensForReferralRewards = _getUint(Keys2.feeDistributorReferralRewardsAmountKey(token));
Expand All @@ -362,10 +381,10 @@ contract FeeDistributor is ReentrancyGuard, RoleModule {
revert Errors.MaxEsGmxReferralRewardsAmountExceeded(tokensForReferralRewards, maxEsGmxReferralRewards);
}

uint256 vaultEsGmxBalance = _getFeeDistributorVaultBalance(esGmx);
uint256 vaultEsGmxBalance = _getFeeDistributorVaultBalance(token);
uint256 esGmxToBeDeposited = tokensForReferralRewards - cumulativeDepositAmount;
if (esGmxToBeDeposited > vaultEsGmxBalance) {
IMintable(esGmx).mint(address(feeDistributorVault), esGmxToBeDeposited - vaultEsGmxBalance);
IMintable(token).mint(address(feeDistributorVault), esGmxToBeDeposited - vaultEsGmxBalance);
}

// update esGMX bonus reward amounts for each account in the vester contract
Expand Down
11 changes: 10 additions & 1 deletion contracts/fee/FeeDistributorUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity ^0.8.0;
import "../data/DataStore.sol";
import "../data/Keys2.sol";
import "../error/Errors.sol";
import "../token/TokenUtils.sol";

enum DistributionState {
None,
Expand All @@ -21,6 +22,14 @@ struct Transfer {

// @title FeeDistributorUtils
library FeeDistributorUtils {
function withdrawNativeToken(DataStore dataStore, address receiver, uint256 amount) external {
TokenUtils.sendNativeToken(dataStore, receiver, amount);
}

function withdrawToken(DataStore dataStore, address token, address receiver, uint256 amount) external {
TokenUtils.transfer(dataStore, token, receiver, amount);
}

function calculateKeeperCosts(DataStore dataStore) external view returns (uint256, uint256) {
address[] memory keepers = dataStore.getAddressArray(Keys2.FEE_DISTRIBUTOR_KEEPER_COSTS);
uint256[] memory keepersTargetBalance = dataStore.getUintArray(Keys2.FEE_DISTRIBUTOR_KEEPER_COSTS);
Expand All @@ -46,7 +55,7 @@ library FeeDistributorUtils {

return (keeperCostsV1, keeperCostsV2);
}

function retrieveChainIds(DataStore dataStore) external view returns (uint256[] memory) {
uint256[] memory chainIds = dataStore.getUintArray(Keys2.FEE_DISTRIBUTOR_CHAIN_ID);
sort(chainIds, 0, int256(chainIds.length - 1));
Expand Down
15 changes: 15 additions & 0 deletions contracts/fee/FeeDistributorVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,19 @@ import "../bank/Bank.sol";

contract FeeDistributorVault is Bank {
constructor(RoleStore _roleStore, DataStore _dataStore) Bank(_roleStore, _dataStore) {}

// @dev withdraw the specified 'amount' of native token from this contract to 'receiver'
// @param receiver the receiver of the native token
// @param amount the amount of native token to withdraw
function withdrawNativeToken(address receiver, uint256 amount) external onlyTimelockAdmin {
TokenUtils.sendNativeToken(dataStore, receiver, amount);
}

// @dev withdraw the specified 'amount' of `token` from this contract to `receiver`
// @param token the token to withdraw
// @param amount the amount to withdraw
// @param receiver the address to withdraw to
function withdrawToken(address token, address receiver, uint256 amount) external onlyTimelockAdmin {
_transferOut(token, receiver, amount);
}
}
6 changes: 2 additions & 4 deletions contracts/mock/MintableToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@ contract MintableToken is ERC20Permit {
// @dev mint tokens to an account
// @param account the account to mint to
// @param amount the amount of tokens to mint
function mint(address account, uint256 amount) external returns (bool) {
function mint(address account, uint256 amount) external {
_mint(account, amount);
return true;
}

// @dev burn tokens from an account
// @param account the account to burn tokens for
// @param amount the amount of tokens to burn
function burn(address account, uint256 amount) external returns (bool) {
function burn(address account, uint256 amount) external {
_burn(account, amount);
return true;
}
}
41 changes: 30 additions & 11 deletions contracts/mock/MockFeeDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ contract MockFeeDistributor is ReentrancyGuard, RoleModule {
gmxForOracle = _mockVariables.gmxForOracle;
}

// @dev withdraw the specified 'amount' of native token from this contract to 'receiver'
// @param receiver the receiver of the native token
// @param amount the amount of native token to withdraw
function withdrawNativeToken(address receiver, uint256 amount) external onlyTimelockAdmin {
FeeDistributorUtils.withdrawNativeToken(dataStore, receiver, amount);
}

// @dev withdraw the specified 'amount' of `token` from this contract to `receiver`
// @param token the token to withdraw
// @param amount the amount to withdraw
// @param receiver the address to withdraw to
function withdrawToken(address token, address receiver, uint256 amount) external onlyTimelockAdmin {
FeeDistributorUtils.withdrawToken(dataStore, token, receiver, amount);
}

// @dev initiate the weekly fee distribution process
//
// The fee distribution process relies on the premise that this function is executed synchronously
Expand Down Expand Up @@ -218,6 +233,8 @@ contract MockFeeDistributor is ReentrancyGuard, RoleModule {

uint256 requiredGmxAmount = Precision.mulDiv(totalFeeAmountGmx, stakedGmxCurrentChain, totalStakedGmx);
uint256 totalGmxBridgedOut;
EventUtils.EventLogData memory eventData;
eventData.uintItems.initItems(3);
// validate that the this chain has sufficient GMX to distribute fees
if (feeAmountGmxCurrentChain >= requiredGmxAmount) {
// only attempt to bridge to other chains if this chain has a surplus of GMX
Expand All @@ -243,15 +260,17 @@ contract MockFeeDistributor is ReentrancyGuard, RoleModule {
}
_setUint(Keys2.feeDistributorFeeAmountGmxKey(mockChainId), newFeeAmountGmxCurrentChain);
}
_setDistributionState(uint256(DistributionState.BridgingCompleted));
uint256 distributionState = uint256(DistributionState.BridgingCompleted);
_setDistributionState(distributionState);
_setUintItem(eventData, 0, "distributionState", distributionState);
} else {
_setDistributionState(uint256(DistributionState.ReadDataReceived));
uint256 distributionState = uint256(DistributionState.ReadDataReceived);
_setDistributionState(distributionState);
_setUintItem(eventData, 0, "distributionState", distributionState);
}

EventUtils.EventLogData memory eventData;
eventData.uintItems.initItems(2);
_setUintItem(eventData, 0, "feeAmountGmxCurrentChain", feeAmountGmxCurrentChain);
_setUintItem(eventData, 1, "totalGmxBridgedOut", totalGmxBridgedOut);
_setUintItem(eventData, 1, "feeAmountGmxCurrentChain", feeAmountGmxCurrentChain);
_setUintItem(eventData, 2, "totalGmxBridgedOut", totalGmxBridgedOut);
eventData.bytesItems.initItems(1);
eventData.bytesItems.setItem(0, "receivedData", abi.encode(receivedData));
_emitFeeDistributionEvent(eventData, "FeeDistributionDataReceived");
Expand Down Expand Up @@ -354,16 +373,16 @@ contract MockFeeDistributor is ReentrancyGuard, RoleModule {
_emitFeeDistributionEvent(eventData, "FeeDistributionCompleted");
}

// @dev deposit the calculated referral rewards in the ClaimVault for the specified accounts
// @param token the token in which the referral rewards will be sent
// @dev deposit the calculated referral rewards into the ClaimVault for the specified accounts
// @param token the token in which the referral rewards will be deposited
// @param distributionId the distribution id
// @param params array of referral rewards deposit parameters
function depositReferralRewards(
address token,
uint256 distributionId,
ClaimUtils.DepositParam[] calldata params
) external nonReentrant onlyFeeDistributionKeeper {
// validate the distribution state and that the accounts and amounts arrays are valid lengths
// validate the distribution state
_validateDistributionState(DistributionState.None);

uint256 tokensForReferralRewards = _getUint(Keys2.feeDistributorReferralRewardsAmountKey(token));
Expand All @@ -375,10 +394,10 @@ contract MockFeeDistributor is ReentrancyGuard, RoleModule {
revert Errors.MaxEsGmxReferralRewardsAmountExceeded(tokensForReferralRewards, maxEsGmxReferralRewards);
}

uint256 vaultEsGmxBalance = _getFeeDistributorVaultBalance(esGmx);
uint256 vaultEsGmxBalance = _getFeeDistributorVaultBalance(token);
uint256 esGmxToBeDeposited = tokensForReferralRewards - cumulativeDepositAmount;
if (esGmxToBeDeposited > vaultEsGmxBalance) {
IMintable(esGmx).mint(address(feeDistributorVault), esGmxToBeDeposited - vaultEsGmxBalance);
IMintable(token).mint(address(feeDistributorVault), esGmxToBeDeposited - vaultEsGmxBalance);
}

// update esGMX bonus reward amounts for each account in the vester contract
Expand Down
79 changes: 79 additions & 0 deletions contracts/mock/MockGMX_LockboxAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol";
import { OFTMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol";

import { OFTAdapter } from "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol";

import { MockOverridableInboundRateLimiter } from "./MockOverridableInboundRateLimiter.sol";

/**
* @title GMX_LockboxAdapter Contract
* @author LayerZero Labs (@shankars99)
* @dev Implementation of a lockbox-style OFT adapter with overridable rate limiting.
* @dev This contract adapts an existing ERC-20 token to OFT functionality using lock/unlock mechanism.
* @dev Unlike MintBurnOFTAdapter, this locks tokens in the contract rather than minting/burning.
* @dev This contract is meant to be used on Arbitrum.
*/
contract MockGMX_LockboxAdapter is OFTAdapter, MockOverridableInboundRateLimiter {
using OFTMsgCodec for bytes;
using OFTMsgCodec for bytes32;

constructor(
RateLimitConfig[] memory _rateLimitConfigs,
address _token,
address _lzEndpoint,
address _delegate
) OFTAdapter(_token, _lzEndpoint, _delegate) Ownable() {
_setRateLimits(_rateLimitConfigs);
}

/**
* @notice Override the base _debit() function to apply rate limiting before super._debit()
* @dev This function is called when a debit is made from the OFT.
* @param _from The address from which the debit is made.
* @param _amountLD The amount to debit in local denomination.
* @param _minAmountLD The minimum amount to debit in local denomination.
* @param _dstEid The destination endpoint ID.
*/
function _debit(
address _from,
uint256 _amountLD,
uint256 _minAmountLD,
uint32 _dstEid
) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {
/// @dev amountSentLD is amountLD with dust removed
/// @dev amountReceivedLD is amountSentLD with other token amount changes such as fee, etc.
/// @dev For lockbox adapters, these are typically the same (no fees)
(amountSentLD, amountReceivedLD) = super._debit(_from, _amountLD, _minAmountLD, _dstEid);

_outflowOverridable(_from, amountSentLD, _dstEid);
}

/**
* @notice Override the base _lzReceive() function to apply rate limiting before super._lzReceive()
* @dev This function is called when a message is received from another chain.
* @param _origin The origin of the message.
* @param _guid The GUID of the message.
* @param _message The message data.
* @param _executor The address of the executor.
* @param _extraData Additional data for the message.
*/
function _lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor, // @dev unused in the default implementation.
bytes calldata _extraData // @dev unused in the default implementation.
) internal virtual override {
address toAddress = _message.sendTo().bytes32ToAddress();

/// @dev We can assume that every layerzero message is an OFT transfer and that there are no non-token messages
_inflowOverridable(_guid, toAddress, _toLD(_message.amountSD()), _origin.srcEid);

super._lzReceive(_origin, _guid, _message, _executor, _extraData);
}
}
Loading
Loading