Skip to content

Commit

Permalink
chore: update EVM Executor reference implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Goulding <goulding@layerzerolabs.org>
  • Loading branch information
ryandgoulding committed Sep 24, 2024
1 parent 7aebbd7 commit 7bdc8e5
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 59 deletions.
131 changes: 125 additions & 6 deletions packages/layerzero-v2/evm/messagelib/contracts/Executor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,58 @@ pragma solidity ^0.8.20;
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import { Proxied } from "hardhat-deploy/solc_0.8/proxy/Proxied.sol";

import { ILayerZeroEndpointV2, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";

import { IUltraLightNode301 } from "./uln/uln301/interfaces/IUltraLightNode301.sol";
import { IExecutor } from "./interfaces/IExecutor.sol";
import { IExecutorFeeLib } from "./interfaces/IExecutorFeeLib.sol";
import { WorkerUpgradeable } from "./upgradeable/WorkerUpgradeable.sol";

interface ILayerZeroEndpointV2 {
function eid() external view returns (uint32);

function lzReceive(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
bytes calldata _message,
bytes calldata _extraData
) external payable;

function lzReceiveAlert(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
uint256 _gas,
uint256 _value,
bytes calldata _message,
bytes calldata _extraData,
bytes calldata _reason
) external;

function lzCompose(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData
) external payable;

function lzComposeAlert(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
uint256 _gas,
uint256 _value,
bytes calldata _message,
bytes calldata _extraData,
bytes calldata _reason
) external;
}

contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IExecutor {
using PacketV1Codec for bytes;

Expand Down Expand Up @@ -49,10 +93,11 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
for (uint256 i = 0; i < _params.length; i++) {
DstConfigParam memory param = _params[i];
dstConfig[param.dstEid] = DstConfig(
param.baseGas,
param.lzReceiveBaseGas,
param.multiplierBps,
param.floorMarginUSD,
param.nativeCap
param.nativeCap,
param.lzComposeBaseGas
);
}
emit DstConfigSet(_params);
Expand Down Expand Up @@ -83,6 +128,66 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
IUltraLightNode301(receiveUln301).commitVerification(_packet, _gasLimit);
}

function execute302(ExecutionParams calldata _executionParams) external payable onlyRole(ADMIN_ROLE) nonReentrant {
try
ILayerZeroEndpointV2(endpoint).lzReceive{ value: msg.value, gas: _executionParams.gasLimit }(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.message,
_executionParams.extraData
)
{
// do nothing
} catch (bytes memory reason) {
ILayerZeroEndpointV2(endpoint).lzReceiveAlert(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.gasLimit,
msg.value,
_executionParams.message,
_executionParams.extraData,
reason
);
}
}

function compose302(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData,
uint256 _gasLimit
) external payable onlyRole(ADMIN_ROLE) nonReentrant {
try
ILayerZeroEndpointV2(endpoint).lzCompose{ value: msg.value, gas: _gasLimit }(
_from,
_to,
_guid,
_index,
_message,
_extraData
)
{
// do nothing
} catch (bytes memory reason) {
ILayerZeroEndpointV2(endpoint).lzComposeAlert(
_from,
_to,
_guid,
_index,
_gasLimit,
msg.value,
_message,
_extraData,
reason
);
}
}

function nativeDropAndExecute302(
NativeDropParams[] calldata _nativeDropParams,
uint256 _nativeDropGasLimit,
Expand All @@ -97,14 +202,28 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
);

uint256 value = msg.value - spent;
// ignore the execution result
try
ILayerZeroEndpointV2(endpoint).lzReceive{ value: value, gas: _executionParams.gasLimit }(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.message,
_executionParams.extraData
);
)
{
// do nothing
} catch (bytes memory reason) {
ILayerZeroEndpointV2(endpoint).lzReceiveAlert(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.gasLimit,
value,
_executionParams.message,
_executionParams.extraData,
reason
);
}
}

// --- Message Lib ---
Expand All @@ -113,7 +232,7 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
address _sender,
uint256 _calldataSize,
bytes calldata _options
) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) returns (uint256 fee) {
) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
IExecutorFeeLib.FeeParams memory params = IExecutorFeeLib.FeeParams(
priceFeed,
_dstEid,
Expand Down
72 changes: 30 additions & 42 deletions packages/layerzero-v2/evm/messagelib/contracts/ExecutorFeeLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external returns (uint256 fee) {
if (_dstConfig.baseGas == 0) revert Executor_EidNotSupported(_params.dstEid);
if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(_params.dstEid);

(uint256 totalDstAmount, uint256 totalGas) = _decodeExecutorOptions(
_isV1Eid(_params.dstEid),
_dstConfig.baseGas,
_dstConfig.lzReceiveBaseGas,
_dstConfig.lzComposeBaseGas,
_dstConfig.nativeCap,
_options
);
Expand All @@ -49,19 +50,10 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
uint128 nativePriceUSD
) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeOnSend(_params.dstEid, _params.calldataSize, totalGas);

fee = _applyPremiumToGas(
totalGasFee,
_dstConfig.multiplierBps,
_params.defaultMultiplierBps,
_dstConfig.floorMarginUSD,
nativePriceUSD
);
fee += _convertAndApplyPremiumToValue(
totalDstAmount,
priceRatio,
priceRatioDenominator,
_params.defaultMultiplierBps
);
uint16 multiplierBps = _dstConfig.multiplierBps == 0 ? _params.defaultMultiplierBps : _dstConfig.multiplierBps;

fee = _applyPremiumToGas(totalGasFee, multiplierBps, _dstConfig.floorMarginUSD, nativePriceUSD);
fee += _convertAndApplyPremiumToValue(totalDstAmount, priceRatio, priceRatioDenominator, multiplierBps);
}

// ================================ View ================================
Expand All @@ -70,11 +62,12 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external view returns (uint256 fee) {
if (_dstConfig.baseGas == 0) revert Executor_EidNotSupported(_params.dstEid);
if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(_params.dstEid);

(uint256 totalDstAmount, uint256 totalGas) = _decodeExecutorOptions(
_isV1Eid(_params.dstEid),
_dstConfig.baseGas,
_dstConfig.lzReceiveBaseGas,
_dstConfig.lzComposeBaseGas,
_dstConfig.nativeCap,
_options
);
Expand All @@ -86,26 +79,18 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
uint128 nativePriceUSD
) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(_params.dstEid, _params.calldataSize, totalGas);

fee = _applyPremiumToGas(
totalGasFee,
_dstConfig.multiplierBps,
_params.defaultMultiplierBps,
_dstConfig.floorMarginUSD,
nativePriceUSD
);
fee += _convertAndApplyPremiumToValue(
totalDstAmount,
priceRatio,
priceRatioDenominator,
_params.defaultMultiplierBps
);
uint16 multiplierBps = _dstConfig.multiplierBps == 0 ? _params.defaultMultiplierBps : _dstConfig.multiplierBps;

fee = _applyPremiumToGas(totalGasFee, multiplierBps, _dstConfig.floorMarginUSD, nativePriceUSD);
fee += _convertAndApplyPremiumToValue(totalDstAmount, priceRatio, priceRatioDenominator, multiplierBps);
}

// ================================ Internal ================================
// @dev decode executor options into dstAmount and totalGas
function _decodeExecutorOptions(
bool _v1Eid,
uint64 _baseGas,
uint64 _lzReceiveBaseGas,
uint64 _lzComposeBaseGas,
uint128 _nativeCap,
bytes calldata _options
) internal pure returns (uint256 dstAmount, uint256 totalGas) {
Expand All @@ -115,8 +100,9 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {

uint256 cursor = 0;
bool ordered = false;
totalGas = _baseGas;
totalGas = _lzReceiveBaseGas; // lz receive only called once

bool v1Eid = _v1Eid; // stack too deep
uint256 lzReceiveGas;
while (cursor < _options.length) {
(uint8 optionType, bytes calldata option, uint256 newCursor) = _options.nextExecutorOption(cursor);
Expand All @@ -126,7 +112,7 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
(uint128 gas, uint128 value) = ExecutorOptions.decodeLzReceiveOption(option);

// endpoint v1 does not support lzReceive with value
if (_v1Eid && value > 0) revert Executor_UnsupportedOptionType(optionType);
if (v1Eid && value > 0) revert Executor_UnsupportedOptionType(optionType);

dstAmount += value;
lzReceiveGas += gas;
Expand All @@ -135,11 +121,16 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
dstAmount += nativeDropAmount;
} else if (optionType == ExecutorOptions.OPTION_TYPE_LZCOMPOSE) {
// endpoint v1 does not support lzCompose
if (_v1Eid) revert Executor_UnsupportedOptionType(optionType);
if (v1Eid) revert Executor_UnsupportedOptionType(optionType);

(, uint128 gas, uint128 value) = ExecutorOptions.decodeLzComposeOption(option);
if (gas == 0) revert Executor_ZeroLzComposeGasProvided();

dstAmount += value;
totalGas += gas;
// lz compose can be called multiple times, based on unique index
// to simplify the quoting, we add lzComposeBaseGas for each lzComposeOption received
// if the same index has multiple compose options, the gas will be added multiple times
totalGas += gas + _lzComposeBaseGas;
} else if (optionType == ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION) {
ordered = true;
} else {
Expand All @@ -158,14 +149,11 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {

function _applyPremiumToGas(
uint256 _fee,
uint16 _bps,
uint16 _defaultBps,
uint16 _multiplierBps,
uint128 _marginUSD,
uint128 _nativePriceUSD
) internal view returns (uint256) {
uint16 multiplierBps = _bps == 0 ? _defaultBps : _bps;

uint256 feeWithMultiplier = (_fee * multiplierBps) / 10000;
uint256 feeWithMultiplier = (_fee * _multiplierBps) / 10000;

if (_nativePriceUSD == 0 || _marginUSD == 0) {
return feeWithMultiplier;
Expand All @@ -179,10 +167,10 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
uint256 _value,
uint128 _ratio,
uint128 _denom,
uint16 _defaultBps
uint16 _multiplierBps
) internal pure returns (uint256 fee) {
if (_value > 0) {
fee = (((_value * _ratio) / _denom) * _defaultBps) / 10000;
fee = (((_value * _ratio) / _denom) * _multiplierBps) / 10000;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ import { ILayerZeroExecutor } from "./ILayerZeroExecutor.sol";
interface IExecutor is IWorker, ILayerZeroExecutor {
struct DstConfigParam {
uint32 dstEid;
uint64 baseGas;
uint64 lzReceiveBaseGas;
uint64 lzComposeBaseGas;
uint16 multiplierBps;
uint128 floorMarginUSD;
uint128 nativeCap;
}

struct DstConfig {
uint64 baseGas; // for verifying / fixed calldata overhead
uint64 lzReceiveBaseGas;
uint16 multiplierBps;
uint128 floorMarginUSD; // uses priceFeed PRICE_RATIO_DENOMINATOR
uint128 nativeCap;
uint64 lzComposeBaseGas;
}

struct ExecutionParams {
Expand All @@ -40,5 +42,5 @@ interface IExecutor is IWorker, ILayerZeroExecutor {
event DstConfigSet(DstConfigParam[] params);
event NativeDropApplied(Origin origin, uint32 dstEid, address oapp, NativeDropParams[] params, bool[] success);

function dstConfig(uint32 _dstEid) external view returns (uint64, uint16, uint128, uint128);
function dstConfig(uint32 _dstEid) external view returns (uint64, uint16, uint128, uint128, uint64);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface IExecutorFeeLib {
error Executor_UnsupportedOptionType(uint8 optionType);
error Executor_InvalidExecutorOptions(uint256 cursor);
error Executor_ZeroLzReceiveGasProvided();
error Executor_ZeroLzComposeGasProvided();
error Executor_EidNotSupported(uint32 eid);

function getFeeOnSend(
Expand Down
Loading

0 comments on commit 7bdc8e5

Please sign in to comment.