Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync ReadLib #121

Merged
merged 2 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 50 additions & 25 deletions packages/layerzero-v2/evm/messagelib/contracts/Executor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx

// endpoint v2
address public endpoint;
uint32 public localEid;
uint32 public localEidV2;

// endpoint v1
address public receiveUln301;
Expand All @@ -80,7 +80,7 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
__ReentrancyGuard_init();
__Worker_init(_messageLibs, _priceFeed, 12000, _roleAdmin, _admins);
endpoint = _endpoint;
localEid = ILayerZeroEndpointV2(_endpoint).eid();
localEidV2 = ILayerZeroEndpointV2(_endpoint).eid();
receiveUln301 = _receiveUln301;
}

Expand Down Expand Up @@ -130,13 +130,13 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx

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
)
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) {
Expand All @@ -163,14 +163,14 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
uint256 _gasLimit
) external payable onlyRole(ADMIN_ROLE) nonReentrant {
try
ILayerZeroEndpointV2(endpoint).lzCompose{ value: msg.value, gas: _gasLimit }(
_from,
_to,
_guid,
_index,
_message,
_extraData
)
ILayerZeroEndpointV2(endpoint).lzCompose{ value: msg.value, gas: _gasLimit }(
_from,
_to,
_guid,
_index,
_message,
_extraData
)
{
// do nothing
} catch (bytes memory reason) {
Expand All @@ -195,21 +195,21 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
) external payable onlyRole(ADMIN_ROLE) nonReentrant {
uint256 spent = _nativeDrop(
_executionParams.origin,
localEid,
localEidV2,
_executionParams.receiver,
_nativeDropParams,
_nativeDropGasLimit
);

uint256 value = msg.value - spent;
try
ILayerZeroEndpointV2(endpoint).lzReceive{ value: value, gas: _executionParams.gasLimit }(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.message,
_executionParams.extraData
)
ILayerZeroEndpointV2(endpoint).lzReceive{ value: value, gas: _executionParams.gasLimit }(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.message,
_executionParams.extraData
)
{
// do nothing
} catch (bytes memory reason) {
Expand Down Expand Up @@ -243,6 +243,19 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
fee = IExecutorFeeLib(workerFeeLib).getFeeOnSend(params, dstConfig[_dstEid], _options);
}

// assignJob for CmdLib
function assignJob(
address _sender,
bytes calldata _options
) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
IExecutorFeeLib.FeeParamsForRead memory params = IExecutorFeeLib.FeeParamsForRead(
priceFeed,
_sender,
defaultMultiplierBps
);
fee = IExecutorFeeLib(workerFeeLib).getFeeOnSend(params, dstConfig[localEidV2], _options);
}

// --- Only ACL ---
function getFee(
uint32 _dstEid,
Expand All @@ -260,6 +273,18 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
fee = IExecutorFeeLib(workerFeeLib).getFee(params, dstConfig[_dstEid], _options);
}

function getFee(
address _sender,
bytes calldata _options
) external view onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
IExecutorFeeLib.FeeParamsForRead memory params = IExecutorFeeLib.FeeParamsForRead(
priceFeed,
_sender,
defaultMultiplierBps
);
fee = IExecutorFeeLib(workerFeeLib).getFee(params, dstConfig[localEidV2], _options);
}

function _nativeDrop(
Origin calldata _origin,
uint32 _dstEid,
Expand Down
129 changes: 93 additions & 36 deletions packages/layerzero-v2/evm/messagelib/contracts/ExecutorFeeLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ pragma solidity ^0.8.20;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

import { Transfer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
import { ExecutorOptions } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol";

import { ILayerZeroPriceFeed } from "./interfaces/ILayerZeroPriceFeed.sol";
import { IExecutor } from "./interfaces/IExecutor.sol";
import { IExecutorFeeLib } from "./interfaces/IExecutorFeeLib.sol";
import { ExecutorOptions } from "./libs/ExecutorOptions.sol";

contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
using ExecutorOptions for bytes;

uint256 private immutable nativeDecimalsRate;
uint32 private immutable localEidV2; // endpoint-v2 only, for read call

constructor(uint256 _nativeDecimalsRate) {
constructor(uint32 _localEidV2, uint256 _nativeDecimalsRate) {
localEidV2 = _localEidV2;
nativeDecimalsRate = _nativeDecimalsRate;
}

Expand All @@ -31,41 +33,58 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
FeeParams calldata _params,
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external returns (uint256 fee) {
) external view returns (uint256 fee) {
fee = getFee(_params, _dstConfig, _options);
}

function getFeeOnSend(
FeeParamsForRead calldata _params,
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external view returns (uint256 fee) {
fee = getFee(_params, _dstConfig, _options);
}

// ================================ View ================================
function getFee(
FeeParams calldata _params,
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) public view returns (uint256 fee) {
if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(_params.dstEid);

(uint256 totalDstAmount, uint256 totalGas) = _decodeExecutorOptions(
(uint256 totalValue, uint256 totalGas, ) = _decodeExecutorOptions(
false,
_isV1Eid(_params.dstEid),
_dstConfig.lzReceiveBaseGas,
_dstConfig.lzComposeBaseGas,
_dstConfig.nativeCap,
_options
);

// for future versions where priceFeed charges a fee
(
uint256 totalGasFee,
uint128 priceRatio,
uint128 priceRatioDenominator,
uint128 nativePriceUSD
) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeOnSend(_params.dstEid, _params.calldataSize, totalGas);
) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(_params.dstEid, _params.calldataSize, totalGas);

uint16 multiplierBps = _dstConfig.multiplierBps == 0 ? _params.defaultMultiplierBps : _dstConfig.multiplierBps;

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

// ================================ View ================================
function getFee(
FeeParams calldata _params,
FeeParamsForRead calldata _params,
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external view returns (uint256 fee) {
if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(_params.dstEid);
) public view returns (uint256 fee) {
if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(localEidV2);

(uint256 totalDstAmount, uint256 totalGas) = _decodeExecutorOptions(
_isV1Eid(_params.dstEid),
(uint256 totalValue, uint256 totalGas, uint32 calldataSize) = _decodeExecutorOptions(
true,
false, // endpoint v2 only
_dstConfig.lzReceiveBaseGas,
_dstConfig.lzComposeBaseGas,
_dstConfig.nativeCap,
Expand All @@ -77,74 +96,108 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
uint128 priceRatio,
uint128 priceRatioDenominator,
uint128 nativePriceUSD
) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(_params.dstEid, _params.calldataSize, totalGas);
) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(localEidV2, calldataSize, totalGas);

uint16 multiplierBps = _dstConfig.multiplierBps == 0 ? _params.defaultMultiplierBps : _dstConfig.multiplierBps;

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

// ================================ Internal ================================
// @dev decode executor options into dstAmount and totalGas
function _decodeExecutorOptions(
bool _isRead,
bool _v1Eid,
uint64 _lzReceiveBaseGas,
uint64 _lzComposeBaseGas,
uint128 _nativeCap,
bytes calldata _options
) internal pure returns (uint256 dstAmount, uint256 totalGas) {
) internal pure returns (uint256 totalValue, uint256 totalGas, uint32 calldataSize) {
ExecutorOptionsAgg memory aggOptions = _parseExecutorOptions(_options, _isRead, _v1Eid, _nativeCap);
totalValue = aggOptions.totalValue;
calldataSize = aggOptions.calldataSize;

// lz receive only called once
// 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 = _lzReceiveBaseGas + aggOptions.totalGas + _lzComposeBaseGas * aggOptions.numLzCompose;
if (aggOptions.ordered) {
totalGas = (totalGas * 102) / 100;
}
}

struct ExecutorOptionsAgg {
uint256 totalValue;
uint256 totalGas;
bool ordered;
uint32 calldataSize;
uint256 numLzCompose;
}

function _parseExecutorOptions(
bytes calldata _options,
bool _isRead,
bool _v1Eid,
uint128 _nativeCap
) internal pure returns (ExecutorOptionsAgg memory options) {
if (_options.length == 0) {
revert Executor_NoOptions();
}

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

bool v1Eid = _v1Eid; // stack too deep
uint256 lzReceiveGas;
uint32 calldataSize;
while (cursor < _options.length) {
(uint8 optionType, bytes calldata option, uint256 newCursor) = _options.nextExecutorOption(cursor);
cursor = newCursor;

if (optionType == ExecutorOptions.OPTION_TYPE_LZRECEIVE) {
// lzRead does not support lzReceive option
if (_isRead) revert Executor_UnsupportedOptionType(optionType);
(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;
options.totalValue += value;
lzReceiveGas += gas;
} else if (optionType == ExecutorOptions.OPTION_TYPE_NATIVE_DROP) {
// lzRead does not support nativeDrop option
if (_isRead) revert Executor_UnsupportedOptionType(optionType);

(uint128 nativeDropAmount, ) = ExecutorOptions.decodeNativeDropOption(option);
dstAmount += nativeDropAmount;
options.totalValue += 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;
// 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;
options.totalValue += value;
options.totalGas += gas;
options.numLzCompose++;
} else if (optionType == ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION) {
ordered = true;
options.ordered = true;
} else if (optionType == ExecutorOptions.OPTION_TYPE_LZREAD) {
if (!_isRead) revert Executor_UnsupportedOptionType(optionType);

(uint128 gas, uint32 size, uint128 value) = ExecutorOptions.decodeLzReadOption(option);
options.totalValue += value;
lzReceiveGas += gas;
calldataSize += size;
} else {
revert Executor_UnsupportedOptionType(optionType);
}
}
if (cursor != _options.length) revert Executor_InvalidExecutorOptions(cursor);
if (dstAmount > _nativeCap) revert Executor_NativeAmountExceedsCap(dstAmount, _nativeCap);
if (options.totalValue > _nativeCap) revert Executor_NativeAmountExceedsCap(options.totalValue, _nativeCap);
if (lzReceiveGas == 0) revert Executor_ZeroLzReceiveGasProvided();
totalGas += lzReceiveGas;

if (ordered) {
totalGas = (totalGas * 102) / 100;
}
if (_isRead && calldataSize == 0) revert Executor_ZeroCalldataSizeProvided();
options.totalGas += lzReceiveGas;
options.calldataSize = calldataSize;
}

function _applyPremiumToGas(
Expand Down Expand Up @@ -179,6 +232,10 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
return _eid < 30000;
}

function version() external pure returns (uint64 major, uint8 minor) {
return (1, 1);
}

// send funds here to pay for price feed directly
receive() external payable {}
}
Loading
Loading