diff --git a/src/core/DepositLocker.sol b/src/core/DepositLocker.sol index 1f2ae9f..7181edd 100644 --- a/src/core/DepositLocker.sol +++ b/src/core/DepositLocker.sol @@ -132,6 +132,9 @@ contract DepositLocker is Ownable2Step, ReentrancyGuardTransient { /// @notice The address of the DepositExecutor on the destination chain. address public depositExecutor; + /// @notice The gas limit when calling lzReceive for bridged tokens on the destination chain. + uint128 public extraLzReceiveGasLimit; + /// @notice Mapping of an ERC20 token to its corresponding LayerZero OFT. /// @dev NOTE: Must implement the IOFT interface. mapping(ERC20 => IOFT) public tokenToLzV2OFT; @@ -301,6 +304,12 @@ contract DepositLocker is Ownable2Step, ReentrancyGuardTransient { */ event DestinationChainLzEidSet(uint32 dstChainLzEid); + /** + * @notice Emitted when the extra gas limit for receiving bridged tokens on the destination chain is set. + * @param extraLzReceiveGasLimit The new extra gas limit for receiving bridged tokens on the destination chain. + */ + event ExtraLzReceiveGasLimitSet(uint128 extraLzReceiveGasLimit); + /** * @notice Emitted when the Deposit Executor address is set. * @param depositExecutor The new address of the Deposit Executor on the destination chain. @@ -1237,7 +1246,9 @@ contract DepositLocker is Ownable2Step, ReentrancyGuardTransient { to: _addressToBytes32(depositExecutor), amountLD: _amountToBridge, minAmountLD: _amountToBridge, - extraOptions: OptionsBuilder.newOptions().addExecutorLzComposeOption(0, _executorGasLimit, 0), + extraOptions: OptionsBuilder.newOptions() + .addExecutorLzReceiveOption(extraLzReceiveGasLimit, 0) + .addExecutorLzComposeOption(0, _executorGasLimit, 0), composeMsg: _composeMsg, oftCmd: "" }); @@ -1330,6 +1341,12 @@ contract DepositLocker is Ownable2Step, ReentrancyGuardTransient { emit DestinationChainLzEidSet(_dstChainLzEid); } + function setLzReceiveGasLimit(uint128 _gasLimit) external onlyOwner { + extraLzReceiveGasLimit = _gasLimit; + emit ExtraLzReceiveGasLimitSet(_gasLimit); + } + + /** * @notice Sets the DepositExecutor address. * @dev Only callable by the contract owner. diff --git a/src/libraries/OptionsBuilder.sol b/src/libraries/OptionsBuilder.sol index 6f88203..d0e7e45 100644 --- a/src/libraries/OptionsBuilder.sol +++ b/src/libraries/OptionsBuilder.sol @@ -3,13 +3,19 @@ pragma solidity ^0.8.0; library SafeCast { function toUint16(uint256 value) internal pure returns (uint16) { - require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); + require( + value <= type(uint16).max, + "SafeCast: value doesn't fit in 16 bits" + ); return uint16(value); } } library BytesLib { - function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { + function toUint16( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { @@ -21,8 +27,20 @@ library BytesLib { library ExecutorOptions { uint8 internal constant WORKER_ID = 1; + + uint8 internal constant OPTION_TYPE_LZRECEIVE = 1; uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3; + function encodeLzReceiveOption( + uint128 _gas, + uint128 _value + ) internal pure returns (bytes memory) { + return + _value == 0 + ? abi.encodePacked(_gas) + : abi.encodePacked(_gas, _value); + } + function encodeLzComposeOption( uint16 _index, uint128 _gas, @@ -41,7 +59,8 @@ library OptionsBuilder { error InvalidOptionType(uint16 optionType); modifier onlyType3(bytes memory _options) { - if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0)); + if (_options.toUint16(0) != TYPE_3) + revert InvalidOptionType(_options.toUint16(0)); _; } @@ -49,14 +68,40 @@ library OptionsBuilder { return abi.encodePacked(TYPE_3); } + function addExecutorLzReceiveOption( + bytes memory _options, + uint128 _gas, + uint128 _value + ) internal pure onlyType3(_options) returns (bytes memory) { + bytes memory option = ExecutorOptions.encodeLzReceiveOption( + _gas, + _value + ); + return + addExecutorOption( + _options, + ExecutorOptions.OPTION_TYPE_LZRECEIVE, + option + ); + } + function addExecutorLzComposeOption( bytes memory _options, uint16 _index, uint128 _gas, uint128 _value ) internal pure onlyType3(_options) returns (bytes memory) { - bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value); - return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option); + bytes memory option = ExecutorOptions.encodeLzComposeOption( + _index, + _gas, + _value + ); + return + addExecutorOption( + _options, + ExecutorOptions.OPTION_TYPE_LZCOMPOSE, + option + ); } function addExecutorOption( @@ -64,12 +109,13 @@ library OptionsBuilder { uint8 _optionType, bytes memory _option ) internal pure onlyType3(_options) returns (bytes memory) { - return abi.encodePacked( - _options, - ExecutorOptions.WORKER_ID, - _option.length.toUint16() + 1, // +1 for _optionType - _optionType, - _option - ); + return + abi.encodePacked( + _options, + ExecutorOptions.WORKER_ID, + _option.length.toUint16() + 1, // +1 for _optionType + _optionType, + _option + ); } }