Skip to content

Commit

Permalink
Sync ReadLib (#121)
Browse files Browse the repository at this point in the history
* chore: add ReadLib and update DVN/Worker,FeeLib

* chore: sync OApp code

---------

Co-authored-by: mok-lz <[email protected]>
  • Loading branch information
mok-lz and moyaying authored Nov 11, 2024
1 parent 2d4d177 commit 943ce4a
Show file tree
Hide file tree
Showing 77 changed files with 6,323 additions and 592 deletions.
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

0 comments on commit 943ce4a

Please sign in to comment.