Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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).

Protocole Overview

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)


    LP vault Overview

  • 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.


    Balancer Overview

  • 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.


    Predictions Overview

Admin manager


Predictions Overview
  • ⚠️ 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).


Predictions Overview

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

ContractAddress
Sender0xa61EF3bCC959494c17a2F06b51Ae045C36999dd4
SenderProxy0x95f2826C0f8dEf3186a7AD9De7DbbA4c3b53f42C

Polygon

ContractAddress
Sender
SenderProxy

Testnet

ContractAddressUpdated At

Mainnet

ContractAddressUpdated At

Roles

RoleDescription
MAINTAINER_ROLEHighest level of access, need multisig.
OPERATOR_ROLERole for current operation
MINTER_ROLERole 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

NameTypeDescription
accessControlleraddressThe 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

ABI

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

NameTypeDescription
routeraddressThe address of the CCIP router according to the chainlink documentation
tokenToBridgeaddressThe token to bridge from the source chain to the destination chain
addressProvideraddressThe 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

NameTypeDescription
destinationChainSelectoruint64The selector of the destination chain.
receiveraddressThe address of the receiver contract on the destination chain.
amountuint256The amount of tokens to send.
_proofbytes32[]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

NameTypeDescription
destinationChainSelectoruint64The selector of the destination chain
allowboolThe permission

updateWhitelistedRoot

This function allows the operator to update the whitelisted merkle root.

function updateWhitelistedRoot(bytes32 newRoot) external onlyUserCentalized;

Parameters

NameTypeDescription
newRootbytes32The 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

NameTypeDescription
newAddressProvideraddressThe 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

NameTypeDescription
sponsoredFeesboolWhether 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

NameTypeDescription
destinationChainSelectoruint64The selector of the destination chain
receiveraddressThe address of the receiver contract on the destination chain
amountuint256The amount of tokens to send

Returns

NameTypeDescription
<none>uint256uint256 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

NameTypeDescription
receiveraddressThe address of the receiver contract on the destination chain.
_databytesThe ABI-encoded data to be sent.
feeTokenAddressaddressThe address of the token to be used for fees, 0 for native token.

Returns

NameTypeDescription
<none>Client.EVM2AnyMessageClient.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

ABI

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

NameTypeDescription
any2EvmMessageClient.Any2EVMMessageThe message coming from the source chain

retryFailedMessage

This function allows protocole to retry failed message

function retryFailedMessage(bytes32 messageId) external override onlyOperator;

Parameters

NameTypeDescription
messageIdbytes32The 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

NameTypeDescription
any2EvmMessageClient.Any2EVMMessageThe message coming from the source chain

setAddressProvider

This function sets the address provider

function setAddressProvider(address addressProvider) external override onlyMaintainer;

Parameters

NameTypeDescription
addressProvideraddressThe 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

NameTypeDescription
sourceChainSelectoruint64The selector of the source chain
allowboolThe permission

setAllowlistSender

This function updates the allowlist for receive message from a sender contract

function setAllowlistSender(address sender, bool allow) external override onlyMaintainer;

Parameters

NameTypeDescription
senderaddressThe address of the sender contract
allowboolThe permission

setConversionRatio

This function sets the conversion ratio

function setConversionRatio(uint256 ratio) external override onlyMaintainer;

Parameters

NameTypeDescription
ratiouint256The 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

NameTypeDescription
messageIdbytes32The ID of the message

Returns

NameTypeDescription
<none>Client.Any2EVMMessageThe 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

NameTypeDescription
amountuint256The amount to convert

Returns

NameTypeDescription
convertedAmountuint256The amount after conversion
finalAmountuint256The final amount after applying the bonus percentage
bonusPercentageuint256The 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

NameTypeDescription
any2EvmMessageClient.Any2EVMMessageThe 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

NameTypeDescription
any2EvmMessageClient.Any2EVMMessageThe message that failed
errorDatabytesThe 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

NameTypeDescription
amountuint256The amount to convert

Returns

NameTypeDescription
_amountuint256The converted amount
finalAmountuint256The final amount after applying the bonus
bonusPercentageuint256The bonus percentage applied

Tokens

TrendexOne

ABI

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

ABI

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

NameTypeDescription
_underlyingaddressThe underlying TrendexONE token.
_addressProvideraddressThe address of the AddressProvider.
_timeLockDurationuint64The 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

NameTypeDescription
_assetAmountuint256The amount of assets to deposit.
_proofbytes32[]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

NameTypeDescription
_assetAmountuint256The amount of assets to deposit.
_useraddressThe 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

NameTypeDescription
_unlockingAmountuint256The amount of assets to unlock.

withdraw

Withdraw multiple withdrawal requests.

function withdraw(uint256[] calldata _ids) external nonReentrant;

Parameters

NameTypeDescription
_idsuint256[]The IDs of the withdrawal requests to withdraw.

RewardDistributor

ABI

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

NameTypeDescription
_addressProvideraddressThe address of the AddressProvider.
_tokenaddressThe address of the token to distribute.

claims

Claims rewards for multi Epoch.

function claims(ClaimCallData[] calldata _claimsData) external whenNotPaused nonReentrant;

Parameters

NameTypeDescription
_claimsDataClaimCallData[]The claim data array info.

forwardExpiredRewards

Transfer expired rewards to the fee collector.

function forwardExpiredRewards(uint64[] calldata _epochIds) external;

Parameters

NameTypeDescription
_epochIdsuint64[]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

NameTypeDescription
_epochIdsuint64[]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

NameTypeDescription
_tokenaddressThe address of the token.
_toaddressThe address of the recipient.
_amountuint256The amount of tokens to rescue.

setAddressProvider

Allow maintainer to update the address of the AddressProvider contract.

function setAddressProvider(address newAddressProvider) external onlyMaintainer;

Parameters

NameTypeDescription
newAddressProvideraddressThe 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

NameTypeDescription
_epochIduint64
_accountaddressThe address of the claimer.
_amountuint256The amount that the account should claim for the epoch.
_proofbytes32[]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

NameTypeDescription
newAddressProvideraddressThe 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

NameTypeDescription
epochIduint64The epoch id.
rootbytes32The merkle's tree root.
totalAmountuint256The totalAmount to distribute.
startTimeuint64The start time of the epoch.
endTimeuint64The 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

NameTypeDescription
tokenaddressThe address of the token.
toaddressThe address of the recipient.
amountuint256The amount of tokens rescued.

RewardsClaimed

Emitted when an account claims rewards.

event RewardsClaimed(uint64 epochId, address account, uint256 amount);

Parameters

NameTypeDescription
epochIduint64The epochId claimed.
accountaddressThe address of the claimer.
amountuint256The amount of rewards claimed.

ExpiredRewardsForwarded

Emitted when expired rewards are forwarded.

event ExpiredRewardsForwarded(uint64 epochId, uint256 amount);

Parameters

NameTypeDescription
epochIduint64The epochId forwarded.
amountuint256The amount of rewards forwarded.

AddressProviderUpdated

Emitted when the addressProvider is updated.

event AddressProviderUpdated(address newAddressProvider);

Parameters

NameTypeDescription
newAddressProvideraddressThe 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

PoolABUSDC
Vault A/USDC1-1
Pool A/B11.05-
Vault B/USDC-11
ActionsWallet
1. Achat de 1A à 1 USDC1A
2. Swap de 1A --> 1.05B1.05B
3. Vente de 1.05B1.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

PoolABUSDC
Vault A/USDC1-1
Pool A/B11-
Pool B/C11.05-
Vault C/USDC-11

On peut penser à des cas où il y aurai encore plus de swap.

ActionsWallet
1. Achat de 1A à 1 USDC1A
2. Swap de 1A --> 1B1B
3. Swap de 1B --> 1.05C1.05C
3. Vente de 1.05C1.05 USDC (-fees *2, slippage *2)

Pool unique

Scénario 1 : Pool A/USDC -> Vault A/USDC

PoolAUSDC
Pool A/USDC1.101
Vault A/USDC11
ActionsWallet
1. Achat de 1.10A à 1 USDC1.10A
2. Vente de 1.10A1.10 USDC (-fee, slippage)