Introduction
Compatibilities
- Solc version : 0.8.30
- Chains used : Base, Sepolia Base
- License SPDX-License-Identifier (BUSL-1.1)
Tesnet
Overview
Protocol allows users to provide liquidity, swap shares and place bets on prediction. Fees collected by activities support the system (vaults) and reward participants. A network component provides additional benefits (not define yet).
This diagram illustrates the overall functioning of the protocol, which is structured around several features :
-
1. Vault (ERC-4626) :
-
Users can deposit USDC into "Vault ERC-4626" representing prediction categories. In return, they receive "Shares X" (tokens representing their share in the vault) and can withdraw their USDC in exchange for their "Shares X".
-
The vault maintain a reserve fluctuating between 2% and 10% of the total value of USDC deposited to cover risks related to predictions (i.e. bookmaker).
-
The vault is fed by USDC generated from fees collected on prediction gains (~8%).
-
The global vault global is also fed by USDC generated from fees collected on prediction gains (~2%).
-
⁇ [To define] : Others fees collected on the protocol (e.g. swap fees, LP fees)
-
-
2. Pool balancer (weighted pool) & swap
-
Users can provide liquidity (USDC, Share X) in the "Balancer Pool" to earn fees on swaps and yield on their BPT (Balancer Pool Token).
-
We can collect fees on swap with a HookContract (not define)
-
⚠️ We need to check whether we need to take any action to make the Balancer Pool accessible on the balancer interface.
Balancer Fees
- Yield fees = 10% (on the BPT yield)
- 50% for balancer
- 50% for us + LPs
- Swap fees = 10%
- 50% for balancer
- 50% for us + LPs
The systeme is designed to be open to arbitrage bots that can regulate the pools by connecting to the Balancer API and the vault directly onchain.
-
-
3. Predictions
-
User can place bets on "Predictions" in USDC.
- Differents types of predictions are available (e.g sports, crypto, etc) managed by the centralized protocol?
-
Winners receive their winnings in USDC, minus a 20% fee for the protocol.
-
The amount won is supported by the vault linked to the prediction (or a unique vault?), which maintains a reserve to cover risks associated with predictions.
-
Admin manager
- ⚠️ We can automate the process :
- Create a vault when creating a categorie (e.g FootballTeam, BITCOIN, etc)
- Link a prediction to a category and then to the correspondant vault
- Create the balancer pool when creating a vault
- Maybe we should wait a certain balance in the vault before creating the balancer pool
CCIP
The CCIP (Cross-Chain Interoperability Protocol) is used to convert METADEXDAO from the source chain (POLYGON) to TRENDEXONE in the destination chain (BASE).
Deployment Addresses
In this section, you'll find the deployment addresses for the protocol contracts used depending on the network.
- Testnet : Contains the addresses for the testnet environment.
- Mainnet : Contains the addresses for the mainnet environment.
Amoy
| Contract | Address |
|---|---|
| Sender | 0xa61EF3bCC959494c17a2F06b51Ae045C36999dd4 |
| SenderProxy | 0x95f2826C0f8dEf3186a7AD9De7DbbA4c3b53f42C |
Polygon
| Contract | Address |
|---|---|
| Sender | |
| SenderProxy |
Testnet
| Contract | Address | Updated At |
|---|
Mainnet
| Contract | Address | Updated At |
|---|
Roles
| Role | Description |
|---|---|
| MAINTAINER_ROLE | Highest level of access, need multisig. |
| OPERATOR_ROLE | Role for current operation |
| MINTER_ROLE | Role necessary to mint token |
Admin
Operator
Core
AccessController
Inherits: AccessControl
This contract extends OpenZeppelin's AccessControl to manage roles and permissions for the protocol.
Functions
constructor
constructor(address maintainer);
AddressProvider
Inherits: IAddressProvider, Initializable, UUPSUpgradeable
This contract serves as a centralized registry for managing and providing access to key addresses used throughout the system.
State Variables
s_accessController
IAccessControl private s_accessController;
s_addresses
mapping(string name => address contractAddress) private s_addresses;
s_names
string[] private s_names;
Functions
onlyMaintainer
modifier onlyMaintainer();
constructor
Note: oz-upgrades-unsafe-allow: constructor
constructor();
initialize
Initialize for the AddressProvider contract.
function initialize(address accessController) external initializer;
Parameters
| Name | Type | Description |
|---|---|---|
accessController | address | The address of the access controller |
setAddress
function setAddress(string calldata name, address addr) external override onlyMaintainer;
setAccessController
function setAccessController(address accessController) external override onlyMaintainer;
getAddress
function getAddress(string calldata name) external view override returns (address);
getContractsName
function getContractsName() external view override returns (string[] memory);
getAccessController
function getAccessController() external view override returns (IAccessControl);
_setAddress
function _setAddress(string memory name, address addr) private;
_setAccessController
function _setAccessController(address accessController) private;
_authorizeUpgrade
function _authorizeUpgrade(address newImplementation) internal override onlyMaintainer;
CCIP
Tracking msg ID or manual execution on failed message :
-
https://ccip.chain.link/
-
FROM : https://ccip.chain.link/address/0x95f2826c0f8def3186a7ad9de7dbba4c3b53f42c
-
TO : https://ccip.chain.link/address/0x720d45a400b86879039db8866b65884539712d05
Sender
Inherits: ISender, Initializable, UUPSUpgradeable
This contract implements the ISender interface to send cross-chain messaging. It allows sending messages to a destination chain, managing allowlists for source, destination chains and sender contracts.
State Variables
MINIMUM_BRIDGE_AMOUNT
uint256 public constant MINIMUM_BRIDGE_AMOUNT = 1e18;
s_gasLimit
uint256 s_gasLimit = 120000;
s_tokenToBridge
address public s_tokenToBridge;
s_router
IRouterClient public s_router;
s_sponsoredFees
bool public s_sponsoredFees = false;
s_addressProvider
IAddressProvider public s_addressProvider;
s_whitelistedMerkleRoot
bytes32 public s_whitelistedMerkleRoot;
s_allowlistedDestinationChains
mapping(uint64 => bool) public s_allowlistedDestinationChains;
Functions
onlyMaintainer
check that msg.sender has MAINTAINER_ROLE
modifier onlyMaintainer();
onlyUserCentalized
check that msg.sender has OPERATOR_ROLE
modifier onlyUserCentalized();
onlyAllowlistedDestinationChain
check that the destination chain is allowlisted
modifier onlyAllowlistedDestinationChain(uint64 destinationChainSelector);
onlyWhitelistedIfSetup
check that msg.sender is whitelisted if the merkle root is set
modifier onlyWhitelistedIfSetup(bytes32[] calldata _proof);
constructor
Note: oz-upgrades-unsafe-allow: constructor
constructor();
initialize
Initialize for the Sender contract.
function initialize(address router, address tokenToBridge, address addressProvider) external initializer;
Parameters
| Name | Type | Description |
|---|---|---|
router | address | The address of the CCIP router according to the chainlink documentation |
tokenToBridge | address | The token to bridge from the source chain to the destination chain |
addressProvider | address | The address of the address provider contract |
receive
enable the receive function to receive native token
receive() external payable;
sendMessage
This function sends a message to a destination chain.
function sendMessage(uint64 destinationChainSelector, address receiver, uint256 amount, bytes32[] calldata _proof)
external
payable
override
onlyAllowlistedDestinationChain(destinationChainSelector)
onlyWhitelistedIfSetup(_proof)
returns (bytes32 messageId);
Parameters
| Name | Type | Description |
|---|---|---|
destinationChainSelector | uint64 | The selector of the destination chain. |
receiver | address | The address of the receiver contract on the destination chain. |
amount | uint256 | The amount of tokens to send. |
_proof | bytes32[] | The merkle proof to verify the sender's whitelisting. |
allowlistDestinationChain
This function updates the allowlist for receiving from a source chain
function allowlistDestinationChain(uint64 destinationChainSelector, bool allow) external override onlyMaintainer;
Parameters
| Name | Type | Description |
|---|---|---|
destinationChainSelector | uint64 | The selector of the destination chain |
allow | bool | The permission |
updateWhitelistedRoot
This function allows the operator to update the whitelisted merkle root.
function updateWhitelistedRoot(bytes32 newRoot) external onlyUserCentalized;
Parameters
| Name | Type | Description |
|---|---|---|
newRoot | bytes32 | The new whitelisted merkle root. |
withdraw
This function is used to withdraw native tokens
function withdraw(address beneficiary) external override onlyMaintainer;
setAddressProvider
Sets the address provider for the messenger contract.
function setAddressProvider(address newAddressProvider) external onlyMaintainer;
Parameters
| Name | Type | Description |
|---|---|---|
newAddressProvider | address | The address of the new address provider |
setSponsoredFees
Sets whether the fees for sending messages are sponsored or not.
If sponsored, the contract will pay the fees for sending messages.
function setSponsoredFees(bool sponsoredFees) external onlyMaintainer;
Parameters
| Name | Type | Description |
|---|---|---|
sponsoredFees | bool | Whether the fees for sending messages are sponsored or not. |
estimateFees
This function estimates the fees for sending a message
function estimateFees(uint64 destinationChainSelector, address receiver, uint256 amount)
external
view
returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
destinationChainSelector | uint64 | The selector of the destination chain |
receiver | address | The address of the receiver contract on the destination chain |
amount | uint256 | The amount of tokens to send |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | uint256 The estimated fees in native token |
_setAddressProvider
function _setAddressProvider(address newAddressProvider) private;
_setSponsoredFees
function _setSponsoredFees(bool sponsoredFees) private;
_buildCCIPMessage
Builds the CCIP message to be sent.
function _buildCCIPMessage(address receiver, bytes memory _data, address feeTokenAddress)
private
pure
returns (Client.EVM2AnyMessage memory);
Parameters
| Name | Type | Description |
|---|---|---|
receiver | address | The address of the receiver contract on the destination chain. |
_data | bytes | The ABI-encoded data to be sent. |
feeTokenAddress | address | The address of the token to be used for fees, 0 for native token. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | Client.EVM2AnyMessage | Client.EVM2AnyMessage The constructed CCIP message. |
_estimateFees
function _estimateFees(Client.EVM2AnyMessage memory evm2AnyMessage, uint64 destinationChainSelector)
private
view
returns (uint256);
_authorizeUpgrade
function _authorizeUpgrade(address newImplementation) internal override onlyMaintainer;
Receiver
Inherits: CCIPReceiver, IReceiver, ReentrancyGuard
State Variables
inError
bool public inError = false;
PRECISION
uint256 public constant PRECISION = 1e18;
BONUS_PRECISION
uint256 public constant BONUS_PRECISION = 100;
i_token
address public immutable i_token;
i_stTrendex
address public immutable i_stTrendex;
s_addressProvider
IAddressProvider public s_addressProvider;
s_conversionRatio
uint256 public s_conversionRatio = 1e18;
s_allowlistedSourceChains
mapping(uint64 => bool) public s_allowlistedSourceChains;
s_allowlistedSenders
mapping(address => bool) public s_allowlistedSenders;
s_processedMessages
mapping(bytes32 messageId => bool isProcessed) public s_processedMessages;
s_failedMessages
mapping(bytes32 messageId => bytes errorData) public s_failedMessages;
s_messageContents
mapping(bytes32 messageId => Client.Any2EVMMessage content) public s_messageContents;
s_amountToMint
mapping(address user => uint256 amount) public s_amountToMint;
Functions
setInError
function setInError() public;
onlyMaintainer
check that msg.sender has MAINTAINER_ROLE
modifier onlyMaintainer();
onlyOperator
check that msg.sender has OPERATOR_ROLE
modifier onlyOperator();
onlyAllowlisted
check that the source chain and the sender are allowlisted
modifier onlyAllowlisted(uint64 sourceChainSelector, address sender);
onlyReceiver
check that the caller is the receiver (address(this))
modifier onlyReceiver();
constructor
constructor(
address stTrendex,
address router,
address token,
address addressProvider,
address sender,
uint64 sourceChain
) CCIPReceiver(router);
ccipReceive
This function is called by the CCIP router to receive messages
It processes the message and increment the amount of tokens to the destination user address.
It catches any errors that occur during the processing of the message and handles them accordingly by storing the error data and emitting an event.
function ccipReceive(Client.Any2EVMMessage calldata any2EvmMessage)
external
override
onlyRouter
onlyAllowlisted(any2EvmMessage.sourceChainSelector, abi.decode(any2EvmMessage.sender, (address)));
Parameters
| Name | Type | Description |
|---|---|---|
any2EvmMessage | Client.Any2EVMMessage | The message coming from the source chain |
retryFailedMessage
This function allows protocole to retry failed message
function retryFailedMessage(bytes32 messageId) external override onlyOperator;
Parameters
| Name | Type | Description |
|---|---|---|
messageId | bytes32 | The ID of the message to retry |
processMessage
This function processes the message received from the CCIP router
function processMessage(Client.Any2EVMMessage memory any2EvmMessage) external onlyReceiver;
Parameters
| Name | Type | Description |
|---|---|---|
any2EvmMessage | Client.Any2EVMMessage | The message coming from the source chain |
setAddressProvider
This function sets the address provider
function setAddressProvider(address addressProvider) external override onlyMaintainer;
Parameters
| Name | Type | Description |
|---|---|---|
addressProvider | address | The address of the address provider |
setAllowlistSourceChain
This function updates the allowlist for receiving from a source chain
function setAllowlistSourceChain(uint64 sourceChainSelector, bool allow) external override onlyMaintainer;
Parameters
| Name | Type | Description |
|---|---|---|
sourceChainSelector | uint64 | The selector of the source chain |
allow | bool | The permission |
setAllowlistSender
This function updates the allowlist for receive message from a sender contract
function setAllowlistSender(address sender, bool allow) external override onlyMaintainer;
Parameters
| Name | Type | Description |
|---|---|---|
sender | address | The address of the sender contract |
allow | bool | The permission |
setConversionRatio
This function sets the conversion ratio
function setConversionRatio(uint256 ratio) external override onlyMaintainer;
Parameters
| Name | Type | Description |
|---|---|---|
ratio | uint256 | The conversion ratio |
getMessageContents
This function returns the contents of a message by its ID
function getMessageContents(bytes32 messageId) external view returns (Client.Any2EVMMessage memory);
Parameters
| Name | Type | Description |
|---|---|---|
messageId | bytes32 | The ID of the message |
Returns
| Name | Type | Description |
|---|---|---|
<none> | Client.Any2EVMMessage | The contents of the message |
getConvertedAmount
This function return the conerted amount and the final amount with bonus percentage
function getConvertedAmount(uint256 amount)
external
view
returns (uint256 convertedAmount, uint256 finalAmount, uint256 bonusPercentage);
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount to convert |
Returns
| Name | Type | Description |
|---|---|---|
convertedAmount | uint256 | The amount after conversion |
finalAmount | uint256 | The final amount after applying the bonus percentage |
bonusPercentage | uint256 | The percentage of the bonus applied |
_ccipReceive
This function is called by the CCIP router to receive messages
function _ccipReceive(Client.Any2EVMMessage memory any2EvmMessage) internal virtual override;
Parameters
| Name | Type | Description |
|---|---|---|
any2EvmMessage | Client.Any2EVMMessage | The message coming from the source chain |
_handleFailedMessage
This function handles a failed message by storing the error data and emitting an event
function _handleFailedMessage(Client.Any2EVMMessage calldata any2EvmMessage, bytes memory errorData) private;
Parameters
| Name | Type | Description |
|---|---|---|
any2EvmMessage | Client.Any2EVMMessage | The message that failed |
errorData | bytes | The error data to store |
_setAddressProvider
function _setAddressProvider(address addressProvider) private;
_setAllowlistSourceChain
function _setAllowlistSourceChain(uint64 sourceChainSelector, bool allow) private;
_setAllowlistSender
function _setAllowlistSender(address sender, bool allow) private;
_setConversionRatio
function _setConversionRatio(uint256 ratio) private;
_calculateConversion
This function calculates the conversion of the amount based on the conversion ratio
function _calculateConversion(uint256 amount)
private
view
returns (uint256 _amount, uint256 finalAmount, uint256 bonusPercentage);
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount to convert |
Returns
| Name | Type | Description |
|---|---|---|
_amount | uint256 | The converted amount |
finalAmount | uint256 | The final amount after applying the bonus |
bonusPercentage | uint256 | The bonus percentage applied |
Tokens
TrendexOne
Inherits: ITrendexOne, ERC20, ERC20Capped, ERC20Burnable, ERC20Pausable, AccessControl
State Variables
PAUSER_ROLE
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
MINTER_ROLE
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
Functions
constructor
constructor(address maintainer) ERC20("TrendexONE", "TRDX1") ERC20Capped(1_000_000_000 * 10 ** decimals());
mint
function mint(address to, uint256 amount) public override onlyRole(MINTER_ROLE);
pause
function pause() public onlyRole(PAUSER_ROLE);
unpause
function unpause() public onlyRole(PAUSER_ROLE);
_update
function _update(address from, address to, uint256 value) internal override(ERC20, ERC20Pausable, ERC20Capped);
Staking
StTrendex
Inherits: TimeLockPenaltyERC20
stTrendex is a staking contract that allows users to deposit TrendexONE assets.
State Variables
NAME
string constant NAME = "Stake TrendexONE";
SYMBOL
string constant SYMBOL = "stTrendex";
Functions
constructor
Deploy the stMeta contract.
constructor(address _underlying, address _addressProvider, uint64 _timeLockDuration)
TimeLockPenaltyERC20(NAME, SYMBOL, _underlying, _addressProvider, _timeLockDuration);
Parameters
| Name | Type | Description |
|---|---|---|
_underlying | address | The underlying TrendexONE token. |
_addressProvider | address | The address of the AddressProvider. |
_timeLockDuration | uint64 | The time lock duration. |
deposit
Deposit assets into the contract and mint the equivalent amount of tokens.
function deposit(uint256 _assetAmount, bytes32[] calldata _proof)
external
whenNotPaused
nonReentrant
onlyWhitelistedIfSetup(_proof);
Parameters
| Name | Type | Description |
|---|---|---|
_assetAmount | uint256 | The amount of assets to deposit. |
_proof | bytes32[] | The proof of the whitelisted address. |
depositFor
Deposit assets into the contract and mint the equivalent amount of tokens.
function depositFor(uint256 _assetAmount, address _user)
external
whenNotPaused
nonReentrant
onlyAllowlistedDepositors(msg.sender);
Parameters
| Name | Type | Description |
|---|---|---|
_assetAmount | uint256 | The amount of assets to deposit. |
_user | address | The user to deposit for. |
emergencyWithdraw
Allow users to emergency withdraw assets without penalties.
This function can only be called when the contract is paused.
function emergencyWithdraw(uint256 _unlockingAmount) external whenPaused nonReentrant;
Parameters
| Name | Type | Description |
|---|---|---|
_unlockingAmount | uint256 | The amount of assets to unlock. |
withdraw
Withdraw multiple withdrawal requests.
function withdraw(uint256[] calldata _ids) external nonReentrant;
Parameters
| Name | Type | Description |
|---|---|---|
_ids | uint256[] | The IDs of the withdrawal requests to withdraw. |
RewardDistributor
Inherits: Pausable, ReentrancyGuard
Contract to distribute rewards using a merkle tree.
State Variables
addressProvider
The addressProvider contract
IAddressProvider public addressProvider;
EPOCH_LENGTH
The length of a claim/allocate epoch
uint64 public constant EPOCH_LENGTH = 28 days;
TOKEN
The token to distribute.
IERC20 public immutable TOKEN;
totalClaimed
The total amount of tokens claimed.
uint256 public totalClaimed;
lastEpochId
The last epoch id.
uint64 public lastEpochId = 1;
merkleDrops
The merkle drops.
mapping(uint64 epochId => MerkleDrop merkleDrop) public merkleDrops;
hasClaimed
The rewards already claimed.
mapping(address account => mapping(uint64 epochId => bool hasClaimed)) public hasClaimed;
totalClaimedPerUser
Tracks total amount of user claimed amounts.
mapping(address account => uint256 totalClaimed) public totalClaimedPerUser;
totalClaimedPerEpoch
Maps total claimed amount per epoch.
mapping(uint64 epochId => uint256 totalClaimed) public totalClaimedPerEpoch;
Functions
onlyMaintainer
check that msg.sender has MAINTAINER_ROLE
modifier onlyMaintainer();
onlyUserCentralized
check that msg.sender has USER_CENTRALIZED_ROLE
modifier onlyUserCentralized();
constructor
Constructs RewardsDistributor contract.
constructor(address _addressProvider, address _token);
Parameters
| Name | Type | Description |
|---|---|---|
_addressProvider | address | The address of the AddressProvider. |
_token | address | The address of the token to distribute. |
claims
Claims rewards for multi Epoch.
function claims(ClaimCallData[] calldata _claimsData) external whenNotPaused nonReentrant;
Parameters
| Name | Type | Description |
|---|---|---|
_claimsData | ClaimCallData[] | The claim data array info. |
forwardExpiredRewards
Transfer expired rewards to the fee collector.
function forwardExpiredRewards(uint64[] calldata _epochIds) external;
Parameters
| Name | Type | Description |
|---|---|---|
_epochIds | uint64[] | The list of epoch that will be claimed. |
getExpiredEpochRewards
Get the total rewards that can be forward to the recipient address for the list of epoch.
will revert if an epoch is not expired.
function getExpiredEpochRewards(uint64[] calldata _epochIds) external view returns (uint256 totalExpiredRewards);
Parameters
| Name | Type | Description |
|---|---|---|
_epochIds | uint64[] | The list of epoch to check. |
updateMerkleDrop
Updates the merkleDrop for a specific epoch.
This function can only be called by AccessManager.
function updateMerkleDrop(uint64 _epoch, MerkleDrop memory _merkleDrop) external onlyUserCentralized;
emergencyRescue
Allow to rescue tokens own by the contract.
function emergencyRescue(address _token, address _to, uint256 _amount) external onlyMaintainer whenPaused;
Parameters
| Name | Type | Description |
|---|---|---|
_token | address | The address of the token. |
_to | address | The address of the recipient. |
_amount | uint256 | The amount of tokens to rescue. |
setAddressProvider
Allow maintainer to update the address of the AddressProvider contract.
function setAddressProvider(address newAddressProvider) external onlyMaintainer;
Parameters
| Name | Type | Description |
|---|---|---|
newAddressProvider | address | The new address to set. |
pause
Allow AccessManager to pause the contract.
This function can only be called by AccessManager.
function pause() external onlyMaintainer;
unpause
Allow AccessManager to unpause the contract
This function can only be called by AccessManager
function unpause() external onlyMaintainer;
_claim
Claims rewards.
function _claim(uint64 _epochId, address _account, uint256 _amount, bytes32[] calldata _proof) private;
Parameters
| Name | Type | Description |
|---|---|---|
_epochId | uint64 | |
_account | address | The address of the claimer. |
_amount | uint256 | The amount that the account should claim for the epoch. |
_proof | bytes32[] | The merkle proof that validates this claim. |
_getEpochExpiredRewards
function _getEpochExpiredRewards(uint64 _epochId) private view returns (uint256 epochExpiredRewards);
_setAddressProvider
Set the addressProvider address.
function _setAddressProvider(address newAddressProvider) private;
Parameters
| Name | Type | Description |
|---|---|---|
newAddressProvider | address | The new value to set |
Events
MerkleDropUpdated
Emitted when the root is updated.
event MerkleDropUpdated(uint64 epochId, bytes32 root, uint256 totalAmount, uint64 startTime, uint64 endTime);
Parameters
| Name | Type | Description |
|---|---|---|
epochId | uint64 | The epoch id. |
root | bytes32 | The merkle's tree root. |
totalAmount | uint256 | The totalAmount to distribute. |
startTime | uint64 | The start time of the epoch. |
endTime | uint64 | The time at which all none claimed token will be send to the expiredRewardsRecipient address. |
EmergencyRescued
Emitted when tokens are rescued.
event EmergencyRescued(address token, address to, uint256 amount);
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The address of the token. |
to | address | The address of the recipient. |
amount | uint256 | The amount of tokens rescued. |
RewardsClaimed
Emitted when an account claims rewards.
event RewardsClaimed(uint64 epochId, address account, uint256 amount);
Parameters
| Name | Type | Description |
|---|---|---|
epochId | uint64 | The epochId claimed. |
account | address | The address of the claimer. |
amount | uint256 | The amount of rewards claimed. |
ExpiredRewardsForwarded
Emitted when expired rewards are forwarded.
event ExpiredRewardsForwarded(uint64 epochId, uint256 amount);
Parameters
| Name | Type | Description |
|---|---|---|
epochId | uint64 | The epochId forwarded. |
amount | uint256 | The amount of rewards forwarded. |
AddressProviderUpdated
Emitted when the addressProvider is updated.
event AddressProviderUpdated(address newAddressProvider);
Parameters
| Name | Type | Description |
|---|---|---|
newAddressProvider | address | The new addressProvider. |
Errors
ProofInvalid
Thrown when the proof is invalid or expired.
error ProofInvalid();
AlreadyClaimed
Thrown when the claimer has already claimed the rewards.
error AlreadyClaimed();
EpochExpired
Thrown when epoch expired.
error EpochExpired();
EpochNotExpired
Thrown when epoch didn't expired.
error EpochNotExpired();
NotStarted
Thrown when claim windows didn't not start.
error NotStarted();
EpochZeroNotAllowed
Thrown when epoch is zero.
error EpochZeroNotAllowed();
EpochCantBeUpdated
Thrown when epoch can't be updated.
error EpochCantBeUpdated();
EpochGapNotAllowed
Thrown when epoch updated create a gap.
error EpochGapNotAllowed();
TotalEpochRewardsExceeded
Thrown when totalAmountClaimed for the epoch after a claim will exceed the total amount to distribute.
error TotalEpochRewardsExceeded();
EmptyArray
Thrown when the epochIds array is empty.
error EmptyArray();
EpochAlreadySet
Thrown when the epoch is already set.
error EpochAlreadySet();
Structs
MerkleDrop
struct MerkleDrop {
bytes32 root;
uint256 totalAmount;
uint64 startTime;
uint64 expiryTime;
}
ClaimCallData
struct ClaimCallData {
uint64 epochId;
address account;
uint256 amount;
bytes32[] merkleProof;
}
Pool Configuration
Initialisation Vault ERC-4626 et des pools balancers
Afin d'éviter les attaques d'inflation il faut initialiser le vault à sa création avec un montant minimum, dans notre cas il faut déposer 0.01 USDC et burn la share récupérée (src Morpho).
L'initialisation des vaults doit correspondre à l'initialisation des pools de liquidité sur balancer pour laisser les opportunités d'arbitrage se créer via les transations des utilisateurs :
e.g : Vault X initialisé avec 100 USDC, Pool X initialisé avec 100 shares X.
L'impact du montant d'initialisation :
Si on initialise avec peu de shares, il y aura plus d'opportunités avec de faibles montants.
Si on initialise avec un montant élevé, il y aura moins d'opportunités mais elles seront plus intéressantes (dans tous les cas on va vers ça, si on commence avec peu on finira par atteindre ce cas là).
Choix de configuration des pools
Option 1 : Pool fragmenté
e.g Pool X/Y - Pool X/Z - ...
Cette option consiste à créer plusieurs pools fragmentés, par exemple un pool pour chaque paire de tokens. Cela permet de maximiser le nombre d'opportunités d'arbitrage, mais peut entraîner une fragmentation de la liquidité et un glissement plus important pour les traders.
Impact des pools fragmentés :
-
Avantages :
- Plus d'opportunités d'arbitrage avec de faibles montants.
-
Inconvénients :
- Montants peu élevés
- Slippage important
- Gas fee plus élevé en cas de multihop
- Possiblement ignoré par les bots car peu ou pas rentable
"S'il existe déjà des pools contenant les mêmes jetons et ayant des pondérations similaires, il peut être préférable d'y ajouter de la liquidité plutôt que d'en créer un nouveau. Nous déconseillons la multiplication des pools similaires, car cela fragmente la liquidité et entraîne un glissement plus important pour les traders. Les pools de liquidité importants (surtout avec des frais relativement faibles) offrent la meilleure expérience aux traders." Balancer
Option 2 : Pool unique
e.g Pool X/USDC - Pool Y/USDC - ...
Cette option consiste à créer un pool unique pour chaque token addosé à l'USDC. Cela permet de regrouper la liquidité et de réduire le slippage pour les traders, mais peut limiter les opportunités d'arbitrage.
Impact des pools uniques :
-
Avantages :
- Slippage réduit pour les traders (pool plus liquide).
-
Inconvénients :
- Opportunités d'arbitrage plus intéressantes mais moins fréquentes.
Opportunités d'arbitrages
Les opportunités d'arbitrage se créent lorsque les prix des tokens dans les pools de liquidité ne sont pas alignés avec les prix du protocole (vaults et pools balancer);
Présentation des différents cas d'arbitrage :
Pool fragmenté
Scénario 1 : Vault A/USDC -> Pool A/B -> Vault B/USDC
| Pool | A | B | USDC |
|---|---|---|---|
| Vault A/USDC | 1 | - | 1 |
| Pool A/B | 1 | 1.05 | - |
| Vault B/USDC | - | 1 | 1 |
| Actions | Wallet |
|---|---|
| 1. Achat de 1A à 1 USDC | 1A |
| 2. Swap de 1A --> 1.05B | 1.05B |
| 3. Vente de 1.05B | 1.05 USDC (-fees, slippage) |
Peut importe le montant, si la pool contient peu de liquidité et que l'opportunité n'est pas suffisament haute elle ne couvrira pas le slippage et les frais de gas.
Scénario 2 (multihop): Vault X/USDC -> Pool X/Y -> Vault Y/USDC
| Pool | A | B | USDC |
|---|---|---|---|
| Vault A/USDC | 1 | - | 1 |
| Pool A/B | 1 | 1 | - |
| Pool B/C | 1 | 1.05 | - |
| Vault C/USDC | - | 1 | 1 |
On peut penser à des cas où il y aurai encore plus de swap.
| Actions | Wallet |
|---|---|
| 1. Achat de 1A à 1 USDC | 1A |
| 2. Swap de 1A --> 1B | 1B |
| 3. Swap de 1B --> 1.05C | 1.05C |
| 3. Vente de 1.05C | 1.05 USDC (-fees *2, slippage *2) |
Pool unique
Scénario 1 : Pool A/USDC -> Vault A/USDC
| Pool | A | USDC |
|---|---|---|
| Pool A/USDC | 1.10 | 1 |
| Vault A/USDC | 1 | 1 |
| Actions | Wallet |
|---|---|
| 1. Achat de 1.10A à 1 USDC | 1.10A |
| 2. Vente de 1.10A | 1.10 USDC (-fee, slippage) |