Index Presto

The Presto verticalization of Covenant Index tokens allows users to create, mint and burn index tokens directly to and from ETH. For example, say you want to create and mint 3 of Index Token "CD'', each of which will be comprised of 1 BUIDL, 1 UNIFI and 1 ARTE. Using Presto, you can do this directly with ETH, without needing to trade any ETH for these tokens manually. It will automatically and precisely perform all 5 transactions for you: 1) swap ETH for 3 BUIDL 2) swap ETH for 3 UNIFI 3) swap ETH for 3 ARTE 4) mint 3 CD Indexes 5) send the 3 CD indexes to you

Create a New Index Token

To create a new Index token, the first type of mint function is used. This function returns the Index token's objectId and its interoperable interface address.

 function mint(
     address prestoAddress,
     PrestoOperation[] memory operations,
     address indexAddress,
     bytes memory indexData
 ) public payable returns(uint256 objectId, address interoperableInterfaceAddress) {

The inputs of the function are as follows:

The indexData parameter must contain all the information required by the mint function to create a new Index token:

  • name -> index token name

  • symbol -> index token symbol

  • uri -> index token uri

  • _tokens -> array containing the addresses of all tokens selected to be indexed as the index token. To input ETH as one of the chosen tokens, you need to pass 0x0000000000000000000000000000000000000000.

  • _amounts -> the amount of each to-be-indexed token. Each token input (in the previous _tokens field) must correspond to its specific amount. An amount cannot be 0.

  • value -> the amount of the Index token to create.

  • receiver -> pass 0x0000000000000000000000000000000000000000 to send Index tokens to msg.sender; otherwise, pass the desired receiver address.

See this Index section for more details.

The _transferToMeAndCheckAllowance method is called by mint , passing as input the PrestoOperation array and the Presto contract address. This method takes care of transferring the required amount of tokens from the msg.sender to this contract.

uint256 eth = _transferToMeAndCheckAllowance(operations, prestoAddress);

To calculate the amount of tokens, the _transferToMeAndCheckAllowance method internally calls the _collectTokensAndCheckAllowance method and then the _collectTokenData method are used. These stores the parameters _tokensToTransfer and _tokenAmounts in the contract's storage (see the Presto section about the _collectTokens and _collectTokenData functions).

If the input token passed in the PrestoOperation array 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, through the Presto contract interface, the execute function is called by mint to execute the requested Operation (as demonstrated in the Presto section), passing the _operations parameter (which represents all of the PrestoOperation passed in thePrestoOperation []array) and the previously calculated eth value (which as mentioned above can be populated or equal to zero depending on whether ETH is being used or not).

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

At this point, the _mint function is called to create the new Index token, passing as input the address of the Index contract and the indexData (see above).

(objectId, interoperableInterfaceAddress, tokenAddresses) = _mint(indexAddress, indexData);

The _mint function retrieves all of the information required by the Index contract, decodes the indexData retrieving the name, symbol, uri, tokens address[ ] , tokens amounts, value, address receiver.

(string memory name, string memory symbol, string memory uri, address[] memory _tokens, uint256[] memory _amounts, uint256 value, address receiver) = abi.decode(indexData, (string, string, string, address[], uint256[], uint256, address));
_approve(indexAddress, tokenAddresses = _tokens, new uint256[](0));

The _mint function then calls the mint function through the Index contract interface and creates the new Index token.

(objectId, interoperableInterfaceAddress) = IIndex(indexAddress).mint{value : _balanceOf(address(0))}(name, symbol, uri, _tokens, _amounts, value, receiver);

Having performed all required operations, the mint function calls the _flushAndClear method.

_flushAndClear(interoperableInterfaceAddress, tokenAddresses, msg.sender)

This _flushAndClear method takes care of three things. First, it sends to the msg.sender the amount of the minted Index token(s) (or to the receivers address if it was passed in the indexData).

_safeTransfer(indexInteroperableInterfaceAddress, receiver, _balanceOf(indexInteroperableInterfaceAddress));

Then, as explained in the Presto section, it 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.

Mint a Pre-Existing Index Token

To interact with Presto index contracts in order to mint a new amount of an Index token, the second mint public function is used (look at this Index section for more infos).

function mint(
    address prestoAddress,
    PrestoOperation[] memory operations,
    address indexAddress,
    uint256 objectId, uint256 value, address receiver) public payable {

The _transferToMeAndCheckAllowance function is called by mint, passing as input the PrestoOperation array and the Presto contract address. This function takes care of transferring tokens from the msg.sender to this 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, which stores the parameters _tokensToTransfer and _tokenAmounts in the contract's storage (see the Presto section where the _collectTokens and _collectTokenData functions are discussed).

In case the input token passed in PrestoOperation is ETH / if the enterInEth value is true, the amount of ETH to be transferred is expressed by the eth variable.

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

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

The function defines the tokenAddresses and amounts parameters by calling the info method and passing it the objectId and value of the token Index to be minted; then, a _safeApprove is performed on the tokens needed to mint the index token.

(address[] memory tokenAddresses, uint256[] memory amounts) = IIndex(indexAddress).info(objectId, value);
_approve(indexAddress, tokenAddresses, amounts);

The mint function, through the Index contract interface, is called to mint the required Index token amount.

IIndex(indexAddress).mint{value : _balanceOf(address(0))}(objectId, value, receiver);

After performing the required operations, the execute function calls the _flushAndClear method.

_flushAndClear(address(INativeV1(IIndex(indexAddress).collection()).asInteroperable(objectId)), tokenAddresses, msg.sender);

This method takes care of three things. First, it sends to the msg.sender the amount of minted Index token(s) (or to the receivers address if it was passed in the indexData).

_safeTransfer(indexInteroperableInterfaceAddress, receiver, _balanceOf(indexInteroperableInterfaceAddress));

Then, as described in the Presto section, it checks if there are any residual tokens in the contract from 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.

Burn an Index Token

To burn one or more Index tokens, use either the safeTransferFrom or the safeBatchTransferFrom method directly from the Index token collection itself. For example, say you want to burn 3 of Index Token "CD'', each of which comprised of 1 BUIDL, 1 UNIFI and 1 ARTE, taking back ETH instead of 3 BUIDL, 3 UNIFI and 3 ARTE. Using Presto, you can do this directly without needing to swap any BUIDL, UNIFI and ARTE to ETH manually. It will automatically and precisely perform all 5 transactions for you: 1) Burn the 3 CD indexes 2) swap 3 BUIDL to ETH 3) swap 3 UNIFI to 3 ETH 4) swap 3 ARTE to ETH 5) send the ETH amount to you

Use either the safeTransferFrom or safeBatchTransferFrom method with the following inputs:

  • from -> the address calling the safeTransferFrom/safeBatchTransferFrom function

  • to -> the index contract address (representing the Item extension address)

  • objectId/Ids -> the objectId of the specific index to burn, or an array of the objectIds of the various index tokens to burn in the case of a safeBatchTransferFrom

  • amount -> the amount of the index token to burn, or an array of the amounts of the various index tokens (corresponding to their specific objectId in the array of the previous input) to burn

Regarding the data input, the payload must contain three parameters:

1) The Presto contract address

2) The array containing the Presto operations to perform

3) The burn data according to the Index burn operation

Regarding point three (as explained in the Index Burn section):

  • in the case of a safeTransferFrom -> the payload can be an address that represents the one who will receive the tokens after burning the Index. The payload can also be empty (with a length == 0) or 0x0000000000000000000000000000000000000000; in both cases, the tokens corresponding to the burned Index will be sent to the one who sent the Index to be burned.

  • in the case of a safeBatchTransferFrom -> the payload must be an array of bytes (and therefore a bytes[ ] type) of the same length of the objectIds array you are sending; there must be one bytes element for each objectId sent. Each element of the bytes array represents the address of the receiver of that specific tokens' amount returned by the burn. Each address can be equal to 0x0000000000000000000000000000000000000000; in this case, the tokens corresponding to the burned Index will be sent to the one who sent the Index to be burned.

The onERC1155Received/onERC1155BatchReceived function takes care of burning the Index token amount received that arrives in the Index extension contract. First, it decodes the payload passed by the _safeTransferFrom method that has retrieved the data containing the burn information, the Presto contract address and the array of Operations to execute (see above).

(address prestoAddress, PrestoOperation[] memory operations, bytes memory payload) = abi.decode(data, (address, PrestoOperation[], bytes));

Subsequently, to perform the burn action, the safeTransferFrom method is called inside the onERC1155Received/onERC1155BatchReceived , which transfers the Index token amount from the Index Presto verticalization contract to the Index contract.

After sending and burning the amount of the Index token, this contract (IndexPresto verticalization) gets back the amount of corresponding tokens sent from the Index contract. Consequently, the onERC1155Received/onERC1155BatchReceived function calls the _afterBurn method to perform Presto's operations.

_afterBurn(prestoAddress, operations, ids, from);
IPresto(prestoAddress).execute{value : _collectTokensAndCheckAllowance(operations, prestoAddress)}(operations);

To calculate the amount of tokens to be transferred, the _collectTokensAndCheckAllowance and then the _collectTokenData methods are used, which store the parameters _tokensToTransfer and _tokenAmounts in the contract's storage (see the Presto section about the _collectTokens and _collectTokenData functions).

After performing the required operations, the execute function calls the _flushAndClear method.

_flushAndClear(address(0), new address[](0), from);

As explained 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. Then, it deletes from the contract's storage the _tokenAmounts and _tokenToTransfer parameters previously used to efficiently manage transfer Operations between the user and the contract.

Last updated