Deploying Your EVM Protocol

This guide provides step-by-step instructions on how to deploy your custom EVM protocol.

Prerequisites

Before deployment, ensure you are familiar with payable functions in Solidity and have initiated a Hardhat project.

Package Installation

To streamline development, start by installing the Entangle Labs UIP Contracts NPM package. It includes ready-to-use code and interfaces for deployment.

npm install @entangle-labs/uip-contracts

Deployment Steps

1

Identify the Endpoint Contract Address

Obtain the endpoint contract address for the chain where you plan to deploy your protocol. You can find endpoint addresses for each connected chain and import them from the NPM package installed in the previous step. They are located in the following folder:

@entangle-labs/uip-contracts/addresses/testnet/
2

Import Required Interfaces and Libraries

Ensure the following imports are included in your contract:

import {IEndpoint, TransmitterParams} from "@entangle-labs/uip-contracts/contracts/interfaces/endpoint/IEndpoint.sol";
import "@entangle-labs/uip-contracts/contracts/lib/SelectorLib.sol";
3

Create a Default Selector Variable

To interact with the Endpoint properly, you need to send message with correct selector to execute its “propose” function. Make sure it's included in your contract like so:

bytes4 public constant DEFAULT_SELECTOR = bytes4(keccak256("execute(bytes calldata)"));
4

Create a Payable Function

Develop a payable function that interacts with the endpoint. For example, sending a message to the endpoint requires calling the propose function using the IEndpoint interface:

function propose(
    uint256 destChainID,
    bytes32 selectorSlot,
    bytes calldata agentParams,
    bytes calldata destAddress,
    bytes calldata params
) external payable

So your code may look like provided below:

IEndpoint(endpoint).propose{value: msg.value}(
    chainID,
    SelectorLib.encodeDefaultSelector(DEFAULT_SELECTOR),
    encodedParams,
    destAddress,
    abi.encode(abi.encode(_msg), abi.encode(_msgSender()))
);

Parameter Explanation

  • chainID: The identifier of the destination chain.

  • SelectorLib.encodeDefaultSelector(): A default selector for the execute function in the endpoint contract (this value should remain unchanged).

  • encodedParams: Encoded parameters such as blockFinalizationOption and customGasLimit inbyte format (details on creating these parameters are provided below).

  • destAddress: The address of the recipient on the destination chain.

  • Payload (abi.encode(...)): A combination of the encoded message and message sender inbytes format. This payload can carry any data in bytes format.

Configuring Encoded Parameters

The encodedParams parameter includes transmitter-related settings. These settings currently support two fields:

  • blockFinalizationOption: This field, defines the amount of blocks to wait before transmitting the message to UIP. Note that increasing the amount of blocks to wait also inceases the message transmission time. Currently there are several available options, such as:

    • HARD finalization: 0 (DEFAULT) - This option is optimal for common usage.

    • SOFT finalization: 1 - This option is safer than the HARD option, as the amount of blocks to be passed before the message would be transmit increases.

    • NO finalization: 2 - Use this option carefully, it sets the amount of blocks to zero.

  • customGasLimit: The maximum gas allowed for executing the protocol contract on the destination chain.

struct TransmitterParams {
    uint256 blockFinalizationOption;
    uint256 customGasLimit;
}

To pack these parameters into a “bytes” type variable, use the abi.encodePacked() function and pass in the TransmitterParams struct fields. Please refer to the latest implementation of TransmitterParamsLib for the up-to-date parameter definitions.

Receiver Contract Requirements

The receiving contract must implement the MessageReceiver interface, which you can find in @entagle-labs/uip-contracts npm package — installed in the Package Installation step — specifically the execute function:

function execute(bytes calldata data) external override payable

The execute function processes the received payload. Note that the Endpoint contract should have access control to this function. Make sure that your contract imports MessageReceiver and inherits from it, for example:

import {MessageReceiver} from "@entangle-labs/uip-contracts/contracts/MessageReceiver.sol";
contract ExampleProtocol is MessageReceiver

Notes

  • Payment: All transactions use the native currency of the chain you're working on. Themsg.value sent during the propose call is considered the combined delivery fee.

  • References: Ensure all configurations adhere to the latest documentation and libraries provided in the @entangle-labs/uip-contracts package.

By following these steps, you can successfully deploy your custom EVM protocol across multiple chains. If you encounter issues, refer to the How to Test Your DApp guide or reach out to us through our contact form.

Last updated

Was this helpful?