Farm Presto

The Presto verticalization of Covenant farming allows farmers to stake positions in farming contract setups without having to manually conduct the transactions themselves. Consider a farmer who only has ETH, but wants to farm BUIDL by staking a position in the ARTE / UNIFI pool. Without Presto, he would have to manually conduct five transactions: 1) sell ETH for ARTE 2) sell ETH for UNIFI 3) manually approve ARTE for staking 4) manually approve UNIFI for staking 5) stake his position in the setup With Presto, he only has to manually conduct one. This is to approve the Presto contract, which can then perform all required transactions automatically. Not only does this save time, it will save him money too; not only Presto will swap precisely the right amount of ETH to begin with (whereas he is likely to swap a bit too much or too little), it will also bypass the token approval transactions for staking ARTE and UNIFI.

Open Position

To open a position in a setup, the openPosition function is used (which returns the positionId of the position created in the setup).

 function openPosition(
     address prestoAddress,
     PrestoOperation[] memory operations,
     address farmMainAddress,
     FarmingPositionRequest memory request
 ) public payable returns(uint256 positionId) {

The inputs of this function are as follows:

To initiate the Presto process, the openPosition function sets the positionOwner of the position being opened. If the positionOwner is equal to 0x0000000000000000000000000000000000000000, the owner is the msg.sender. Otherwise, it is set to the passed input address.

request.positionOwner = request.positionOwner != address(0) ? request.positionOwner : msg.sender;

The openPosition function then internally calls the _transferToMeAndCheckAllowance method, passing as input the PrestoOperation array and the Presto contract address. This begins the transferal of the required amount of tokens from the msg.sender to the contract.

uint256 eth = _transferToMeAndCheckAllowance(operations, prestoAddress);

To calculate the amount of tokens to be transferred, the _transferToMeAndCheckAllowance method internally calls the _collectTokensAndCheckAllowance and then the _collectTokenData methods. These stores the parameters _tokensToTransfer and _tokenAmounts in the contract's storage (for more on this, see the Presto section about the _collectTokens and _collectTokenData functions).

If the input token passed into PrestoOperation is ETH / if the enterInEth value is true, the amount of ETH to be transferred is expressed by the eth variable; otherwise, the variable eth is equal to zero.

Now, the execute function is called through the Presto contract interface to execute the requested operation (as demonstrated in the Presto section), passing the _operations parameter (which represents all PrestoOperation just passed into thePrestoOperation []array) and the previously calculated eth value (which, as mentioned above, can be populated or equal to zero, depending on whether or not ETH is being used).

IPresto(prestoAddress).execute{value : eth}(_operations);

The farming contract interface is then retrieved from its address and the _calculateAmountsAndApprove method is called both by the openPosition to retrieve and approve the tokens and their amounts needed to open the Farming position.

IFarmMain farmMain = IFarmMain(farmMainAddress);
(address[] memory tokenAddresses, uint256 ethereumValue) = _calculateAmountsAndApprove(farmMain, request.setupIndex, request.amount);

For this, the byTokenAmount method of the AMM Aggregator is used by the _calculateAmountsAndApprove, passing as values the liquidityPoolTokenAddress and mainTokenAddress of the FarmingSetupInfo struct, and the Amount of the FarmingPositionRequest struct, to define the tokensAmounts and tokenAddresses parameters.

(, tokensAmounts, tokenAddresses) = IAMM(setupInfo.ammPlugin).byTokenAmount(setupInfo.liquidityPoolTokenAddress, setupInfo.mainTokenAddress, requestAmount);

If the involvingEth parameter (of the FarmingSetupInfo) is true and one of the tokens included in the tokenAddresses returned by the byTokenAmount method is equal to the ethereumAddress of the setup (FarmingSetupInfo), then the tokensAmounts value is expressed by the ethereumValue variable.

Otherwise, the _safeApprove method is called on the tokens to approve the Farming contract and spend the tokens to open the position.

for(uint256 i = 0; i < tokenAddresses.length; i++) {
    if(setupInfo.involvingETH && tokenAddresses[i] == setupInfo.ethereumAddress) {
        ethereumValue = tokensAmounts[i];
    }
    if(tokenAddresses[i] != address(0)) {
        _safeApprove(tokenAddresses[i], address(farmMain), tokensAmounts[i]);
    }

The position is opened by openPosition calling the Farming openPosition method of the Farming contract, passing the FarmingPositionRequest and the eventual previously calculated ethereumValue (which as mentioned above can be populated or equal to zero depending on whether ETH is being used or not). This creates the attendant positionId in the Farming contract setup.

positionId = farmMain.openPosition{value : ethereumValue}(request);

Having performed the required operations, the execute function calls the _flushAndClear method.

_flushAndClear(tokenAddresses, msg.sender);

As described in the Presto section, this method checks if there are any residual tokens in the contract from the Operations, and sends them back to the msg.sender. Finally, it deletes from the contract storage the _tokenAmounts and _tokenToTransfer parameters previously used to efficiently manage transfer Operations between the user and the contract.

Last updated