From 921d483cedcd0011acb6014fc5f997c0c2fd7c61 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Wed, 28 Aug 2024 09:32:07 -0700 Subject: [PATCH] Files had many dependencies, bringing in more file --- LayerZeroV1/.DS_Store | Bin 0 -> 6148 bytes .../DefaultProxyAdmin.sol | 0 .../LayerZeroV1 => LayerZeroV1}/Endpoint.sol | 0 .../FPValidator.sol | 0 .../MPTValidator.sol | 0 .../NonceContract.sol | 0 .../LayerZeroV1 => LayerZeroV1}/PriceFeed.sol | 2 +- .../LayerZeroV1 => LayerZeroV1}/RelayerV2.sol | 2 +- .../TreasuryV2.sol | 2 +- .../UltraLightNodeV2.sol | 2 +- .../LayerZeroV2 => LayerZeroV2}/DVN.sol | 0 .../LayerZeroV2 => LayerZeroV2}/DVNFeeLib.sol | 0 .../EndpointV2View.sol | 0 .../LayerZeroV2 => LayerZeroV2}/Executor.sol | 0 .../ExecutorFeeLib.sol | 0 .../LzExecutor.sol | 0 .../ProxyAdmin.sol | 0 .../ReceiveUln301.sol | 0 .../ReceiveUln301View.sol | 0 .../ReceiveUln302.sol | 0 .../ReceiveUln302View.sol | 0 .../SendUln301.sol | 0 .../SendUln302.sol | 0 .../LayerZeroV2 => LayerZeroV2}/Treasury.sol | 0 .../TreasuryFeeHandler.sol | 0 .../V1Contracts/contracts/Endpoint.sol | 382 ++++++++++ .../V1Contracts/contracts/EndpointLite.sol | 379 ++++++++++ .../V1Contracts/contracts/FPValidator.sol | 177 +++++ .../V1Contracts/contracts/FeeHandler.sol | 38 + .../V1Contracts/contracts/NonceContract.sol | 23 + .../contracts/NonceContractRadar.sol | 31 + .../V1Contracts/contracts/PriceFeed.sol | 194 +++++ .../V1Contracts/contracts/Relayer.sol | 217 ++++++ .../V1Contracts/contracts/RelayerV2.sol | 397 +++++++++++ .../V1Contracts/contracts/RelayerV2Radar.sol | 294 ++++++++ .../V1Contracts/contracts/Treasury.sol | 70 ++ .../V1Contracts/contracts/TreasuryV2.sol | 76 ++ .../V1Contracts/contracts/TreasuryV2Radar.sol | 68 ++ .../V1Contracts/contracts/UltraLightNode.sol | 614 ++++++++++++++++ .../contracts/UltraLightNodeV2.sol | 588 +++++++++++++++ .../contracts/UltraLightNodeV2AltToken.sol | 583 +++++++++++++++ .../contracts/UltraLightNodeV2Radar.sol | 671 ++++++++++++++++++ .../contracts/arbitrum/interfaces/ArbSys.sol | 99 +++ .../contracts/example/AltTokenUA.sol | 43 ++ .../contracts/interfaces/IContractOne.sol | 9 + .../interfaces/ILayerZeroEndpoint.sol | 112 +++ .../interfaces/ILayerZeroMessagingLibrary.sol | 38 + .../ILayerZeroMessagingLibraryV2.sol | 10 + .../contracts/interfaces/ILayerZeroOracle.sol | 20 + .../interfaces/ILayerZeroOracleV2.sol | 34 + .../interfaces/ILayerZeroPriceFeed.sol | 50 ++ .../interfaces/ILayerZeroPriceFeedV2.sol | 49 ++ .../interfaces/ILayerZeroReceiver.sol | 12 + .../interfaces/ILayerZeroRelayer.sol | 29 + .../interfaces/ILayerZeroRelayerV2.sol | 38 + .../ILayerZeroRelayerV2PriceData.sol | 22 + .../interfaces/ILayerZeroTreasury.sol | 7 + .../interfaces/ILayerZeroUltraLightNodeV1.sol | 27 + .../interfaces/ILayerZeroUltraLightNodeV2.sol | 84 +++ .../ILayerZeroUserApplicationConfig.sol | 25 + .../ILayerZeroValidationLibrary.sol | 14 + .../interfaces/IValidationLibraryHelper.sol | 31 + .../interfaces/IValidationLibraryHelperV2.sol | 31 + .../contracts/libs/RateLimiter.sol | 58 ++ .../contracts/mocks/ContractOne.sol | 11 + .../contracts/mocks/ContractTwo.sol | 17 + .../V1Contracts/contracts/mocks/GIN.sol | 52 ++ .../contracts/mocks/IsContract.sol | 15 + .../mocks/LayerZeroOracleBadMock.sol | 93 +++ .../contracts/mocks/LayerZeroOracleMock.sol | 71 ++ .../contracts/mocks/LayerZeroOracleMockV2.sol | 76 ++ .../contracts/mocks/LayerZeroTokenMock.sol | 11 + .../V1Contracts/contracts/mocks/MockToken.sol | 25 + .../contracts/mocks/OmniCounter.sol | 247 +++++++ .../contracts/mocks/PacketData.sol | 31 + .../V1Contracts/contracts/mocks/PingPong.sol | 157 ++++ .../contracts/mocks/PriceFeedV2Mock.sol | 188 +++++ .../V1Contracts/contracts/mocks/Token.sol | 26 + .../contracts/mocks/UltraLightNodeV2Mock.sol | 58 ++ .../contracts/precrime/PreCrime.sol | 90 +++ .../contracts/precrime/PreCrimeBase.sol | 182 +++++ .../contracts/precrime/PreCrimeView.sol | 47 ++ .../precrime/example/inherit/CounterI.sol | 74 ++ .../example/inherit/CounterPrecrime.sol | 182 +++++ .../example/view/CounterPrecrimeView.sol | 177 +++++ .../precrime/example/view/CounterV.sol | 46 ++ .../precrime/example/view/CounterView.sol | 61 ++ .../precrime/interfaces/IPreCrime.sol | 16 + .../precrime/interfaces/IPreCrimeBase.sol | 36 + .../precrime/interfaces/IPreCrimeView.sol | 16 + .../contracts/proof/FPValidator.sol | 177 +++++ .../contracts/proof/MPTValidator.sol | 136 ++++ .../contracts/proof/MPTValidator01.sol | 211 ++++++ .../contracts/proof/MPTValidatorStgV3.sol | 262 +++++++ .../contracts/proof/MPTValidatorV2.sol | 133 ++++ .../contracts/proof/MPTValidatorV4.sol | 282 ++++++++ .../contracts/proof/MPTValidatorV5.sol | 291 ++++++++ .../contracts/proof/utility/Buffer.sol | 171 +++++ .../proof/utility/LayerZeroPacket.sol | 178 +++++ .../contracts/proof/utility/RLPDecode.sol | 382 ++++++++++ .../utility/UltraLightNodeEVMDecoder.sol | 47 ++ .../contracts/proxy/DefaultProxyAdmin.sol | 84 +++ .../proxy/TransparentUpgradeableProxy.sol | 151 ++++ .../contracts/utility/LayerZeroPacket.sol | 178 +++++ .../ILayerZeroValidationLibrary.sol | 14 + .../interfaces/IValidationLibraryHelperV2.sol | 31 + .../proof/utility/LayerZeroPacket.sol | 178 +++++ endpoint/hardhat.config.ts | 10 - endpoint/package.json | 160 +++-- endpoint/pnpm-lock.yaml | 256 +++---- oldpackage.json | 83 +++ 111 files changed, 10881 insertions(+), 191 deletions(-) create mode 100644 LayerZeroV1/.DS_Store rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/DefaultProxyAdmin.sol (100%) rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/Endpoint.sol (100%) rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/FPValidator.sol (100%) rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/MPTValidator.sol (100%) rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/NonceContract.sol (100%) rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/PriceFeed.sol (99%) rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/RelayerV2.sol (99%) rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/TreasuryV2.sol (97%) rename {endpoint/contracts/LayerZeroV1 => LayerZeroV1}/UltraLightNodeV2.sol (99%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/DVN.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/DVNFeeLib.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/EndpointV2View.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/Executor.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/ExecutorFeeLib.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/LzExecutor.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/ProxyAdmin.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/ReceiveUln301.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/ReceiveUln301View.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/ReceiveUln302.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/ReceiveUln302View.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/SendUln301.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/SendUln302.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/Treasury.sol (100%) rename {endpoint/contracts/LayerZeroV2 => LayerZeroV2}/TreasuryFeeHandler.sol (100%) create mode 100644 endpoint/contracts/V1Contracts/contracts/Endpoint.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/EndpointLite.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/FPValidator.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/FeeHandler.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/NonceContract.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/NonceContractRadar.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/PriceFeed.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/Relayer.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/RelayerV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/RelayerV2Radar.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/Treasury.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/TreasuryV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/TreasuryV2Radar.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/UltraLightNode.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2AltToken.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2Radar.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/arbitrum/interfaces/ArbSys.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/example/AltTokenUA.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/IContractOne.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroEndpoint.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroMessagingLibrary.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroMessagingLibraryV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroOracle.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroOracleV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroPriceFeed.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroPriceFeedV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroReceiver.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayer.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayerV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayerV2PriceData.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroTreasury.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUltraLightNodeV1.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUltraLightNodeV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUserApplicationConfig.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroValidationLibrary.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/IValidationLibraryHelper.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/interfaces/IValidationLibraryHelperV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/libs/RateLimiter.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/ContractOne.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/ContractTwo.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/GIN.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/IsContract.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleBadMock.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleMock.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleMockV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroTokenMock.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/MockToken.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/OmniCounter.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/PacketData.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/PingPong.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/PriceFeedV2Mock.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/Token.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/mocks/UltraLightNodeV2Mock.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/PreCrime.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/PreCrimeBase.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/PreCrimeView.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/example/inherit/CounterI.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/example/inherit/CounterPrecrime.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterPrecrimeView.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterV.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterView.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrime.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrimeBase.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrimeView.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/FPValidator.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/MPTValidator.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/MPTValidator01.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorStgV3.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV2.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV4.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV5.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/utility/Buffer.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/utility/LayerZeroPacket.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/utility/RLPDecode.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proof/utility/UltraLightNodeEVMDecoder.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proxy/DefaultProxyAdmin.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/proxy/TransparentUpgradeableProxy.sol create mode 100644 endpoint/contracts/V1Contracts/contracts/utility/LayerZeroPacket.sol create mode 100644 endpoint/contracts/V1Contracts/interfaces/ILayerZeroValidationLibrary.sol create mode 100644 endpoint/contracts/V1Contracts/interfaces/IValidationLibraryHelperV2.sol create mode 100644 endpoint/contracts/V1Contracts/proof/utility/LayerZeroPacket.sol create mode 100644 oldpackage.json diff --git a/LayerZeroV1/.DS_Store b/LayerZeroV1/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..bdfae0a43916e80c7389fd48aaa80338b9e4c95a GIT binary patch literal 6148 zcmeH~y-LME5QWd^EiQ;~OUr!%!8eGPt6(Si0&;%}ArMgP(^>h ILayerZeroMessagingLibrary) public libraryLookup; // version -> ILayerZeroEndpointLibrary + + // default send/receive libraries + uint16 public defaultSendVersion; + uint16 public defaultReceiveVersion; + ILayerZeroMessagingLibrary public defaultSendLibrary; + address public defaultReceiveLibraryAddress; + + struct LibraryConfig { + uint16 sendVersion; + uint16 receiveVersion; + address receiveLibraryAddress; + ILayerZeroMessagingLibrary sendLibrary; + } + + struct StoredPayload { + uint64 payloadLength; + address dstAddress; + bytes32 payloadHash; + } + + // user app config = [uaAddress] + mapping(address => LibraryConfig) public uaConfigLookup; + // inboundNonce = [srcChainId][srcAddress]. + mapping(uint16 => mapping(bytes => uint64)) public inboundNonce; + // outboundNonce = [dstChainId][srcAddress]. + mapping(uint16 => mapping(address => uint64)) public outboundNonce; + // storedPayload = [srcChainId][srcAddress] + mapping(uint16 => mapping(bytes => StoredPayload)) public storedPayload; + + // library versioning events + event NewLibraryVersionAdded(uint16 version); + event DefaultSendVersionSet(uint16 version); + event DefaultReceiveVersionSet(uint16 version); + event UaSendVersionSet(address ua, uint16 version); + event UaReceiveVersionSet(address ua, uint16 version); + event UaForceResumeReceive(uint16 chainId, bytes srcAddress); + // payload events + event PayloadCleared(uint16 srcChainId, bytes srcAddress, uint64 nonce, address dstAddress); + event PayloadStored( + uint16 srcChainId, + bytes srcAddress, + address dstAddress, + uint64 nonce, + bytes payload, + bytes reason + ); + + constructor(uint16 _chainId) { + chainId = _chainId; + } + + //--------------------------------------------------------------------------- + // send and receive nonreentrant lock + uint8 internal constant _NOT_ENTERED = 1; + uint8 internal constant _ENTERED = 2; + uint8 internal _send_entered_state = 1; + uint8 internal _receive_entered_state = 1; + + modifier sendNonReentrant() { + require(_send_entered_state == _NOT_ENTERED, "LayerZero: no send reentrancy"); + _send_entered_state = _ENTERED; + _; + _send_entered_state = _NOT_ENTERED; + } + modifier receiveNonReentrant() { + require(_receive_entered_state == _NOT_ENTERED, "LayerZero: no receive reentrancy"); + _receive_entered_state = _ENTERED; + _; + _receive_entered_state = _NOT_ENTERED; + } + + // BLOCK_VERSION is also a valid version + modifier validVersion(uint16 _version) { + require(_version <= latestVersion || _version == BLOCK_VERSION, "LayerZero: invalid messaging library version"); + _; + } + + //--------------------------------------------------------------------------- + // User Application Calls - Endpoint Interface + + function send( + uint16 _dstChainId, + bytes calldata _destination, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable override sendNonReentrant { + LibraryConfig storage uaConfig = uaConfigLookup[msg.sender]; + uint64 nonce = ++outboundNonce[_dstChainId][msg.sender]; + _getSendLibrary(uaConfig).send{value: msg.value}( + msg.sender, + nonce, + _dstChainId, + _destination, + _payload, + _refundAddress, + _zroPaymentAddress, + _adapterParams + ); + } + + //--------------------------------------------------------------------------- + // authenticated Library (msg.sender) Calls to pass through Endpoint to UA (dstAddress) + function receivePayload( + uint16 _srcChainId, + bytes calldata _srcAddress, + address _dstAddress, + uint64 _nonce, + uint _gasLimit, + bytes calldata _payload + ) external override receiveNonReentrant { + // assert and increment the nonce. no message shuffling + require(_nonce == ++inboundNonce[_srcChainId][_srcAddress], "LayerZero: wrong nonce"); + + LibraryConfig storage uaConfig = uaConfigLookup[_dstAddress]; + + // authentication to prevent cross-version message validation + // protects against a malicious library from passing arbitrary data + if (uaConfig.receiveVersion == DEFAULT_VERSION) { + require(defaultReceiveLibraryAddress == msg.sender, "LayerZero: invalid default library"); + } else { + require(uaConfig.receiveLibraryAddress == msg.sender, "LayerZero: invalid library"); + } + + // block if any message blocking + StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress]; + require(sp.payloadHash == bytes32(0), "LayerZero: in message blocking"); + + try ILayerZeroReceiver(_dstAddress).lzReceive{gas: _gasLimit}(_srcChainId, _srcAddress, _nonce, _payload) { + // success, do nothing, end of the message delivery + } catch (bytes memory reason) { + // revert nonce if any uncaught errors/exceptions if the ua chooses the blocking mode + storedPayload[_srcChainId][_srcAddress] = StoredPayload( + uint64(_payload.length), + _dstAddress, + keccak256(_payload) + ); + emit PayloadStored(_srcChainId, _srcAddress, _dstAddress, _nonce, _payload, reason); + } + } + + function retryPayload( + uint16 _srcChainId, + bytes calldata _srcAddress, + bytes calldata _payload + ) external override receiveNonReentrant { + StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress]; + require(sp.payloadHash != bytes32(0), "LayerZero: no stored payload"); + require( + _payload.length == sp.payloadLength && keccak256(_payload) == sp.payloadHash, + "LayerZero: invalid payload" + ); + + address dstAddress = sp.dstAddress; + // empty the storedPayload + sp.payloadLength = 0; + sp.dstAddress = address(0); + sp.payloadHash = bytes32(0); + + uint64 nonce = inboundNonce[_srcChainId][_srcAddress]; + + ILayerZeroReceiver(dstAddress).lzReceive(_srcChainId, _srcAddress, nonce, _payload); + emit PayloadCleared(_srcChainId, _srcAddress, nonce, dstAddress); + } + + //--------------------------------------------------------------------------- + // Owner Calls, only new library version upgrade (3 steps) + + // note libraryLookup[0] = 0x0, no library implementation + // LIBRARY UPGRADE step 1: set _newLayerZeroLibraryAddress be the new version + function newVersion(address _newLayerZeroLibraryAddress) external onlyOwner { + require(_newLayerZeroLibraryAddress != address(0x0), "LayerZero: new version cannot be zero address"); + require(latestVersion < 65535, "LayerZero: can not add new messaging library"); + latestVersion++; + libraryLookup[latestVersion] = ILayerZeroMessagingLibrary(_newLayerZeroLibraryAddress); + emit NewLibraryVersionAdded(latestVersion); + } + + // LIBRARY UPGRADE step 2: stop sending messages from the old version + function setDefaultSendVersion( + uint16 _newDefaultSendVersion + ) external onlyOwner validVersion(_newDefaultSendVersion) { + require(_newDefaultSendVersion != DEFAULT_VERSION, "LayerZero: default send version must > 0"); + defaultSendVersion = _newDefaultSendVersion; + defaultSendLibrary = libraryLookup[defaultSendVersion]; + emit DefaultSendVersionSet(_newDefaultSendVersion); + } + + // LIBRARY UPGRADE step 3: stop receiving messages from the old version + function setDefaultReceiveVersion( + uint16 _newDefaultReceiveVersion + ) external onlyOwner validVersion(_newDefaultReceiveVersion) { + require(_newDefaultReceiveVersion != DEFAULT_VERSION, "LayerZero: default receive version must > 0"); + defaultReceiveVersion = _newDefaultReceiveVersion; + defaultReceiveLibraryAddress = address(libraryLookup[defaultReceiveVersion]); + emit DefaultReceiveVersionSet(_newDefaultReceiveVersion); + } + + //--------------------------------------------------------------------------- + // User Application Calls - UA set/get Interface + + function setConfig( + uint16 _version, + uint16 _chainId, + uint _configType, + bytes calldata _config + ) external override validVersion(_version) { + if (_version == DEFAULT_VERSION) { + require( + defaultSendVersion == defaultReceiveVersion, + "LayerZero: can not set Config during DEFAULT migration" + ); + _version = defaultSendVersion; + } + require(_version != BLOCK_VERSION, "LayerZero: can not set config for BLOCK_VERSION"); + libraryLookup[_version].setConfig(_chainId, msg.sender, _configType, _config); + } + + // Migration step 1: set the send version + // Define what library the UA points too + function setSendVersion(uint16 _newVersion) external override validVersion(_newVersion) { + // write into config + LibraryConfig storage uaConfig = uaConfigLookup[msg.sender]; + uaConfig.sendVersion = _newVersion; + // the libraryLookup[BLOCK_VERSION || DEFAULT_VERSION] = 0x0 + uaConfig.sendLibrary = libraryLookup[_newVersion]; + emit UaSendVersionSet(msg.sender, _newVersion); + } + + // Migration step 2: set the receive version + // after all messages sent from the old version are received + // the UA can now safely switch to the new receive version + // it is the UA's responsibility make sure all messages from the old version are processed + function setReceiveVersion(uint16 _newVersion) external override validVersion(_newVersion) { + // write into config + LibraryConfig storage uaConfig = uaConfigLookup[msg.sender]; + uaConfig.receiveVersion = _newVersion; + // the libraryLookup[BLOCK_VERSION || DEFAULT_VERSION] = 0x0 + uaConfig.receiveLibraryAddress = address(libraryLookup[_newVersion]); + emit UaReceiveVersionSet(msg.sender, _newVersion); + } + + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override { + StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress]; + // revert if no messages are cached. safeguard malicious UA behaviour + require(sp.payloadHash != bytes32(0), "LayerZero: no stored payload"); + require(sp.dstAddress == msg.sender, "LayerZero: invalid caller"); + + // empty the storedPayload + sp.payloadLength = 0; + sp.dstAddress = address(0); + sp.payloadHash = bytes32(0); + + // emit the event with the new nonce + emit UaForceResumeReceive(_srcChainId, _srcAddress); + } + + //--------------------------------------------------------------------------- + // view helper function + + function estimateFees( + uint16 _dstChainId, + address _userApplication, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParams + ) external view override returns (uint nativeFee, uint zroFee) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + ILayerZeroMessagingLibrary lib = uaConfig.sendVersion == DEFAULT_VERSION + ? defaultSendLibrary + : uaConfig.sendLibrary; + return lib.estimateFees(_dstChainId, _userApplication, _payload, _payInZRO, _adapterParams); + } + + function _getSendLibrary(LibraryConfig storage uaConfig) internal view returns (ILayerZeroMessagingLibrary) { + if (uaConfig.sendVersion == DEFAULT_VERSION) { + // check if the in send-blocking upgrade + require(defaultSendVersion != BLOCK_VERSION, "LayerZero: default in BLOCK_VERSION"); + return defaultSendLibrary; + } else { + // check if the in send-blocking upgrade + require(uaConfig.sendVersion != BLOCK_VERSION, "LayerZero: in BLOCK_VERSION"); + return uaConfig.sendLibrary; + } + } + + function getSendLibraryAddress( + address _userApplication + ) external view override returns (address sendLibraryAddress) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + uint16 sendVersion = uaConfig.sendVersion; + require(sendVersion != BLOCK_VERSION, "LayerZero: send version is BLOCK_VERSION"); + if (sendVersion == DEFAULT_VERSION) { + require(defaultSendVersion != BLOCK_VERSION, "LayerZero: send version (default) is BLOCK_VERSION"); + sendLibraryAddress = address(defaultSendLibrary); + } else { + sendLibraryAddress = address(uaConfig.sendLibrary); + } + } + + function getReceiveLibraryAddress( + address _userApplication + ) external view override returns (address receiveLibraryAddress) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + uint16 receiveVersion = uaConfig.receiveVersion; + require(receiveVersion != BLOCK_VERSION, "LayerZero: receive version is BLOCK_VERSION"); + if (receiveVersion == DEFAULT_VERSION) { + require(defaultReceiveVersion != BLOCK_VERSION, "LayerZero: receive version (default) is BLOCK_VERSION"); + receiveLibraryAddress = defaultReceiveLibraryAddress; + } else { + receiveLibraryAddress = uaConfig.receiveLibraryAddress; + } + } + + function isSendingPayload() external view override returns (bool) { + return _send_entered_state == _ENTERED; + } + + function isReceivingPayload() external view override returns (bool) { + return _receive_entered_state == _ENTERED; + } + + function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) external view override returns (uint64) { + return inboundNonce[_srcChainId][_srcAddress]; + } + + function getOutboundNonce(uint16 _dstChainId, address _srcAddress) external view override returns (uint64) { + return outboundNonce[_dstChainId][_srcAddress]; + } + + function getChainId() external view override returns (uint16) { + return chainId; + } + + function getSendVersion(address _userApplication) external view override returns (uint16) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + return uaConfig.sendVersion == DEFAULT_VERSION ? defaultSendVersion : uaConfig.sendVersion; + } + + function getReceiveVersion(address _userApplication) external view override returns (uint16) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + return uaConfig.receiveVersion == DEFAULT_VERSION ? defaultReceiveVersion : uaConfig.receiveVersion; + } + + function getConfig( + uint16 _version, + uint16 _chainId, + address _userApplication, + uint _configType + ) external view override validVersion(_version) returns (bytes memory) { + if (_version == DEFAULT_VERSION) { + require(defaultSendVersion == defaultReceiveVersion, "LayerZero: no DEFAULT config while migration"); + _version = defaultSendVersion; + } + require(_version != BLOCK_VERSION, "LayerZero: can not get config for BLOCK_VERSION"); + return libraryLookup[_version].getConfig(_chainId, _userApplication, _configType); + } + + function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) external view override returns (bool) { + StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress]; + return sp.payloadHash != bytes32(0); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/EndpointLite.sol b/endpoint/contracts/V1Contracts/contracts/EndpointLite.sol new file mode 100644 index 0000000..3a061c5 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/EndpointLite.sol @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "./interfaces/ILayerZeroReceiver.sol"; +import "./interfaces/ILayerZeroEndpoint.sol"; +import "./interfaces/ILayerZeroMessagingLibrary.sol"; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract EndpointLite is Ownable, ILayerZeroEndpoint { + uint16 public immutable chainId; + + // installed libraries and reserved versions + uint16 public constant BLOCK_VERSION = 65535; + uint16 public constant DEFAULT_VERSION = 0; + uint16 public latestVersion; + mapping(uint16 => ILayerZeroMessagingLibrary) public libraryLookup; // version -> ILayerZeroEndpointLibrary + + // default send/receive libraries + uint16 public defaultSendVersion; + uint16 public defaultReceiveVersion; + ILayerZeroMessagingLibrary public defaultSendLibrary; + address public defaultReceiveLibraryAddress; + + struct LibraryConfig { + uint16 sendVersion; + uint16 receiveVersion; + address receiveLibraryAddress; + ILayerZeroMessagingLibrary sendLibrary; + } + + struct StoredPayload { + uint64 payloadLength; + address dstAddress; + bytes32 payloadHash; + } + + // user app config = [uaAddress] + mapping(address => LibraryConfig) public uaConfigLookup; + // inboundNonce = [srcChainId][srcAddress]. + mapping(uint16 => mapping(bytes => uint64)) public inboundNonce; + // storedPayload = [srcChainId][srcAddress] + mapping(uint16 => mapping(bytes => StoredPayload)) public storedPayload; + + // library versioning events + event NewLibraryVersionAdded(uint16 version); + event DefaultSendVersionSet(uint16 version); + event DefaultReceiveVersionSet(uint16 version); + event UaSendVersionSet(address ua, uint16 version); + event UaReceiveVersionSet(address ua, uint16 version); + event UaForceResumeReceive(uint16 chainId, bytes srcAddress); + // payload events + event PayloadCleared(uint16 srcChainId, bytes srcAddress, uint64 nonce, address dstAddress); + event PayloadStored( + uint16 srcChainId, + bytes srcAddress, + address dstAddress, + uint64 nonce, + bytes payload, + bytes reason + ); + + constructor(uint16 _chainId) { + chainId = _chainId; + } + + //--------------------------------------------------------------------------- + // send and receive nonreentrant lock + uint8 internal constant _NOT_ENTERED = 1; + uint8 internal constant _ENTERED = 2; + uint8 internal _send_entered_state = 1; + uint8 internal _receive_entered_state = 1; + + modifier sendNonReentrant() { + require(_send_entered_state == _NOT_ENTERED, "LayerZero: no send reentrancy"); + _send_entered_state = _ENTERED; + _; + _send_entered_state = _NOT_ENTERED; + } + modifier receiveNonReentrant() { + require(_receive_entered_state == _NOT_ENTERED, "LayerZero: no receive reentrancy"); + _receive_entered_state = _ENTERED; + _; + _receive_entered_state = _NOT_ENTERED; + } + + // BLOCK_VERSION is also a valid version + modifier validVersion(uint16 _version) { + require(_version <= latestVersion || _version == BLOCK_VERSION, "LayerZero: invalid messaging library version"); + _; + } + + //--------------------------------------------------------------------------- + // User Application Calls - Endpoint Interface + + function send( + uint16 _dstChainId, + bytes calldata _destination, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable override sendNonReentrant { + LibraryConfig storage uaConfig = uaConfigLookup[msg.sender]; + _getSendLibrary(uaConfig).send{value: msg.value}( + msg.sender, + 0, + _dstChainId, + _destination, + _payload, + _refundAddress, + _zroPaymentAddress, + _adapterParams + ); + } + + //--------------------------------------------------------------------------- + // authenticated Library (msg.sender) Calls to pass through Endpoint to UA (dstAddress) + function receivePayload( + uint16 _srcChainId, + bytes calldata _srcAddress, + address _dstAddress, + uint64 _nonce, + uint _gasLimit, + bytes calldata _payload + ) external override receiveNonReentrant { + // assert and increment the nonce. no message shuffling + require(_nonce == ++inboundNonce[_srcChainId][_srcAddress], "LayerZero: wrong nonce"); + + LibraryConfig storage uaConfig = uaConfigLookup[_dstAddress]; + + // authentication to prevent cross-version message validation + // protects against a malicious library from passing arbitrary data + if (uaConfig.receiveVersion == DEFAULT_VERSION) { + require(defaultReceiveLibraryAddress == msg.sender, "LayerZero: invalid default library"); + } else { + require(uaConfig.receiveLibraryAddress == msg.sender, "LayerZero: invalid library"); + } + + // block if any message blocking + StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress]; + require(sp.payloadHash == bytes32(0), "LayerZero: in message blocking"); + + try ILayerZeroReceiver(_dstAddress).lzReceive{gas: _gasLimit}(_srcChainId, _srcAddress, _nonce, _payload) { + // success, do nothing, end of the message delivery + } catch (bytes memory reason) { + // revert nonce if any uncaught errors/exceptions if the ua chooses the blocking mode + storedPayload[_srcChainId][_srcAddress] = StoredPayload( + uint64(_payload.length), + _dstAddress, + keccak256(_payload) + ); + emit PayloadStored(_srcChainId, _srcAddress, _dstAddress, _nonce, _payload, reason); + } + } + + function retryPayload( + uint16 _srcChainId, + bytes calldata _srcAddress, + bytes calldata _payload + ) external override receiveNonReentrant { + StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress]; + require(sp.payloadHash != bytes32(0), "LayerZero: no stored payload"); + require( + _payload.length == sp.payloadLength && keccak256(_payload) == sp.payloadHash, + "LayerZero: invalid payload" + ); + + address dstAddress = sp.dstAddress; + // empty the storedPayload + sp.payloadLength = 0; + sp.dstAddress = address(0); + sp.payloadHash = bytes32(0); + + uint64 nonce = inboundNonce[_srcChainId][_srcAddress]; + + ILayerZeroReceiver(dstAddress).lzReceive(_srcChainId, _srcAddress, nonce, _payload); + emit PayloadCleared(_srcChainId, _srcAddress, nonce, dstAddress); + } + + //--------------------------------------------------------------------------- + // Owner Calls, only new library version upgrade (3 steps) + + // note libraryLookup[0] = 0x0, no library implementation + // LIBRARY UPGRADE step 1: set _newLayerZeroLibraryAddress be the new version + function newVersion(address _newLayerZeroLibraryAddress) external onlyOwner { + require(_newLayerZeroLibraryAddress != address(0x0), "LayerZero: new version cannot be zero address"); + require(latestVersion < 65535, "LayerZero: can not add new messaging library"); + latestVersion++; + libraryLookup[latestVersion] = ILayerZeroMessagingLibrary(_newLayerZeroLibraryAddress); + emit NewLibraryVersionAdded(latestVersion); + } + + // LIBRARY UPGRADE step 2: stop sending messages from the old version + function setDefaultSendVersion( + uint16 _newDefaultSendVersion + ) external onlyOwner validVersion(_newDefaultSendVersion) { + require(_newDefaultSendVersion != DEFAULT_VERSION, "LayerZero: default send version must > 0"); + defaultSendVersion = _newDefaultSendVersion; + defaultSendLibrary = libraryLookup[defaultSendVersion]; + emit DefaultSendVersionSet(_newDefaultSendVersion); + } + + // LIBRARY UPGRADE step 3: stop receiving messages from the old version + function setDefaultReceiveVersion( + uint16 _newDefaultReceiveVersion + ) external onlyOwner validVersion(_newDefaultReceiveVersion) { + require(_newDefaultReceiveVersion != DEFAULT_VERSION, "LayerZero: default receive version must > 0"); + defaultReceiveVersion = _newDefaultReceiveVersion; + defaultReceiveLibraryAddress = address(libraryLookup[defaultReceiveVersion]); + emit DefaultReceiveVersionSet(_newDefaultReceiveVersion); + } + + //--------------------------------------------------------------------------- + // User Application Calls - UA set/get Interface + + function setConfig( + uint16 _version, + uint16 _chainId, + uint _configType, + bytes calldata _config + ) external override validVersion(_version) { + if (_version == DEFAULT_VERSION) { + require( + defaultSendVersion == defaultReceiveVersion, + "LayerZero: can not set Config during DEFAULT migration" + ); + _version = defaultSendVersion; + } + require(_version != BLOCK_VERSION, "LayerZero: can not set config for BLOCK_VERSION"); + libraryLookup[_version].setConfig(_chainId, msg.sender, _configType, _config); + } + + // Migration step 1: set the send version + // Define what library the UA points too + function setSendVersion(uint16 _newVersion) external override validVersion(_newVersion) { + // write into config + LibraryConfig storage uaConfig = uaConfigLookup[msg.sender]; + uaConfig.sendVersion = _newVersion; + // the libraryLookup[BLOCK_VERSION || DEFAULT_VERSION] = 0x0 + uaConfig.sendLibrary = libraryLookup[_newVersion]; + emit UaSendVersionSet(msg.sender, _newVersion); + } + + // Migration step 2: set the receive version + // after all messages sent from the old version are received + // the UA can now safely switch to the new receive version + // it is the UA's responsibility make sure all messages from the old version are processed + function setReceiveVersion(uint16 _newVersion) external override validVersion(_newVersion) { + // write into config + LibraryConfig storage uaConfig = uaConfigLookup[msg.sender]; + uaConfig.receiveVersion = _newVersion; + // the libraryLookup[BLOCK_VERSION || DEFAULT_VERSION] = 0x0 + uaConfig.receiveLibraryAddress = address(libraryLookup[_newVersion]); + emit UaReceiveVersionSet(msg.sender, _newVersion); + } + + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override { + StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress]; + // revert if no messages are cached. safeguard malicious UA behaviour + require(sp.payloadHash != bytes32(0), "LayerZero: no stored payload"); + require(sp.dstAddress == msg.sender, "LayerZero: invalid caller"); + + // empty the storedPayload + sp.payloadLength = 0; + sp.dstAddress = address(0); + sp.payloadHash = bytes32(0); + + // emit the event with the new nonce + emit UaForceResumeReceive(_srcChainId, _srcAddress); + } + + //--------------------------------------------------------------------------- + // view helper function + + function estimateFees( + uint16 _dstChainId, + address _userApplication, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParams + ) external view override returns (uint nativeFee, uint zroFee) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + ILayerZeroMessagingLibrary lib = uaConfig.sendVersion == DEFAULT_VERSION + ? defaultSendLibrary + : uaConfig.sendLibrary; + return lib.estimateFees(_dstChainId, _userApplication, _payload, _payInZRO, _adapterParams); + } + + function _getSendLibrary(LibraryConfig storage uaConfig) internal view returns (ILayerZeroMessagingLibrary) { + if (uaConfig.sendVersion == DEFAULT_VERSION) { + // check if the in send-blocking upgrade + require(defaultSendVersion != BLOCK_VERSION, "LayerZero: default in BLOCK_VERSION"); + return defaultSendLibrary; + } else { + // check if the in send-blocking upgrade + require(uaConfig.sendVersion != BLOCK_VERSION, "LayerZero: in BLOCK_VERSION"); + return uaConfig.sendLibrary; + } + } + + function getSendLibraryAddress( + address _userApplication + ) external view override returns (address sendLibraryAddress) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + uint16 sendVersion = uaConfig.sendVersion; + require(sendVersion != BLOCK_VERSION, "LayerZero: send version is BLOCK_VERSION"); + if (sendVersion == DEFAULT_VERSION) { + require(defaultSendVersion != BLOCK_VERSION, "LayerZero: send version (default) is BLOCK_VERSION"); + sendLibraryAddress = address(defaultSendLibrary); + } else { + sendLibraryAddress = address(uaConfig.sendLibrary); + } + } + + function getReceiveLibraryAddress( + address _userApplication + ) external view override returns (address receiveLibraryAddress) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + uint16 receiveVersion = uaConfig.receiveVersion; + require(receiveVersion != BLOCK_VERSION, "LayerZero: receive version is BLOCK_VERSION"); + if (receiveVersion == DEFAULT_VERSION) { + require(defaultReceiveVersion != BLOCK_VERSION, "LayerZero: receive version (default) is BLOCK_VERSION"); + receiveLibraryAddress = defaultReceiveLibraryAddress; + } else { + receiveLibraryAddress = uaConfig.receiveLibraryAddress; + } + } + + function isSendingPayload() external view override returns (bool) { + return _send_entered_state == _ENTERED; + } + + function isReceivingPayload() external view override returns (bool) { + return _receive_entered_state == _ENTERED; + } + + function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) external view override returns (uint64) { + return inboundNonce[_srcChainId][_srcAddress]; + } + + function getOutboundNonce(uint16, address) external pure override returns (uint64) { + revert("LayerZero: no outbound nonce at endpoint"); + } + + function getChainId() external view override returns (uint16) { + return chainId; + } + + function getSendVersion(address _userApplication) external view override returns (uint16) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + return uaConfig.sendVersion == DEFAULT_VERSION ? defaultSendVersion : uaConfig.sendVersion; + } + + function getReceiveVersion(address _userApplication) external view override returns (uint16) { + LibraryConfig storage uaConfig = uaConfigLookup[_userApplication]; + return uaConfig.receiveVersion == DEFAULT_VERSION ? defaultReceiveVersion : uaConfig.receiveVersion; + } + + function getConfig( + uint16 _version, + uint16 _chainId, + address _userApplication, + uint _configType + ) external view override validVersion(_version) returns (bytes memory) { + if (_version == DEFAULT_VERSION) { + require(defaultSendVersion == defaultReceiveVersion, "LayerZero: no DEFAULT config while migration"); + _version = defaultSendVersion; + } + require(_version != BLOCK_VERSION, "LayerZero: can not get config for BLOCK_VERSION"); + return libraryLookup[_version].getConfig(_chainId, _userApplication, _configType); + } + + function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) external view override returns (bool) { + StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress]; + return sp.payloadHash != bytes32(0); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/FPValidator.sol b/endpoint/contracts/V1Contracts/contracts/FPValidator.sol new file mode 100644 index 0000000..e05390e --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/FPValidator.sol @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./utility/LayerZeroPacket.sol"; +import "../interfaces/ILayerZeroValidationLibrary.sol"; +import "../interfaces/IValidationLibraryHelperV2.sol"; + +interface IStargate { + // Stargate objects for abi encoding / decoding + struct SwapObj { + uint amount; + uint eqFee; + uint eqReward; + uint lpFee; + uint protocolFee; + uint lkbRemove; + } + + struct CreditObj { + uint credits; + uint idealBalance; + } +} + +contract FPValidator is ILayerZeroValidationLibrary, IValidationLibraryHelperV2 { + uint8 public proofType = 2; + uint8 public utilsVersion = 1; + + address public immutable stargateBridgeAddress; + address public immutable stargateTokenAddress; + + constructor(address _stargateBridgeAddress, address _stargateTokenAddress) { + stargateBridgeAddress = _stargateBridgeAddress; + stargateTokenAddress = _stargateTokenAddress; + } + + function validateProof( + bytes32 _packetHash, + bytes calldata _transactionProof, + uint _remoteAddressSize + ) external view override returns (LayerZeroPacket.Packet memory packet) { + require(_remoteAddressSize > 0, "ProofLib: invalid address size"); + // _transactionProof = srcUlnAddress (32 bytes) + lzPacket + require( + _transactionProof.length > 32 && keccak256(_transactionProof) == _packetHash, + "ProofLib: invalid transaction proof" + ); + + bytes memory ulnAddressBytes = bytes(_transactionProof[0:32]); + bytes32 ulnAddress; + assembly { + ulnAddress := mload(add(ulnAddressBytes, 32)) + } + packet = LayerZeroPacket.getPacketV3(_transactionProof[32:], _remoteAddressSize, ulnAddress); + + if (packet.dstAddress == stargateBridgeAddress) packet.payload = _secureStgPayload(packet.payload); + if (packet.dstAddress == stargateTokenAddress) packet.payload = _secureStgTokenPayload(packet.payload); + + return packet; + } + + function _secureStgTokenPayload(bytes memory _payload) internal pure returns (bytes memory) { + (bytes memory toAddressBytes, uint qty) = abi.decode(_payload, (bytes, uint)); + + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + if (toAddress == address(0)) { + address deadAddress = address(0x000000000000000000000000000000000000dEaD); + bytes memory newToAddressBytes = abi.encodePacked(deadAddress); + return abi.encode(newToAddressBytes, qty); + } + + // default to return the original payload + return _payload; + } + + function _secureStgPayload(bytes memory _payload) internal view returns (bytes memory) { + // functionType is uint8 even though the encoding will take up the side of uint256 + uint8 functionType; + assembly { + functionType := mload(add(_payload, 32)) + } + + // TYPE_SWAP_REMOTE == 1 && only if the payload has a payload + // only swapRemote inside of stargate can call sgReceive on an user supplied to address + // thus we do not care about the other type functions even if the toAddress is overly long. + if (functionType == 1) { + // decode the _payload with its types + ( + , + uint srcPoolId, + uint dstPoolId, + uint dstGasForCall, + IStargate.CreditObj memory c, + IStargate.SwapObj memory s, + bytes memory toAddressBytes, + bytes memory contractCallPayload + ) = abi.decode(_payload, (uint8, uint, uint, uint, IStargate.CreditObj, IStargate.SwapObj, bytes, bytes)); + + // if contractCallPayload.length > 0 need to check if the to address is a contract or not + if (contractCallPayload.length > 0) { + // otherwise, need to check if the payload can be delivered to the toAddress + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + // check if the toAddress is a contract. We are not concerned about addresses that pretend to be wallets. because worst case we just delete their payload if being malicious + // we can guarantee that if a size > 0, then the contract is definitely a contract address in this context + uint size; + assembly { + size := extcodesize(toAddress) + } + + if (size == 0) { + // size == 0 indicates its not a contract, payload wont be delivered + // secure the _payload to make sure funds can be delivered to the toAddress + bytes memory newToAddressBytes = abi.encodePacked(toAddress); + bytes memory securePayload = abi.encode( + functionType, + srcPoolId, + dstPoolId, + dstGasForCall, + c, + s, + newToAddressBytes, + bytes("") + ); + return securePayload; + } + } + } + + // default to return the original payload + return _payload; + } + + function secureStgTokenPayload(bytes memory _payload) external pure returns (bytes memory) { + return _secureStgTokenPayload(_payload); + } + + function secureStgPayload(bytes memory _payload) external view returns (bytes memory) { + return _secureStgPayload(_payload); + } + + function getUtilsVersion() external view override returns (uint8) { + return utilsVersion; + } + + function getProofType() external view override returns (uint8) { + return proofType; + } + + function getVerifyLog( + bytes32, + uint[] calldata, + uint, + bytes[] calldata proof + ) external pure override returns (ULNLog memory log) {} + + function getPacket( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure override returns (LayerZeroPacket.Packet memory) { + return LayerZeroPacket.getPacketV3(data, sizeOfSrcAddress, ulnAddress); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/FeeHandler.sol b/endpoint/contracts/V1Contracts/contracts/FeeHandler.sol new file mode 100644 index 0000000..4352029 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/FeeHandler.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract FeeHandler is Ownable { + using SafeERC20 for IERC20; + + IERC20 public feeToken; + mapping(address => bool) public approved; + + constructor() {} + + function setFeeToken(address _feeToken) external onlyOwner { + require(address(feeToken) == address(0x0), "FeeHandler: feeToken already set"); + feeToken = IERC20(_feeToken); + } + + function approve(address _uln) external onlyOwner { + approved[_uln] = true; + } + + function creditFee(address[] calldata _receivers, uint[] calldata _amounts, address _refundAddress) external { + require(_receivers.length == _amounts.length, "FeeHandler: invalid parameters"); + require(approved[msg.sender], "FeeHandler: not approved"); + + for (uint i = 0; i < _receivers.length; i++) { + feeToken.safeTransfer(_receivers[i], _amounts[i]); + } + uint remaining = feeToken.balanceOf(address(this)); + if (remaining > 0) { + feeToken.safeTransfer(_refundAddress, remaining); + } + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/NonceContract.sol b/endpoint/contracts/V1Contracts/contracts/NonceContract.sol new file mode 100644 index 0000000..73442fb --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/NonceContract.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "./interfaces/ILayerZeroEndpoint.sol"; + +contract NonceContract { + ILayerZeroEndpoint public immutable endpoint; + // outboundNonce = [dstChainId][remoteAddress + localAddress] + mapping(uint16 => mapping(bytes => uint64)) public outboundNonce; + + constructor(address _endpoint) { + endpoint = ILayerZeroEndpoint(_endpoint); + } + + function increment(uint16 _chainId, address _ua, bytes calldata _path) external returns (uint64) { + require( + endpoint.getSendLibraryAddress(_ua) == msg.sender, + "NonceContract: msg.sender is not valid sendlibrary" + ); + return ++outboundNonce[_chainId][_path]; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/NonceContractRadar.sol b/endpoint/contracts/V1Contracts/contracts/NonceContractRadar.sol new file mode 100644 index 0000000..9bcd23a --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/NonceContractRadar.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "./interfaces/ILayerZeroEndpoint.sol"; + +contract NonceContractRadar { + ILayerZeroEndpoint public immutable endpoint; + address public immutable ulnv2Radar; + // outboundNonce = [dstChainId][remoteAddress + localAddress] + mapping(uint16 => mapping(bytes => uint64)) public outboundNonce; + + constructor(address _endpoint, address _ulnv2Radar) { + endpoint = ILayerZeroEndpoint(_endpoint); + ulnv2Radar = _ulnv2Radar; + } + + function increment(uint16 _chainId, address _ua, bytes calldata _path) external returns (uint64) { + require( + endpoint.getSendLibraryAddress(_ua) == msg.sender, + "NonceContract: msg.sender is not valid sendlibrary" + ); + return ++outboundNonce[_chainId][_path]; + } + + // only ulnv2Radar can call this function + function initRadarOutboundNonce(uint16 _dstChainId, bytes calldata _path, uint64 _nonce) external { + require(msg.sender == ulnv2Radar, "NonceContract: only ulnv2Radar"); + outboundNonce[_dstChainId][_path] = _nonce; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/PriceFeed.sol b/endpoint/contracts/V1Contracts/contracts/PriceFeed.sol new file mode 100644 index 0000000..98d5881 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/PriceFeed.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +// import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "hardhat-deploy/solc_0.7/proxy/Proxied.sol"; +import "./interfaces/ILayerZeroPriceFeed.sol"; + +contract PriceFeed is ILayerZeroPriceFeed, OwnableUpgradeable, Proxied { + using SafeMath for uint; + + uint128 public PRICE_RATIO_DENOMINATOR; + + // sets pricing + mapping(address => bool) public priceUpdater; + + // [_chainId] + mapping(uint16 => Price) public defaultModelPrice; + ArbitrumPriceExt public arbitrumPriceExt; + + uint128 public override nativeTokenPriceUSD; // uses PRICE_RATIO_DENOMINATOR + + // upgrade: arbitrum compression - percentage of callDataSize after brotli compression + uint128 public ARBITRUM_COMPRESSION_PERCENT; + + // ============================ Constructor =================================== + + function initialize(address _priceUpdater) public proxied initializer { + __Ownable_init(); + priceUpdater[_priceUpdater] = true; + PRICE_RATIO_DENOMINATOR = 1e10; // gas optimization: if you change this, you also need to change the RelayerV2 + ARBITRUM_COMPRESSION_PERCENT = 47; + } + + function onUpgrade() public proxied { + PRICE_RATIO_DENOMINATOR = 1e10; // gas optimization: if you change this, you also need to change the RelayerV2 + ARBITRUM_COMPRESSION_PERCENT = 47; + } + + // ============================ Modifier ====================================== + + // owner is always approved + modifier onlyPriceUpdater() { + if (owner() != msg.sender) { + require(priceUpdater[msg.sender], "PriceFeed: not price updater"); + } + _; + } + + // ============================ OnlyOwner ===================================== + + function setPriceUpdater(address _addr, bool _active) external onlyOwner { + priceUpdater[_addr] = _active; + } + + function setPriceRatioDenominator(uint128 _denominator) external onlyOwner { + PRICE_RATIO_DENOMINATOR = _denominator; + } + + function setArbitrumCompressionPercent(uint128 _compressionPercent) external onlyOwner { + ARBITRUM_COMPRESSION_PERCENT = _compressionPercent; + } + + // ============================ OnlyPriceUpdater ===================================== + + function setPrice(UpdatePrice[] calldata _price) external onlyPriceUpdater { + for (uint i = 0; i < _price.length; i++) { + UpdatePrice calldata _update = _price[i]; + _setPrice(_update.chainId, _update.price); + } + } + + function setPriceForArbitrum(UpdatePriceExt[] calldata _price) external onlyPriceUpdater { + for (uint i = 0; i < _price.length; i++) { + UpdatePriceExt calldata _update = _price[i]; + + _setPrice(_update.chainId, _update.price); + + uint64 gasPerL2Tx = _update.extend.gasPerL2Tx; + uint32 gasPerL1CalldataByte = _update.extend.gasPerL1CallDataByte; + + arbitrumPriceExt.gasPerL2Tx = gasPerL2Tx; + arbitrumPriceExt.gasPerL1CallDataByte = gasPerL1CalldataByte; + } + } + + function setNativeTokenPriceUSD(uint128 _nativeTokenPriceUSD) external onlyPriceUpdater { + nativeTokenPriceUSD = _nativeTokenPriceUSD; + } + + // ============================ Internal ========================================== + function _setPrice(uint16 chainId, Price memory _price) internal { + uint128 priceRatio = _price.priceRatio; + uint64 gasPriceInUnit = _price.gasPriceInUnit; + uint32 gasPerByte = _price.gasPerByte; + defaultModelPrice[chainId] = Price(priceRatio, gasPriceInUnit, gasPerByte); + } + + // For optimism l1 gas price lookup + function _getL1LookupId(uint16 _l2ChainId) internal pure returns (uint16) { + if (_l2ChainId == 111) { + return 101; + } else if (_l2ChainId == 10132) { + return 10121; // ethereum-goerli + } else if (_l2ChainId == 20132) { + return 20121; // ethereum-goerli + } else { + revert("PriceFeed: unknown l2 chain id"); + } + } + + // ============================ View ========================================== + + function getPrice(uint16 _dstChainId) external view override returns (Price memory price) { + price = defaultModelPrice[_dstChainId]; + } + + function getPriceRatioDenominator() external view override returns (uint128) { + return PRICE_RATIO_DENOMINATOR; + } + + function estimateFeeByChain( + uint16 _dstChainId, + uint _callDataSize, + uint _gas + ) external view override returns (uint fee, uint128 priceRatio) { + if (_dstChainId == 110 || _dstChainId == 10143 || _dstChainId == 20143) { + return estimateFeeWithArbitrumModel(_dstChainId, _callDataSize, _gas); + } else if (_dstChainId == 111 || _dstChainId == 10132 || _dstChainId == 20132) { + return estimateFeeWithOptimismModel(_dstChainId, _callDataSize, _gas); + } else { + return estimateFeeWithDefaultModel(_dstChainId, _callDataSize, _gas); + } + } + + function estimateFeeWithDefaultModel( + uint16 _dstChainId, + uint _callDataSize, + uint _gas + ) public view returns (uint fee, uint128 priceRatio) { + Price storage remotePrice = defaultModelPrice[_dstChainId]; + + // assuming the _gas includes (1) the 21,000 overhead and (2) not the calldata gas + uint gasForCallData = _callDataSize.mul(remotePrice.gasPerByte); + uint remoteFee = (gasForCallData.add(_gas)).mul(remotePrice.gasPriceInUnit); + return ((remoteFee.mul(remotePrice.priceRatio)).div(PRICE_RATIO_DENOMINATOR), remotePrice.priceRatio); + } + + function estimateFeeWithOptimismModel( + uint16 _dstChainId, + uint _callDataSize, + uint _gas + ) public view returns (uint fee, uint128 priceRatio) { + uint16 ethereumId = _getL1LookupId(_dstChainId); + + // L1 fee + Price storage ethereumPrice = defaultModelPrice[ethereumId]; + uint gasForL1CallData = _callDataSize.mul(ethereumPrice.gasPerByte).add(3188); // 2100 + 68 * 16 + uint l1Fee = gasForL1CallData.mul(ethereumPrice.gasPriceInUnit); + + // L2 fee + Price storage optimismPrice = defaultModelPrice[_dstChainId]; + uint gasForL2CallData = _callDataSize.mul(optimismPrice.gasPerByte); + uint l2Fee = (gasForL2CallData.add(_gas)).mul(optimismPrice.gasPriceInUnit); + + uint l1FeeInSrcPrice = l1Fee.mul(ethereumPrice.priceRatio).div(PRICE_RATIO_DENOMINATOR); + uint l2FeeInSrcPrice = l2Fee.mul(optimismPrice.priceRatio).div(PRICE_RATIO_DENOMINATOR); + uint gasFee = l1FeeInSrcPrice.add(l2FeeInSrcPrice); + return (gasFee, optimismPrice.priceRatio); + } + + function estimateFeeWithArbitrumModel( + uint16 _dstChainId, + uint _callDataSize, + uint _gas + ) public view returns (uint fee, uint128 priceRatio) { + Price storage arbitrumPrice = defaultModelPrice[_dstChainId]; + + // L1 fee + uint gasForL1CallData = _callDataSize.mul(ARBITRUM_COMPRESSION_PERCENT).div(100).mul( + arbitrumPriceExt.gasPerL1CallDataByte + ); + // L2 Fee + uint gasForL2CallData = _callDataSize.mul(arbitrumPrice.gasPerByte); + uint gasFee = (_gas.add(arbitrumPriceExt.gasPerL2Tx).add(gasForL1CallData).add(gasForL2CallData)).mul( + arbitrumPrice.gasPriceInUnit + ); + + return ((gasFee.mul(arbitrumPrice.priceRatio)).div(PRICE_RATIO_DENOMINATOR), arbitrumPrice.priceRatio); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/Relayer.sol b/endpoint/contracts/V1Contracts/contracts/Relayer.sol new file mode 100644 index 0000000..a3e52e0 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/Relayer.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "hardhat-deploy/solc_0.7/proxy/Proxied.sol"; + +import "./interfaces/ILayerZeroRelayer.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV1.sol"; + +contract Relayer is ILayerZeroRelayer, ReentrancyGuard, OwnableUpgradeable, Proxied { + using SafeERC20 for IERC20; + using SafeMath for uint; + using SafeMath for uint128; + using SafeMath for uint64; + + ILayerZeroUltraLightNodeV1 public uln; + + struct DstPrice { + uint128 dstPriceRatio; // 10^10 + uint128 dstGasPriceInWei; + } + + struct DstConfig { + uint128 dstNativeAmtCap; + uint64 baseGas; + uint64 gasPerByte; + } + + // [_chainId] => DstPriceData. change often + mapping(uint16 => DstPrice) public dstPriceLookup; + // [_chainId][_outboundProofType] => DstConfig. change much less often + mapping(uint16 => mapping(uint16 => DstConfig)) public dstConfigLookup; + mapping(address => bool) public approvedAddresses; + + event WithdrawTokens(address token, address to, uint amount); + event Withdraw(address to, uint amount); + event ApproveAddress(address addr, bool approved); + + address public stargateBridgeAddress; + + event InvalidPayloadSize(uint indexed payloadSize); + event ValueTransferFailed(address indexed to, uint indexed quantity); + + //---------------------------------------------------------------------------------- + // Relayer Interface V2 Change + + event AssignJob(uint16 dstChainId, uint16 outboundProofType, address userApplication, uint totalFee); + event Ulnv2Set(address ulnv2); + + // new pauseable relayer + bool public paused; + + // owner is always approved + modifier onlyApproved() { + if (owner() != msg.sender) { + require(isApproved(msg.sender), "Relayer: not approved "); + } + _; + } + + function initialize(address _uln) public proxied initializer { + __Ownable_init(); + uln = ILayerZeroUltraLightNodeV1(_uln); + setApprovedAddress(address(this), true); + } + + //---------------------------------------------------------------------------------- + // onlyApproved + function validateTransactionProofV2( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _blockHash, + bytes calldata _transactionProof, + address payable _to + ) external payable onlyApproved nonReentrant { + (bool sent, ) = _to.call{value: msg.value}(""); + //require(sent, "Relayer: failed to send ether"); + if (!sent) { + emit ValueTransferFailed(_to, msg.value); + } + uln.validateTransactionProof(_srcChainId, _dstAddress, _gasLimit, _blockHash, _transactionProof); + } + + function validateTransactionProofV1( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _blockHash, + bytes calldata _transactionProof + ) external onlyApproved nonReentrant { + uln.validateTransactionProof(_srcChainId, _dstAddress, _gasLimit, _blockHash, _transactionProof); + } + + function setDstPrice(uint16 _chainId, uint128 _dstPriceRatio, uint128 _dstGasPriceInWei) external onlyApproved { + dstPriceLookup[_chainId] = DstPrice(_dstPriceRatio, _dstGasPriceInWei); + } + + function setDstConfig( + uint16 _chainId, + uint16 _outboundProofType, + uint128 _dstNativeAmtCap, + uint64 _baseGas, + uint64 _gasPerByte + ) external onlyApproved { + dstConfigLookup[_chainId][_outboundProofType] = DstConfig(_dstNativeAmtCap, _baseGas, _gasPerByte); + } + + // other relayer fees are withdrawn through the RelayerFee interface + // uint8 public constant WITHDRAW_TYPE_RELAYER_QUOTED_FEES = 2; + function withdrawQuotedFromULN(address payable _to, uint _amount) external onlyApproved { + uln.withdrawNative(2, address(0x0), _to, _amount); + } + + //---------------------------------------------------------------------------------- + // onlyOwner + + function setApprovedAddress(address _relayerAddress, bool _approve) public onlyOwner { + approvedAddresses[_relayerAddress] = _approve; + emit ApproveAddress(_relayerAddress, _approve); + } + + function setPause(bool _paused) public onlyOwner { + paused = _paused; + } + + //---------------------------------------------------------------------------------- + // view functions + + // txType 1 + // bytes [2 32 ] + // fields [txType extraGas] + // txType 2 + // bytes [2 32 32 bytes[] ] + // fields [txType extraGas dstNativeAmt dstNativeAddress] + // User App Address is not used in this version + function _getPrices( + uint16 _dstChainId, + uint16 _outboundProofType, + address, + bytes memory _adapterParameters + ) internal view returns (uint basePrice, uint pricePerByte) { + require(!paused, "Admin: paused"); + // decoding the _adapterParameters - reverts if type 2 and there is no dstNativeAddress + require( + _adapterParameters.length == 34 || _adapterParameters.length > 66, + "Relayer: wrong _adapterParameters size" + ); + uint16 txType; + uint extraGas; + assembly { + txType := mload(add(_adapterParameters, 2)) + extraGas := mload(add(_adapterParameters, 34)) + } + require(extraGas > 0, "Relayer: gas too low"); + require(txType == 1 || txType == 2, "Relayer: unsupported txType"); + + DstPrice storage dstPrice = dstPriceLookup[_dstChainId]; + DstConfig storage dstConfig = dstConfigLookup[_dstChainId][_outboundProofType]; + + uint totalRemoteToken; // = baseGas + extraGas + requiredNativeAmount + if (txType == 2) { + uint dstNativeAmt; + assembly { + dstNativeAmt := mload(add(_adapterParameters, 66)) + } + require(dstConfig.dstNativeAmtCap >= dstNativeAmt, "Relayer: dstNativeAmt too large "); + totalRemoteToken = totalRemoteToken.add(dstNativeAmt); + } + // remoteGasTotal = dstGasPriceInWei * (baseGas + extraGas) + uint remoteGasTotal = dstPrice.dstGasPriceInWei.mul(dstConfig.baseGas.add(extraGas)); + + totalRemoteToken = totalRemoteToken.add(remoteGasTotal); + + // tokenConversionRate = dstPrice / localPrice + // basePrice = totalRemoteToken * tokenConversionRate + basePrice = totalRemoteToken.mul(dstPrice.dstPriceRatio).div(10 ** 10); + + // pricePerByte = (dstGasPriceInWei * gasPerBytes) * tokenConversionRate + pricePerByte = dstPrice.dstGasPriceInWei.mul(dstConfig.gasPerByte).mul(dstPrice.dstPriceRatio).div(10 ** 10); + } + + function notifyRelayer( + uint16 _dstChainId, + uint16 _outboundProofType, + bytes calldata _adapterParams + ) external override { + //do nothing + } + + function getPrice( + uint16 _dstChainId, + uint16 _outboundProofType, + address _userApplication, + uint payloadSize, + bytes calldata _adapterParams + ) external view override returns (uint) { + (uint basePrice, uint pricePerByte) = _getPrices( + _dstChainId, + _outboundProofType, + _userApplication, + _adapterParams + ); + return basePrice.add(payloadSize.mul(pricePerByte)); + } + + function isApproved(address _relayerAddress) public view override returns (bool) { + return approvedAddresses[_relayerAddress]; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/RelayerV2.sol b/endpoint/contracts/V1Contracts/contracts/RelayerV2.sol new file mode 100644 index 0000000..88f4e7c --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/RelayerV2.sol @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "hardhat-deploy/solc_0.7/proxy/Proxied.sol"; + +import "./interfaces/ILayerZeroRelayerV2.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV2.sol"; +import "./interfaces/ILayerZeroPriceFeedV2.sol"; +import "./libs/RateLimiter.sol"; + +interface IStargateComposer { + function isSending() external view returns (bool); +} + +contract RelayerV2 is ReentrancyGuard, OwnableUpgradeable, Proxied, ILayerZeroRelayerV2 { + using SafeERC20 for IERC20; + using SafeMath for uint; + using SafeMath for uint128; + using SafeMath for uint64; + using RateLimiter for RateLimiter.Info; + + ILayerZeroUltraLightNodeV2 public uln; + address public stargateBridgeAddress; + uint public constant AIRDROP_GAS_LIMIT = 10000; + + struct DstPrice { + uint128 dstPriceRatio; // 10^10 + uint128 dstGasPriceInWei; + } + + struct DstConfig { + uint128 dstNativeAmtCap; + uint64 baseGas; + uint64 gasPerByte; + } + + struct DstMultiplier { + uint16 chainId; + uint128 multiplier; + } + + struct DstFloorMargin { + uint16 chainId; + uint128 floorMargin; + } + + // [_chainId] => DstPriceData. change often + mapping(uint16 => DstPrice) public dstPriceLookupOld; + // [_chainId][_outboundProofType] => DstConfig. change much less often + mapping(uint16 => mapping(uint16 => DstConfig)) public dstConfigLookup; + mapping(address => bool) public approvedAddresses; + + event Withdraw(address to, uint amount); + event ApproveAddress(address addr, bool approved); + event SetPriceConfigUpdater(address priceConfigUpdater, bool allow); + event AssignJob(uint totalFee); + event ValueTransferFailed(address indexed to, uint indexed quantity); + event SetDstPrice(uint16 chainId, uint128 dstPriceRatio, uint128 dstGasPriceInWei); + event SetDstConfig( + uint16 chainId, + uint16 outboundProofType, + uint128 dstNativeAmtCap, + uint64 baseGas, + uint64 gasPerByte + ); + + // new pauseable relayer + bool public paused; + + // Update for Price Feed + ILayerZeroPriceFeedV2 public priceFeed; + // multipler for airdrop + uint128 public multiplierBps; + + // PriceFeedContract Upgrade + // all encoded param bytes except for proof for validateTransactionProofV1 + uint16 public validateProofBytes; + uint16 public fpBytes; + uint16 public mptOverhead; + + // [chainId] => [multiplier] + mapping(uint16 => uint128) public dstMultipliers; + // [chainId] => [floor margin in USD] + mapping(uint16 => uint128) public dstFloorMarginsUSD; + mapping(address => bool) public priceConfigUpdaters; + + // stargate guard + IStargateComposer public stargateComposer; + address public stargateBridgeAddr; + + uint256 public nativeDecimalsRate; + + RateLimiter.Info public limiter; // deprecated + + event RateLimiterSet(uint64 capacity, uint64 rate); // deprecated + + mapping(address => bool) public limiterWhitelist; // if true, sender bypasses rate limits + mapping(uint16 => RateLimiter.Info) public limiters; + event DstRateLimiterSet(uint16 dstChain, uint64 capacity, uint64 rate); + + // owner is always approved + modifier onlyApproved() { + if (owner() != msg.sender) { + require(isApproved(msg.sender), "Relayer: not approved"); + } + _; + } + + modifier onlyPriceConfigUpdater() { + if (owner() != msg.sender && !approvedAddresses[msg.sender]) { + require(priceConfigUpdaters[msg.sender], "Relayer: not updater"); + } + _; + } + + function initialize( + address _uln, + address _priceFeed, + address _stargateBridgeAddr, + address _stargateComposer, + uint256 _nativeDecimalsRate + ) public proxied initializer { + __Ownable_init(); + uln = ILayerZeroUltraLightNodeV2(_uln); + setApprovedAddress(address(this), true); + multiplierBps = 12000; + priceFeed = ILayerZeroPriceFeedV2(_priceFeed); + validateProofBytes = 164; + fpBytes = 160; + mptOverhead = 500; + stargateBridgeAddr = _stargateBridgeAddr; + stargateComposer = IStargateComposer(_stargateComposer); + nativeDecimalsRate = _nativeDecimalsRate; + } + + function onUpgrade( + address _stargateBridgeAddr, + address _stargateComposer, + uint256 _nativeDecimalsRate + ) public proxied { + stargateBridgeAddr = _stargateBridgeAddr; + stargateComposer = IStargateComposer(_stargateComposer); + nativeDecimalsRate = _nativeDecimalsRate; + } + + //---------------------------------------------------------------------------------- + // onlyApproved + function setLimiterWhitelist(address _addr, bool _skipRateLimits) external onlyApproved { + limiterWhitelist[_addr] = _skipRateLimits; + } + + function setDstPrice(uint16 _chainId, uint128 _dstPriceRatio, uint128 _dstGasPriceInWei) external onlyApproved { + // No longer used: Write prices in PriceFeed. + } + + function setPriceFeed(address _priceFeed) external onlyApproved { + priceFeed = ILayerZeroPriceFeedV2(_priceFeed); + } + + function setPriceMultiplierBps(uint128 _multiplierBps) external onlyApproved { + multiplierBps = _multiplierBps; + } + + function setDstPriceMultipliers(DstMultiplier[] calldata _multipliers) external onlyPriceConfigUpdater { + for (uint i = 0; i < _multipliers.length; i++) { + DstMultiplier calldata _data = _multipliers[i]; + dstMultipliers[_data.chainId] = _data.multiplier; + } + } + + function setDstFloorMarginsUSD(DstFloorMargin[] calldata _margins) external onlyPriceConfigUpdater { + for (uint i = 0; i < _margins.length; i++) { + DstFloorMargin calldata _data = _margins[i]; + dstFloorMarginsUSD[_data.chainId] = _data.floorMargin; + } + } + + function setDstConfig( + uint16 _chainId, + uint16 _outboundProofType, + uint128 _dstNativeAmtCap, + uint64 _baseGas, + uint64 _gasPerByte + ) external onlyApproved { + dstConfigLookup[_chainId][_outboundProofType] = DstConfig(_dstNativeAmtCap, _baseGas, _gasPerByte); + emit SetDstConfig(_chainId, _outboundProofType, _dstNativeAmtCap, _baseGas, _gasPerByte); + } + + function setStargateAddress(address _stargateAddress) external onlyApproved { + stargateBridgeAddress = _stargateAddress; + } + + //---------------------------------------------------------------------------------- + // onlyOwner + function configRateLimiter(uint16 _dstChainId, uint64 _capacity, uint64 _rate) external onlyOwner { + RateLimiter.Info storage dstLimiter = limiters[_dstChainId]; + dstLimiter.setCapacity(_capacity); + dstLimiter.setRate(_rate); + emit DstRateLimiterSet(_dstChainId, _capacity, _rate); + } + + function setApprovedAddress(address _relayerAddress, bool _approve) public onlyOwner { + approvedAddresses[_relayerAddress] = _approve; + emit ApproveAddress(_relayerAddress, _approve); + } + + function setPriceConfigUpdater(address _priceConfigUpdater, bool _allow) public onlyOwner { + priceConfigUpdaters[_priceConfigUpdater] = _allow; + emit SetPriceConfigUpdater(_priceConfigUpdater, _allow); + } + + function setPause(bool _paused) public onlyOwner { + paused = _paused; + } + + // txType 1 + // bytes [2 32 ] + // fields [txType extraGas] + // txType 2 + // bytes [2 32 32 bytes[] ] + // fields [txType extraGas dstNativeAmt dstNativeAddress] + // User App Address is not used in this version + function _getPrices( + uint16 _dstChainId, + uint16 _outboundProofType, + address, + uint _payloadSize, + bytes memory _adapterParameters + ) internal view returns (uint) { + require(!paused, "Admin: paused"); + // decoding the _adapterParameters - reverts if type 2 and there is no dstNativeAddress + require( + _adapterParameters.length == 34 || _adapterParameters.length > 66, + "Relayer: wrong _adapterParameters size" + ); + uint16 txType; + uint extraGas; + assembly { + txType := mload(add(_adapterParameters, 2)) + extraGas := mload(add(_adapterParameters, 34)) + } + require(extraGas > 0, "Relayer: gas too low"); + require(txType == 1 || txType == 2, "Relayer: unsupported txType"); + + DstConfig storage dstConfig = dstConfigLookup[_dstChainId][_outboundProofType]; + + // validateTransactionProof bytes = fixedBytes + proofBytes + // V2 has an extra 32 bytes for payable address + uint totalFixedBytes = txType == 2 ? uint(validateProofBytes).add(32) : validateProofBytes; + uint proofBytes = _outboundProofType == 2 ? _payloadSize.add(fpBytes) : _payloadSize.add(mptOverhead); + + uint16 dstChainId = _dstChainId; // stack too deep + (uint fee, uint128 priceRatio, uint128 priceRatioDenominator, uint128 nativePriceUSD) = priceFeed + .estimateFeeByEid(dstChainId, totalFixedBytes.add(proofBytes), dstConfig.baseGas.add(extraGas)); + + uint dstNativeAmt = 0; + if (txType == 2) { + assembly { + dstNativeAmt := mload(add(_adapterParameters, 66)) + } + require(dstConfig.dstNativeAmtCap >= dstNativeAmt, "Relayer: dstNativeAmt too large"); + } + uint airdropAmount = 0; + if (dstNativeAmt > 0) { + // gas saver if no airdrop + airdropAmount = dstNativeAmt.mul(priceRatio).div(priceRatioDenominator).mul(multiplierBps).div(10000); // cheaper than priceFeed.getPriceRatioDenominator() + } + return _getDstTxCost(dstChainId, fee, nativePriceUSD).add(airdropAmount); + } + + function _getDstTxCost(uint16 _dstChainId, uint _fee, uint128 nativeTokenPriceUSD) private view returns (uint) { + uint128 _dstMultiplier = dstMultipliers[_dstChainId]; + if (_dstMultiplier == 0) { + _dstMultiplier = multiplierBps; + } + uint dstTxCostWithMultiplier = _fee.mul(_dstMultiplier).div(10000); + + if (nativeTokenPriceUSD == 0) { + return dstTxCostWithMultiplier; + } + + uint dstTxCostWithMargin = _fee.add( + dstFloorMarginsUSD[_dstChainId].mul(nativeDecimalsRate).div(nativeTokenPriceUSD) + ); + + return dstTxCostWithMargin > dstTxCostWithMultiplier ? dstTxCostWithMargin : dstTxCostWithMultiplier; + } + + function getFee( + uint16 _dstChainId, + uint16 _outboundProofType, + address _userApplication, + uint _payloadSize, + bytes calldata _adapterParams + ) external view override returns (uint) { + require(_payloadSize <= 10000, "Relayer: _payloadSize tooooo big"); + return _getPrices(_dstChainId, _outboundProofType, _userApplication, _payloadSize, _adapterParams); + } + + // view function to convert pricefeed price to current price (for backwards compatibility) + function dstPriceLookup(uint16 _dstChainId) public view returns (DstPrice memory) { + ILayerZeroPriceFeedV2.Price memory price = priceFeed.getPrice(_dstChainId); + return DstPrice(price.priceRatio, price.gasPriceInUnit); + } + + function isApproved(address _relayerAddress) public view returns (bool) { + return approvedAddresses[_relayerAddress]; + } + + function assignJob( + uint16 _dstChainId, + uint16 _outboundProofType, + address _userApplication, + uint _payloadSize, + bytes calldata _adapterParams + ) external override returns (uint fee) { + if (_dstChainId >= 10000) { + RateLimiter.Info storage dstLimiter = limiters[_dstChainId]; + // sandbox or testnet + if (!limiterWhitelist[_userApplication] && dstLimiter.capacity > 0) { + dstLimiter.tryConsume(10000); + } + } + + require(msg.sender == address(uln), "Relayer: invalid uln"); + require(_payloadSize <= 10000, "Relayer: _payloadSize > 10000"); + + if (_userApplication == stargateBridgeAddr) { + // following way also prevents user from inputting to address greater than 32 bytes + bool validPayload = (_payloadSize == 544 || // swap with no payload + _payloadSize == 320 || // redeem local callback + _payloadSize == 288 || // redeem local + _payloadSize == 160); // send credits + + if (!validPayload) { + require(stargateComposer.isSending(), "Relayer: stargate composer is not sending"); + } + } + + fee = _getPrices(_dstChainId, _outboundProofType, _userApplication, _payloadSize, _adapterParams); + emit AssignJob(fee); + } + + function withdrawFee(address payable _to, uint _amount) external override onlyApproved { + uint totalFee = uln.accruedNativeFee(address(this)); + require(_amount <= totalFee, "Relayer: not enough fee for withdrawal"); + uln.withdrawNative(_to, _amount); + } + + function withdrawToken(address _token, address _to, uint _amount) external onlyApproved { + if (_token == address(0)) { + uint total = address(this).balance; + require(_amount <= total, "Relayer: not enough native fee for withdrawal"); + (bool sent, ) = payable(_to).call{ value: _amount }(""); + require(sent, "Relayer: failed to send ether"); + } else { + uint total = IERC20(_token).balanceOf(address(this)); + require(_amount <= total, "Relayer: not enough fee for withdrawal"); + IERC20(_token).safeTransfer(_to, _amount); + } + } + + function validateTransactionProofV2( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _blockHash, + bytes32 _data, + bytes calldata _transactionProof, + address payable _to + ) external payable onlyApproved nonReentrant { + (bool sent, ) = _to.call{ gas: AIRDROP_GAS_LIMIT, value: msg.value }(""); + //require(sent, "Relayer: failed to send ether"); + if (!sent) { + emit ValueTransferFailed(_to, msg.value); + } + uln.validateTransactionProof(_srcChainId, _dstAddress, _gasLimit, _blockHash, _data, _transactionProof); + } + + function validateTransactionProofV1( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _blockHash, + bytes32 _data, + bytes calldata _transactionProof + ) external onlyApproved nonReentrant { + uln.validateTransactionProof(_srcChainId, _dstAddress, _gasLimit, _blockHash, _data, _transactionProof); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/RelayerV2Radar.sol b/endpoint/contracts/V1Contracts/contracts/RelayerV2Radar.sol new file mode 100644 index 0000000..e82b7cc --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/RelayerV2Radar.sol @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; + +pragma abicoder v2; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "hardhat-deploy/solc_0.7/proxy/Proxied.sol"; + +import "./interfaces/ILayerZeroRelayerV2.sol"; +import "./interfaces/ILayerZeroRelayerV2PriceData.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV2.sol"; +import "./interfaces/ILayerZeroPriceFeed.sol"; + +// RelayerV2Radar has an instance of RelayerV2. +// It does not need to set prices. +// it has view functions that use the internal RelayerV2 price data. +contract RelayerV2Radar is + ReentrancyGuard, + OwnableUpgradeable, + Proxied, + ILayerZeroRelayerV2, + ILayerZeroRelayerV2PriceData +{ + using SafeERC20 for IERC20; + using SafeMath for uint; + using SafeMath for uint128; + using SafeMath for uint64; + + ILayerZeroUltraLightNodeV2 public uln; // UltraLightNodeV2Radar + ILayerZeroRelayerV2PriceData public relayerV2; + + address public stargateBridgeAddress; + + // struct DstPrice { + // uint128 dstPriceRatio; // 10^10 + // uint128 dstGasPriceInWei; + // } + // + // struct DstConfig { + // uint128 dstNativeAmtCap; + // uint64 baseGas; + // uint64 gasPerByte; + // } + + // // [_chainId] => DstPriceData. change often + // mapping(uint16 => RelayerV2.DstPrice) public dstPriceLookup; + // // [_chainId][_outboundProofType] => DstConfig. change much less often + // mapping(uint16 => mapping(uint16 => RelayerV2.DstConfig)) public dstConfigLookup; + mapping(address => bool) public approvedAddresses; + + event Withdraw(address to, uint amount); + event ApproveAddress(address addr, bool approved); + event AssignJob(uint totalFee); + event ValueTransferFailed(address indexed to, uint indexed quantity); + event SetDstPrice(uint16 chainId, uint128 dstPriceRatio, uint128 dstGasPriceInWei); + event SetDstConfig( + uint16 chainId, + uint16 outboundProofType, + uint128 dstNativeAmtCap, + uint64 baseGas, + uint64 gasPerByte + ); + + // new pauseable relayer + bool public paused; + + // map of legacyChainid => v2 chainId. allows lookup thru to RelayerV2 contract (which is updated frequently) + mapping(uint16 => uint16) public legacyToV2ChainId; // legacy ChainId => v2 chainId + + // Upgrade + ILayerZeroPriceFeed public priceFeed; + // all encoded param bytes except for proof for validateTransactionProofV1 + uint128 public multiplierBps; + uint16 public validateProofBytes; + uint16 public fpBytes; + uint16 public mptOverhead; // average overhead for mpt + + // owner is always approved + modifier onlyApproved() { + if (owner() != msg.sender) { + require(isApproved(msg.sender), "Relayer: not approved"); + } + _; + } + + function initialize(address _ulnRadar, address _relayerV2, address _priceFeed) public proxied initializer { + __Ownable_init(); + uln = ILayerZeroUltraLightNodeV2(_ulnRadar); + setApprovedAddress(address(this), true); + + relayerV2 = ILayerZeroRelayerV2PriceData(_relayerV2); + + legacyToV2ChainId[1] = 101; // ethereum + legacyToV2ChainId[2] = 102; // bsc + legacyToV2ChainId[12] = 112; // fantom + + priceFeed = ILayerZeroPriceFeed(_priceFeed); + validateProofBytes = 164; + fpBytes = 160; + mptOverhead = 500; + } + + function onUpgrade(address _priceFeed) public proxied { + multiplierBps = 12000; + priceFeed = ILayerZeroPriceFeed(_priceFeed); + validateProofBytes = 164; + fpBytes = 160; + mptOverhead = 500; + } + + function getV2ChainId(uint16 _legacyChainId) public view returns (uint16) { + require(legacyToV2ChainId[_legacyChainId] != 0, "getLegacyChainId(): lookup not found"); + return legacyToV2ChainId[_legacyChainId]; + } + + //---------------------------------------------------------------------------------- + // onlyApproved + + function setDstPrice(uint16 _chainId, uint128 _dstPriceRatio, uint128 _dstGasPriceInWei) external onlyApproved { + // dstPriceLookup[_chainId] = DstPrice(_dstPriceRatio, _dstGasPriceInWei); + // emit SetDstPrice(_chainId, _dstPriceRatio, _dstGasPriceInWei); + } + + function setPriceFeed(address _priceFeed) external onlyApproved { + priceFeed = ILayerZeroPriceFeed(_priceFeed); + } + + function setPriceMultiplierBps(uint128 _multiplierBps) external onlyApproved { + multiplierBps = _multiplierBps; + } + + function setDstConfig( + uint16 _chainId, + uint16 _outboundProofType, + uint128 _dstNativeAmtCap, + uint64 _baseGas, + uint64 _gasPerByte + ) external onlyApproved { + // dstConfigLookup[_chainId][_outboundProofType] = DstConfig(_dstNativeAmtCap, _baseGas, _gasPerByte); + // emit SetDstConfig(_chainId, _outboundProofType, _dstNativeAmtCap, _baseGas, _gasPerByte); + } + + function dstPriceLookup( + uint16 _legacyChainId + ) public view override returns (ILayerZeroRelayerV2PriceData.DstPrice memory) { + return relayerV2.dstPriceLookup(getV2ChainId(_legacyChainId)); + } + + function dstConfigLookup( + uint16 _legacyChainId, + uint16 _outboundProofType + ) public view override returns (ILayerZeroRelayerV2PriceData.DstConfig memory) { + return relayerV2.dstConfigLookup(getV2ChainId(_legacyChainId), _outboundProofType); + } + + function setStargateAddress(address _stargateAddress) external onlyApproved { + stargateBridgeAddress = _stargateAddress; + } + + //---------------------------------------------------------------------------------- + // onlyOwner + + function setApprovedAddress(address _relayerAddress, bool _approve) public onlyOwner { + approvedAddresses[_relayerAddress] = _approve; + emit ApproveAddress(_relayerAddress, _approve); + } + + function setPause(bool _paused) public onlyOwner { + paused = _paused; + } + + // txType 1 + // bytes [2 32 ] + // fields [txType extraGas] + // txType 2 + // bytes [2 32 32 bytes[] ] + // fields [txType extraGas dstNativeAmt dstNativeAddress] + // User App Address is not used in this version + function _getPrices( + uint16 _dstChainId, + uint16 _outboundProofType, + address, + uint _payloadSize, + bytes memory _adapterParameters + ) internal view returns (uint) { + require(!paused, "Admin: paused"); + // decoding the _adapterParameters - reverts if type 2 and there is no dstNativeAddress + require( + _adapterParameters.length == 34 || _adapterParameters.length > 66, + "Relayer: wrong _adapterParameters size" + ); + uint16 txType; + uint extraGas; + assembly { + txType := mload(add(_adapterParameters, 2)) + extraGas := mload(add(_adapterParameters, 34)) + } + require(extraGas > 0, "Relayer: gas too low"); + require(txType == 1 || txType == 2, "Relayer: unsupported txType"); + + DstConfig memory dstConfig = dstConfigLookup(_dstChainId, _outboundProofType); + + uint dstNativeAmt = 0; + if (txType == 2) { + assembly { + dstNativeAmt := mload(add(_adapterParameters, 66)) + } + require(dstConfig.dstNativeAmtCap >= dstNativeAmt, "Relayer: dstNativeAmt too large"); + } + + // validateTransactionProof bytes = fixedBytes + proofBytes + // V2 has an extra 32 bytes for payable address + uint totalFixedBytes = txType == 2 ? uint(validateProofBytes).add(32) : validateProofBytes; + uint proofBytes = _outboundProofType == 2 ? fpBytes : _payloadSize.add(mptOverhead); + uint totalCallDataBytes = totalFixedBytes.add(proofBytes); + + uint16 dstChainId = _dstChainId; // stack too deep + (uint fee, uint128 priceRatio) = priceFeed.estimateFeeByChain( + getV2ChainId(dstChainId), + totalCallDataBytes, + dstConfig.baseGas.add(extraGas) + ); + uint airdropAmount = dstNativeAmt.mul(priceRatio).div(10 ** 10); + return fee.add(airdropAmount).mul(multiplierBps).div(10000); + } + + function getFee( + uint16 _dstChainId, + uint16 _outboundProofType, + address _userApplication, + uint _payloadSize, + bytes calldata _adapterParams + ) external view override returns (uint) { + require(_payloadSize <= 10000, "Relayer: _payloadSize tooooo big"); + return _getPrices(_dstChainId, _outboundProofType, _userApplication, _payloadSize, _adapterParams); + } + + function isApproved(address _relayerAddress) public view returns (bool) { + return approvedAddresses[_relayerAddress]; + } + + function assignJob( + uint16 _dstChainId, + uint16 _outboundProofType, + address _userApplication, + uint _payloadSize, + bytes calldata _adapterParams + ) external override returns (uint) { + require(msg.sender == address(uln), "Relayer: invalid uln"); + require(_payloadSize <= 10000, "Relayer: _payloadSize tooooo big"); + uint fee = _getPrices(_dstChainId, _outboundProofType, _userApplication, _payloadSize, _adapterParams); + emit AssignJob(fee); + return fee; + } + + function withdrawFee(address payable _to, uint _amount) external override onlyApproved { + uint totalFee = uln.accruedNativeFee(address(this)); + require(_amount <= totalFee, "Relayer: not enough fee for withdrawal"); + uln.withdrawNative(_to, _amount); + } + + function validateTransactionProofV2( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _blockHash, + bytes32 _data, + bytes calldata _transactionProof, + address payable _to + ) external payable onlyApproved nonReentrant { + (bool sent, ) = _to.call{value: msg.value}(""); + //require(sent, "Relayer: failed to send ether"); + if (!sent) { + emit ValueTransferFailed(_to, msg.value); + } + uln.validateTransactionProof(_srcChainId, _dstAddress, _gasLimit, _blockHash, _data, _transactionProof); + } + + function validateTransactionProofV1( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _blockHash, + bytes32 _data, + bytes calldata _transactionProof + ) external onlyApproved nonReentrant { + uln.validateTransactionProof(_srcChainId, _dstAddress, _gasLimit, _blockHash, _data, _transactionProof); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/Treasury.sol b/endpoint/contracts/V1Contracts/contracts/Treasury.sol new file mode 100644 index 0000000..195e8ec --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/Treasury.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "./interfaces/ILayerZeroTreasury.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV1.sol"; + +contract Treasury is ILayerZeroTreasury, Ownable { + using SafeMath for uint; + + uint public nativeBP; + uint public zroFee; + bool public feeEnabled; + bool public zroEnabled; + + ILayerZeroUltraLightNodeV1 public immutable uln; + + event NativeBP(uint bp); + event ZroFee(uint zroFee); + event FeeEnabled(bool feeEnabled); + event ZroEnabled(bool zroEnabled); + + constructor(address _uln) { + uln = ILayerZeroUltraLightNodeV1(_uln); + } + + function getFees(bool payInZro, uint relayerFee, uint oracleFee) external view override returns (uint) { + if (feeEnabled) { + if (payInZro) { + require(zroEnabled, "LayerZero: ZRO is not enabled"); + return zroFee; + } else { + return relayerFee.add(oracleFee).mul(nativeBP).div(10000); + } + } + return 0; + } + + function setFeeEnabled(bool _feeEnabled) external onlyOwner { + feeEnabled = _feeEnabled; + emit FeeEnabled(_feeEnabled); + } + + function setZroEnabled(bool _zroEnabled) external onlyOwner { + zroEnabled = _zroEnabled; + emit ZroEnabled(_zroEnabled); + } + + function setNativeBP(uint _nativeBP) external onlyOwner { + nativeBP = _nativeBP; + emit NativeBP(_nativeBP); + } + + function setZroFee(uint _zroFee) external onlyOwner { + zroFee = _zroFee; + emit ZroFee(_zroFee); + } + + // uint8 public constant WITHDRAW_TYPE_TREASURY_PROTOCOL_FEES = 0; + function withdrawZROFromULN(address _to, uint _amount) external onlyOwner { + uln.withdrawZRO(_to, _amount); + } + + // uint8 public constant WITHDRAW_TYPE_TREASURY_PROTOCOL_FEES = 0; + function withdrawNativeFromULN(address payable _to, uint _amount) external onlyOwner { + uln.withdrawNative(0, address(0x0), _to, _amount); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/TreasuryV2.sol b/endpoint/contracts/V1Contracts/contracts/TreasuryV2.sol new file mode 100644 index 0000000..2ffb7c0 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/TreasuryV2.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; + +import "./interfaces/ILayerZeroTreasury.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV2.sol"; + +contract TreasuryV2 is ILayerZeroTreasury, Ownable { + using SafeERC20 for IERC20; + using SafeMath for uint; + + uint public nativeBP; + uint public zroFee; + bool public feeEnabled; + bool public zroEnabled; + + ILayerZeroUltraLightNodeV2 public uln; + + event NativeBP(uint bp); + event ZroFee(uint zroFee); + event FeeEnabled(bool feeEnabled); + event ZroEnabled(bool zroEnabled); + + constructor(address _ulnv2) { + uln = ILayerZeroUltraLightNodeV2(_ulnv2); + } + + function getFees(bool payInZro, uint relayerFee, uint oracleFee) external view override returns (uint) { + if (feeEnabled) { + if (payInZro) { + require(zroEnabled, "LayerZero: ZRO is not enabled"); + return zroFee; + } else { + return relayerFee.add(oracleFee).mul(nativeBP).div(10000); + } + } + return 0; + } + + function setFeeEnabled(bool _feeEnabled) external onlyOwner { + feeEnabled = _feeEnabled; + emit FeeEnabled(_feeEnabled); + } + + function setZroEnabled(bool _zroEnabled) external onlyOwner { + zroEnabled = _zroEnabled; + emit ZroEnabled(_zroEnabled); + } + + function setNativeBP(uint _nativeBP) external onlyOwner { + nativeBP = _nativeBP; + emit NativeBP(_nativeBP); + } + + function setZroFee(uint _zroFee) external onlyOwner { + zroFee = _zroFee; + emit ZroFee(_zroFee); + } + + function withdrawZROFromULN(address _to, uint _amount) external onlyOwner { + uln.withdrawZRO(_to, _amount); + } + + function withdrawNativeFromULN(address payable _to, uint _amount) external onlyOwner { + uln.withdrawNative(_to, _amount); + } + + function withdrawToken(address _token, address _to, uint _amount) external onlyOwner { + IERC20(_token).safeTransfer(_to, _amount); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/TreasuryV2Radar.sol b/endpoint/contracts/V1Contracts/contracts/TreasuryV2Radar.sol new file mode 100644 index 0000000..cc0a6cb --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/TreasuryV2Radar.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "./interfaces/ILayerZeroTreasury.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV2.sol"; + +contract TreasuryV2Radar is ILayerZeroTreasury, Ownable { + using SafeMath for uint; + + uint public nativeBP; + uint public zroFee; + bool public feeEnabled; + bool public zroEnabled; + + ILayerZeroUltraLightNodeV2 public uln; + + event NativeBP(uint bp); + event ZroFee(uint zroFee); + event FeeEnabled(bool feeEnabled); + event ZroEnabled(bool zroEnabled); + + constructor(address _ulnv2) { + uln = ILayerZeroUltraLightNodeV2(_ulnv2); + } + + function getFees(bool payInZro, uint relayerFee, uint oracleFee) external view override returns (uint) { + if (feeEnabled) { + if (payInZro) { + require(zroEnabled, "LayerZero: ZRO is not enabled"); + return zroFee; + } else { + return relayerFee.add(oracleFee).mul(nativeBP).div(10000); + } + } + return 0; + } + + function setFeeEnabled(bool _feeEnabled) external onlyOwner { + feeEnabled = _feeEnabled; + emit FeeEnabled(_feeEnabled); + } + + function setZroEnabled(bool _zroEnabled) external onlyOwner { + zroEnabled = _zroEnabled; + emit ZroEnabled(_zroEnabled); + } + + function setNativeBP(uint _nativeBP) external onlyOwner { + nativeBP = _nativeBP; + emit NativeBP(_nativeBP); + } + + function setZroFee(uint _zroFee) external onlyOwner { + zroFee = _zroFee; + emit ZroFee(_zroFee); + } + + function withdrawZROFromULN(address _to, uint _amount) external onlyOwner { + uln.withdrawZRO(_to, _amount); + } + + function withdrawNativeFromULN(address payable _to, uint _amount) external onlyOwner { + uln.withdrawNative(_to, _amount); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/UltraLightNode.sol b/endpoint/contracts/V1Contracts/contracts/UltraLightNode.sol new file mode 100644 index 0000000..1fcfca5 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/UltraLightNode.sol @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; + +import "./interfaces/ILayerZeroValidationLibrary.sol"; +import "./interfaces/ILayerZeroMessagingLibrary.sol"; +import "./interfaces/ILayerZeroReceiver.sol"; +import "./interfaces/ILayerZeroRelayer.sol"; +import "./interfaces/ILayerZeroTreasury.sol"; +import "./interfaces/ILayerZeroOracle.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV1.sol"; +import "./interfaces/ILayerZeroEndpoint.sol"; + +contract UltraLightNode is ILayerZeroMessagingLibrary, ILayerZeroUltraLightNodeV1, ReentrancyGuard, Ownable { + using SafeERC20 for IERC20; + using SafeMath for uint; + + struct BlockData { + uint confirmations; + bytes32 data; + } + + // Application config + uint public constant CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION = 1; + uint public constant CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS = 2; + uint public constant CONFIG_TYPE_RELAYER = 3; + uint public constant CONFIG_TYPE_OUTBOUND_PROOF_TYPE = 4; + uint public constant CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS = 5; + uint public constant CONFIG_TYPE_ORACLE = 6; + + struct ApplicationConfiguration { + uint16 inboundProofLibraryVersion; + uint64 inboundBlockConfirmations; + address relayer; + uint16 outboundProofType; + uint64 outboundBlockConfirmations; + address oracle; + } + + // Token and Contracts + IERC20 public layerZeroToken; + ILayerZeroTreasury public treasuryContract; + + // Fee management + uint public constant BP_DENOMINATOR = 10000; + // treasury and relayer share the protocol fee, either in native token or ZRO + uint8 public constant WITHDRAW_TYPE_TREASURY_PROTOCOL_FEES = 0; + uint8 public constant WITHDRAW_TYPE_ORACLE_QUOTED_FEES = 1; // quoted fee refers to the fee in block relaying + uint8 public constant WITHDRAW_TYPE_RELAYER_QUOTED_FEES = 2; //quoted fee refers the fee in msg relaying + + mapping(address => uint) public oracleQuotedFees; + mapping(address => uint) public relayerQuotedFees; + uint public treasuryNativeFees; + uint public treasuryZROFees; + + // User Application + mapping(address => mapping(uint16 => ApplicationConfiguration)) public appConfig; // app address => chainId => config + mapping(uint16 => ApplicationConfiguration) public defaultAppConfig; // default UA settings if no version specified + mapping(uint16 => mapping(uint16 => bytes)) public defaultAdapterParams; + + // Validation + mapping(uint16 => mapping(uint16 => address)) public inboundProofLibrary; // chainId => library Id => inboundProofLibrary contract + mapping(uint16 => uint16) public maxInboundProofLibrary; // chainId => inboundProofLibrary + mapping(uint16 => mapping(uint16 => bool)) public supportedOutboundProof; // chainId => outboundProofType => enabled + mapping(uint16 => uint) public chainAddressSizeMap; + mapping(address => mapping(uint16 => mapping(bytes32 => BlockData))) public hashLookup; + mapping(uint16 => bytes32) public ulnLookup; // remote ulns + + ILayerZeroEndpoint public immutable endpoint; + + // Events + event AppConfigUpdated(address userApplication, uint configType, bytes newConfig); + event AddInboundProofLibraryForChain(uint16 chainId, address lib); + event EnableSupportedOutboundProof(uint16 chainId, uint16 proofType); + event HashReceived(uint16 srcChainId, address oracle, uint confirmations, bytes32 blockhash); + event Packet(uint16 chainId, bytes payload); + event RelayerParams(uint16 chainId, uint64 nonce, uint16 outboundProofType, bytes adapterParams); + event SetChainAddressSize(uint16 chainId, uint size); + event SetDefaultConfigForChainId( + uint16 chainId, + uint16 inboundProofLib, + uint64 inboundBlockConfirm, + address relayer, + uint16 outboundProofType, + uint16 outboundBlockConfirm, + address oracle + ); + event SetDefaultAdapterParamsForChainId(uint16 chainId, uint16 proofType, bytes adapterParams); + event SetLayerZeroToken(address tokenAddress); + event SetRelayerFeeContract(address relayerFeeContract); + event SetRemoteUln(uint16 chainId, bytes32 uln); + event SetTreasury(address treasuryAddress); + event WithdrawZRO(address _msgSender, address _to, uint _amount); + event WithdrawNative(uint8 _type, address _owner, address _msgSender, address _to, uint _amount); + + constructor(address _endpoint) { + require(_endpoint != address(0x0), "LayerZero: endpoint cannot be zero address"); + endpoint = ILayerZeroEndpoint(_endpoint); + } + + // only the endpoint can call SEND() and setConfig() + modifier onlyEndpoint() { + require(address(endpoint) == msg.sender, "LayerZero: only endpoint"); + _; + } + + //---------------------------------------------------------------------------------- + // PROTOCOL + + // This function completes delivery of a LayerZero message. + // + // In order to deliver the message, this function: + // (a) takes the _transactionProof submitted by UA's relayer, and + // (b) retrieve UA's validation library + // (c) takes the _blockData submitted by the UA's oracle given the their configuration (and blockConfirmations), + // (d) decodes using UA's validation library using (a) and (c) + // then, this functions asserts that + // (e) the payload originated from the known Ultra Light Node from source chain, and + // (f) the _dstAddress the specified destination contract + function validateTransactionProof( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _lookupHash, + bytes calldata _transactionProof + ) external override { + // retrieve UA's configuration using the _dstAddress from arguments. + ApplicationConfiguration memory uaConfig = getAppConfig(_srcChainId, _dstAddress); + + // (a) assert that the caller == UA's relayer + require(uaConfig.relayer == msg.sender, "LayerZero: invalid relayer"); + + LayerZeroPacket.Packet memory _packet; + { + // (b) retrieve UA's validation library + address inboundProofLib = inboundProofLibrary[_srcChainId][uaConfig.inboundProofLibraryVersion]; + + // (c) assert that the data submitted by UA's oracle have no fewer confirmations than UA's configuration + BlockData storage blockData = hashLookup[uaConfig.oracle][_srcChainId][_lookupHash]; + require( + blockData.confirmations >= uaConfig.inboundBlockConfirmations, + "LayerZero: not enough block confirmations" + ); + + // (d) decode + uint remoteAddressSize = chainAddressSizeMap[_srcChainId]; + _packet = ILayerZeroValidationLibrary(inboundProofLib).validateProof( + blockData.data, + _transactionProof, + remoteAddressSize + ); + } + + // (e) assert that the packet was emitted by the source ultra light node + require(ulnLookup[_srcChainId] == _packet.ulnAddress, "LayerZero: _packet.ulnAddress is invalid"); + + // (f) assert that the _packet._dstAddress == the _dstAddress specified by the UAs message + require(_packet.dstAddress == _dstAddress, "LayerZero: invalid dst address"); + + // publish the payload and _gasLimit to the endpoint for calling lzReceive at _dstAddress + endpoint.receivePayload( + _packet.srcChainId, + _packet.srcAddress, + _packet.dstAddress, + _packet.nonce, + _gasLimit, + _packet.payload + ); + } + + // Called (by the Endpoint) with the information required to send a LayerZero message for a User Application. + // This function: + // (a) pays the protocol (native token or ZRO), oracle (native token) and relayer (native token) for their roles in sending the message. + // (b) generates the message payload and emits events of the message and adapterParams + // (c) notifies the oracle + function send( + address _ua, + uint64 _nonce, + uint16 _chainId, + bytes calldata _destination, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable override onlyEndpoint { + ApplicationConfiguration memory uaConfig = getAppConfig(_chainId, _ua); + address ua = _ua; + uint64 nonce = _nonce; + uint16 chainId = _chainId; + require(ulnLookup[chainId] != bytes32(0), "LayerZero: chainId does not exist"); + + uint totalNativeFee; + { + uint oracleFee; + // (a - 1), pay the oracle + { + oracleFee = ILayerZeroOracle(uaConfig.oracle).getPrice(chainId, uaConfig.outboundProofType); + oracleQuotedFees[uaConfig.oracle] = oracleQuotedFees[uaConfig.oracle].add(oracleFee); + } + + // (a - 2), pay the relayer + { + uint payloadSize = _payload.length; + ILayerZeroRelayer relayer = ILayerZeroRelayer(uaConfig.relayer); + if (_adapterParams.length == 0) { + bytes memory defaultAdaptorParam = defaultAdapterParams[chainId][uaConfig.outboundProofType]; + totalNativeFee = relayer.getPrice( + chainId, + uaConfig.outboundProofType, + ua, + payloadSize, + defaultAdaptorParam + ); + relayer.notifyRelayer(chainId, uaConfig.outboundProofType, defaultAdaptorParam); + } else { + totalNativeFee = relayer.getPrice( + chainId, + uaConfig.outboundProofType, + ua, + payloadSize, + _adapterParams + ); + relayer.notifyRelayer(chainId, uaConfig.outboundProofType, _adapterParams); + } + relayerQuotedFees[uaConfig.relayer] = relayerQuotedFees[uaConfig.relayer].add(totalNativeFee); // totalNativeFee == relayerFee here + + // emit the param events + emit RelayerParams(chainId, nonce, uaConfig.outboundProofType, _adapterParams); + } + + // (a - 3), pay the protocol + { + // if no ZRO token or not specifying a payment address, pay in native token + bool payInNative = _zroPaymentAddress == address(0x0) || address(layerZeroToken) == address(0x0); + uint protocolFee = treasuryContract.getFees(!payInNative, totalNativeFee, oracleFee); // totalNativeFee == relayerFee here + + if (protocolFee > 0) { + if (payInNative) { + treasuryNativeFees = treasuryNativeFees.add(protocolFee); + totalNativeFee = totalNativeFee.add(protocolFee); + } else { + // zro payment address must equal the _ua or the tx.origin otherwise the transaction reverts + require( + _zroPaymentAddress == ua || _zroPaymentAddress == tx.origin, + "LayerZero: must be paid by sender or origin" + ); + + // transfer the LayerZero token to this contract from the payee + layerZeroToken.safeTransferFrom(_zroPaymentAddress, address(this), protocolFee); + + treasuryZROFees = treasuryZROFees.add(protocolFee); + } + } + } + + totalNativeFee = totalNativeFee.add(oracleFee); + } + + // (b) emit payload and the adapterParams if any + { + bytes memory encodedPayload = abi.encodePacked(nonce, ua, _destination, _payload); + emit Packet(chainId, encodedPayload); + // (c) notify the oracle + ILayerZeroOracle(uaConfig.oracle).notifyOracle( + chainId, + uaConfig.outboundProofType, + uaConfig.outboundBlockConfirmations + ); + } + + require(totalNativeFee <= msg.value, "LayerZero: not enough native for fees"); + // refund if they send too much + uint amount = msg.value.sub(totalNativeFee); + if (amount > 0) { + (bool success, ) = _refundAddress.call{value: amount}(""); + require(success, "LayerZero: failed to refund"); + } + } + + // Can be called by any address to update a block header + // can only upload new block data or the same block data with more confirmations + function updateHash(uint16 _srcChainId, bytes32 _lookupHash, uint _confirmations, bytes32 _data) external override { + // this function may revert with a default message if the oracle address is not an ILayerZeroOracle + BlockData storage bd = hashLookup[msg.sender][_srcChainId][_lookupHash]; + // if it has a record, requires a larger confirmation. + require( + bd.confirmations < _confirmations, + "LayerZero: oracle data can only update if it has more confirmations" + ); + + // set the new information into storage + bd.confirmations = _confirmations; + bd.data = _data; + + emit HashReceived(_srcChainId, msg.sender, _confirmations, _lookupHash); + } + + //---------------------------------------------------------------------------------- + // Other Library Interfaces + + // default to DEFAULT setting if ZERO value + function getAppConfig( + uint16 _chainId, + address userApplicationAddress + ) public view returns (ApplicationConfiguration memory) { + ApplicationConfiguration memory config = appConfig[userApplicationAddress][_chainId]; + ApplicationConfiguration storage defaultConfig = defaultAppConfig[_chainId]; + + if (config.inboundProofLibraryVersion == 0) { + config.inboundProofLibraryVersion = defaultConfig.inboundProofLibraryVersion; + } + + if (config.inboundBlockConfirmations == 0) { + config.inboundBlockConfirmations = defaultConfig.inboundBlockConfirmations; + } + + if (config.relayer == address(0x0)) { + config.relayer = defaultConfig.relayer; + } + + if (config.outboundProofType == 0) { + config.outboundProofType = defaultConfig.outboundProofType; + } + + if (config.outboundBlockConfirmations == 0) { + config.outboundBlockConfirmations = defaultConfig.outboundBlockConfirmations; + } + + if (config.oracle == address(0x0)) { + config.oracle = defaultConfig.oracle; + } + + return config; + } + + function setConfig( + uint16 chainId, + address _ua, + uint _configType, + bytes calldata _config + ) external override onlyEndpoint { + ApplicationConfiguration storage uaConfig = appConfig[_ua][chainId]; + if (_configType == CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION) { + uint16 inboundProofLibraryVersion = abi.decode(_config, (uint16)); + require( + inboundProofLibraryVersion <= maxInboundProofLibrary[chainId], + "LayerZero: invalid inbound proof library version" + ); + uaConfig.inboundProofLibraryVersion = inboundProofLibraryVersion; + } else if (_configType == CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS) { + uint64 blockConfirmations = abi.decode(_config, (uint64)); + uaConfig.inboundBlockConfirmations = blockConfirmations; + } else if (_configType == CONFIG_TYPE_RELAYER) { + address relayer = abi.decode(_config, (address)); + uaConfig.relayer = relayer; + } else if (_configType == CONFIG_TYPE_OUTBOUND_PROOF_TYPE) { + uint16 outboundProofType = abi.decode(_config, (uint16)); + require( + supportedOutboundProof[chainId][outboundProofType] || outboundProofType == 0, + "LayerZero: invalid outbound proof type" + ); + uaConfig.outboundProofType = outboundProofType; + } else if (_configType == CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS) { + uint64 blockConfirmations = abi.decode(_config, (uint64)); + uaConfig.outboundBlockConfirmations = blockConfirmations; + } else if (_configType == CONFIG_TYPE_ORACLE) { + address oracle = abi.decode(_config, (address)); + uaConfig.oracle = oracle; + } else { + revert("LayerZero: Invalid config type"); + } + + emit AppConfigUpdated(_ua, _configType, _config); + } + + function getConfig( + uint16 _chainId, + address userApplicationAddress, + uint _configType + ) external view override returns (bytes memory) { + ApplicationConfiguration storage uaConfig = appConfig[userApplicationAddress][_chainId]; + + if (_configType == CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION) { + if (uaConfig.inboundProofLibraryVersion == 0) { + return abi.encode(defaultAppConfig[_chainId].inboundProofLibraryVersion); + } + return abi.encode(uaConfig.inboundProofLibraryVersion); + } else if (_configType == CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS) { + if (uaConfig.inboundBlockConfirmations == 0) { + return abi.encode(defaultAppConfig[_chainId].inboundBlockConfirmations); + } + return abi.encode(uaConfig.inboundBlockConfirmations); + } else if (_configType == CONFIG_TYPE_RELAYER) { + if (uaConfig.relayer == address(0x0)) { + return abi.encode(defaultAppConfig[_chainId].relayer); + } + return abi.encode(uaConfig.relayer); + } else if (_configType == CONFIG_TYPE_OUTBOUND_PROOF_TYPE) { + if (uaConfig.outboundProofType == 0) { + return abi.encode(defaultAppConfig[_chainId].outboundProofType); + } + return abi.encode(uaConfig.outboundProofType); + } else if (_configType == CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS) { + if (uaConfig.outboundBlockConfirmations == 0) { + return abi.encode(defaultAppConfig[_chainId].outboundBlockConfirmations); + } + return abi.encode(uaConfig.outboundBlockConfirmations); + } else if (_configType == CONFIG_TYPE_ORACLE) { + if (uaConfig.oracle == address(0x0)) { + return abi.encode(defaultAppConfig[_chainId].oracle); + } + return abi.encode(uaConfig.oracle); + } else { + revert("LayerZero: Invalid config type"); + } + } + + // returns the native fee the UA pays to cover fees + function estimateFees( + uint16 _chainId, + address _ua, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParams + ) external view override returns (uint nativeFee, uint zroFee) { + uint16 chainId = _chainId; + address ua = _ua; + uint payloadSize = _payload.length; + bytes memory adapterParam = _adapterParams; + + ApplicationConfiguration memory uaConfig = getAppConfig(chainId, ua); + + // Relayer Fee + uint relayerFee; + { + if (adapterParam.length == 0) { + bytes memory defaultAdaptorParam = defaultAdapterParams[chainId][uaConfig.outboundProofType]; + relayerFee = ILayerZeroRelayer(uaConfig.relayer).getPrice( + chainId, + uaConfig.outboundProofType, + ua, + payloadSize, + defaultAdaptorParam + ); + } else { + relayerFee = ILayerZeroRelayer(uaConfig.relayer).getPrice( + chainId, + uaConfig.outboundProofType, + ua, + payloadSize, + adapterParam + ); + } + } + + // Oracle Fee + uint oracleFee = ILayerZeroOracle(uaConfig.oracle).getPrice(chainId, uaConfig.outboundProofType); + + // LayerZero Fee + { + uint protocolFee = treasuryContract.getFees(_payInZRO, relayerFee, oracleFee); + _payInZRO ? zroFee = protocolFee : nativeFee = protocolFee; + } + + // return the sum of fees + nativeFee = nativeFee.add(relayerFee).add(oracleFee); + } + + //--------------------------------------------------------------------------- + // Claim Fees + + // universal withdraw ZRO token function + function withdrawZRO(address _to, uint _amount) external override nonReentrant { + require(msg.sender == address(treasuryContract), "LayerZero: only treasury"); + treasuryZROFees = treasuryZROFees.sub(_amount); + layerZeroToken.safeTransfer(_to, _amount); + emit WithdrawZRO(msg.sender, _to, _amount); + } + + // universal withdraw native token function. + // the source contract should perform all the authentication control + // safemath overflow if the amount is not enough + function withdrawNative( + uint8 _type, + address _owner, + address payable _to, + uint _amount + ) external override nonReentrant { + if (_type == WITHDRAW_TYPE_TREASURY_PROTOCOL_FEES) { + require(msg.sender == address(treasuryContract), "LayerZero:only treasury"); + treasuryNativeFees = treasuryNativeFees.sub(_amount); + } else if (_type == WITHDRAW_TYPE_ORACLE_QUOTED_FEES) { + oracleQuotedFees[msg.sender] = oracleQuotedFees[msg.sender].sub(_amount); + } else if (_type == WITHDRAW_TYPE_RELAYER_QUOTED_FEES) { + relayerQuotedFees[msg.sender] = relayerQuotedFees[msg.sender].sub(_amount); + } else { + revert("LayerZero: unsupported withdraw type"); + } + + (bool success, ) = _to.call{value: _amount}(""); + require(success, "LayerZero: withdraw failed"); + emit WithdrawNative(_type, _owner, msg.sender, _to, _amount); + } + + //--------------------------------------------------------------------------- + // Owner calls, configuration only. + function setLayerZeroToken(address _layerZeroToken) external onlyOwner { + require(_layerZeroToken != address(0x0), "LayerZero: _layerZeroToken cannot be zero address"); + layerZeroToken = IERC20(_layerZeroToken); + emit SetLayerZeroToken(_layerZeroToken); + } + + function setTreasury(address _treasury) external onlyOwner { + require(_treasury != address(0x0), "LayerZero: treasury cannot be zero address"); + treasuryContract = ILayerZeroTreasury(_treasury); + emit SetTreasury(_treasury); + } + + function addInboundProofLibraryForChain(uint16 _chainId, address _library) external onlyOwner { + require(_library != address(0x0), "LayerZero: library cannot be zero address"); + require(maxInboundProofLibrary[_chainId] < 65535, "LayerZero: can not add new library"); + maxInboundProofLibrary[_chainId]++; + inboundProofLibrary[_chainId][maxInboundProofLibrary[_chainId]] = _library; + emit AddInboundProofLibraryForChain(_chainId, _library); + } + + function enableSupportedOutboundProof(uint16 _chainId, uint16 _proofType) external onlyOwner { + supportedOutboundProof[_chainId][_proofType] = true; + emit EnableSupportedOutboundProof(_chainId, _proofType); + } + + function setDefaultConfigForChainId( + uint16 _chainId, + uint16 _inboundProofLibraryVersion, + uint64 _inboundBlockConfirmations, + address _relayer, + uint16 _outboundProofType, + uint16 _outboundBlockConfirmations, + address _oracle + ) external onlyOwner { + require( + _inboundProofLibraryVersion <= maxInboundProofLibrary[_chainId] && _inboundProofLibraryVersion > 0, + "LayerZero: invalid inbound proof library version" + ); + require(_inboundBlockConfirmations > 0, "LayerZero: invalid inbound block confirmation"); + require(_relayer != address(0x0), "LayerZero: invalid relayer address"); + require(supportedOutboundProof[_chainId][_outboundProofType], "LayerZero: invalid outbound proof type"); + require(_outboundBlockConfirmations > 0, "LayerZero: invalid outbound block confirmation"); + require(_oracle != address(0x0), "LayerZero: invalid oracle address"); + defaultAppConfig[_chainId] = ApplicationConfiguration( + _inboundProofLibraryVersion, + _inboundBlockConfirmations, + _relayer, + _outboundProofType, + _outboundBlockConfirmations, + _oracle + ); + emit SetDefaultConfigForChainId( + _chainId, + _inboundProofLibraryVersion, + _inboundBlockConfirmations, + _relayer, + _outboundProofType, + _outboundBlockConfirmations, + _oracle + ); + } + + function setDefaultAdapterParamsForChainId( + uint16 _chainId, + uint16 _proofType, + bytes calldata _adapterParams + ) external onlyOwner { + defaultAdapterParams[_chainId][_proofType] = _adapterParams; + emit SetDefaultAdapterParamsForChainId(_chainId, _proofType, _adapterParams); + } + + function setRemoteUln(uint16 _remoteChainId, bytes32 _remoteUln) external onlyOwner { + require(ulnLookup[_remoteChainId] == bytes32(0), "LayerZero: remote uln already set"); + ulnLookup[_remoteChainId] = _remoteUln; + emit SetRemoteUln(_remoteChainId, _remoteUln); + } + + function setChainAddressSize(uint16 _chainId, uint _size) external onlyOwner { + require(chainAddressSizeMap[_chainId] == 0, "LayerZero: remote chain address size already set"); + chainAddressSizeMap[_chainId] = _size; + emit SetChainAddressSize(_chainId, _size); + } + + //---------------------------------------------------------------------------------- + // view functions + function getBlockHeaderData( + address _oracle, + uint16 _remoteChainId, + bytes32 _lookupHash + ) external view returns (BlockData memory blockData) { + return hashLookup[_oracle][_remoteChainId][_lookupHash]; + } + + function oracleQuotedAmount(address _oracle) external view override returns (uint) { + return oracleQuotedFees[_oracle]; + } + + function relayerQuotedAmount(address _relayer) external view override returns (uint) { + return relayerQuotedFees[_relayer]; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2.sol b/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2.sol new file mode 100644 index 0000000..66fc1b5 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2.sol @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; + +import "./interfaces/ILayerZeroValidationLibrary.sol"; +import "./interfaces/ILayerZeroReceiver.sol"; +import "./interfaces/ILayerZeroTreasury.sol"; +import "./interfaces/ILayerZeroEndpoint.sol"; +// v2 +import "./interfaces/ILayerZeroMessagingLibraryV2.sol"; +import "./interfaces/ILayerZeroOracleV2.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV2.sol"; +import "./interfaces/ILayerZeroRelayerV2.sol"; +import "./NonceContract.sol"; + +contract UltraLightNodeV2 is ILayerZeroMessagingLibraryV2, ILayerZeroUltraLightNodeV2, ReentrancyGuard, Ownable { + using SafeERC20 for IERC20; + using SafeMath for uint; + + // Application config + uint public constant CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION = 1; + uint public constant CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS = 2; + uint public constant CONFIG_TYPE_RELAYER = 3; + uint public constant CONFIG_TYPE_OUTBOUND_PROOF_TYPE = 4; + uint public constant CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS = 5; + uint public constant CONFIG_TYPE_ORACLE = 6; + + // Token and Contracts + IERC20 public layerZeroToken; + ILayerZeroTreasury public treasuryContract; + + mapping(address => uint) public nativeFees; + uint public treasuryZROFees; + + // User Application + mapping(address => mapping(uint16 => ApplicationConfiguration)) public appConfig; // app address => chainId => config + mapping(uint16 => ApplicationConfiguration) public defaultAppConfig; // default UA settings if no version specified + mapping(uint16 => mapping(uint16 => bytes)) public defaultAdapterParams; + + // Validation + mapping(uint16 => mapping(uint16 => address)) public inboundProofLibrary; // chainId => library Id => inboundProofLibrary contract + mapping(uint16 => uint16) public maxInboundProofLibrary; // chainId => inboundProofLibrary + mapping(uint16 => mapping(uint16 => bool)) public supportedOutboundProof; // chainId => outboundProofType => enabled + mapping(uint16 => uint) public chainAddressSizeMap; + mapping(address => mapping(uint16 => mapping(bytes32 => mapping(bytes32 => uint)))) public hashLookup; //[oracle][srcChainId][blockhash][datahash] -> confirmation + mapping(uint16 => bytes32) public ulnLookup; // remote ulns + + ILayerZeroEndpoint public immutable endpoint; + uint16 public immutable localChainId; + NonceContract public immutable nonceContract; + + constructor(address _endpoint, address _nonceContract, uint16 _localChainId) { + require(_endpoint != address(0x0), "LayerZero: endpoint cannot be zero address"); + require(_nonceContract != address(0x0), "LayerZero: nonceContract cannot be zero address"); + ILayerZeroEndpoint lzEndpoint = ILayerZeroEndpoint(_endpoint); + localChainId = _localChainId; + endpoint = lzEndpoint; + nonceContract = NonceContract(_nonceContract); + } + + // only the endpoint can call SEND() and setConfig() + modifier onlyEndpoint() { + require(address(endpoint) == msg.sender, "LayerZero: only endpoint"); + _; + } + + //---------------------------------------------------------------------------------- + // PROTOCOL + function validateTransactionProof( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _lookupHash, + bytes32 _blockData, + bytes calldata _transactionProof + ) external override { + // retrieve UA's configuration using the _dstAddress from arguments. + ApplicationConfiguration memory uaConfig = _getAppConfig(_srcChainId, _dstAddress); + + // assert that the caller == UA's relayer + require(uaConfig.relayer == msg.sender, "LayerZero: invalid relayer"); + + LayerZeroPacket.Packet memory _packet; + uint remoteAddressSize = chainAddressSizeMap[_srcChainId]; + require(remoteAddressSize != 0, "LayerZero: incorrect remote address size"); + { + // assert that the data submitted by UA's oracle have no fewer confirmations than UA's configuration + uint storedConfirmations = hashLookup[uaConfig.oracle][_srcChainId][_lookupHash][_blockData]; + require( + storedConfirmations > 0 && storedConfirmations >= uaConfig.inboundBlockConfirmations, + "LayerZero: not enough block confirmations" + ); + + // decode + address inboundProofLib = inboundProofLibrary[_srcChainId][uaConfig.inboundProofLibraryVersion]; + _packet = ILayerZeroValidationLibrary(inboundProofLib).validateProof( + _blockData, + _transactionProof, + remoteAddressSize + ); + } + + // packet content assertion + require( + ulnLookup[_srcChainId] == _packet.ulnAddress && _packet.ulnAddress != bytes32(0), + "LayerZero: invalid _packet.ulnAddress" + ); + require(_packet.srcChainId == _srcChainId, "LayerZero: invalid srcChain Id"); + // failsafe because the remoteAddress size being passed into validateProof trims the address this should not hit + require(_packet.srcAddress.length == remoteAddressSize, "LayerZero: invalid srcAddress size"); + require(_packet.dstChainId == localChainId, "LayerZero: invalid dstChain Id"); + require(_packet.dstAddress == _dstAddress, "LayerZero: invalid dstAddress"); + + // if the dst is not a contract, then emit and return early. This will break inbound nonces, but this particular + // path is already broken and wont ever be able to deliver anyways + if (!_isContract(_dstAddress)) { + emit InvalidDst( + _packet.srcChainId, + _packet.srcAddress, + _packet.dstAddress, + _packet.nonce, + keccak256(_packet.payload) + ); + return; + } + + bytes memory pathData = abi.encodePacked(_packet.srcAddress, _packet.dstAddress); + emit PacketReceived( + _packet.srcChainId, + _packet.srcAddress, + _packet.dstAddress, + _packet.nonce, + keccak256(_packet.payload) + ); + endpoint.receivePayload(_srcChainId, pathData, _dstAddress, _packet.nonce, _gasLimit, _packet.payload); + } + + function send( + address _ua, + uint64, + uint16 _dstChainId, + bytes calldata _path, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable override onlyEndpoint { + address ua = _ua; + uint16 dstChainId = _dstChainId; + require(ulnLookup[dstChainId] != bytes32(0), "LayerZero: dstChainId does not exist"); + + bytes memory dstAddress; + uint64 nonce; + // code block for solving 'Stack Too Deep' + { + uint chainAddressSize = chainAddressSizeMap[dstChainId]; + // path = remoteAddress + localAddress + require( + chainAddressSize != 0 && _path.length == 20 + chainAddressSize, + "LayerZero: incorrect remote address size" + ); + address srcInPath; + bytes memory path = _path; // copy to memory + assembly { + srcInPath := mload(add(add(path, 20), chainAddressSize)) // chainAddressSize + 20 + } + require(ua == srcInPath, "LayerZero: wrong path data"); + dstAddress = _path[0:chainAddressSize]; + nonce = nonceContract.increment(dstChainId, ua, path); + } + + bytes memory payload = _payload; + ApplicationConfiguration memory uaConfig = _getAppConfig(dstChainId, ua); + + // compute all the fees + uint relayerFee = _handleRelayer(dstChainId, uaConfig, ua, payload.length, _adapterParams); + uint oracleFee = _handleOracle(dstChainId, uaConfig, ua); + uint nativeProtocolFee = _handleProtocolFee(relayerFee, oracleFee, ua, _zroPaymentAddress); + + // total native fee, does not include ZRO protocol fee + uint totalNativeFee = relayerFee.add(oracleFee).add(nativeProtocolFee); + + // assert the user has attached enough native token for this address + require(totalNativeFee <= msg.value, "LayerZero: not enough native for fees"); + // refund if they send too much + uint amount = msg.value.sub(totalNativeFee); + if (amount > 0) { + (bool success, ) = _refundAddress.call{value: amount}(""); + require(success, "LayerZero: failed to refund"); + } + + // emit the data packet + bytes memory encodedPayload = abi.encodePacked(nonce, localChainId, ua, dstChainId, dstAddress, payload); + emit Packet(encodedPayload); + } + + function _handleRelayer( + uint16 _dstChainId, + ApplicationConfiguration memory _uaConfig, + address _ua, + uint _payloadSize, + bytes memory _adapterParams + ) internal returns (uint relayerFee) { + if (_adapterParams.length == 0) { + _adapterParams = defaultAdapterParams[_dstChainId][_uaConfig.outboundProofType]; + } + address relayerAddress = _uaConfig.relayer; + ILayerZeroRelayerV2 relayer = ILayerZeroRelayerV2(relayerAddress); + relayerFee = relayer.assignJob(_dstChainId, _uaConfig.outboundProofType, _ua, _payloadSize, _adapterParams); + + _creditNativeFee(relayerAddress, relayerFee); + + // emit the param events + emit RelayerParams(_adapterParams, _uaConfig.outboundProofType); + } + + function _handleOracle( + uint16 _dstChainId, + ApplicationConfiguration memory _uaConfig, + address _ua + ) internal returns (uint oracleFee) { + address oracleAddress = _uaConfig.oracle; + oracleFee = ILayerZeroOracleV2(oracleAddress).assignJob( + _dstChainId, + _uaConfig.outboundProofType, + _uaConfig.outboundBlockConfirmations, + _ua + ); + + _creditNativeFee(oracleAddress, oracleFee); + } + + function _handleProtocolFee( + uint _relayerFee, + uint _oracleFee, + address _ua, + address _zroPaymentAddress + ) internal returns (uint protocolNativeFee) { + // if no ZRO token or not specifying a payment address, pay in native token + bool payInNative = _zroPaymentAddress == address(0x0) || address(layerZeroToken) == address(0x0); + uint protocolFee = treasuryContract.getFees(!payInNative, _relayerFee, _oracleFee); + + if (protocolFee > 0) { + if (payInNative) { + address treasuryAddress = address(treasuryContract); + _creditNativeFee(treasuryAddress, protocolFee); + protocolNativeFee = protocolFee; + } else { + // zro payment address must equal the ua or the tx.origin otherwise the transaction reverts + require( + _zroPaymentAddress == _ua || _zroPaymentAddress == tx.origin, + "LayerZero: must be paid by sender or origin" + ); + + // transfer the LayerZero token to this contract from the payee + layerZeroToken.safeTransferFrom(_zroPaymentAddress, address(this), protocolFee); + + treasuryZROFees = treasuryZROFees.add(protocolFee); + } + } + } + + function _creditNativeFee(address _receiver, uint _amount) internal { + nativeFees[_receiver] = nativeFees[_receiver].add(_amount); + } + + // Can be called by any address to update a block header + // can only upload new block data or the same block data with more confirmations + function updateHash( + uint16 _srcChainId, + bytes32 _lookupHash, + uint _confirmations, + bytes32 _blockData + ) external override { + uint storedConfirmations = hashLookup[msg.sender][_srcChainId][_lookupHash][_blockData]; + + // if it has a record, requires a larger confirmation. + require( + storedConfirmations < _confirmations, + "LayerZero: oracle data can only update if it has more confirmations" + ); + + // set the new information into storage + hashLookup[msg.sender][_srcChainId][_lookupHash][_blockData] = _confirmations; + + emit HashReceived(_srcChainId, msg.sender, _lookupHash, _blockData, _confirmations); + } + + //---------------------------------------------------------------------------------- + // Other Library Interfaces + + // default to DEFAULT setting if ZERO value + function getAppConfig( + uint16 _remoteChainId, + address _ua + ) external view override returns (ApplicationConfiguration memory) { + return _getAppConfig(_remoteChainId, _ua); + } + + function _getAppConfig(uint16 _remoteChainId, address _ua) internal view returns (ApplicationConfiguration memory) { + ApplicationConfiguration memory config = appConfig[_ua][_remoteChainId]; + ApplicationConfiguration storage defaultConfig = defaultAppConfig[_remoteChainId]; + + if (config.inboundProofLibraryVersion == 0) { + config.inboundProofLibraryVersion = defaultConfig.inboundProofLibraryVersion; + } + + if (config.inboundBlockConfirmations == 0) { + config.inboundBlockConfirmations = defaultConfig.inboundBlockConfirmations; + } + + if (config.relayer == address(0x0)) { + config.relayer = defaultConfig.relayer; + } + + if (config.outboundProofType == 0) { + config.outboundProofType = defaultConfig.outboundProofType; + } + + if (config.outboundBlockConfirmations == 0) { + config.outboundBlockConfirmations = defaultConfig.outboundBlockConfirmations; + } + + if (config.oracle == address(0x0)) { + config.oracle = defaultConfig.oracle; + } + + return config; + } + + function setConfig( + uint16 _remoteChainId, + address _ua, + uint _configType, + bytes calldata _config + ) external override onlyEndpoint { + ApplicationConfiguration storage uaConfig = appConfig[_ua][_remoteChainId]; + if (_configType == CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION) { + uint16 inboundProofLibraryVersion = abi.decode(_config, (uint16)); + require( + inboundProofLibraryVersion <= maxInboundProofLibrary[_remoteChainId], + "LayerZero: invalid inbound proof library version" + ); + uaConfig.inboundProofLibraryVersion = inboundProofLibraryVersion; + } else if (_configType == CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS) { + uint64 blockConfirmations = abi.decode(_config, (uint64)); + uaConfig.inboundBlockConfirmations = blockConfirmations; + } else if (_configType == CONFIG_TYPE_RELAYER) { + address relayer = abi.decode(_config, (address)); + uaConfig.relayer = relayer; + } else if (_configType == CONFIG_TYPE_OUTBOUND_PROOF_TYPE) { + uint16 outboundProofType = abi.decode(_config, (uint16)); + require( + supportedOutboundProof[_remoteChainId][outboundProofType] || outboundProofType == 0, + "LayerZero: invalid outbound proof type" + ); + uaConfig.outboundProofType = outboundProofType; + } else if (_configType == CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS) { + uint64 blockConfirmations = abi.decode(_config, (uint64)); + uaConfig.outboundBlockConfirmations = blockConfirmations; + } else if (_configType == CONFIG_TYPE_ORACLE) { + address oracle = abi.decode(_config, (address)); + uaConfig.oracle = oracle; + } else { + revert("LayerZero: Invalid config type"); + } + + emit AppConfigUpdated(_ua, _configType, _config); + } + + function getConfig( + uint16 _remoteChainId, + address _ua, + uint _configType + ) external view override returns (bytes memory) { + ApplicationConfiguration storage uaConfig = appConfig[_ua][_remoteChainId]; + + if (_configType == CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION) { + if (uaConfig.inboundProofLibraryVersion == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].inboundProofLibraryVersion); + } + return abi.encode(uaConfig.inboundProofLibraryVersion); + } else if (_configType == CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS) { + if (uaConfig.inboundBlockConfirmations == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].inboundBlockConfirmations); + } + return abi.encode(uaConfig.inboundBlockConfirmations); + } else if (_configType == CONFIG_TYPE_RELAYER) { + if (uaConfig.relayer == address(0x0)) { + return abi.encode(defaultAppConfig[_remoteChainId].relayer); + } + return abi.encode(uaConfig.relayer); + } else if (_configType == CONFIG_TYPE_OUTBOUND_PROOF_TYPE) { + if (uaConfig.outboundProofType == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].outboundProofType); + } + return abi.encode(uaConfig.outboundProofType); + } else if (_configType == CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS) { + if (uaConfig.outboundBlockConfirmations == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].outboundBlockConfirmations); + } + return abi.encode(uaConfig.outboundBlockConfirmations); + } else if (_configType == CONFIG_TYPE_ORACLE) { + if (uaConfig.oracle == address(0x0)) { + return abi.encode(defaultAppConfig[_remoteChainId].oracle); + } + return abi.encode(uaConfig.oracle); + } else { + revert("LayerZero: Invalid config type"); + } + } + + // returns the native fee the UA pays to cover fees + function estimateFees( + uint16 _dstChainId, + address _ua, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParams + ) external view override returns (uint nativeFee, uint zroFee) { + ApplicationConfiguration memory uaConfig = _getAppConfig(_dstChainId, _ua); + + // Relayer Fee + bytes memory adapterParams; + if (_adapterParams.length > 0) { + adapterParams = _adapterParams; + } else { + adapterParams = defaultAdapterParams[_dstChainId][uaConfig.outboundProofType]; + } + uint relayerFee = ILayerZeroRelayerV2(uaConfig.relayer).getFee( + _dstChainId, + uaConfig.outboundProofType, + _ua, + _payload.length, + adapterParams + ); + + // Oracle Fee + address ua = _ua; // stack too deep + uint oracleFee = ILayerZeroOracleV2(uaConfig.oracle).getFee( + _dstChainId, + uaConfig.outboundProofType, + uaConfig.outboundBlockConfirmations, + ua + ); + + // LayerZero Fee + uint protocolFee = treasuryContract.getFees(_payInZRO, relayerFee, oracleFee); + _payInZRO ? zroFee = protocolFee : nativeFee = protocolFee; + + // return the sum of fees + nativeFee = nativeFee.add(relayerFee).add(oracleFee); + } + + //--------------------------------------------------------------------------- + // Claim Fees + + // universal withdraw ZRO token function + function withdrawZRO(address _to, uint _amount) external override nonReentrant { + require(msg.sender == address(treasuryContract), "LayerZero: only treasury"); + treasuryZROFees = treasuryZROFees.sub(_amount); + layerZeroToken.safeTransfer(_to, _amount); + emit WithdrawZRO(msg.sender, _to, _amount); + } + + // universal withdraw native token function. + // the source contract should perform all the authentication control + function withdrawNative(address payable _to, uint _amount) external override nonReentrant { + require(_to != address(0x0), "LayerZero: _to cannot be zero address"); + nativeFees[msg.sender] = nativeFees[msg.sender].sub(_amount); + + (bool success, ) = _to.call{value: _amount}(""); + require(success, "LayerZero: withdraw failed"); + emit WithdrawNative(msg.sender, _to, _amount); + } + + //--------------------------------------------------------------------------- + // Owner calls, configuration only. + function setLayerZeroToken(address _layerZeroToken) external onlyOwner { + require(_layerZeroToken != address(0x0), "LayerZero: _layerZeroToken cannot be zero address"); + layerZeroToken = IERC20(_layerZeroToken); + emit SetLayerZeroToken(_layerZeroToken); + } + + function setTreasury(address _treasury) external onlyOwner { + require(_treasury != address(0x0), "LayerZero: treasury cannot be zero address"); + treasuryContract = ILayerZeroTreasury(_treasury); + emit SetTreasury(_treasury); + } + + function addInboundProofLibraryForChain(uint16 _chainId, address _library) external onlyOwner { + require(_library != address(0x0), "LayerZero: library cannot be zero address"); + uint16 libId = maxInboundProofLibrary[_chainId]; + require(libId < 65535, "LayerZero: can not add new library"); + maxInboundProofLibrary[_chainId] = ++libId; + inboundProofLibrary[_chainId][libId] = _library; + emit AddInboundProofLibraryForChain(_chainId, _library); + } + + function enableSupportedOutboundProof(uint16 _chainId, uint16 _proofType) external onlyOwner { + supportedOutboundProof[_chainId][_proofType] = true; + emit EnableSupportedOutboundProof(_chainId, _proofType); + } + + function setDefaultConfigForChainId( + uint16 _chainId, + uint16 _inboundProofLibraryVersion, + uint64 _inboundBlockConfirmations, + address _relayer, + uint16 _outboundProofType, + uint64 _outboundBlockConfirmations, + address _oracle + ) external onlyOwner { + require( + _inboundProofLibraryVersion <= maxInboundProofLibrary[_chainId] && _inboundProofLibraryVersion > 0, + "LayerZero: invalid inbound proof library version" + ); + require(_inboundBlockConfirmations > 0, "LayerZero: invalid inbound block confirmation"); + require(_relayer != address(0x0), "LayerZero: invalid relayer address"); + require(supportedOutboundProof[_chainId][_outboundProofType], "LayerZero: invalid outbound proof type"); + require(_outboundBlockConfirmations > 0, "LayerZero: invalid outbound block confirmation"); + require(_oracle != address(0x0), "LayerZero: invalid oracle address"); + defaultAppConfig[_chainId] = ApplicationConfiguration( + _inboundProofLibraryVersion, + _inboundBlockConfirmations, + _relayer, + _outboundProofType, + _outboundBlockConfirmations, + _oracle + ); + emit SetDefaultConfigForChainId( + _chainId, + _inboundProofLibraryVersion, + _inboundBlockConfirmations, + _relayer, + _outboundProofType, + _outboundBlockConfirmations, + _oracle + ); + } + + function setDefaultAdapterParamsForChainId( + uint16 _chainId, + uint16 _proofType, + bytes calldata _adapterParams + ) external onlyOwner { + defaultAdapterParams[_chainId][_proofType] = _adapterParams; + emit SetDefaultAdapterParamsForChainId(_chainId, _proofType, _adapterParams); + } + + function setRemoteUln(uint16 _remoteChainId, bytes32 _remoteUln) external onlyOwner { + require(ulnLookup[_remoteChainId] == bytes32(0), "LayerZero: remote uln already set"); + ulnLookup[_remoteChainId] = _remoteUln; + emit SetRemoteUln(_remoteChainId, _remoteUln); + } + + function setChainAddressSize(uint16 _chainId, uint _size) external onlyOwner { + require(chainAddressSizeMap[_chainId] == 0, "LayerZero: remote chain address size already set"); + chainAddressSizeMap[_chainId] = _size; + emit SetChainAddressSize(_chainId, _size); + } + + //---------------------------------------------------------------------------------- + // view functions + + function accruedNativeFee(address _address) external view override returns (uint) { + return nativeFees[_address]; + } + + function getOutboundNonce(uint16 _chainId, bytes calldata _path) external view override returns (uint64) { + return nonceContract.outboundNonce(_chainId, _path); + } + + function _isContract(address addr) internal view returns (bool) { + uint size; + assembly { + size := extcodesize(addr) + } + return size != 0; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2AltToken.sol b/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2AltToken.sol new file mode 100644 index 0000000..23ed64c --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2AltToken.sol @@ -0,0 +1,583 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; + +import "./interfaces/ILayerZeroValidationLibrary.sol"; +import "./interfaces/ILayerZeroReceiver.sol"; +import "./interfaces/ILayerZeroTreasury.sol"; +import "./interfaces/ILayerZeroEndpoint.sol"; +// v2 +import "./interfaces/ILayerZeroMessagingLibraryV2.sol"; +import "./interfaces/ILayerZeroOracleV2.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV2.sol"; +import "./interfaces/ILayerZeroRelayerV2.sol"; +import "./FeeHandler.sol"; +import "./NonceContract.sol"; + +contract UltraLightNodeV2AltToken is + ILayerZeroMessagingLibraryV2, + ILayerZeroUltraLightNodeV2, + ReentrancyGuard, + Ownable +{ + using SafeERC20 for IERC20; + using SafeMath for uint; + + // Application config + uint public constant CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION = 1; + uint public constant CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS = 2; + uint public constant CONFIG_TYPE_RELAYER = 3; + uint public constant CONFIG_TYPE_OUTBOUND_PROOF_TYPE = 4; + uint public constant CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS = 5; + uint public constant CONFIG_TYPE_ORACLE = 6; + + // Token and Contracts + IERC20 public layerZeroToken; + ILayerZeroTreasury public treasuryContract; + + mapping(address => uint) public nativeFees; + uint public treasuryZROFees; + + // User Application + mapping(address => mapping(uint16 => ApplicationConfiguration)) public appConfig; // app address => chainId => config + mapping(uint16 => ApplicationConfiguration) public defaultAppConfig; // default UA settings if no version specified + mapping(uint16 => mapping(uint16 => bytes)) public defaultAdapterParams; + + // Validation + mapping(uint16 => mapping(uint16 => address)) public inboundProofLibrary; // chainId => library Id => inboundProofLibrary contract + mapping(uint16 => uint16) public maxInboundProofLibrary; // chainId => inboundProofLibrary + mapping(uint16 => mapping(uint16 => bool)) public supportedOutboundProof; // chainId => outboundProofType => enabled + mapping(uint16 => uint) public chainAddressSizeMap; + mapping(address => mapping(uint16 => mapping(bytes32 => mapping(bytes32 => uint)))) public hashLookup; //[oracle][srcChainId][blockhash][datahash] -> confirmation + mapping(uint16 => bytes32) public ulnLookup; // remote ulns + + ILayerZeroEndpoint public immutable endpoint; + uint16 public immutable localChainId; + NonceContract public immutable nonceContract; + FeeHandler public immutable feeHandler; + + constructor(address _endpoint, address _nonceContract, uint16 _localChainId, address _feeHandler) { + require(_endpoint != address(0x0), "LayerZero: endpoint cannot be zero address"); + require(_nonceContract != address(0x0), "LayerZero: nonceContract cannot be zero address"); + ILayerZeroEndpoint lzEndpoint = ILayerZeroEndpoint(_endpoint); + localChainId = _localChainId; + endpoint = lzEndpoint; + nonceContract = NonceContract(_nonceContract); + feeHandler = FeeHandler(_feeHandler); + } + + // only the endpoint can call SEND() and setConfig() + modifier onlyEndpoint() { + require(address(endpoint) == msg.sender, "LayerZero: only endpoint"); + _; + } + + //---------------------------------------------------------------------------------- + // PROTOCOL + function validateTransactionProof( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _lookupHash, + bytes32 _blockData, + bytes calldata _transactionProof + ) external override { + // retrieve UA's configuration using the _dstAddress from arguments. + ApplicationConfiguration memory uaConfig = _getAppConfig(_srcChainId, _dstAddress); + + // assert that the caller == UA's relayer + require(uaConfig.relayer == msg.sender, "LayerZero: invalid relayer"); + + LayerZeroPacket.Packet memory _packet; + uint remoteAddressSize = chainAddressSizeMap[_srcChainId]; + require(remoteAddressSize != 0, "LayerZero: incorrect remote address size"); + { + // assert that the data submitted by UA's oracle have no fewer confirmations than UA's configuration + uint storedConfirmations = hashLookup[uaConfig.oracle][_srcChainId][_lookupHash][_blockData]; + require( + storedConfirmations > 0 && storedConfirmations >= uaConfig.inboundBlockConfirmations, + "LayerZero: not enough block confirmations" + ); + + // decode + address inboundProofLib = inboundProofLibrary[_srcChainId][uaConfig.inboundProofLibraryVersion]; + _packet = ILayerZeroValidationLibrary(inboundProofLib).validateProof( + _blockData, + _transactionProof, + remoteAddressSize + ); + } + + // packet content assertion + require( + ulnLookup[_srcChainId] == _packet.ulnAddress && _packet.ulnAddress != bytes32(0), + "LayerZero: invalid _packet.ulnAddress" + ); + require(_packet.srcChainId == _srcChainId, "LayerZero: invalid srcChain Id"); + // failsafe because the remoteAddress size being passed into validateProof trims the address this should not hit + require(_packet.srcAddress.length == remoteAddressSize, "LayerZero: invalid srcAddress size"); + require(_packet.dstChainId == localChainId, "LayerZero: invalid dstChain Id"); + require(_packet.dstAddress == _dstAddress, "LayerZero: invalid dstAddress"); + + // if the dst is not a contract, then emit and return early. This will break inbound nonces, but this particular + // path is already broken and wont ever be able to deliver anyways + if (!_isContract(_dstAddress)) { + emit InvalidDst( + _packet.srcChainId, + _packet.srcAddress, + _packet.dstAddress, + _packet.nonce, + keccak256(_packet.payload) + ); + return; + } + + bytes memory pathData = abi.encodePacked(_packet.srcAddress, _packet.dstAddress); + emit PacketReceived( + _packet.srcChainId, + _packet.srcAddress, + _packet.dstAddress, + _packet.nonce, + keccak256(_packet.payload) + ); + endpoint.receivePayload(_srcChainId, pathData, _dstAddress, _packet.nonce, _gasLimit, _packet.payload); + } + + function send( + address _ua, + uint64, + uint16 _dstChainId, + bytes calldata _path, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable override onlyEndpoint { + address ua = _ua; + uint16 dstChainId = _dstChainId; + require(ulnLookup[dstChainId] != bytes32(0), "LayerZero: dstChainId does not exist"); + + bytes memory dstAddress; + uint64 nonce; + // code block for solving 'Stack Too Deep' + { + uint chainAddressSize = chainAddressSizeMap[dstChainId]; + // path = remoteAddress + localAddress + require( + chainAddressSize != 0 && _path.length == 20 + chainAddressSize, + "LayerZero: incorrect remote address size" + ); + address srcInPath; + bytes memory path = _path; // copy to memory + assembly { + srcInPath := mload(add(add(path, 20), chainAddressSize)) // chainAddressSize + 20 + } + require(ua == srcInPath, "LayerZero: wrong path data"); + dstAddress = _path[0:chainAddressSize]; + nonce = nonceContract.increment(dstChainId, ua, path); + } + + bytes memory payload = _payload; + ApplicationConfiguration memory uaConfig = _getAppConfig(dstChainId, ua); + + // compute all the fees + uint[] memory fees = new uint[](3); + fees[0] = _handleRelayer(dstChainId, uaConfig, ua, payload.length, _adapterParams); + fees[1] = _handleOracle(dstChainId, uaConfig, ua); + fees[2] = _handleProtocolFee(fees[0], fees[1], ua, _zroPaymentAddress); + + address[] memory receivers = new address[](3); + receivers[0] = uaConfig.relayer; + receivers[1] = uaConfig.oracle; + receivers[2] = address(treasuryContract); + + feeHandler.creditFee(receivers, fees, _refundAddress); + + // emit the data packet + bytes memory encodedPayload = abi.encodePacked(nonce, localChainId, ua, dstChainId, dstAddress, payload); + emit Packet(encodedPayload); + } + + function _handleRelayer( + uint16 _dstChainId, + ApplicationConfiguration memory _uaConfig, + address _ua, + uint _payloadSize, + bytes memory _adapterParams + ) internal returns (uint relayerFee) { + if (_adapterParams.length == 0) { + _adapterParams = defaultAdapterParams[_dstChainId][_uaConfig.outboundProofType]; + } + address relayerAddress = _uaConfig.relayer; + ILayerZeroRelayerV2 relayer = ILayerZeroRelayerV2(relayerAddress); + relayerFee = relayer.assignJob(_dstChainId, _uaConfig.outboundProofType, _ua, _payloadSize, _adapterParams); + + // emit the param events + emit RelayerParams(_adapterParams, _uaConfig.outboundProofType); + } + + function _handleOracle( + uint16 _dstChainId, + ApplicationConfiguration memory _uaConfig, + address _ua + ) internal returns (uint oracleFee) { + address oracleAddress = _uaConfig.oracle; + oracleFee = ILayerZeroOracleV2(oracleAddress).assignJob( + _dstChainId, + _uaConfig.outboundProofType, + _uaConfig.outboundBlockConfirmations, + _ua + ); + } + + function _handleProtocolFee( + uint _relayerFee, + uint _oracleFee, + address _ua, + address _zroPaymentAddress + ) internal returns (uint protocolNativeFee) { + // if no ZRO token or not specifying a payment address, pay in native token + bool payInNative = _zroPaymentAddress == address(0x0) || address(layerZeroToken) == address(0x0); + uint protocolFee = treasuryContract.getFees(!payInNative, _relayerFee, _oracleFee); + + if (protocolFee > 0) { + if (payInNative) { + address treasuryAddress = address(treasuryContract); + protocolNativeFee = protocolFee; + } else { + // zro payment address must equal the ua or the tx.origin otherwise the transaction reverts + require( + _zroPaymentAddress == _ua || _zroPaymentAddress == tx.origin, + "LayerZero: must be paid by sender or origin" + ); + + // transfer the LayerZero token to this contract from the payee + layerZeroToken.safeTransferFrom(_zroPaymentAddress, address(this), protocolFee); + + treasuryZROFees = treasuryZROFees.add(protocolFee); + } + } + } + + // Can be called by any address to update a block header + // can only upload new block data or the same block data with more confirmations + function updateHash( + uint16 _srcChainId, + bytes32 _lookupHash, + uint _confirmations, + bytes32 _blockData + ) external override { + uint storedConfirmations = hashLookup[msg.sender][_srcChainId][_lookupHash][_blockData]; + + // if it has a record, requires a larger confirmation. + require( + storedConfirmations < _confirmations, + "LayerZero: oracle data can only update if it has more confirmations" + ); + + // set the new information into storage + hashLookup[msg.sender][_srcChainId][_lookupHash][_blockData] = _confirmations; + + emit HashReceived(_srcChainId, msg.sender, _lookupHash, _blockData, _confirmations); + } + + //---------------------------------------------------------------------------------- + // Other Library Interfaces + + // default to DEFAULT setting if ZERO value + function getAppConfig( + uint16 _remoteChainId, + address _ua + ) external view override returns (ApplicationConfiguration memory) { + return _getAppConfig(_remoteChainId, _ua); + } + + function _getAppConfig(uint16 _remoteChainId, address _ua) internal view returns (ApplicationConfiguration memory) { + ApplicationConfiguration memory config = appConfig[_ua][_remoteChainId]; + ApplicationConfiguration storage defaultConfig = defaultAppConfig[_remoteChainId]; + + if (config.inboundProofLibraryVersion == 0) { + config.inboundProofLibraryVersion = defaultConfig.inboundProofLibraryVersion; + } + + if (config.inboundBlockConfirmations == 0) { + config.inboundBlockConfirmations = defaultConfig.inboundBlockConfirmations; + } + + if (config.relayer == address(0x0)) { + config.relayer = defaultConfig.relayer; + } + + if (config.outboundProofType == 0) { + config.outboundProofType = defaultConfig.outboundProofType; + } + + if (config.outboundBlockConfirmations == 0) { + config.outboundBlockConfirmations = defaultConfig.outboundBlockConfirmations; + } + + if (config.oracle == address(0x0)) { + config.oracle = defaultConfig.oracle; + } + + return config; + } + + function setConfig( + uint16 _remoteChainId, + address _ua, + uint _configType, + bytes calldata _config + ) external override onlyEndpoint { + ApplicationConfiguration storage uaConfig = appConfig[_ua][_remoteChainId]; + if (_configType == CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION) { + uint16 inboundProofLibraryVersion = abi.decode(_config, (uint16)); + require( + inboundProofLibraryVersion <= maxInboundProofLibrary[_remoteChainId], + "LayerZero: invalid inbound proof library version" + ); + uaConfig.inboundProofLibraryVersion = inboundProofLibraryVersion; + } else if (_configType == CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS) { + uint64 blockConfirmations = abi.decode(_config, (uint64)); + uaConfig.inboundBlockConfirmations = blockConfirmations; + } else if (_configType == CONFIG_TYPE_RELAYER) { + address relayer = abi.decode(_config, (address)); + uaConfig.relayer = relayer; + } else if (_configType == CONFIG_TYPE_OUTBOUND_PROOF_TYPE) { + uint16 outboundProofType = abi.decode(_config, (uint16)); + require( + supportedOutboundProof[_remoteChainId][outboundProofType] || outboundProofType == 0, + "LayerZero: invalid outbound proof type" + ); + uaConfig.outboundProofType = outboundProofType; + } else if (_configType == CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS) { + uint64 blockConfirmations = abi.decode(_config, (uint64)); + uaConfig.outboundBlockConfirmations = blockConfirmations; + } else if (_configType == CONFIG_TYPE_ORACLE) { + address oracle = abi.decode(_config, (address)); + uaConfig.oracle = oracle; + } else { + revert("LayerZero: Invalid config type"); + } + + emit AppConfigUpdated(_ua, _configType, _config); + } + + function getConfig( + uint16 _remoteChainId, + address _ua, + uint _configType + ) external view override returns (bytes memory) { + ApplicationConfiguration storage uaConfig = appConfig[_ua][_remoteChainId]; + + if (_configType == CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION) { + if (uaConfig.inboundProofLibraryVersion == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].inboundProofLibraryVersion); + } + return abi.encode(uaConfig.inboundProofLibraryVersion); + } else if (_configType == CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS) { + if (uaConfig.inboundBlockConfirmations == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].inboundBlockConfirmations); + } + return abi.encode(uaConfig.inboundBlockConfirmations); + } else if (_configType == CONFIG_TYPE_RELAYER) { + if (uaConfig.relayer == address(0x0)) { + return abi.encode(defaultAppConfig[_remoteChainId].relayer); + } + return abi.encode(uaConfig.relayer); + } else if (_configType == CONFIG_TYPE_OUTBOUND_PROOF_TYPE) { + if (uaConfig.outboundProofType == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].outboundProofType); + } + return abi.encode(uaConfig.outboundProofType); + } else if (_configType == CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS) { + if (uaConfig.outboundBlockConfirmations == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].outboundBlockConfirmations); + } + return abi.encode(uaConfig.outboundBlockConfirmations); + } else if (_configType == CONFIG_TYPE_ORACLE) { + if (uaConfig.oracle == address(0x0)) { + return abi.encode(defaultAppConfig[_remoteChainId].oracle); + } + return abi.encode(uaConfig.oracle); + } else { + revert("LayerZero: Invalid config type"); + } + } + + // returns the native fee the UA pays to cover fees + function estimateFees( + uint16 _dstChainId, + address _ua, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParams + ) external view override returns (uint nativeFee, uint zroFee) { + ApplicationConfiguration memory uaConfig = _getAppConfig(_dstChainId, _ua); + + // Relayer Fee + bytes memory adapterParams; + if (_adapterParams.length > 0) { + adapterParams = _adapterParams; + } else { + adapterParams = defaultAdapterParams[_dstChainId][uaConfig.outboundProofType]; + } + uint relayerFee = ILayerZeroRelayerV2(uaConfig.relayer).getFee( + _dstChainId, + uaConfig.outboundProofType, + _ua, + _payload.length, + adapterParams + ); + + // Oracle Fee + address ua = _ua; // stack too deep + uint oracleFee = ILayerZeroOracleV2(uaConfig.oracle).getFee( + _dstChainId, + uaConfig.outboundProofType, + uaConfig.outboundBlockConfirmations, + ua + ); + + // LayerZero Fee + uint protocolFee = treasuryContract.getFees(_payInZRO, relayerFee, oracleFee); + _payInZRO ? zroFee = protocolFee : nativeFee = protocolFee; + + // return the sum of fees + nativeFee = nativeFee.add(relayerFee).add(oracleFee); + } + + //--------------------------------------------------------------------------- + // Claim Fees + + // universal withdraw ZRO token function + function withdrawZRO(address _to, uint _amount) external override nonReentrant { + require(msg.sender == address(treasuryContract), "LayerZero: only treasury"); + treasuryZROFees = treasuryZROFees.sub(_amount); + layerZeroToken.safeTransfer(_to, _amount); + emit WithdrawZRO(msg.sender, _to, _amount); + } + + // universal withdraw native token function. + // the source contract should perform all the authentication control + function withdrawNative(address payable _to, uint _amount) external override nonReentrant { + require(_to != address(0x0), "LayerZero: _to cannot be zero address"); + nativeFees[msg.sender] = nativeFees[msg.sender].sub(_amount); + + (bool success, ) = _to.call{value: _amount}(""); + require(success, "LayerZero: withdraw failed"); + emit WithdrawNative(msg.sender, _to, _amount); + } + + //--------------------------------------------------------------------------- + // Owner calls, configuration only. + function setLayerZeroToken(address _layerZeroToken) external onlyOwner { + require(_layerZeroToken != address(0x0), "LayerZero: _layerZeroToken cannot be zero address"); + layerZeroToken = IERC20(_layerZeroToken); + emit SetLayerZeroToken(_layerZeroToken); + } + + function setTreasury(address _treasury) external onlyOwner { + require(_treasury != address(0x0), "LayerZero: treasury cannot be zero address"); + treasuryContract = ILayerZeroTreasury(_treasury); + emit SetTreasury(_treasury); + } + + function addInboundProofLibraryForChain(uint16 _chainId, address _library) external onlyOwner { + require(_library != address(0x0), "LayerZero: library cannot be zero address"); + uint16 libId = maxInboundProofLibrary[_chainId]; + require(libId < 65535, "LayerZero: can not add new library"); + maxInboundProofLibrary[_chainId] = ++libId; + inboundProofLibrary[_chainId][libId] = _library; + emit AddInboundProofLibraryForChain(_chainId, _library); + } + + function enableSupportedOutboundProof(uint16 _chainId, uint16 _proofType) external onlyOwner { + supportedOutboundProof[_chainId][_proofType] = true; + emit EnableSupportedOutboundProof(_chainId, _proofType); + } + + function setDefaultConfigForChainId( + uint16 _chainId, + uint16 _inboundProofLibraryVersion, + uint64 _inboundBlockConfirmations, + address _relayer, + uint16 _outboundProofType, + uint64 _outboundBlockConfirmations, + address _oracle + ) external onlyOwner { + require( + _inboundProofLibraryVersion <= maxInboundProofLibrary[_chainId] && _inboundProofLibraryVersion > 0, + "LayerZero: invalid inbound proof library version" + ); + require(_inboundBlockConfirmations > 0, "LayerZero: invalid inbound block confirmation"); + require(_relayer != address(0x0), "LayerZero: invalid relayer address"); + require(supportedOutboundProof[_chainId][_outboundProofType], "LayerZero: invalid outbound proof type"); + require(_outboundBlockConfirmations > 0, "LayerZero: invalid outbound block confirmation"); + require(_oracle != address(0x0), "LayerZero: invalid oracle address"); + defaultAppConfig[_chainId] = ApplicationConfiguration( + _inboundProofLibraryVersion, + _inboundBlockConfirmations, + _relayer, + _outboundProofType, + _outboundBlockConfirmations, + _oracle + ); + emit SetDefaultConfigForChainId( + _chainId, + _inboundProofLibraryVersion, + _inboundBlockConfirmations, + _relayer, + _outboundProofType, + _outboundBlockConfirmations, + _oracle + ); + } + + function setDefaultAdapterParamsForChainId( + uint16 _chainId, + uint16 _proofType, + bytes calldata _adapterParams + ) external onlyOwner { + defaultAdapterParams[_chainId][_proofType] = _adapterParams; + emit SetDefaultAdapterParamsForChainId(_chainId, _proofType, _adapterParams); + } + + function setRemoteUln(uint16 _remoteChainId, bytes32 _remoteUln) external onlyOwner { + require(ulnLookup[_remoteChainId] == bytes32(0), "LayerZero: remote uln already set"); + ulnLookup[_remoteChainId] = _remoteUln; + emit SetRemoteUln(_remoteChainId, _remoteUln); + } + + function setChainAddressSize(uint16 _chainId, uint _size) external onlyOwner { + require(chainAddressSizeMap[_chainId] == 0, "LayerZero: remote chain address size already set"); + chainAddressSizeMap[_chainId] = _size; + emit SetChainAddressSize(_chainId, _size); + } + + //---------------------------------------------------------------------------------- + // view functions + + function accruedNativeFee(address _address) external view override returns (uint) { + return nativeFees[_address]; + } + + function getOutboundNonce(uint16 _chainId, bytes calldata _path) external view override returns (uint64) { + return nonceContract.outboundNonce(_chainId, _path); + } + + function _isContract(address addr) internal view returns (bool) { + uint size; + assembly { + size := extcodesize(addr) + } + return size != 0; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2Radar.sol b/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2Radar.sol new file mode 100644 index 0000000..36d662e --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/UltraLightNodeV2Radar.sol @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; + +import "./interfaces/ILayerZeroValidationLibrary.sol"; +import "./interfaces/ILayerZeroReceiver.sol"; +import "./interfaces/ILayerZeroTreasury.sol"; +import "./interfaces/ILayerZeroEndpoint.sol"; +// v2 +import "./interfaces/ILayerZeroMessagingLibraryV2.sol"; +import "./interfaces/ILayerZeroOracleV2.sol"; +import "./interfaces/ILayerZeroUltraLightNodeV2.sol"; +import "./interfaces/ILayerZeroRelayerV2.sol"; +import "./NonceContractRadar.sol"; + +contract UltraLightNodeV2Radar is ILayerZeroMessagingLibraryV2, ILayerZeroUltraLightNodeV2, ReentrancyGuard, Ownable { + using SafeERC20 for IERC20; + using SafeMath for uint; + + // Application config + uint public constant CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION = 1; + uint public constant CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS = 2; + uint public constant CONFIG_TYPE_RELAYER = 3; + uint public constant CONFIG_TYPE_OUTBOUND_PROOF_TYPE = 4; + uint public constant CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS = 5; + uint public constant CONFIG_TYPE_ORACLE = 6; + + // Token and Contracts + IERC20 public layerZeroToken; + ILayerZeroTreasury public treasuryContract; + + mapping(address => uint) public nativeFees; + uint public treasuryZROFees; + + // User Application + mapping(address => mapping(uint16 => ApplicationConfiguration)) public appConfig; // app address => chainId => config + mapping(uint16 => ApplicationConfiguration) public defaultAppConfig; // default UA settings if no version specified + mapping(uint16 => mapping(uint16 => bytes)) public defaultAdapterParams; + + // Validation + mapping(uint16 => mapping(uint16 => address)) public inboundProofLibrary; // chainId => library Id => inboundProofLibrary contract + mapping(uint16 => uint16) public maxInboundProofLibrary; // chainId => inboundProofLibrary + mapping(uint16 => mapping(uint16 => bool)) public supportedOutboundProof; // chainId => outboundProofType => enabled + mapping(uint16 => uint) public chainAddressSizeMap; + mapping(address => mapping(uint16 => mapping(bytes32 => mapping(bytes32 => uint)))) public hashLookup; //[oracle][srcChainId][blockhash][datahash] -> confirmation + mapping(uint16 => bytes32) public ulnLookup; // remote ulns + + ILayerZeroEndpoint public immutable endpoint; + uint16 public immutable localChainId; + NonceContractRadar public immutable nonceContract; + + constructor(address _endpoint, address _nonceContract, uint16 _localChainId, address _dappRadar) { + require(_endpoint != address(0x0), "LayerZero: endpoint cannot be zero address"); + require(_nonceContract != address(0x0), "LayerZero: nonceContract cannot be zero address"); + ILayerZeroEndpoint lzEndpoint = ILayerZeroEndpoint(_endpoint); + localChainId = _localChainId; + endpoint = lzEndpoint; + nonceContract = NonceContractRadar(_nonceContract); + + // dappRadar + dappRadar = _dappRadar; + } + + // only the endpoint can call SEND() and setConfig() + modifier onlyEndpoint() { + require(address(endpoint) == msg.sender, "LayerZero: only endpoint"); + _; + } + + // Manual for DappRadar handling. This contract is dappRadar-only (send/receive) + // 1. Layerzero deploys UltraLightNodeV2Radar and NonceContractRadar using old chain ID with the local dappRadar address in constructor + // 2. Dapp Radar sets the messaging library to UltraLightNodeV2Radar + // 3. Dapp Radar sets the trustedRemote to the full path + // 4. Layerzero initializes the outboundNonce (1 call) and inboundNonce (batch call) + // 5. Test message flows + // + // 6. after an agree-upon period of time, decommission this contract (one-way trip). + + // + // DappRadar constructs + // + address public immutable dappRadar; + bool public decommissioned; + mapping(uint16 => bool) public outboundNonceSet; + mapping(address => uint64) public inboundNonceCap; + + // only dappRadar + function initRadarOutboundNonce(uint16 _dstChainId, address _dstRadarAddress) external onlyOwner { + // can only inherit the outbound nonce from previous path once + // assuming dappRadar has only 1 remote peer at a destination chain. + require(!outboundNonceSet[_dstChainId], "LayerZero: dappRadar nonce already set"); + uint64 inheritedNonce = endpoint.getOutboundNonce(_dstChainId, dappRadar); + outboundNonceSet[_dstChainId] = true; + + // can only set the path owned by the dappRadar + // dappRadar is only deployed on EVM chains so the address is 20 bytes for all + bytes memory radarPath = abi.encodePacked(_dstRadarAddress, dappRadar); //// remote + local + + // insert into the nonce contract + nonceContract.initRadarOutboundNonce(_dstChainId, radarPath, inheritedNonce); + } + + // generate a message from the new dappRadar-owned path with now payload + // dappRadar needs to first change the trustedRemote + // messages will fail locally in the nonBlockingLzApp from the nonce checking + // can only increment the nonce till we hit the legacy nonce + function incrementRadarInboundNonce( + uint16 _srcChainId, + address _srcRadarAddress, + uint _gasLimitPerCall, + uint _steps + ) external onlyOwner { + // initialize the inboundNonceCap, only once + if (inboundNonceCap[_srcRadarAddress] == 0) { + // check if the _srcRadarAddress is a legacy address by checking the nonce + uint64 inheritNonce = endpoint.getInboundNonce(_srcChainId, abi.encodePacked(_srcRadarAddress)); + require(inheritNonce > 0, "LayerZero: not legacy radar address"); + + inboundNonceCap[_srcRadarAddress] = inheritNonce; + } + + // can only set the path owned by the dappRadar + // dappRadar is only deployed on EVM chains so the address is 20 bytes for all + bytes memory radarPath = abi.encodePacked(_srcRadarAddress, dappRadar); // remote + local + uint64 radarPathNonce = endpoint.getInboundNonce(_srcChainId, radarPath); + uint64 nonceCap = inboundNonceCap[_srcRadarAddress]; + + for (uint i = 0; i < _steps; i++) { + // ensure that the nonce of the new path is not already at the cap + radarPathNonce++; + if (radarPathNonce > nonceCap) { + break; + } + // receive the message with null Payload + endpoint.receivePayload(_srcChainId, radarPath, dappRadar, radarPathNonce, _gasLimitPerCall, bytes("")); + } + } + + // this contract will only serve for a period of time + function decommission() external onlyOwner { + decommissioned = true; + } + + //---------------------------------------------------------------------------------- + // PROTOCOL + function validateTransactionProof( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _lookupHash, + bytes32 _blockData, + bytes calldata _transactionProof + ) external override { + require(_dstAddress == dappRadar, "LayerZero: only dappRadar"); + require(!decommissioned, "LayerZero: decommissioned"); + + // retrieve UA's configuration using the _dstAddress from arguments. + ApplicationConfiguration memory uaConfig = _getAppConfig(_srcChainId, _dstAddress); + + // assert that the caller == UA's relayer + require(uaConfig.relayer == msg.sender, "LayerZero: invalid relayer"); + + LayerZeroPacket.Packet memory _packet; + uint remoteAddressSize = chainAddressSizeMap[_srcChainId]; + require(remoteAddressSize != 0, "LayerZero: incorrect remote address size"); + { + // assert that the data submitted by UA's oracle have no fewer confirmations than UA's configuration + uint storedConfirmations = hashLookup[uaConfig.oracle][_srcChainId][_lookupHash][_blockData]; + require( + storedConfirmations > 0 && storedConfirmations >= uaConfig.inboundBlockConfirmations, + "LayerZero: not enough block confirmations" + ); + + // decode + address inboundProofLib = inboundProofLibrary[_srcChainId][uaConfig.inboundProofLibraryVersion]; + _packet = ILayerZeroValidationLibrary(inboundProofLib).validateProof( + _blockData, + _transactionProof, + remoteAddressSize + ); + } + + // packet content assertion + require( + ulnLookup[_srcChainId] == _packet.ulnAddress && _packet.ulnAddress != bytes32(0), + "LayerZero: invalid _packet.ulnAddress" + ); + require(_packet.srcChainId == _srcChainId, "LayerZero: invalid srcChain Id"); + // failsafe because the remoteAddress size being passed into validateProof trims the address this should not hit + require(_packet.srcAddress.length == remoteAddressSize, "LayerZero: invalid srcAddress size"); + require(_packet.dstChainId == localChainId, "LayerZero: invalid dstChain Id"); + require(_packet.dstAddress == _dstAddress, "LayerZero: invalid dstAddress"); + + // if the dst is not a contract, then emit and return early. This will break inbound nonces, but this particular + // path is already broken and wont ever be able to deliver anyways + if (!_isContract(_dstAddress)) { + emit InvalidDst( + _packet.srcChainId, + _packet.srcAddress, + _packet.dstAddress, + _packet.nonce, + keccak256(_packet.payload) + ); + return; + } + + bytes memory pathData = abi.encodePacked(_packet.srcAddress, _packet.dstAddress); + emit PacketReceived( + _packet.srcChainId, + _packet.srcAddress, + _packet.dstAddress, + _packet.nonce, + keccak256(_packet.payload) + ); + endpoint.receivePayload(_srcChainId, pathData, _dstAddress, _packet.nonce, _gasLimit, _packet.payload); + } + + function send( + address _ua, + uint64, + uint16 _dstChainId, + bytes calldata _path, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable override onlyEndpoint { + require(_ua == dappRadar, "LayerZero: only dappRadar"); + require(!decommissioned, "LayerZero: decommissioned"); + + address ua = _ua; + uint16 dstChainId = _dstChainId; + require(ulnLookup[dstChainId] != bytes32(0), "LayerZero: dstChainId does not exist"); + + bytes memory dstAddress; + uint64 nonce; + // code block for solving 'Stack Too Deep' + { + uint chainAddressSize = chainAddressSizeMap[dstChainId]; + // path = remoteAddress + localAddress + require( + chainAddressSize != 0 && _path.length == 20 + chainAddressSize, + "LayerZero: incorrect remote address size" + ); + address srcInPath; + bytes memory path = _path; // copy to memory + assembly { + srcInPath := mload(add(add(path, 20), chainAddressSize)) // chainAddressSize + 20 + } + require(ua == srcInPath, "LayerZero: wrong path data"); + dstAddress = _path[0:chainAddressSize]; + nonce = nonceContract.increment(dstChainId, ua, path); + } + + bytes memory payload = _payload; + ApplicationConfiguration memory uaConfig = _getAppConfig(dstChainId, ua); + + // compute all the fees + uint relayerFee = _handleRelayer(dstChainId, uaConfig, ua, payload.length, _adapterParams); + uint oracleFee = _handleOracle(dstChainId, uaConfig, ua); + uint nativeProtocolFee = _handleProtocolFee(relayerFee, oracleFee, ua, _zroPaymentAddress); + + // total native fee, does not include ZRO protocol fee + uint totalNativeFee = relayerFee.add(oracleFee).add(nativeProtocolFee); + + // assert the user has attached enough native token for this address + require(totalNativeFee <= msg.value, "LayerZero: not enough native for fees"); + // refund if they send too much + uint amount = msg.value.sub(totalNativeFee); + if (amount > 0) { + (bool success, ) = _refundAddress.call{value: amount}(""); + require(success, "LayerZero: failed to refund"); + } + + // emit the data packet + bytes memory encodedPayload = abi.encodePacked(nonce, localChainId, ua, dstChainId, dstAddress, payload); + emit Packet(encodedPayload); + } + + function _handleRelayer( + uint16 _dstChainId, + ApplicationConfiguration memory _uaConfig, + address _ua, + uint _payloadSize, + bytes memory _adapterParams + ) internal returns (uint relayerFee) { + if (_adapterParams.length == 0) { + _adapterParams = defaultAdapterParams[_dstChainId][_uaConfig.outboundProofType]; + } + address relayerAddress = _uaConfig.relayer; + ILayerZeroRelayerV2 relayer = ILayerZeroRelayerV2(relayerAddress); + relayerFee = relayer.assignJob(_dstChainId, _uaConfig.outboundProofType, _ua, _payloadSize, _adapterParams); + + _creditNativeFee(relayerAddress, relayerFee); + + // emit the param events + emit RelayerParams(_adapterParams, _uaConfig.outboundProofType); + } + + function _handleOracle( + uint16 _dstChainId, + ApplicationConfiguration memory _uaConfig, + address _ua + ) internal returns (uint oracleFee) { + address oracleAddress = _uaConfig.oracle; + oracleFee = ILayerZeroOracleV2(oracleAddress).assignJob( + _dstChainId, + _uaConfig.outboundProofType, + _uaConfig.outboundBlockConfirmations, + _ua + ); + + _creditNativeFee(oracleAddress, oracleFee); + } + + function _handleProtocolFee( + uint _relayerFee, + uint _oracleFee, + address _ua, + address _zroPaymentAddress + ) internal returns (uint protocolNativeFee) { + // if no ZRO token or not specifying a payment address, pay in native token + bool payInNative = _zroPaymentAddress == address(0x0) || address(layerZeroToken) == address(0x0); + uint protocolFee = treasuryContract.getFees(!payInNative, _relayerFee, _oracleFee); + + if (protocolFee > 0) { + if (payInNative) { + address treasuryAddress = address(treasuryContract); + _creditNativeFee(treasuryAddress, protocolFee); + protocolNativeFee = protocolFee; + } else { + // zro payment address must equal the ua or the tx.origin otherwise the transaction reverts + require( + _zroPaymentAddress == _ua || _zroPaymentAddress == tx.origin, + "LayerZero: must be paid by sender or origin" + ); + + // transfer the LayerZero token to this contract from the payee + layerZeroToken.safeTransferFrom(_zroPaymentAddress, address(this), protocolFee); + + treasuryZROFees = treasuryZROFees.add(protocolFee); + } + } + } + + function _creditNativeFee(address _receiver, uint _amount) internal { + nativeFees[_receiver] = nativeFees[_receiver].add(_amount); + } + + // Can be called by any address to update a block header + // can only upload new block data or the same block data with more confirmations + function updateHash( + uint16 _srcChainId, + bytes32 _lookupHash, + uint _confirmations, + bytes32 _blockData + ) external override { + uint storedConfirmations = hashLookup[msg.sender][_srcChainId][_lookupHash][_blockData]; + + // if it has a record, requires a larger confirmation. + require( + storedConfirmations < _confirmations, + "LayerZero: oracle data can only update if it has more confirmations" + ); + + // set the new information into storage + hashLookup[msg.sender][_srcChainId][_lookupHash][_blockData] = _confirmations; + + emit HashReceived(_srcChainId, msg.sender, _lookupHash, _blockData, _confirmations); + } + + //---------------------------------------------------------------------------------- + // Other Library Interfaces + + // default to DEFAULT setting if ZERO value + function getAppConfig( + uint16 _remoteChainId, + address _ua + ) external view override returns (ApplicationConfiguration memory) { + return _getAppConfig(_remoteChainId, _ua); + } + + function _getAppConfig(uint16 _remoteChainId, address _ua) internal view returns (ApplicationConfiguration memory) { + ApplicationConfiguration memory config = appConfig[_ua][_remoteChainId]; + ApplicationConfiguration storage defaultConfig = defaultAppConfig[_remoteChainId]; + + if (config.inboundProofLibraryVersion == 0) { + config.inboundProofLibraryVersion = defaultConfig.inboundProofLibraryVersion; + } + + if (config.inboundBlockConfirmations == 0) { + config.inboundBlockConfirmations = defaultConfig.inboundBlockConfirmations; + } + + if (config.relayer == address(0x0)) { + config.relayer = defaultConfig.relayer; + } + + if (config.outboundProofType == 0) { + config.outboundProofType = defaultConfig.outboundProofType; + } + + if (config.outboundBlockConfirmations == 0) { + config.outboundBlockConfirmations = defaultConfig.outboundBlockConfirmations; + } + + if (config.oracle == address(0x0)) { + config.oracle = defaultConfig.oracle; + } + + return config; + } + + function setConfig( + uint16 _remoteChainId, + address _ua, + uint _configType, + bytes calldata _config + ) external override onlyEndpoint { + ApplicationConfiguration storage uaConfig = appConfig[_ua][_remoteChainId]; + if (_configType == CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION) { + uint16 inboundProofLibraryVersion = abi.decode(_config, (uint16)); + require( + inboundProofLibraryVersion <= maxInboundProofLibrary[_remoteChainId], + "LayerZero: invalid inbound proof library version" + ); + uaConfig.inboundProofLibraryVersion = inboundProofLibraryVersion; + } else if (_configType == CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS) { + uint64 blockConfirmations = abi.decode(_config, (uint64)); + uaConfig.inboundBlockConfirmations = blockConfirmations; + } else if (_configType == CONFIG_TYPE_RELAYER) { + address relayer = abi.decode(_config, (address)); + uaConfig.relayer = relayer; + } else if (_configType == CONFIG_TYPE_OUTBOUND_PROOF_TYPE) { + uint16 outboundProofType = abi.decode(_config, (uint16)); + require( + supportedOutboundProof[_remoteChainId][outboundProofType] || outboundProofType == 0, + "LayerZero: invalid outbound proof type" + ); + uaConfig.outboundProofType = outboundProofType; + } else if (_configType == CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS) { + uint64 blockConfirmations = abi.decode(_config, (uint64)); + uaConfig.outboundBlockConfirmations = blockConfirmations; + } else if (_configType == CONFIG_TYPE_ORACLE) { + address oracle = abi.decode(_config, (address)); + uaConfig.oracle = oracle; + } else { + revert("LayerZero: Invalid config type"); + } + + emit AppConfigUpdated(_ua, _configType, _config); + } + + function getConfig( + uint16 _remoteChainId, + address _ua, + uint _configType + ) external view override returns (bytes memory) { + ApplicationConfiguration storage uaConfig = appConfig[_ua][_remoteChainId]; + + if (_configType == CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION) { + if (uaConfig.inboundProofLibraryVersion == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].inboundProofLibraryVersion); + } + return abi.encode(uaConfig.inboundProofLibraryVersion); + } else if (_configType == CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS) { + if (uaConfig.inboundBlockConfirmations == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].inboundBlockConfirmations); + } + return abi.encode(uaConfig.inboundBlockConfirmations); + } else if (_configType == CONFIG_TYPE_RELAYER) { + if (uaConfig.relayer == address(0x0)) { + return abi.encode(defaultAppConfig[_remoteChainId].relayer); + } + return abi.encode(uaConfig.relayer); + } else if (_configType == CONFIG_TYPE_OUTBOUND_PROOF_TYPE) { + if (uaConfig.outboundProofType == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].outboundProofType); + } + return abi.encode(uaConfig.outboundProofType); + } else if (_configType == CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS) { + if (uaConfig.outboundBlockConfirmations == 0) { + return abi.encode(defaultAppConfig[_remoteChainId].outboundBlockConfirmations); + } + return abi.encode(uaConfig.outboundBlockConfirmations); + } else if (_configType == CONFIG_TYPE_ORACLE) { + if (uaConfig.oracle == address(0x0)) { + return abi.encode(defaultAppConfig[_remoteChainId].oracle); + } + return abi.encode(uaConfig.oracle); + } else { + revert("LayerZero: Invalid config type"); + } + } + + // returns the native fee the UA pays to cover fees + function estimateFees( + uint16 _dstChainId, + address _ua, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParams + ) external view override returns (uint nativeFee, uint zroFee) { + ApplicationConfiguration memory uaConfig = _getAppConfig(_dstChainId, _ua); + + // Relayer Fee + bytes memory adapterParams; + if (_adapterParams.length > 0) { + adapterParams = _adapterParams; + } else { + adapterParams = defaultAdapterParams[_dstChainId][uaConfig.outboundProofType]; + } + uint relayerFee = ILayerZeroRelayerV2(uaConfig.relayer).getFee( + _dstChainId, + uaConfig.outboundProofType, + _ua, + _payload.length, + adapterParams + ); + + // Oracle Fee + address ua = _ua; // stack too deep + uint oracleFee = ILayerZeroOracleV2(uaConfig.oracle).getFee( + _dstChainId, + uaConfig.outboundProofType, + uaConfig.outboundBlockConfirmations, + ua + ); + + // LayerZero Fee + uint protocolFee = treasuryContract.getFees(_payInZRO, relayerFee, oracleFee); + _payInZRO ? zroFee = protocolFee : nativeFee = protocolFee; + + // return the sum of fees + nativeFee = nativeFee.add(relayerFee).add(oracleFee); + } + + //--------------------------------------------------------------------------- + // Claim Fees + + // universal withdraw ZRO token function + function withdrawZRO(address _to, uint _amount) external override nonReentrant { + require(msg.sender == address(treasuryContract), "LayerZero: only treasury"); + treasuryZROFees = treasuryZROFees.sub(_amount); + layerZeroToken.safeTransfer(_to, _amount); + emit WithdrawZRO(msg.sender, _to, _amount); + } + + // universal withdraw native token function. + // the source contract should perform all the authentication control + function withdrawNative(address payable _to, uint _amount) external override nonReentrant { + require(_to != address(0x0), "LayerZero: _to cannot be zero address"); + nativeFees[msg.sender] = nativeFees[msg.sender].sub(_amount); + + (bool success, ) = _to.call{value: _amount}(""); + require(success, "LayerZero: withdraw failed"); + emit WithdrawNative(msg.sender, _to, _amount); + } + + //--------------------------------------------------------------------------- + // Owner calls, configuration only. + function setLayerZeroToken(address _layerZeroToken) external onlyOwner { + require(_layerZeroToken != address(0x0), "LayerZero: _layerZeroToken cannot be zero address"); + layerZeroToken = IERC20(_layerZeroToken); + emit SetLayerZeroToken(_layerZeroToken); + } + + function setTreasury(address _treasury) external onlyOwner { + require(_treasury != address(0x0), "LayerZero: treasury cannot be zero address"); + treasuryContract = ILayerZeroTreasury(_treasury); + emit SetTreasury(_treasury); + } + + function addInboundProofLibraryForChain(uint16 _chainId, address _library) external onlyOwner { + require(_library != address(0x0), "LayerZero: library cannot be zero address"); + uint16 libId = maxInboundProofLibrary[_chainId]; + require(libId < 65535, "LayerZero: can not add new library"); + maxInboundProofLibrary[_chainId] = ++libId; + inboundProofLibrary[_chainId][libId] = _library; + emit AddInboundProofLibraryForChain(_chainId, _library); + } + + function enableSupportedOutboundProof(uint16 _chainId, uint16 _proofType) external onlyOwner { + supportedOutboundProof[_chainId][_proofType] = true; + emit EnableSupportedOutboundProof(_chainId, _proofType); + } + + function setDefaultConfigForChainId( + uint16 _chainId, + uint16 _inboundProofLibraryVersion, + uint64 _inboundBlockConfirmations, + address _relayer, + uint16 _outboundProofType, + uint64 _outboundBlockConfirmations, + address _oracle + ) external onlyOwner { + require( + _inboundProofLibraryVersion <= maxInboundProofLibrary[_chainId] && _inboundProofLibraryVersion > 0, + "LayerZero: invalid inbound proof library version" + ); + require(_inboundBlockConfirmations > 0, "LayerZero: invalid inbound block confirmation"); + require(_relayer != address(0x0), "LayerZero: invalid relayer address"); + require(supportedOutboundProof[_chainId][_outboundProofType], "LayerZero: invalid outbound proof type"); + require(_outboundBlockConfirmations > 0, "LayerZero: invalid outbound block confirmation"); + require(_oracle != address(0x0), "LayerZero: invalid oracle address"); + defaultAppConfig[_chainId] = ApplicationConfiguration( + _inboundProofLibraryVersion, + _inboundBlockConfirmations, + _relayer, + _outboundProofType, + _outboundBlockConfirmations, + _oracle + ); + emit SetDefaultConfigForChainId( + _chainId, + _inboundProofLibraryVersion, + _inboundBlockConfirmations, + _relayer, + _outboundProofType, + _outboundBlockConfirmations, + _oracle + ); + } + + function setDefaultAdapterParamsForChainId( + uint16 _chainId, + uint16 _proofType, + bytes calldata _adapterParams + ) external onlyOwner { + defaultAdapterParams[_chainId][_proofType] = _adapterParams; + emit SetDefaultAdapterParamsForChainId(_chainId, _proofType, _adapterParams); + } + + function setRemoteUln(uint16 _remoteChainId, bytes32 _remoteUln) external onlyOwner { + require(ulnLookup[_remoteChainId] == bytes32(0), "LayerZero: remote uln already set"); + ulnLookup[_remoteChainId] = _remoteUln; + emit SetRemoteUln(_remoteChainId, _remoteUln); + } + + function setChainAddressSize(uint16 _chainId, uint _size) external onlyOwner { + require(chainAddressSizeMap[_chainId] == 0, "LayerZero: remote chain address size already set"); + chainAddressSizeMap[_chainId] = _size; + emit SetChainAddressSize(_chainId, _size); + } + + //---------------------------------------------------------------------------------- + // view functions + + function accruedNativeFee(address _address) external view override returns (uint) { + return nativeFees[_address]; + } + + function getOutboundNonce(uint16 _chainId, bytes calldata _path) external view override returns (uint64) { + return nonceContract.outboundNonce(_chainId, _path); + } + + function _isContract(address addr) internal view returns (bool) { + uint size; + assembly { + size := extcodesize(addr) + } + return size != 0; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/arbitrum/interfaces/ArbSys.sol b/endpoint/contracts/V1Contracts/contracts/arbitrum/interfaces/ArbSys.sol new file mode 100644 index 0000000..ffd9dd9 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/arbitrum/interfaces/ArbSys.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.4.21 <0.9.0; + +/** + * @title Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. Exposes a variety of system-level functionality. + */ +interface ArbSys { + /** + * @notice Get internal version number identifying an ArbOS build + * @return version number as int + */ + function arbOSVersion() external pure returns (uint); + + function arbChainID() external view returns (uint); + + /** + * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) + * @return block number as int + */ + function arbBlockNumber() external view returns (uint); + + /** + * @notice Send given amount of Eth to dest from sender. + * This is a convenience function, which is equivalent to calling sendTxToL1 with empty calldataForL1. + * @param destination recipient address on L1 + * @return unique identifier for this L2-to-L1 transaction. + */ + function withdrawEth(address destination) external payable returns (uint); + + /** + * @notice Send a transaction to L1 + * @param destination recipient address on L1 + * @param calldataForL1 (optional) calldata for L1 contract call + * @return a unique identifier for this L2-to-L1 transaction. + */ + function sendTxToL1(address destination, bytes calldata calldataForL1) external payable returns (uint); + + /** + * @notice get the number of transactions issued by the given external account or the account sequence number of the given contract + * @param account target account + * @return the number of transactions issued by the given external account or the account sequence number of the given contract + */ + function getTransactionCount(address account) external view returns (uint256); + + /** + * @notice get the value of target L2 storage slot + * This function is only callable from address 0 to prevent contracts from being able to call it + * @param account target account + * @param index target index of storage slot + * @return stotage value for the given account at the given index + */ + function getStorageAt(address account, uint256 index) external view returns (uint256); + + /** + * @notice check if current call is coming from l1 + * @return true if the caller of this was called directly from L1 + */ + function isTopLevelCall() external view returns (bool); + + /** + * @notice check if the caller (of this caller of this) is an aliased L1 contract address + * @return true iff the caller's address is an alias for an L1 contract address + */ + function wasMyCallersAddressAliased() external view returns (bool); + + /** + * @notice return the address of the caller (of this caller of this), without applying L1 contract address aliasing + * @return address of the caller's caller, without applying L1 contract address aliasing + */ + function myCallersAddressWithoutAliasing() external view returns (address); + + /** + * @notice map L1 sender contract address to its L2 alias + * @param sender sender address + * @param dest destination address + * @return aliased sender address + */ + function mapL1SenderContractAddressToL2Alias(address sender, address dest) external pure returns (address); + + /** + * @notice get the caller's amount of available storage gas + * @return amount of storage gas available to the caller + */ + function getStorageGasAvailable() external view returns (uint); + + event L2ToL1Transaction( + address caller, + address indexed destination, + uint indexed uniqueId, + uint indexed batchNumber, + uint indexInBatch, + uint arbBlockNum, + uint ethBlockNum, + uint timestamp, + uint callvalue, + bytes data + ); +} diff --git a/endpoint/contracts/V1Contracts/contracts/example/AltTokenUA.sol b/endpoint/contracts/V1Contracts/contracts/example/AltTokenUA.sol new file mode 100644 index 0000000..e019e70 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/example/AltTokenUA.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "../interfaces/ILayerZeroEndpoint.sol"; +import "../interfaces/ILayerZeroReceiver.sol"; + +// An example UA running on Shrapnel using ERC20 as payment token +// To demonstrate how feeToken payment needs to be atomic with layerzero send +contract AltTokenUA is ILayerZeroReceiver { + ILayerZeroEndpoint public endpoint; + IERC20 public feeToken; + address public feeHandler; + + constructor(address _endpoint, address _feeToken, address _feeHandler) { + endpoint = ILayerZeroEndpoint(_endpoint); + feeToken = IERC20(_feeToken); + feeHandler = _feeHandler; + } + + function send( + uint16 _dstChainId, + address _dstAddress, + bytes memory _payload, + bytes memory _adapterParams, + uint _fee + ) public payable { + feeToken.transferFrom(msg.sender, feeHandler, _fee); + + bytes memory path = abi.encodePacked(_dstAddress, address(this)); + endpoint.send(_dstChainId, path, _payload, msg.sender, address(0), _adapterParams); + } + + function lzReceive( + uint16 _srcChainId, + bytes memory _fromAddress, + uint64 /*_nonce*/, + bytes memory _payload + ) external virtual override { + //do nothing + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/IContractOne.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/IContractOne.sol new file mode 100644 index 0000000..d7fc6ab --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/IContractOne.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.5.0; + +import "./ILayerZeroUserApplicationConfig.sol"; + +interface IContractOne { + function setIt(uint x) external; +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroEndpoint.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroEndpoint.sol new file mode 100644 index 0000000..05dec92 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroEndpoint.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.5.0; + +import "./ILayerZeroUserApplicationConfig.sol"; + +interface ILayerZeroEndpoint is ILayerZeroUserApplicationConfig { + // @notice send a LayerZero message to the specified address at a LayerZero endpoint. + // @param _dstChainId - the destination chain identifier + // @param _destination - the address on destination chain (in bytes). address length/format may vary by chains + // @param _payload - a custom bytes payload to send to the destination contract + // @param _refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address + // @param _zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction + // @param _adapterParams - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination + function send( + uint16 _dstChainId, + bytes calldata _destination, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable; + + // @notice used by the messaging library to publish verified payload + // @param _srcChainId - the source chain identifier + // @param _srcAddress - the source contract (as bytes) at the source chain + // @param _dstAddress - the address on destination chain + // @param _nonce - the unbound message ordering nonce + // @param _gasLimit - the gas limit for external contract execution + // @param _payload - verified payload to send to the destination contract + function receivePayload( + uint16 _srcChainId, + bytes calldata _srcAddress, + address _dstAddress, + uint64 _nonce, + uint _gasLimit, + bytes calldata _payload + ) external; + + // @notice get the inboundNonce of a receiver from a source chain which could be EVM or non-EVM chain + // @param _srcChainId - the source chain identifier + // @param _srcAddress - the source chain contract address + function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (uint64); + + // @notice get the outboundNonce from this source chain which, consequently, is always an EVM + // @param _srcAddress - the source chain contract address + function getOutboundNonce(uint16 _dstChainId, address _srcAddress) external view returns (uint64); + + // @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery + // @param _dstChainId - the destination chain identifier + // @param _userApplication - the user app address on this EVM chain + // @param _payload - the custom message to send over LayerZero + // @param _payInZRO - if false, user app pays the protocol fee in native token + // @param _adapterParam - parameters for the adapter service, e.g. send some dust native token to dstChain + function estimateFees( + uint16 _dstChainId, + address _userApplication, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParam + ) external view returns (uint nativeFee, uint zroFee); + + // @notice get this Endpoint's immutable source identifier + function getChainId() external view returns (uint16); + + // @notice the interface to retry failed message on this Endpoint destination + // @param _srcChainId - the source chain identifier + // @param _srcAddress - the source chain contract address + // @param _payload - the payload to be retried + function retryPayload(uint16 _srcChainId, bytes calldata _srcAddress, bytes calldata _payload) external; + + // @notice query if any STORED payload (message blocking) at the endpoint. + // @param _srcChainId - the source chain identifier + // @param _srcAddress - the source chain contract address + function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool); + + // @notice query if the _libraryAddress is valid for sending msgs. + // @param _userApplication - the user app address on this EVM chain + function getSendLibraryAddress(address _userApplication) external view returns (address); + + // @notice query if the _libraryAddress is valid for receiving msgs. + // @param _userApplication - the user app address on this EVM chain + function getReceiveLibraryAddress(address _userApplication) external view returns (address); + + // @notice query if the non-reentrancy guard for send() is on + // @return true if the guard is on. false otherwise + function isSendingPayload() external view returns (bool); + + // @notice query if the non-reentrancy guard for receive() is on + // @return true if the guard is on. false otherwise + function isReceivingPayload() external view returns (bool); + + // @notice get the configuration of the LayerZero messaging library of the specified version + // @param _version - messaging library version + // @param _chainId - the chainId for the pending config change + // @param _userApplication - the contract address of the user application + // @param _configType - type of configuration. every messaging library has its own convention. + function getConfig( + uint16 _version, + uint16 _chainId, + address _userApplication, + uint _configType + ) external view returns (bytes memory); + + // @notice get the send() LayerZero messaging library version + // @param _userApplication - the contract address of the user application + function getSendVersion(address _userApplication) external view returns (uint16); + + // @notice get the lzReceive() LayerZero messaging library version + // @param _userApplication - the contract address of the user application + function getReceiveVersion(address _userApplication) external view returns (uint16); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroMessagingLibrary.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroMessagingLibrary.sol new file mode 100644 index 0000000..db661e1 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroMessagingLibrary.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; + +import "./ILayerZeroUserApplicationConfig.sol"; + +interface ILayerZeroMessagingLibrary { + // send(), messages will be inflight. + function send( + address _userApplication, + uint64 _lastNonce, + uint16 _chainId, + bytes calldata _destination, + bytes calldata _payload, + address payable refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable; + + // estimate native fee at the send side + function estimateFees( + uint16 _chainId, + address _userApplication, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParam + ) external view returns (uint nativeFee, uint zroFee); + + //--------------------------------------------------------------------------- + // setConfig / getConfig are User Application (UA) functions to specify Oracle, Relayer, blockConfirmations, libraryVersion + function setConfig(uint16 _chainId, address _userApplication, uint _configType, bytes calldata _config) external; + + function getConfig( + uint16 _chainId, + address _userApplication, + uint _configType + ) external view returns (bytes memory); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroMessagingLibraryV2.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroMessagingLibraryV2.sol new file mode 100644 index 0000000..75f748f --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroMessagingLibraryV2.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; + +import "./ILayerZeroUserApplicationConfig.sol"; +import "./ILayerZeroMessagingLibrary.sol"; + +interface ILayerZeroMessagingLibraryV2 is ILayerZeroMessagingLibrary { + function getOutboundNonce(uint16 _chainId, bytes calldata _path) external view returns (uint64); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroOracle.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroOracle.sol new file mode 100644 index 0000000..98512fb --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroOracle.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; + +interface ILayerZeroOracle { + // @notice query the oracle price for relaying block information to the destination chain + // @param _dstChainId the destination endpoint identifier + // @param _outboundProofType the proof type identifier to specify the data to be relayed + function getPrice(uint16 _dstChainId, uint16 _outboundProofType) external view returns (uint price); + + // @notice Ultra-Light Node notifies the Oracle of a new block information relaying request + // @param _dstChainId the destination endpoint identifier + // @param _outboundProofType the proof type identifier to specify the data to be relayed + // @param _outboundBlockConfirmations the number of source chain block confirmation needed + function notifyOracle(uint16 _dstChainId, uint16 _outboundProofType, uint64 _outboundBlockConfirmations) external; + + // @notice query if the address is an approved actor for privileges like data submission and fee withdrawal etc. + // @param _address the address to be checked + function isApproved(address _address) external view returns (bool approved); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroOracleV2.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroOracleV2.sol new file mode 100644 index 0000000..3d26f8d --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroOracleV2.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; + +interface ILayerZeroOracleV2 { + // @notice query price and assign jobs at the same time + // @param _dstChainId - the destination endpoint identifier + // @param _outboundProofType - the proof type identifier to specify proof to be relayed + // @param _outboundBlockConfirmation - block confirmation delay before relaying blocks + // @param _userApplication - the source sending contract address + function assignJob( + uint16 _dstChainId, + uint16 _outboundProofType, + uint64 _outboundBlockConfirmation, + address _userApplication + ) external returns (uint price); + + // @notice query the oracle price for relaying block information to the destination chain + // @param _dstChainId the destination endpoint identifier + // @param _outboundProofType the proof type identifier to specify the data to be relayed + // @param _outboundBlockConfirmation - block confirmation delay before relaying blocks + // @param _userApplication - the source sending contract address + function getFee( + uint16 _dstChainId, + uint16 _outboundProofType, + uint64 _outboundBlockConfirmation, + address _userApplication + ) external view returns (uint price); + + // @notice withdraw the accrued fee in ultra light node + // @param _to - the fee receiver + // @param _amount - the withdrawal amount + function withdrawFee(address payable _to, uint _amount) external; +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroPriceFeed.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroPriceFeed.sol new file mode 100644 index 0000000..21f3371 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroPriceFeed.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +interface ILayerZeroPriceFeed { + /** + * @dev + * priceRatio: (USD price of 1 unit of remote native token in unit of local native token) * PRICE_RATIO_DENOMINATOR + */ + + struct Price { + uint128 priceRatio; // float value * 10 ^ 10, decimal awared. for aptos to evm, the basis would be (10^18 / 10^8) * 10 ^10 = 10 ^ 20. + uint64 gasPriceInUnit; // for evm, it is in wei, for aptos, it is in octas. + uint32 gasPerByte; + } + + struct UpdatePrice { + uint16 chainId; + Price price; + } + + /** + * @dev + * ArbGasInfo.go:GetPricesInArbGas + * + */ + struct ArbitrumPriceExt { + uint64 gasPerL2Tx; // L2 overhead + uint32 gasPerL1CallDataByte; + } + + struct UpdatePriceExt { + uint16 chainId; + Price price; + ArbitrumPriceExt extend; + } + + function getPrice(uint16 _dstChainId) external view returns (Price memory); + + function getPriceRatioDenominator() external view returns (uint128); + + function estimateFeeByChain( + uint16 _dstChainId, + uint _callDataSize, + uint _gas + ) external view returns (uint fee, uint128 priceRatio); + + function nativeTokenPriceUSD() external view returns (uint128); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroPriceFeedV2.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroPriceFeedV2.sol new file mode 100644 index 0000000..9a8394b --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroPriceFeedV2.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +// copy of "@layerzerolabs/lz-evm-messagelib-v2/contracts/interfaces/ILayerZeroPriceFeed.sol" +interface ILayerZeroPriceFeedV2 { + /** + * @dev + * priceRatio: (USD price of 1 unit of remote native token in unit of local native token) * PRICE_RATIO_DENOMINATOR + */ + + struct Price { + uint128 priceRatio; // float value * 10 ^ 20, decimal awared. for aptos to evm, the basis would be (10^18 / 10^8) * 10 ^20 = 10 ^ 30. + uint64 gasPriceInUnit; // for evm, it is in wei, for aptos, it is in octas. + uint32 gasPerByte; + } + + struct UpdatePrice { + uint32 eid; + Price price; + } + + /** + * @dev + * ArbGasInfo.go:GetPricesInArbGas + * + */ + struct ArbitrumPriceExt { + uint64 gasPerL2Tx; // L2 overhead + uint32 gasPerL1CallDataByte; + } + + struct UpdatePriceExt { + uint32 eid; + Price price; + ArbitrumPriceExt extend; + } + + function getPrice(uint32 _dstEid) external view returns (Price memory); + + function getPriceRatioDenominator() external view returns (uint128); + + function estimateFeeByEid( + uint32 _dstEid, + uint _callDataSize, + uint _gas + ) external view returns (uint fee, uint128 priceRatio, uint128 priceRatioDenominator, uint128 nativePriceUSD); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroReceiver.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroReceiver.sol new file mode 100644 index 0000000..51481d3 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroReceiver.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.5.0; + +interface ILayerZeroReceiver { + // @notice LayerZero endpoint will invoke this function to deliver the message on the destination + // @param _srcChainId - the source endpoint identifier + // @param _srcAddress - the source sending contract address from the source chain + // @param _nonce - the ordered message nonce + // @param _payload - the signed payload is the UA bytes has encoded to be sent + function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external; +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayer.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayer.sol new file mode 100644 index 0000000..c0169d0 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayer.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; + +interface ILayerZeroRelayer { + // @notice query the relayer price for relaying the payload and its proof to the destination chain + // @param _dstChainId - the destination endpoint identifier + // @param _outboundProofType - the proof type identifier to specify proof to be relayed + // @param _userApplication - the source sending contract address. relayers may apply price discrimination to user apps + // @param _payloadSize - the length of the payload. it is an indicator of gas usage for relaying cross-chain messages + // @param _adapterParams - optional parameters for extra service plugins, e.g. sending dust tokens at the destination chain + function getPrice( + uint16 _dstChainId, + uint16 _outboundProofType, + address _userApplication, + uint _payloadSize, + bytes calldata _adapterParams + ) external view returns (uint price); + + // @notice Ultra-Light Node notifies the Oracle of a new block information relaying request + // @param _dstChainId - the destination endpoint identifier + // @param _outboundProofType - the proof type identifier to specify the data to be relayed + // @param _adapterParams - optional parameters for extra service plugins, e.g. sending dust tokens at the destination chain + function notifyRelayer(uint16 _dstChainId, uint16 _outboundProofType, bytes calldata _adapterParams) external; + + // @notice query if the address is an approved actor for privileges like data submission and fee withdrawal etc. + // @param _address - the address to be checked + function isApproved(address _address) external view returns (bool approved); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayerV2.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayerV2.sol new file mode 100644 index 0000000..e6bfab4 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayerV2.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; + +interface ILayerZeroRelayerV2 { + // @notice query price and assign jobs at the same time + // @param _dstChainId - the destination endpoint identifier + // @param _outboundProofType - the proof type identifier to specify proof to be relayed + // @param _userApplication - the source sending contract address. relayers may apply price discrimination to user apps + // @param _payloadSize - the length of the payload. it is an indicator of gas usage for relaying cross-chain messages + // @param _adapterParams - optional parameters for extra service plugins, e.g. sending dust tokens at the destination chain + function assignJob( + uint16 _dstChainId, + uint16 _outboundProofType, + address _userApplication, + uint _payloadSize, + bytes calldata _adapterParams + ) external returns (uint price); + + // @notice query the relayer price for relaying the payload and its proof to the destination chain + // @param _dstChainId - the destination endpoint identifier + // @param _outboundProofType - the proof type identifier to specify proof to be relayed + // @param _userApplication - the source sending contract address. relayers may apply price discrimination to user apps + // @param _payloadSize - the length of the payload. it is an indicator of gas usage for relaying cross-chain messages + // @param _adapterParams - optional parameters for extra service plugins, e.g. sending dust tokens at the destination chain + function getFee( + uint16 _dstChainId, + uint16 _outboundProofType, + address _userApplication, + uint _payloadSize, + bytes calldata _adapterParams + ) external view returns (uint price); + + // @notice withdraw the accrued fee in ultra light node + // @param _to - the fee receiver + // @param _amount - the withdrawal amount + function withdrawFee(address payable _to, uint _amount) external; +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayerV2PriceData.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayerV2PriceData.sol new file mode 100644 index 0000000..51e595a --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroRelayerV2PriceData.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; + +pragma abicoder v2; + +interface ILayerZeroRelayerV2PriceData { + struct DstPrice { + uint128 dstPriceRatio; // 10^10 + uint128 dstGasPriceInWei; + } + + struct DstConfig { + uint128 dstNativeAmtCap; + uint64 baseGas; + uint64 gasPerByte; + } + + function dstPriceLookup(uint16 _chainId) external view returns (DstPrice memory); + + function dstConfigLookup(uint16 _chainId, uint16 _outboundProofType) external view returns (DstConfig memory); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroTreasury.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroTreasury.sol new file mode 100644 index 0000000..81c5e54 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroTreasury.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.5.0; + +interface ILayerZeroTreasury { + function getFees(bool payInZro, uint relayerFee, uint oracleFee) external view returns (uint); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUltraLightNodeV1.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUltraLightNodeV1.sol new file mode 100644 index 0000000..c646256 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUltraLightNodeV1.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; + +interface ILayerZeroUltraLightNodeV1 { + // a Relayer can execute the validateTransactionProof() + function validateTransactionProof( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _lookupHash, + bytes calldata _transactionProof + ) external; + + // an Oracle delivers the block data using updateHash() + function updateHash(uint16 _remoteChainId, bytes32 _lookupHash, uint _confirmations, bytes32 _data) external; + + // can only withdraw the receivable of the msg.sender + function withdrawNative(uint8 _type, address _owner, address payable _to, uint _amount) external; + + function withdrawZRO(address _to, uint _amount) external; + + // view functions + function oracleQuotedAmount(address _oracle) external view returns (uint); + + function relayerQuotedAmount(address _relayer) external view returns (uint); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUltraLightNodeV2.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUltraLightNodeV2.sol new file mode 100644 index 0000000..35720e5 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUltraLightNodeV2.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +interface ILayerZeroUltraLightNodeV2 { + // Relayer functions + function validateTransactionProof( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _lookupHash, + bytes32 _blockData, + bytes calldata _transactionProof + ) external; + + // an Oracle delivers the block data using updateHash() + function updateHash(uint16 _srcChainId, bytes32 _lookupHash, uint _confirmations, bytes32 _blockData) external; + + // can only withdraw the receivable of the msg.sender + function withdrawNative(address payable _to, uint _amount) external; + + function withdrawZRO(address _to, uint _amount) external; + + // view functions + function getAppConfig( + uint16 _remoteChainId, + address _userApplicationAddress + ) external view returns (ApplicationConfiguration memory); + + function accruedNativeFee(address _address) external view returns (uint); + + struct ApplicationConfiguration { + uint16 inboundProofLibraryVersion; + uint64 inboundBlockConfirmations; + address relayer; + uint16 outboundProofType; + uint64 outboundBlockConfirmations; + address oracle; + } + + event HashReceived( + uint16 indexed srcChainId, + address indexed oracle, + bytes32 lookupHash, + bytes32 blockData, + uint confirmations + ); + event RelayerParams(bytes adapterParams, uint16 outboundProofType); + event Packet(bytes payload); + event InvalidDst( + uint16 indexed srcChainId, + bytes srcAddress, + address indexed dstAddress, + uint64 nonce, + bytes32 payloadHash + ); + event PacketReceived( + uint16 indexed srcChainId, + bytes srcAddress, + address indexed dstAddress, + uint64 nonce, + bytes32 payloadHash + ); + event AppConfigUpdated(address indexed userApplication, uint indexed configType, bytes newConfig); + event AddInboundProofLibraryForChain(uint16 indexed chainId, address lib); + event EnableSupportedOutboundProof(uint16 indexed chainId, uint16 proofType); + event SetChainAddressSize(uint16 indexed chainId, uint size); + event SetDefaultConfigForChainId( + uint16 indexed chainId, + uint16 inboundProofLib, + uint64 inboundBlockConfirm, + address relayer, + uint16 outboundProofType, + uint64 outboundBlockConfirm, + address oracle + ); + event SetDefaultAdapterParamsForChainId(uint16 indexed chainId, uint16 indexed proofType, bytes adapterParams); + event SetLayerZeroToken(address indexed tokenAddress); + event SetRemoteUln(uint16 indexed chainId, bytes32 uln); + event SetTreasury(address indexed treasuryAddress); + event WithdrawZRO(address indexed msgSender, address indexed to, uint amount); + event WithdrawNative(address indexed msgSender, address indexed to, uint amount); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUserApplicationConfig.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUserApplicationConfig.sol new file mode 100644 index 0000000..a7a12c2 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroUserApplicationConfig.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.5.0; + +interface ILayerZeroUserApplicationConfig { + // @notice set the configuration of the LayerZero messaging library of the specified version + // @param _version - messaging library version + // @param _chainId - the chainId for the pending config change + // @param _configType - type of configuration. every messaging library has its own convention. + // @param _config - configuration in the bytes. can encode arbitrary content. + function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external; + + // @notice set the send() LayerZero messaging library version to _version + // @param _version - new messaging library version + function setSendVersion(uint16 _version) external; + + // @notice set the lzReceive() LayerZero messaging library version to _version + // @param _version - new messaging library version + function setReceiveVersion(uint16 _version) external; + + // @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload + // @param _srcChainId - the chainId of the source chain + // @param _srcAddress - the contract address of the source contract at the source chain + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external; +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroValidationLibrary.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroValidationLibrary.sol new file mode 100644 index 0000000..8bc01a7 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/ILayerZeroValidationLibrary.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +import "../proof/utility/LayerZeroPacket.sol"; + +interface ILayerZeroValidationLibrary { + function validateProof( + bytes32 blockData, + bytes calldata _data, + uint _remoteAddressSize + ) external returns (LayerZeroPacket.Packet memory packet); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/IValidationLibraryHelper.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/IValidationLibraryHelper.sol new file mode 100644 index 0000000..739d2e8 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/IValidationLibraryHelper.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +import "../proof/utility/LayerZeroPacket.sol"; +import "./ILayerZeroValidationLibrary.sol"; + +interface IValidationLibraryHelper { + struct ULNLog { + bytes32 contractAddress; + bytes32 topicZeroSig; + bytes data; + } + + function getVerifyLog( + bytes32 hashRoot, + uint[] memory receiptSlotIndex, + uint logIndex, + bytes[] memory proof + ) external pure returns (ULNLog memory); + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure returns (LayerZeroPacket.Packet memory); + + function getUtilsVersion() external view returns (uint8); +} diff --git a/endpoint/contracts/V1Contracts/contracts/interfaces/IValidationLibraryHelperV2.sol b/endpoint/contracts/V1Contracts/contracts/interfaces/IValidationLibraryHelperV2.sol new file mode 100644 index 0000000..67d119c --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/interfaces/IValidationLibraryHelperV2.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +import "../proof/utility/LayerZeroPacket.sol"; + +interface IValidationLibraryHelperV2 { + struct ULNLog { + bytes32 contractAddress; + bytes32 topicZeroSig; + bytes data; + } + + function getVerifyLog( + bytes32 hashRoot, + uint[] calldata receiptSlotIndex, + uint logIndex, + bytes[] calldata proof + ) external pure returns (ULNLog memory); + + function getPacket( + bytes calldata data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure returns (LayerZeroPacket.Packet memory); + + function getUtilsVersion() external view returns (uint8); + + function getProofType() external view returns (uint8); +} diff --git a/endpoint/contracts/V1Contracts/contracts/libs/RateLimiter.sol b/endpoint/contracts/V1Contracts/contracts/libs/RateLimiter.sol new file mode 100644 index 0000000..e4ae59d --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/libs/RateLimiter.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BUSL-1.1 +// Copyright 2023 LayerZero Labs Ltd. +// You may obtain a copy of the License at +// https://github.com/LayerZero-Labs/license/blob/main/LICENSE-LZBL-1.1 + +pragma solidity ^0.7.6; + +library RateLimiter { + struct Info { + // capacity of the token bucket. This is the maximum number of tokens that the bucket can hold at any given time + uint64 capacity; + // current number of tokens in the bucket + uint64 tokens; + // number of tokens refilled per second + uint64 rate; + // timestamp of last refill + uint64 lastRefillTime; + } + + function setCapacity(Info storage _self, uint64 _capacity) internal { + _self.capacity = _capacity; + _self.tokens = _capacity; + _self.lastRefillTime = uint64(block.timestamp); + } + + function setRate(Info storage _self, uint64 _rate) internal { + refill(_self, 0); + _self.rate = _rate; + } + + function tryConsume(Info storage _self, uint64 _amount) internal returns (uint64) { + refill(_self, 0); + + uint64 tokens = _self.tokens; + require(tokens >= _amount, "RelayerV2: out of counters - try again later!"); + + uint64 newTokens = tokens - _amount; + _self.tokens = newTokens; + return newTokens; + } + + function refill(Info storage _self, uint64 _extraTokens) internal { + uint newTokens = _extraTokens; + + uint64 currentTime = uint64(block.timestamp); + if (currentTime > _self.lastRefillTime) { + uint timeElapsedInSeconds = currentTime - _self.lastRefillTime; + newTokens += timeElapsedInSeconds * _self.rate; + } + + if (newTokens > 0) { + newTokens += _self.tokens; + _self.tokens = newTokens > _self.capacity ? _self.capacity : uint64(newTokens); + } + + _self.lastRefillTime = currentTime; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/ContractOne.sol b/endpoint/contracts/V1Contracts/contracts/mocks/ContractOne.sol new file mode 100644 index 0000000..4563dff --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/ContractOne.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; + +contract ContractOne { + uint x; + + function setIt(uint _x) external { + x = _x; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/ContractTwo.sol b/endpoint/contracts/V1Contracts/contracts/mocks/ContractTwo.sol new file mode 100644 index 0000000..afb0293 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/ContractTwo.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; + +import "../interfaces/IContractOne.sol"; + +contract ContractTwo { + address contractOne; + + constructor(address _contractOne) { + contractOne = _contractOne; + } + + function callSetIt(uint _gasLimit) external { + IContractOne(contractOne).setIt{gas: _gasLimit}(1); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/GIN.sol b/endpoint/contracts/V1Contracts/contracts/mocks/GIN.sol new file mode 100644 index 0000000..8445dae --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/GIN.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; +pragma abicoder v2; +import "../interfaces/ILayerZeroReceiver.sol"; +import "../interfaces/ILayerZeroEndpoint.sol"; + +contract GIN is ILayerZeroReceiver { + bool public messageComplete; + ILayerZeroEndpoint public endpoint; + + event Message(bytes32 indexed message); + + constructor(address _endpoint) { + endpoint = ILayerZeroEndpoint(_endpoint); + } + + function lzReceive(uint16, bytes memory /*_fromAddress*/, uint64, bytes memory _payload) external override { + require(msg.sender == address(endpoint)); + bytes32 message; + assembly { + message := mload(add(_payload, 32)) + } + emit Message(message); + messageComplete = true; + } + + function sendFirstMessage( + uint gasAmountForDst, + uint16[] calldata chainIds, + bytes[] calldata dstAddresses + ) external payable { + require(!messageComplete, "The first message of LayerZero has already been sent"); + uint16 version = 1; + bytes memory _relayerParams = abi.encodePacked(version, gasAmountForDst); + + bytes32 message = "GIN"; + bytes memory messageString = bytes(abi.encodePacked(message)); + uint length = chainIds.length; + uint fee = msg.value / length; + for (uint i = 0; i < length; i++) { + endpoint.send{value: fee}( + chainIds[i], + dstAddresses[i], + messageString, + msg.sender, + address(0x0), + _relayerParams + ); + } + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/IsContract.sol b/endpoint/contracts/V1Contracts/contracts/mocks/IsContract.sol new file mode 100644 index 0000000..346331d --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/IsContract.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; + +contract IsContract { + function isAddressContract(address addr) external view returns (bool) { + // check if the toAddress is a contract. We are not concerned about addresses that pretend to be wallets. because worst case we just delete their payload if being malicious + // we can guarantee that if a size > 0, then the contract is definitely a contract address in this context + uint size; + assembly { + size := extcodesize(addr) + } + return size != 0; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleBadMock.sol b/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleBadMock.sol new file mode 100644 index 0000000..fb74561 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleBadMock.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "../interfaces/ILayerZeroOracle.sol"; +import "../interfaces/ILayerZeroUltraLightNodeV1.sol"; +import "../interfaces/ILayerZeroEndpoint.sol"; + +// mock is designed to try and send the msg it just received again to trigger a revert +contract LayerZeroOracleBadMock is ILayerZeroOracle, Ownable, ReentrancyGuard { + mapping(address => bool) public approvedAddresses; + mapping(uint16 => mapping(uint16 => uint)) public chainPriceLookup; + uint public fee; + ILayerZeroUltraLightNodeV1 public uln; // ultraLightNode instance + ILayerZeroEndpoint public endpoint; // for bad oracles trying to talk to endpoint + + event OracleNotified(uint16 dstChainId, uint16 _outboundProofType, uint blockConfirmations); + event Withdraw(address to, uint amount); + + constructor() { + approvedAddresses[msg.sender] = true; + } + + function notifyOracle( + uint16 _dstChainId, + uint16 _outboundProofType, + uint64 _outboundBlockConfirmations + ) external override { + // oracle is set to do bad things and try to call send again, this should revert + endpoint.send(_dstChainId, "0x", "0x", address(0), address(0), "0x"); + emit OracleNotified(_dstChainId, _outboundProofType, _outboundBlockConfirmations); + } + + function updateHash(uint16 _remoteChainId, bytes32 _blockHash, uint _confirmations, bytes32 _data) external { + require(approvedAddresses[msg.sender], "LayerZeroOracleMock: caller must be approved"); + uln.updateHash(_remoteChainId, _blockHash, _confirmations, _data); + } + + function withdraw(address payable _to, uint _amount) public onlyOwner nonReentrant { + (bool success, ) = _to.call{value: _amount}(""); + require(success, "failed to withdraw"); + emit Withdraw(_to, _amount); + } + + // owner can set uln + function setUln(address ulnAddress) external onlyOwner { + uln = ILayerZeroUltraLightNodeV1(ulnAddress); + } + + // owner can set uln + function setEndpoint(address endpointAddress) external onlyOwner { + endpoint = ILayerZeroEndpoint(endpointAddress); + } + + // mock, doesnt do anything + function setJob(uint16 _chain, address _oracle, bytes32 _id, uint _fee) public onlyOwner {} + + // mocked for now + function assignJob( + uint16 _dstChainId, + uint16 /*_outboundProofType*/, + uint64 /*_outboundBlockConfirmation*/, + address + ) external returns (uint price) { + // oracle is set to do bad things and try to call send again, this should revert + endpoint.send(_dstChainId, "0x", "0x", address(0), address(0), "0x"); + return 1; + } + + function setDeliveryAddress(uint16 _dstChainId, address _deliveryAddress) public onlyOwner {} + + function setPrice(uint16 _destinationChainId, uint16 _outboundProofType, uint _price) external onlyOwner { + chainPriceLookup[_outboundProofType][_destinationChainId] = _price; + } + + function setApprovedAddress(address _oracleAddress, bool _approve) external { + approvedAddresses[_oracleAddress] = _approve; + } + + function isApproved(address _relayerAddress) public view override returns (bool) { + return approvedAddresses[_relayerAddress]; + } + + function getPrice(uint16 _destinationChainId, uint16 _outboundProofType) external view override returns (uint) { + return chainPriceLookup[_outboundProofType][_destinationChainId]; + } + + fallback() external payable {} + + receive() external payable {} +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleMock.sol b/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleMock.sol new file mode 100644 index 0000000..d763cb0 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleMock.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "../interfaces/ILayerZeroOracle.sol"; +import "../interfaces/ILayerZeroUltraLightNodeV1.sol"; + +contract LayerZeroOracleMock is ILayerZeroOracle, Ownable, ReentrancyGuard { + mapping(address => bool) public approvedAddresses; + mapping(uint16 => mapping(uint16 => uint)) public chainPriceLookup; + uint public fee; + ILayerZeroUltraLightNodeV1 public uln; // ultraLightNode instance + + event OracleNotified(uint16 dstChainId, uint16 _outboundProofType, uint blockConfirmations); + event Withdraw(address to, uint amount); + + constructor() { + approvedAddresses[msg.sender] = true; + } + + function notifyOracle( + uint16 _dstChainId, + uint16 _outboundProofType, + uint64 _outboundBlockConfirmations + ) external override { + emit OracleNotified(_dstChainId, _outboundProofType, _outboundBlockConfirmations); + } + + function updateHash(uint16 _remoteChainId, bytes32 _blockHash, uint _confirmations, bytes32 _data) external { + require(approvedAddresses[msg.sender], "LayerZeroOracleMock: caller must be approved"); + uln.updateHash(_remoteChainId, _blockHash, _confirmations, _data); + } + + function withdraw(address payable _to, uint _amount) public onlyOwner nonReentrant { + (bool success, ) = _to.call{value: _amount}(""); + require(success, "failed to withdraw"); + emit Withdraw(_to, _amount); + } + + // owner can set uln + function setUln(address ulnAddress) external onlyOwner { + uln = ILayerZeroUltraLightNodeV1(ulnAddress); + } + + // mock, doesnt do anything + function setJob(uint16 _chain, address _oracle, bytes32 _id, uint _fee) public onlyOwner {} + + function setDeliveryAddress(uint16 _dstChainId, address _deliveryAddress) public onlyOwner {} + + function setPrice(uint16 _destinationChainId, uint16 _outboundProofType, uint _price) external onlyOwner { + chainPriceLookup[_outboundProofType][_destinationChainId] = _price; + } + + function setApprovedAddress(address _oracleAddress, bool _approve) external { + approvedAddresses[_oracleAddress] = _approve; + } + + function isApproved(address _relayerAddress) public view override returns (bool) { + return approvedAddresses[_relayerAddress]; + } + + function getPrice(uint16 _destinationChainId, uint16 _outboundProofType) external view override returns (uint) { + return chainPriceLookup[_outboundProofType][_destinationChainId]; + } + + fallback() external payable {} + + receive() external payable {} +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleMockV2.sol b/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleMockV2.sol new file mode 100644 index 0000000..7a266ab --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroOracleMockV2.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "../interfaces/ILayerZeroOracleV2.sol"; +import "../interfaces/ILayerZeroUltraLightNodeV2.sol"; + +contract LayerZeroOracleMockV2 is ILayerZeroOracleV2, Ownable, ReentrancyGuard { + mapping(address => bool) public approvedAddresses; + mapping(uint16 => mapping(uint16 => uint)) public chainPriceLookup; + mapping(uint16 => mapping(uint16 => uint64)) public jobs; // mocked, not used for anything yet + uint public fee; + ILayerZeroUltraLightNodeV2 public uln; // ultraLightNode instance + + event OracleNotified(uint16 dstChainId, uint16 _outboundProofType, uint blockConfirmations); + event Withdraw(address to, uint amount); + + constructor() { + approvedAddresses[msg.sender] = true; + } + + // mocked for now, will auto accept the job, and return the price at the same time + function assignJob( + uint16 _dstChainId, + uint16 _outboundProofType, + uint64 _outboundBlockConfirmation, + address + ) external override returns (uint price) { + jobs[_dstChainId][_outboundProofType] = _outboundBlockConfirmation; + return chainPriceLookup[_outboundProofType][_dstChainId]; + } + + function getFee( + uint16 _dstChainId, + uint16 _outboundProofType, + uint64 /*_outboundBlockConfirmation*/, + address + ) external view override returns (uint) { + return chainPriceLookup[_outboundProofType][_dstChainId]; + } + + function withdrawFee(address payable _to, uint _amount) public override onlyOwner nonReentrant { + (bool success, ) = _to.call{value: _amount}(""); + require(success, "failed to withdraw"); + emit Withdraw(_to, _amount); + } + + function updateHash(uint16 _remoteChainId, bytes32 _blockHash, uint _confirmations, bytes32 _data) external { + require(approvedAddresses[msg.sender], "LayerZeroOracleMock: caller must be approved"); + uln.updateHash(_remoteChainId, _blockHash, _confirmations, _data); + } + + function setUln(address ulnAddress) external onlyOwner { + uln = ILayerZeroUltraLightNodeV2(ulnAddress); + } + + function setDeliveryAddress(uint16 _dstChainId, address _deliveryAddress) public onlyOwner {} + + function setPrice(uint16 _destinationChainId, uint16 _outboundProofType, uint _price) external onlyOwner { + chainPriceLookup[_outboundProofType][_destinationChainId] = _price; + } + + function setApprovedAddress(address _oracleAddress, bool _approve) external { + approvedAddresses[_oracleAddress] = _approve; + } + + function isApproved(address _relayerAddress) public view returns (bool) { + return approvedAddresses[_relayerAddress]; + } + + fallback() external payable {} + + receive() external payable {} +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroTokenMock.sol b/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroTokenMock.sol new file mode 100644 index 0000000..434643e --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/LayerZeroTokenMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract LayerZeroTokenMock is ERC20 { + constructor() ERC20("LayerZeroTokenMock", "LZTM") { + _mint(msg.sender, 1_000_000 * 10 ** 18); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/MockToken.sol b/endpoint/contracts/V1Contracts/contracts/mocks/MockToken.sol new file mode 100644 index 0000000..779f887 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/MockToken.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +// this is a MOCK +contract MockToken is ERC20 { + // this is a MOCK + constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) { + _mint(msg.sender, 1000000000000000000000); + } + + // this is a MOCK + function mint(address _to, uint _amount) public { + _mint(_to, _amount); + } + + //Mocked to imitate what happens if a transfer fails + function transfer(address recipient, uint amount) public virtual override returns (bool) { + require(recipient != address(0x1)); + _transfer(_msgSender(), recipient, amount); + return true; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/OmniCounter.sol b/endpoint/contracts/V1Contracts/contracts/mocks/OmniCounter.sol new file mode 100644 index 0000000..669e6b2 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/OmniCounter.sol @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; +pragma abicoder v2; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "../interfaces/ILayerZeroReceiver.sol"; +import "../interfaces/ILayerZeroEndpoint.sol"; +import "../interfaces/ILayerZeroUserApplicationConfig.sol"; +import "../Relayer.sol"; + +contract OmniCounter is Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig { + using SafeMath for uint; + + // keep track of how many messages have been received from other chains + uint public messageCounter; + mapping(address => uint) public remoteAddressCounter; + // required: the LayerZero endpoint which is passed in the constructor + ILayerZeroEndpoint public endpoint; + bool public payInZRO; + + mapping(uint16 => bytes) public trustedRemoteLookup; + + constructor(address _endpoint) { + endpoint = ILayerZeroEndpoint(_endpoint); + } + + function getCounter() public view returns (uint) { + return messageCounter; + } + + // overrides lzReceive function in ILayerZeroReceiver. + // automatically invoked on the receiving chain after the source chain calls endpoint.send(...) + function lzReceive( + uint16 _srcChainId, + bytes memory _fromAddress, + uint64 /*_nonce*/, + bytes memory _payload + ) external virtual override { + require(msg.sender == address(endpoint)); + _verifySourceAddress(_srcChainId, _fromAddress); + + address fromAddress; + assembly { + fromAddress := mload(add(_fromAddress, 20)) + } + + // used for testing reentrant, retry sending the payload through the relayer before the initial receive has been resolved + // ff == '0x6666' on the payload side + if ( + keccak256(abi.encodePacked((_payload))) == keccak256(abi.encodePacked((bytes2("ff")))) || + keccak256(abi.encodePacked((_payload))) == keccak256(abi.encodePacked((bytes10("ff")))) + ) { + endpoint.receivePayload(1, bytes(""), address(0x0), 1, 1, bytes("")); + } + + remoteAddressCounter[fromAddress] += 1; + messageCounter += 1; + } + + function incrementCounter( + uint16 _dstChainId, + bytes calldata _adapterParams, + bytes calldata payload + ) public payable { + address zroPaymentAddress = payInZRO ? address(this) : address(0x0); + _incrementCounter(_dstChainId, payload, msg.sender, zroPaymentAddress, _adapterParams); + } + + // call send() to multiple destinations in the same transaction! + function multiIncrementCounter( + uint16[] calldata _dstChainIds, + bytes calldata _adapterParams, + bytes calldata payload + ) public payable { + // send() each chainId + dst address pair + uint16[] memory dstChainIds = _dstChainIds; + bytes memory adapterParams = _adapterParams; + + uint _refund = msg.value; + // send() each chainId + dst address pair + for (uint i = 0; i < dstChainIds.length; ++i) { + (uint valueToSend, ) = endpoint.estimateFees( + dstChainIds[i], + address(this), + payload, + payInZRO, + adapterParams + ); + _refund = _refund.sub(valueToSend); + // a Communicator.sol instance is the 'endpoint' + // .send() each payload to the destination chainId + UA destination address + address zroPaymentAddress = payInZRO ? address(this) : address(0x0); + _incrementCounter(_dstChainIds[i], payload, msg.sender, zroPaymentAddress, adapterParams); + } + // refund eth if too much was sent into this contract call + msg.sender.transfer(_refund); + } + + function _incrementCounter( + uint16 _dstChainId, + bytes memory _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes memory _adapterParams + ) public payable { + bytes memory trustedRemote = trustedRemoteLookup[_dstChainId]; + require(trustedRemote.length > 0, "*** trustedRemote cant be 0x "); + endpoint.send{value: msg.value}( + _dstChainId, + trustedRemote, + _payload, + _refundAddress, + _zroPaymentAddress, + _adapterParams + ); + } + + function setConfig( + uint16 /*_version*/, + uint16 _chainId, + uint _configType, + bytes calldata _config + ) external override { + endpoint.setConfig(endpoint.getSendVersion(address(this)), _chainId, _configType, _config); + } + + function getConfig(uint16, uint16 _chainId, address, uint _configType) external view returns (bytes memory) { + return endpoint.getConfig(endpoint.getSendVersion(address(this)), _chainId, address(this), _configType); + } + + function setSendVersion(uint16 version) external override { + endpoint.setSendVersion(version); + } + + function setReceiveVersion(uint16 version) external override { + endpoint.setReceiveVersion(version); + } + + function getSendVersion() external view returns (uint16) { + return endpoint.getSendVersion(address(this)); + } + + function getReceiveVersion() external view returns (uint16) { + return endpoint.getReceiveVersion(address(this)); + } + + function setOutboundBlockConfirmations(uint16 dstChainId, uint64 confirmations) external { + // should technically be onlyOwner but this is a mock + uint TYPE_OUTBOUND_BLOCK_CONFIRMATIONS = 6; + endpoint.setConfig( + endpoint.getSendVersion(address(this)), + dstChainId, + TYPE_OUTBOUND_BLOCK_CONFIRMATIONS, + abi.encodePacked(confirmations) + ); + } + + function getOutboundBlockConfirmations(uint16 remoteChainId) external view returns (bytes memory _confirmations) { + return endpoint.getConfig(endpoint.getSendVersion(address(this)), remoteChainId, address(this), 5); + } + + // set the Oracle to be used by this UA for LayerZero messages + function setOracle(uint16 dstChainId, address oracle) external { + // should technically be onlyOwner but this is a mock + uint TYPE_ORACLE = 6; // from UltraLightNode + // set the Oracle + // uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config + endpoint.setConfig(endpoint.getSendVersion(address(this)), dstChainId, TYPE_ORACLE, abi.encode(oracle)); + } + + // get the configured oracle + function getOracle(uint16 remoteChainId) external view returns (address _oracle) { + bytes memory bytesOracle = endpoint.getConfig( + endpoint.getSendVersion(address(this)), + remoteChainId, + address(this), + 6 + ); + assembly { + _oracle := mload(add(bytesOracle, 32)) + } + } + + // set the Relayer to be used by this UA for LayerZero messages + function setRelayer(uint16 dstChainId, address relayer) external { + uint TYPE_RELAYER = 3; + endpoint.setConfig(endpoint.getSendVersion(address(this)), dstChainId, TYPE_RELAYER, abi.encode(relayer)); + } + + // set the inbound block confirmations + function setInboundConfirmations(uint16 remoteChainId, uint16 confirmations) external { + endpoint.setConfig( + endpoint.getSendVersion(address(this)), + remoteChainId, + 2, // CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS + abi.encode(confirmations) + ); + } + + // set outbound block confirmations + function setOutboundConfirmations(uint16 remoteChainId, uint16 confirmations) external { + endpoint.setConfig( + endpoint.getSendVersion(address(this)), + remoteChainId, + 5, // CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS + abi.encode(confirmations) + ); + } + + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override { + // ignored for this contract + } + + function setPayInZRO(bool _payInZRO) external onlyOwner { + payInZRO = _payInZRO; + } + + function approveTokenSpender(address token, address spender, uint amount) external onlyOwner { + IERC20(token).approve(spender, amount); + } + + // allow this contract to receive ether + fallback() external payable {} + + receive() external payable { + // Mock the ability to reject payments + require( + msg.value < 1000 && msg.value != 10, + "Did you mean to send a blocked amount - check receive() / fallback()" + ); + } + + // allow owner to set it multiple times. + function setTrustedRemote(uint16 _srcChainId, bytes calldata _srcAddress) external onlyOwner { + trustedRemoteLookup[_srcChainId] = _srcAddress; + } + + function _verifySourceAddress(uint16 _srcChainId, bytes memory _fromAddress) internal view { + bytes memory trustedRemote = trustedRemoteLookup[_srcChainId]; + require( + trustedRemote.length != 0 && keccak256(_fromAddress) == keccak256(trustedRemote), + "source counter is not trusted" + ); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/PacketData.sol b/endpoint/contracts/V1Contracts/contracts/mocks/PacketData.sol new file mode 100644 index 0000000..1e6a501 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/PacketData.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; + +contract PacketData { + event Packet(uint16 chainId, bytes payload); + event Packet(bytes payload); + + function emitPacketV1( + uint64 nonce, + address ua, + uint16 dstChainId, + address dstAddress, + bytes calldata payload + ) public { + bytes memory encodedPayload = abi.encodePacked(nonce, ua, dstAddress, payload); + emit Packet(dstChainId, encodedPayload); + } + + function emitPacketV2( + uint64 nonce, + uint16 localChainId, + address ua, + uint16 dstChainId, + address dstAddress, + bytes calldata payload + ) public { + bytes memory encodedPayload = abi.encodePacked(nonce, localChainId, ua, dstChainId, dstAddress, payload); + emit Packet(encodedPayload); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/PingPong.sol b/endpoint/contracts/V1Contracts/contracts/mocks/PingPong.sol new file mode 100644 index 0000000..e14952a --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/PingPong.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: BUSL-1.1 + +// +// Note: you will need to fund each deployed contract with gas +// +// PingPong sends a LayerZero message back and forth between chains until stopped! +// +// Demonstrates: +// 1. a recursive feature of calling send() from inside lzReceive() +// 2. how to `estimateFees` for a send()'ing a LayerZero message +// 3. the contract pays the message fee + +pragma solidity ^0.7.6; +pragma abicoder v2; +import "../interfaces/ILayerZeroReceiver.sol"; +import "../interfaces/ILayerZeroEndpoint.sol"; +import "../interfaces/ILayerZeroUserApplicationConfig.sol"; + +contract PingPong is ILayerZeroReceiver, ILayerZeroUserApplicationConfig { + // the LayerZero endpoint calls .send() to send a cross chain message + ILayerZeroEndpoint public endpoint; + // whether PingPong is ping-ponging + bool public pingsEnabled; + // event emitted every ping() to keep track of consecutive pings count + event Ping(uint pings); + // the maxPings before ending the loop + uint public maxPings; + // keep track of the totalPings sent + uint public numPings; + bool public isUlnV2; + + // constructor requires the LayerZero endpoint for this chain + constructor(address _layerZeroEndpoint, bool _isUlnV2) { + pingsEnabled = true; + endpoint = ILayerZeroEndpoint(_layerZeroEndpoint); + maxPings = 5; + isUlnV2 = _isUlnV2; + } + + // disable ping-ponging + function disable() external { + pingsEnabled = false; + } + + // pings the destination chain, along with the current number of pings sent + function ping( + uint16 _dstChainId, // send a ping to this destination chainId + address _dstPingPongAddr, // destination address of PingPong contract + uint pings // the uint to start at. use 0 as a default + ) public { + require(address(this).balance > 0, "the balance of this contract is 0. pls send gas for message fees"); + require(pingsEnabled, "pingsEnabled is false. messages stopped"); + require(maxPings > pings, "maxPings has been reached, no more looping"); + + emit Ping(pings); + + // abi.encode() the payload with the number of pings sent + bytes memory payload = abi.encode(pings); + + // encode adapterParams to specify more gas for the destination + uint16 version = 1; + uint gasForDestinationLzReceive = 350000; + bytes memory adapterParams = abi.encodePacked(version, gasForDestinationLzReceive); + + // get the fees we need to pay to LayerZero + Relayer to cover message delivery + // see Communicator.sol's .estimateNativeFees() function for more details. + (uint messageFee, ) = endpoint.estimateFees(_dstChainId, address(this), payload, false, adapterParams); + require( + address(this).balance >= messageFee, + "address(this).balance < messageFee. pls send gas for message fees" + ); + + bytes memory path = isUlnV2 + ? abi.encodePacked(_dstPingPongAddr, address(this)) + : abi.encodePacked(_dstPingPongAddr); + + // send LayerZero message + endpoint.send{value: messageFee}( // {value: messageFee} will be paid out of this contract! + _dstChainId, // destination chainId + path, + payload, // abi.encode()'ed bytes + payable(this), // (msg.sender will be this contract) refund address (LayerZero will refund any extra gas back to caller of send() + address(0x0), // 'zroPaymentAddress' unused for this mock/example + adapterParams // 'adapterParams' unused for this mock/example + ); + } + + // receive the bytes payload from the source chain via LayerZero + // _srcChainId: the chainId that we are receiving the message from. + // _fromAddress: the source PingPong address + function lzReceive( + uint16 _srcChainId, + bytes memory _fromAddress, + uint64 /*_nonce*/, + bytes memory _payload + ) external override { + require(msg.sender == address(endpoint)); // boilerplate! lzReceive must be called by the endpoint for security + + // use assembly to extract the address from the bytes memory parameter + address fromAddress; + assembly { + fromAddress := mload(add(_fromAddress, 20)) + } + + // decode the number of pings sent thus far + uint pings = abi.decode(_payload, (uint)); + + // "recursively" call ping in order to *pong* (and increment pings) + ++pings; + numPings = pings; + + ping(_srcChainId, fromAddress, pings); + } + + function setConfig( + uint16 /*_version*/, + uint16 _dstChainId, + uint _configType, + bytes memory _config + ) external override { + endpoint.setConfig(_dstChainId, endpoint.getSendVersion(address(this)), _configType, _config); + } + + function getConfig( + uint16 /*_dstChainId*/, + uint16 _chainId, + address, + uint _configType + ) external view returns (bytes memory) { + return endpoint.getConfig(endpoint.getSendVersion(address(this)), _chainId, address(this), _configType); + } + + function setSendVersion(uint16 version) external override { + endpoint.setSendVersion(version); + } + + function setReceiveVersion(uint16 version) external override { + endpoint.setReceiveVersion(version); + } + + function getSendVersion() external view returns (uint16) { + return endpoint.getSendVersion(address(this)); + } + + function getReceiveVersion() external view returns (uint16) { + return endpoint.getReceiveVersion(address(this)); + } + + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override { + // do nth + } + + // allow this contract to receive ether + fallback() external payable {} + + receive() external payable {} +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/PriceFeedV2Mock.sol b/endpoint/contracts/V1Contracts/contracts/mocks/PriceFeedV2Mock.sol new file mode 100644 index 0000000..66ae97e --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/PriceFeedV2Mock.sol @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "../interfaces/ILayerZeroPriceFeedV2.sol"; + +// copy of "@layerzerolabs/lz-evm-messagelib-v2/contracts/interfaces/ILayerZeroPriceFeed.sol" +// PriceFeed is updated based on v1 eids +// v2 eids will fall to the convention of v1 eid + 30,000 +contract PriceFeedV2Mock is ILayerZeroPriceFeedV2, OwnableUpgradeable { + uint128 internal PRICE_RATIO_DENOMINATOR; + + // sets pricing + mapping(address => bool) public priceUpdater; + + mapping(uint32 => Price) public defaultModelPrice; + ArbitrumPriceExt public arbitrumPriceExt; + + uint128 public nativeTokenPriceUSD; // uses PRICE_RATIO_DENOMINATOR + + // upgrade: arbitrum compression - percentage of callDataSize after brotli compression + uint128 public ARBITRUM_COMPRESSION_PERCENT; + + // ============================ Constructor =================================== + + function initialize(address _priceUpdater) public initializer { + __Ownable_init(); + priceUpdater[_priceUpdater] = true; + PRICE_RATIO_DENOMINATOR = 1e20; + ARBITRUM_COMPRESSION_PERCENT = 47; + } + + // ============================ Modifier ====================================== + + // owner is always approved + modifier onlyPriceUpdater() { + if (owner() != msg.sender) { + require(priceUpdater[msg.sender], "PriceFeed: not price updater"); + } + _; + } + + // ============================ OnlyOwner ===================================== + + function setPriceUpdater(address _addr, bool _active) external onlyOwner { + priceUpdater[_addr] = _active; + } + + function setPriceRatioDenominator(uint128 _denominator) external onlyOwner { + PRICE_RATIO_DENOMINATOR = _denominator; + } + + function setArbitrumCompressionPercent(uint128 _compressionPercent) external onlyOwner { + ARBITRUM_COMPRESSION_PERCENT = _compressionPercent; + } + + // ============================ OnlyPriceUpdater ===================================== + + function setPrice(UpdatePrice[] calldata _price) external onlyPriceUpdater { + for (uint i = 0; i < _price.length; i++) { + UpdatePrice calldata _update = _price[i]; + _setPrice(_update.eid, _update.price); + } + } + + function setPriceForArbitrum(UpdatePriceExt calldata _update) external onlyPriceUpdater { + _setPrice(_update.eid, _update.price); + + uint64 gasPerL2Tx = _update.extend.gasPerL2Tx; + uint32 gasPerL1CalldataByte = _update.extend.gasPerL1CallDataByte; + + arbitrumPriceExt.gasPerL2Tx = gasPerL2Tx; + arbitrumPriceExt.gasPerL1CallDataByte = gasPerL1CalldataByte; + } + + function setNativeTokenPriceUSD(uint128 _nativeTokenPriceUSD) external onlyPriceUpdater { + nativeTokenPriceUSD = _nativeTokenPriceUSD; + } + + // ============================ Internal ========================================== + function _setPrice(uint32 _dstEid, Price memory _price) internal { + uint128 priceRatio = _price.priceRatio; + uint64 gasPriceInUnit = _price.gasPriceInUnit; + uint32 gasPerByte = _price.gasPerByte; + defaultModelPrice[_dstEid] = Price(priceRatio, gasPriceInUnit, gasPerByte); + } + + function _getL1LookupId(uint32 _l2Eid) internal pure returns (uint32) { + uint32 l2Eid = _l2Eid % 30_000; + if (l2Eid == 111) { + return 101; + } else if (l2Eid == 10132) { + return 10121; // ethereum-goerli + } else if (l2Eid == 20132) { + return 20121; // ethereum-goerli + } else { + revert("PriceFeed: unknown l2 chain id"); + } + } + + // ============================ View ========================================== + + function getPrice(uint32 _dstEid) external view override returns (Price memory price) { + price = defaultModelPrice[_dstEid]; + } + + function getPriceRatioDenominator() external view override returns (uint128) { + return PRICE_RATIO_DENOMINATOR; + } + + function estimateFeeByEid( + uint32 _dstEid, + uint _callDataSize, + uint _gas + ) + external + view + override + returns (uint fee, uint128 priceRatio, uint128 priceRatioDenominator, uint128 nativePriceUSD) + { + uint32 dstEid = _dstEid % 30_000; + if (dstEid == 110 || dstEid == 10143 || dstEid == 20143) { + (fee, priceRatio) = _estimateFeeWithArbitrumModel(dstEid, _callDataSize, _gas); + } else if (dstEid == 111 || dstEid == 10132 || dstEid == 20132) { + (fee, priceRatio) = _estimateFeeWithOptimismModel(dstEid, _callDataSize, _gas); + } else { + (fee, priceRatio) = _estimateFeeWithDefaultModel(dstEid, _callDataSize, _gas); + } + priceRatioDenominator = PRICE_RATIO_DENOMINATOR; + nativePriceUSD = nativeTokenPriceUSD; + } + + function _estimateFeeWithDefaultModel( + uint32 _dstEid, + uint _callDataSize, + uint _gas + ) internal view returns (uint fee, uint128 priceRatio) { + Price storage remotePrice = defaultModelPrice[_dstEid]; + + // assuming the _gas includes (1) the 21,000 overhead and (2) not the calldata gas + uint gasForCallData = _callDataSize * remotePrice.gasPerByte; + uint remoteFee = (gasForCallData + _gas) * remotePrice.gasPriceInUnit; + return ((remoteFee * remotePrice.priceRatio) / PRICE_RATIO_DENOMINATOR, remotePrice.priceRatio); + } + + function _estimateFeeWithOptimismModel( + uint32 _dstEid, + uint _callDataSize, + uint _gas + ) internal view returns (uint fee, uint128 priceRatio) { + uint32 ethereumId = _getL1LookupId(_dstEid); + + // L1 fee + Price storage ethereumPrice = defaultModelPrice[ethereumId]; + uint gasForL1CallData = (_callDataSize * ethereumPrice.gasPerByte) + 3188; // 2100 + 68 * 16 + uint l1Fee = gasForL1CallData * ethereumPrice.gasPriceInUnit; + + // L2 fee + Price storage optimismPrice = defaultModelPrice[_dstEid]; + uint gasForL2CallData = _callDataSize * optimismPrice.gasPerByte; + uint l2Fee = (gasForL2CallData + _gas) * optimismPrice.gasPriceInUnit; + + uint l1FeeInSrcPrice = (l1Fee * ethereumPrice.priceRatio) / PRICE_RATIO_DENOMINATOR; + uint l2FeeInSrcPrice = (l2Fee * optimismPrice.priceRatio) / PRICE_RATIO_DENOMINATOR; + uint gasFee = l1FeeInSrcPrice + l2FeeInSrcPrice; + return (gasFee, optimismPrice.priceRatio); + } + + function _estimateFeeWithArbitrumModel( + uint32 _dstEid, + uint _callDataSize, + uint _gas + ) internal view returns (uint fee, uint128 priceRatio) { + Price storage arbitrumPrice = defaultModelPrice[_dstEid]; + + // L1 fee + uint gasForL1CallData = ((_callDataSize * ARBITRUM_COMPRESSION_PERCENT) / 100) * + arbitrumPriceExt.gasPerL1CallDataByte; + // L2 Fee + uint gasForL2CallData = _callDataSize * arbitrumPrice.gasPerByte; + uint gasFee = (_gas + arbitrumPriceExt.gasPerL2Tx + gasForL1CallData + gasForL2CallData) * + arbitrumPrice.gasPriceInUnit; + + return ((gasFee * arbitrumPrice.priceRatio) / PRICE_RATIO_DENOMINATOR, arbitrumPrice.priceRatio); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/Token.sol b/endpoint/contracts/V1Contracts/contracts/mocks/Token.sol new file mode 100644 index 0000000..c2d6679 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/Token.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract Token is ERC20 { + event tried(); + event caught(); + + constructor() ERC20("test", "test") { + _mint(msg.sender, 10_000_000_000 * 10 ** 18); + } + + function selfDestruct() public { + selfdestruct(address(0x0)); + } + + function tryCatch(address _tokenAddress) public { + try ERC20(_tokenAddress).totalSupply() { + emit tried(); + } catch { + emit caught(); + } + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/mocks/UltraLightNodeV2Mock.sol b/endpoint/contracts/V1Contracts/contracts/mocks/UltraLightNodeV2Mock.sol new file mode 100644 index 0000000..b25fd21 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/mocks/UltraLightNodeV2Mock.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +import "../interfaces/ILayerZeroUltraLightNodeV2.sol"; + +contract UltraLightNodeV2Mock is ILayerZeroUltraLightNodeV2 { + event executed(); + event paidTo(address _to, uint _amount); + + mapping(address => mapping(uint16 => mapping(bytes32 => mapping(bytes32 => uint)))) public hashLookup; //[oracle][srcChainId][blockhash][datahash] -> confirmation + + constructor() {} + + //Mock version of uln for testing the withdraw fee function. If we need to use this again we can expand on it. + // Relayer functions + function validateTransactionProof( + uint16 _srcChainId, + address _dstAddress, + uint _gasLimit, + bytes32 _lookupHash, + bytes32 _blockData, + bytes calldata _transactionProof + ) external override { + emit executed(); + } + + // an Oracle delivers the block data using updateHash() + function updateHash( + uint16 _srcChainId, + bytes32 _lookupHash, + uint _confirmations, + bytes32 _blockData + ) external override { + emit executed(); + } + + // can only withdraw the receivable of the msg.sender + function withdrawNative(address payable _to, uint _amount) external override { + (bool success, ) = _to.call{value: _amount}(""); + emit paidTo(_to, _amount); + } + + function withdrawZRO(address _to, uint _amount) external override { + emit executed(); + } + + // view functions + function getAppConfig( + uint16 _remoteChainId, + address _userApplicationAddress + ) external view override returns (ApplicationConfiguration memory) {} + + function accruedNativeFee(address _address) external view override returns (uint) {} + + receive() external payable {} +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/PreCrime.sol b/endpoint/contracts/V1Contracts/contracts/precrime/PreCrime.sol new file mode 100644 index 0000000..e4f6ccb --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/PreCrime.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./interfaces/IPreCrime.sol"; +import "./PreCrimeBase.sol"; + +abstract contract PreCrime is PreCrimeBase, IPreCrime { + bytes4 private constant SIMULATE_REVERT_SELECTOR = bytes4(keccak256("SimulateRevert(uint16,bytes)")); + + /** + * @dev 10000 - 20000 is for view mode, 20000 - 30000 is for precrime inherit mode + */ + uint16 public constant PRECRIME_VERSION = 20001; + + constructor(uint16 _localChainId) PreCrimeBase(_localChainId) {} + + /** + * @dev simulate run cross chain packets and get a simulation result for precrime later + * @param _packets packets, the packets item should group by srcChainId, srcAddress, then sort by nonce + * @return code simulation result code; see the error code defination + * @return data the result is use for precrime params + */ + function simulate(Packet[] calldata _packets) external override returns (uint16 code, bytes memory data) { + // params check + (code, data) = _checkPacketsMaxSizeAndNonceOrder(_packets); + if (code != CODE_SUCCESS) { + return (code, data); + } + + (bool success, bytes memory result) = address(this).call( + abi.encodeWithSelector(this._simulateAndRevert.selector, _packets) + ); + require(!success, "simulate should never return success"); + + // parse code and data + (code, data) = _parseRevertResult(result); + if (code == CODE_SUCCESS) { + data = abi.encode(localChainId, data); // add localChainId to the header + } + } + + function _parseRevertResult(bytes memory result) internal pure returns (uint16 code, bytes memory data) { + // check revert selector + bytes4 selector; + assembly { + selector := mload(add(result, 0x20)) // skip the length and get bytes4 selector + } + if (selector != SIMULATE_REVERT_SELECTOR) { + // bubble up the internal error + assembly { + revert(add(result, 0x20), mload(result)) + } + } + + // parse code and result + assembly { + // Slice the sighash. Remove the selector which is the first 4 bytes + result := add(result, 0x04) + } + return abi.decode(result, (uint16, bytes)); + } + + /** + * @dev internal function, no one should call + * @param _packets packets + */ + function _simulateAndRevert(Packet[] calldata _packets) external virtual { + require(msg.sender == address(this)); + (uint16 code, bytes memory simulation) = _simulate(_packets); + // equal to: revert SimulateRevert(code, result); + bytes memory revertData = abi.encodePacked(SIMULATE_REVERT_SELECTOR, abi.encode(code, simulation)); + assembly { + revert(add(revertData, 32), mload(revertData)) + } + } + + /** + * @dev UA execute the logic by _packets, and return simulation result for precrime. would revert state after returned result. + * @param _packets packets + * @return code + * @return result + */ + function _simulate(Packet[] calldata _packets) internal virtual returns (uint16 code, bytes memory result); + + function version() external pure override returns (uint16) { + return PRECRIME_VERSION; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/PreCrimeBase.sol b/endpoint/contracts/V1Contracts/contracts/precrime/PreCrimeBase.sol new file mode 100644 index 0000000..3fe0dec --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/PreCrimeBase.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./interfaces/IPreCrime.sol"; + +abstract contract PreCrimeBase is IPreCrimeBase { + uint16 public constant CONFIG_VERSION = 1; + + //---------------- error code ---------------------- + // --- UA scope code --- + uint16 public constant CODE_SUCCESS = 0; // success + uint16 public constant CODE_PRECRIME_FAILURE = 1; // !!! crimes found + + // --- protocol scope error code --- + // simualte + uint16 public constant CODE_PACKETS_OVERSIZE = 2; // packets number bigger then max size + uint16 public constant CODE_PACKETS_UNSORTED = 3; // packets are unsorted, need backfill and keep order + // precrime + uint16 public constant CODE_MISS_SIMULATE_RESULT = 4; // miss simulation result + + uint16 public immutable localChainId; + uint64 public maxBatchSize; + address public precrimeAdmin; + + modifier onlyPrecrimeAdmin() { + require(msg.sender == precrimeAdmin, "only admin"); + _; + } + + constructor(uint16 _localChainId) { + localChainId = _localChainId; + precrimeAdmin = msg.sender; + } + + function setPrecrimeAdmin(address _precrimeAdmin) external onlyPrecrimeAdmin { + precrimeAdmin = _precrimeAdmin; + } + + function setMaxBatchSize(uint64 _maxBatchSize) external onlyPrecrimeAdmin { + maxBatchSize = _maxBatchSize; + } + + /** + * @dev get precrime config, + * @param _packets packets + * @return configation bytes + */ + function getConfig(Packet[] calldata _packets) external view virtual override returns (bytes memory) { + (uint16[] memory remoteChains, bytes32[] memory remoteAddresses) = _remotePrecrimeAddress(_packets); + return + abi.encodePacked( + CONFIG_VERSION, + //---- max packets size for simulate batch --- + maxBatchSize, + //------------- remote precrimes ------------- + remoteChains.length, + remoteChains, + remoteAddresses + ); + } + + /** + * @dev + * @param _simulation all simulation results from difference chains + * @return code precrime result code; check out the error code definition + * @return reason error reason + */ + function precrime( + Packet[] calldata _packets, + bytes[] calldata _simulation + ) external view override returns (uint16 code, bytes memory reason) { + bytes[] memory originSimulateResult = new bytes[](_simulation.length); + uint16[] memory chainIds = new uint16[](_simulation.length); + for (uint256 i = 0; i < _simulation.length; i++) { + (uint16 chainId, bytes memory simulateResult) = abi.decode(_simulation[i], (uint16, bytes)); + chainIds[i] = chainId; + originSimulateResult[i] = simulateResult; + } + + (code, reason) = _checkResultsCompleteness(_packets, chainIds); + if (code != CODE_SUCCESS) { + return (code, reason); + } + + (code, reason) = _precrime(originSimulateResult); + } + + function _checkPacketsMaxSizeAndNonceOrder( + Packet[] calldata _packets + ) internal view returns (uint16 code, bytes memory reason) { + if (_packets.length > maxBatchSize) { + return (CODE_PACKETS_OVERSIZE, abi.encodePacked("packets size exceed limited")); + } + + // check packets nonce, sequence order + // packets should group by srcChainId and srcAddress, then sort by nonce ascending + if (_packets.length > 0) { + uint16 srcChainId; + bytes32 srcAddress; + uint64 nonce; + for (uint256 i = 0; i < _packets.length; i++) { + Packet memory packet = _packets[i]; + // start from a new chain packet or a new source UA + if (packet.srcChainId != srcChainId || packet.srcAddress != srcAddress) { + srcChainId = packet.srcChainId; + srcAddress = packet.srcAddress; + nonce = packet.nonce; + uint64 nextInboundNonce = _getInboundNonce(packet) + 1; + // the first packet's nonce must equal to dst InboundNonce+1 + if (nonce != nextInboundNonce) { + return (CODE_PACKETS_UNSORTED, abi.encodePacked("skipped inboundNonce forbidden")); + } + } else { + // the following packet's nonce add 1 in order + if (packet.nonce != ++nonce) { + return (CODE_PACKETS_UNSORTED, abi.encodePacked("unsorted packets")); + } + } + } + } + return (CODE_SUCCESS, ""); + } + + function _checkResultsCompleteness( + Packet[] calldata _packets, + uint16[] memory _resultChainIds + ) internal view returns (uint16 code, bytes memory reason) { + // check if all remote result included + if (_packets.length > 0) { + (uint16[] memory remoteChains, ) = _remotePrecrimeAddress(_packets); + for (uint256 i = 0; i < remoteChains.length; i++) { + bool resultChainIdChecked; + for (uint256 j = 0; j < _resultChainIds.length; j++) { + if (_resultChainIds[j] == remoteChains[i]) { + resultChainIdChecked = true; + break; + } + } + if (!resultChainIdChecked) { + return (CODE_MISS_SIMULATE_RESULT, "missing remote simulation result"); + } + } + } + // check if local result included + bool localChainIdResultChecked; + for (uint256 j = 0; j < _resultChainIds.length; j++) { + if (_resultChainIds[j] == localChainId) { + localChainIdResultChecked = true; + break; + } + } + if (!localChainIdResultChecked) { + return (CODE_MISS_SIMULATE_RESULT, "missing local simulation result"); + } + + return (CODE_SUCCESS, ""); + } + + /** + * @dev + * @param _simulation all simulation results from difference chains + * @return code precrime result code; check out the error code defination + * @return reason error reason + */ + function _precrime(bytes[] memory _simulation) internal view virtual returns (uint16 code, bytes memory reason); + + /** + * @dev UA return trusted remote precrimes by packets + * @param _packets packets + * @return + */ + function _remotePrecrimeAddress( + Packet[] calldata _packets + ) internal view virtual returns (uint16[] memory, bytes32[] memory); + + /** + * get srcChain & srcAddress InboundNonce by packet + */ + function _getInboundNonce(Packet memory packet) internal view virtual returns (uint64 nonce); +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/PreCrimeView.sol b/endpoint/contracts/V1Contracts/contracts/precrime/PreCrimeView.sol new file mode 100644 index 0000000..8be437c --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/PreCrimeView.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./interfaces/IPreCrimeView.sol"; +import "./PreCrimeBase.sol"; + +abstract contract PreCrimeView is PreCrimeBase, IPreCrimeView { + /** + * @dev 10000 - 20000 is for view mode, 20000 - 30000 is for precrime inherit mode + */ + uint16 public constant PRECRIME_VERSION = 10001; + + constructor(uint16 _localChainId) PreCrimeBase(_localChainId) {} + + /** + * @dev simulate run cross chain packets and get a simulation result for precrime later + * @param _packets packets, the packets item should group by srcChainId, srcAddress, then sort by nonce + * @return code simulation result code; see the error code defination + * @return data the result is use for precrime params + */ + function simulate(Packet[] calldata _packets) external view override returns (uint16 code, bytes memory data) { + // params check + (code, data) = _checkPacketsMaxSizeAndNonceOrder(_packets); + if (code != CODE_SUCCESS) { + return (code, data); + } + + (code, data) = _simulate(_packets); + if (code == CODE_SUCCESS) { + data = abi.encode(localChainId, data); // add localChainId to the header + } + } + + /** + * @dev UA execute the logic by _packets, and return simulation result for precrime. would revert state after returned result. + * @param _packets packets + * @return code + * @return result + */ + function _simulate(Packet[] calldata _packets) internal view virtual returns (uint16 code, bytes memory result); + + function version() external pure override returns (uint16) { + return PRECRIME_VERSION; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/example/inherit/CounterI.sol b/endpoint/contracts/V1Contracts/contracts/precrime/example/inherit/CounterI.sol new file mode 100644 index 0000000..5ec5e7b --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/example/inherit/CounterI.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import "./CounterPrecrime.sol"; + +contract CounterI is CounterPrecrime { + // keep track of how many messages have been received from other chains + uint public totalCount; + mapping(uint32 => uint) public inboundCount; + mapping(uint32 => uint) public outboundCount; + + event Send(uint16 dstChainId); + event Receive(uint16 srcChainId); + + uint16 public chainId; + + constructor(uint16 _chainId) CounterPrecrime(_chainId) { + chainId = _chainId; + } + + function lzReceive(uint16 _srcChainId) public { + inboundCount[_srcChainId]++; + totalCount++; + + emit Receive(_srcChainId); + } + + function increment(uint16 _dstChainId) external { + outboundCount[_dstChainId]++; + + emit Send(_dstChainId); + } + + function brokeIncrement(uint16 _dstChainId) external { + emit Send(_dstChainId); + } + + function brokeTotalCount() external { + totalCount++; + } + + function getInboundNonce(uint32 _chainId) public view returns (uint) { + // in reality, this would be a call to the LayerZero endpoint + return inboundCount[_chainId]; + } + + // ------------- precrime ----------------- + function _lzReceiveByPacket(Packet calldata _packet) internal virtual override { + lzReceive(uint16(_packet.srcChainId)); + } + + function getCountState(uint16[] memory peers) public view virtual override returns (CountState memory) { + ChainPathCount[] memory chainPathCounts = new ChainPathCount[](peers.length); + for (uint i = 0; i < peers.length; i++) { + uint16 peer = peers[i]; + chainPathCounts[i] = ChainPathCount({ + eid: peer, + inboundCount: inboundCount[peer], + outboundCount: outboundCount[peer] + }); + } + + CountState memory countState = CountState({totalCount: totalCount, chainPathCounts: chainPathCounts}); + + return countState; + } + + function _getInboundNonce(Packet memory packet) internal view override returns (uint64) { + // in reality, this would be a call to the LayerZero endpoint + return uint64(inboundCount[packet.srcChainId]); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/example/inherit/CounterPrecrime.sol b/endpoint/contracts/V1Contracts/contracts/precrime/example/inherit/CounterPrecrime.sol new file mode 100644 index 0000000..db87133 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/example/inherit/CounterPrecrime.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import "../../PreCrime.sol"; + +abstract contract CounterPrecrime is PreCrime { + uint16[] public remoteChainIds; + bytes32[] public remotePrecrimeAddresses; + + struct SimulationResult { + uint16 chainId; + CountState countState; + } + + struct CountState { + uint totalCount; + ChainPathCount[] chainPathCounts; + } + + struct ChainPathCount { + uint16 eid; + uint inboundCount; + uint outboundCount; + } + + constructor(uint16 _localChainId) PreCrime(_localChainId) { + maxBatchSize = 10; + } + + function setRemotePrecrimeAddresses( + uint16[] memory _remoteChainIds, + bytes32[] memory _remotePrecrimeAddresses + ) public onlyPrecrimeAdmin { + require(_remoteChainIds.length == _remotePrecrimeAddresses.length, "Precrime: invalid size"); + remoteChainIds = _remoteChainIds; + remotePrecrimeAddresses = _remotePrecrimeAddresses; + } + + function _simulate(Packet[] calldata _packets) internal override returns (uint16 code, bytes memory data) { + // simulate + for (uint256 i = 0; i < _packets.length; i++) { + _lzReceiveByPacket(_packets[i]); + } + + // return simulation result + bytes memory result = abi.encode( + SimulationResult({chainId: localChainId, countState: getCountState(remoteChainIds)}) + ); + return (CODE_SUCCESS, result); + } + + function _precrime( + bytes[] memory _simulation + ) internal view virtual override returns (uint16 code, bytes memory reason) { + (SimulationResult memory localResult, SimulationResult[] memory results) = _getLocalSimulateResult(_simulation); + + uint sumOutboundCount = 0; + // for each chainPathCount, find the chainPathCount and check the counts + for (uint256 j = 0; j < localResult.countState.chainPathCounts.length; j++) { + ChainPathCount memory cpCount = localResult.countState.chainPathCounts[j]; + // find remote to local count state + // should provide all peers simulated results + ChainPathCount memory remoteCp = _getRemoteCpCount(results, cpCount.eid); + (code, reason) = _assertChainPathCount(cpCount, remoteCp); + if (code != CODE_SUCCESS) { + return (code, reason); + } + sumOutboundCount += remoteCp.outboundCount; // remote to local outbound count + } + (code, reason) = _assertTotalCount(localResult.countState, sumOutboundCount); + if (code != CODE_SUCCESS) { + return (code, reason); + } + + return (CODE_SUCCESS, ""); + } + + function _lzReceiveByPacket(Packet calldata _packet) internal virtual; + + /** + * @notice Get the count states for a list of peers + * @param peers - the list of remote chainId to get counts for + * @return counts - the CountState with the total count and the chain path counts + */ + function getCountState(uint16[] memory peers) public view virtual returns (CountState memory); + + function _getRemoteCpCount( + SimulationResult[] memory _results, + uint16 _remoteId + ) internal view returns (ChainPathCount memory) { + uint localEid = localChainId; + for (uint256 i = 0; i < _results.length; i++) { + SimulationResult memory remoteResult = _results[i]; + if (remoteResult.chainId == _remoteId) { + for (uint256 j = 0; j < remoteResult.countState.chainPathCounts.length; j++) { + ChainPathCount memory cpCount = remoteResult.countState.chainPathCounts[j]; + if (cpCount.eid == localEid) { + // find to localEid path + return cpCount; + } + } + } + } + revert("Precrime: countState not found, are you missing a simulation?"); + } + + function _getLocalSimulateResult( + bytes[] memory _simulation + ) internal view returns (SimulationResult memory localResult, SimulationResult[] memory results) { + // decode results + results = new SimulationResult[](_simulation.length); + for (uint256 i = 0; i < _simulation.length; i++) { + bytes memory result = _simulation[i]; + results[i] = abi.decode(result, (SimulationResult)); + if (results[i].chainId == localChainId) { + localResult = results[i]; + } + } + } + + function _assertChainPathCount( + ChainPathCount memory _localCpCount, + ChainPathCount memory _remoteCpCount + ) internal pure returns (uint16 code, bytes memory reason) { + if (_localCpCount.inboundCount > _remoteCpCount.outboundCount) { + return (CODE_PRECRIME_FAILURE, "Precrime: inboundCount > outboundCount"); + } + if (_localCpCount.outboundCount > _remoteCpCount.inboundCount) { + return (CODE_PRECRIME_FAILURE, "Precrime: outboundCount > inboundCount"); + } + return (CODE_SUCCESS, ""); + } + + function _assertTotalCount( + CountState memory _localCount, + uint _sumOutbound + ) internal pure returns (uint16 code, bytes memory reason) { + if (_localCount.totalCount > _sumOutbound) { + return (CODE_PRECRIME_FAILURE, "Precrime: totalCount > sum outboundCount"); + } + } + + function _remotePrecrimeAddress( + Packet[] calldata _packets + ) internal view override returns (uint16[] memory chainIds, bytes32[] memory precrimeAddresses) { + if (_packets.length == 0) { + return (remoteChainIds, remotePrecrimeAddresses); + } + + // only return related remotes + uint16 size = _getRelatedRemoteSize(_packets); + if (size > 0) { + chainIds = new uint16[](size); + uint256 k = 0; + precrimeAddresses = new bytes32[](size); + for (uint16 i = 0; i < remoteChainIds.length; i++) { + for (uint16 j = 0; j < _packets.length; j++) { + uint16 srcChainId = _packets[j].srcChainId; + if (remoteChainIds[i] == srcChainId) { + chainIds[k] = srcChainId; + precrimeAddresses[k] = remotePrecrimeAddresses[i]; + k++; + break; + } + } + } + } + } + + function _getRelatedRemoteSize(Packet[] memory _packets) internal view returns (uint16 size) { + for (uint16 i = 0; i < remoteChainIds.length; i++) { + for (uint16 j = 0; j < _packets.length; j++) { + if (remoteChainIds[i] == _packets[j].srcChainId) { + size++; + break; + } + } + } + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterPrecrimeView.sol b/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterPrecrimeView.sol new file mode 100644 index 0000000..b4796e0 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterPrecrimeView.sol @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import "../../PreCrimeView.sol"; +import "./CounterView.sol"; +import "./CounterV.sol"; + +contract CounterPrecrimeView is PreCrimeView { + CounterView public counterView; + + uint16[] public remoteChainIds; + bytes32[] public remotePrecrimeAddresses; + + struct SimulationResult { + uint16 chainId; + CounterView.CountState countState; + } + + constructor(uint16 _localChainId, address _counterView) PreCrimeView(_localChainId) { + counterView = CounterView(_counterView); + maxBatchSize = 10; + } + + function setRemotePrecrimeAddresses( + uint16[] memory _remoteChainIds, + bytes32[] memory _remotePrecrimeAddresses + ) public onlyPrecrimeAdmin { + require(_remoteChainIds.length == _remotePrecrimeAddresses.length, "Precrime: invalid size"); + remoteChainIds = _remoteChainIds; + remotePrecrimeAddresses = _remotePrecrimeAddresses; + } + + function setCounterView(address _counterView) public onlyPrecrimeAdmin { + counterView = CounterView(_counterView); + } + + function _simulate(Packet[] calldata _packets) internal view override returns (uint16 code, bytes memory data) { + // get state from counter + CounterView.CountState memory countState = counterView.getCountState(remoteChainIds); + + // simulate + for (uint256 i = 0; i < _packets.length; i++) { + Packet memory packet = _packets[i]; + countState = counterView.lzReceive(countState, packet.srcChainId); + } + + // return simulation result + return (CODE_SUCCESS, abi.encode(SimulationResult({chainId: localChainId, countState: countState}))); + } + + function _precrime( + bytes[] memory _simulation + ) internal view virtual override returns (uint16 code, bytes memory reason) { + (SimulationResult memory localResult, SimulationResult[] memory results) = _getLocalSimulateResult(_simulation); + + uint sumOutboundCount = 0; + // for each chainPathCount, find the chainPathCount and check the counts + for (uint256 j = 0; j < localResult.countState.chainPathCounts.length; j++) { + CounterView.ChainPathCount memory cpCount = localResult.countState.chainPathCounts[j]; + // find remote to local count state + // should provide all peers simulated results + CounterView.ChainPathCount memory remoteCp = _getRemoteCpCount(results, cpCount.eid); + (code, reason) = _assertChainPathCount(cpCount, remoteCp); + if (code != CODE_SUCCESS) { + return (code, reason); + } + sumOutboundCount += remoteCp.outboundCount; // remote to local outbound count + } + (code, reason) = _assertTotalCount(localResult.countState, sumOutboundCount); + if (code != CODE_SUCCESS) { + return (code, reason); + } + + return (CODE_SUCCESS, ""); + } + + function _getRemoteCpCount( + SimulationResult[] memory _results, + uint16 _remoteId + ) internal view returns (CounterView.ChainPathCount memory) { + uint localEid = localChainId; + for (uint256 i = 0; i < _results.length; i++) { + SimulationResult memory remoteResult = _results[i]; + if (remoteResult.chainId == _remoteId) { + for (uint256 j = 0; j < remoteResult.countState.chainPathCounts.length; j++) { + CounterView.ChainPathCount memory cpCount = remoteResult.countState.chainPathCounts[j]; + if (cpCount.eid == localEid) { + // find to localEid path + return cpCount; + } + } + } + } + revert("Precrime: count state not found"); + } + + function _getLocalSimulateResult( + bytes[] memory _simulation + ) internal view returns (SimulationResult memory localResult, SimulationResult[] memory results) { + // decode results + results = new SimulationResult[](_simulation.length); + for (uint256 i = 0; i < _simulation.length; i++) { + results[i] = abi.decode(_simulation[i], (SimulationResult)); + if (results[i].chainId == localChainId) { + localResult = results[i]; + } + } + } + + function _assertChainPathCount( + CounterView.ChainPathCount memory _localCpCount, + CounterView.ChainPathCount memory _remoteCpCount + ) internal pure returns (uint16 code, bytes memory reason) { + if (_localCpCount.inboundCount > _remoteCpCount.outboundCount) { + return (CODE_PRECRIME_FAILURE, "Precrime: inboundCount > outboundCount"); + } + if (_localCpCount.outboundCount > _remoteCpCount.inboundCount) { + return (CODE_PRECRIME_FAILURE, "Precrime: outboundCount > inboundCount"); + } + return (CODE_SUCCESS, ""); + } + + function _assertTotalCount( + CounterView.CountState memory _localCount, + uint _sumOutbound + ) internal pure returns (uint16 code, bytes memory reason) { + if (_localCount.totalCount > _sumOutbound) { + return (CODE_PRECRIME_FAILURE, "Precrime: totalCount > sum outboundCount"); + } + return (CODE_SUCCESS, ""); + } + + function _remotePrecrimeAddress( + Packet[] calldata _packets + ) internal view override returns (uint16[] memory chainIds, bytes32[] memory precrimeAddresses) { + if (_packets.length == 0) { + return (remoteChainIds, remotePrecrimeAddresses); + } + + // only return related remotes + uint16 size = _getRelatedRemoteSize(_packets); + if (size > 0) { + chainIds = new uint16[](size); + uint256 k = 0; + precrimeAddresses = new bytes32[](size); + for (uint16 i = 0; i < remoteChainIds.length; i++) { + for (uint16 j = 0; j < _packets.length; j++) { + uint16 srcChainId = _packets[j].srcChainId; + if (remoteChainIds[i] == srcChainId) { + chainIds[k] = srcChainId; + precrimeAddresses[k] = remotePrecrimeAddresses[i]; + k++; + break; + } + } + } + } + } + + function _getRelatedRemoteSize(Packet[] memory _packets) internal view returns (uint16 size) { + for (uint16 i = 0; i < remoteChainIds.length; i++) { + for (uint16 j = 0; j < _packets.length; j++) { + if (remoteChainIds[i] == _packets[j].srcChainId) { + size++; + break; + } + } + } + } + + function _getInboundNonce(Packet memory packet) internal view override returns (uint64) { + CounterV counter = counterView.counter(); + return counter.getInboundNonce(packet.srcChainId); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterV.sol b/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterV.sol new file mode 100644 index 0000000..b14b57c --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterV.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; +pragma abicoder v2; + +contract CounterV { + // keep track of how many messages have been received from other chains + uint public totalCount; + mapping(uint32 => uint) public inboundCount; + mapping(uint32 => uint) public outboundCount; + + event Send(uint16 dstChainId); + event Receive(uint16 srcChainId); + + uint16 public chainId; + + constructor(uint16 _chainId) { + chainId = _chainId; + } + + function lzReceive(uint16 _srcChainId) external { + inboundCount[_srcChainId]++; + totalCount++; + + emit Receive(_srcChainId); + } + + function increment(uint16 _dstChainId) external { + outboundCount[_dstChainId]++; + + emit Send(_dstChainId); + } + + function brokeIncrement(uint16 _dstChainId) external { + emit Send(_dstChainId); + } + + function brokeTotalCount() external { + totalCount++; + } + + function getInboundNonce(uint16 _chainId) public view returns (uint64) { + // in reality, this would be a call to the LayerZero endpoint + return uint64(inboundCount[_chainId]); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterView.sol b/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterView.sol new file mode 100644 index 0000000..58f79de --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/example/view/CounterView.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import "./CounterV.sol"; + +contract CounterView { + struct CountState { + uint totalCount; + ChainPathCount[] chainPathCounts; + } + + struct ChainPathCount { + uint16 eid; + uint inboundCount; + uint outboundCount; + } + + CounterV public counter; + + constructor(address _counter) { + counter = CounterV(_counter); + } + + function lzReceive(CountState memory countState, uint16 _srcChainId) public pure returns (CountState memory) { + // do receive logic + for (uint i = 0; i < countState.chainPathCounts.length; i++) { + ChainPathCount memory chainPathCount = countState.chainPathCounts[i]; + if (chainPathCount.eid == _srcChainId) { + countState.totalCount++; + chainPathCount.inboundCount++; + countState.chainPathCounts[i] = chainPathCount; + break; + } + } + return countState; + } + + /** + * @notice Get the count states for a list of peers + * @param peers - the list of remote chainId to get counts for + * @return counts - the CountState with the total count and the chain path counts + */ + function getCountState(uint16[] calldata peers) public view returns (CountState memory) { + ChainPathCount[] memory chainPathCounts = new ChainPathCount[](peers.length); + + for (uint i = 0; i < peers.length; i++) { + uint16 peer = peers[i]; + chainPathCounts[i] = ChainPathCount({ + eid: peer, + inboundCount: counter.inboundCount(peer), + outboundCount: counter.outboundCount(peer) + }); + } + + CountState memory countState = CountState({totalCount: counter.totalCount(), chainPathCounts: chainPathCounts}); + + return countState; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrime.sol b/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrime.sol new file mode 100644 index 0000000..070695d --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrime.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +import "./IPreCrimeBase.sol"; + +interface IPreCrime is IPreCrimeBase { + /** + * @dev simulate run cross chain packets and get a simulation result for precrime later + * @param _packets packets, the packets item should group by srcChainId, srcAddress, then sort by nonce + * @return code simulation result code; see the error code defination + * @return result the result is use for precrime params + */ + function simulate(Packet[] calldata _packets) external returns (uint16 code, bytes memory result); +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrimeBase.sol b/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrimeBase.sol new file mode 100644 index 0000000..4b071a9 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrimeBase.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +interface IPreCrimeBase { + struct Packet { + uint16 srcChainId; // source chain id + bytes32 srcAddress; // srouce UA address + uint64 nonce; + bytes payload; + } + + /** + * @dev get precrime config, + * @param _packets packets + * @return bytes of [maxBatchSize, remotePrecrimes] + */ + function getConfig(Packet[] calldata _packets) external view returns (bytes memory); + + /** + * @dev + * @param _simulation all simulation results from difference chains + * @return code precrime result code; check out the error code defination + * @return reason error reason + */ + function precrime( + Packet[] calldata _packets, + bytes[] calldata _simulation + ) external view returns (uint16 code, bytes memory reason); + + /** + * @dev protocol version + */ + function version() external view returns (uint16); +} diff --git a/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrimeView.sol b/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrimeView.sol new file mode 100644 index 0000000..ee0c3c6 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/precrime/interfaces/IPreCrimeView.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +import "./IPreCrimeBase.sol"; + +interface IPreCrimeView is IPreCrimeBase { + /** + * @dev simulate run cross chain packets and get a simulation result for precrime later + * @param _packets packets, the packets item should group by srcChainId, srcAddress, then sort by nonce + * @return code simulation result code; see the error code defination + * @return result the result is use for precrime params + */ + function simulate(Packet[] calldata _packets) external view returns (uint16 code, bytes memory result); +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/FPValidator.sol b/endpoint/contracts/V1Contracts/contracts/proof/FPValidator.sol new file mode 100644 index 0000000..e05390e --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/FPValidator.sol @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./utility/LayerZeroPacket.sol"; +import "../interfaces/ILayerZeroValidationLibrary.sol"; +import "../interfaces/IValidationLibraryHelperV2.sol"; + +interface IStargate { + // Stargate objects for abi encoding / decoding + struct SwapObj { + uint amount; + uint eqFee; + uint eqReward; + uint lpFee; + uint protocolFee; + uint lkbRemove; + } + + struct CreditObj { + uint credits; + uint idealBalance; + } +} + +contract FPValidator is ILayerZeroValidationLibrary, IValidationLibraryHelperV2 { + uint8 public proofType = 2; + uint8 public utilsVersion = 1; + + address public immutable stargateBridgeAddress; + address public immutable stargateTokenAddress; + + constructor(address _stargateBridgeAddress, address _stargateTokenAddress) { + stargateBridgeAddress = _stargateBridgeAddress; + stargateTokenAddress = _stargateTokenAddress; + } + + function validateProof( + bytes32 _packetHash, + bytes calldata _transactionProof, + uint _remoteAddressSize + ) external view override returns (LayerZeroPacket.Packet memory packet) { + require(_remoteAddressSize > 0, "ProofLib: invalid address size"); + // _transactionProof = srcUlnAddress (32 bytes) + lzPacket + require( + _transactionProof.length > 32 && keccak256(_transactionProof) == _packetHash, + "ProofLib: invalid transaction proof" + ); + + bytes memory ulnAddressBytes = bytes(_transactionProof[0:32]); + bytes32 ulnAddress; + assembly { + ulnAddress := mload(add(ulnAddressBytes, 32)) + } + packet = LayerZeroPacket.getPacketV3(_transactionProof[32:], _remoteAddressSize, ulnAddress); + + if (packet.dstAddress == stargateBridgeAddress) packet.payload = _secureStgPayload(packet.payload); + if (packet.dstAddress == stargateTokenAddress) packet.payload = _secureStgTokenPayload(packet.payload); + + return packet; + } + + function _secureStgTokenPayload(bytes memory _payload) internal pure returns (bytes memory) { + (bytes memory toAddressBytes, uint qty) = abi.decode(_payload, (bytes, uint)); + + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + if (toAddress == address(0)) { + address deadAddress = address(0x000000000000000000000000000000000000dEaD); + bytes memory newToAddressBytes = abi.encodePacked(deadAddress); + return abi.encode(newToAddressBytes, qty); + } + + // default to return the original payload + return _payload; + } + + function _secureStgPayload(bytes memory _payload) internal view returns (bytes memory) { + // functionType is uint8 even though the encoding will take up the side of uint256 + uint8 functionType; + assembly { + functionType := mload(add(_payload, 32)) + } + + // TYPE_SWAP_REMOTE == 1 && only if the payload has a payload + // only swapRemote inside of stargate can call sgReceive on an user supplied to address + // thus we do not care about the other type functions even if the toAddress is overly long. + if (functionType == 1) { + // decode the _payload with its types + ( + , + uint srcPoolId, + uint dstPoolId, + uint dstGasForCall, + IStargate.CreditObj memory c, + IStargate.SwapObj memory s, + bytes memory toAddressBytes, + bytes memory contractCallPayload + ) = abi.decode(_payload, (uint8, uint, uint, uint, IStargate.CreditObj, IStargate.SwapObj, bytes, bytes)); + + // if contractCallPayload.length > 0 need to check if the to address is a contract or not + if (contractCallPayload.length > 0) { + // otherwise, need to check if the payload can be delivered to the toAddress + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + // check if the toAddress is a contract. We are not concerned about addresses that pretend to be wallets. because worst case we just delete their payload if being malicious + // we can guarantee that if a size > 0, then the contract is definitely a contract address in this context + uint size; + assembly { + size := extcodesize(toAddress) + } + + if (size == 0) { + // size == 0 indicates its not a contract, payload wont be delivered + // secure the _payload to make sure funds can be delivered to the toAddress + bytes memory newToAddressBytes = abi.encodePacked(toAddress); + bytes memory securePayload = abi.encode( + functionType, + srcPoolId, + dstPoolId, + dstGasForCall, + c, + s, + newToAddressBytes, + bytes("") + ); + return securePayload; + } + } + } + + // default to return the original payload + return _payload; + } + + function secureStgTokenPayload(bytes memory _payload) external pure returns (bytes memory) { + return _secureStgTokenPayload(_payload); + } + + function secureStgPayload(bytes memory _payload) external view returns (bytes memory) { + return _secureStgPayload(_payload); + } + + function getUtilsVersion() external view override returns (uint8) { + return utilsVersion; + } + + function getProofType() external view override returns (uint8) { + return proofType; + } + + function getVerifyLog( + bytes32, + uint[] calldata, + uint, + bytes[] calldata proof + ) external pure override returns (ULNLog memory log) {} + + function getPacket( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure override returns (LayerZeroPacket.Packet memory) { + return LayerZeroPacket.getPacketV3(data, sizeOfSrcAddress, ulnAddress); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/MPTValidator.sol b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidator.sol new file mode 100644 index 0000000..64f9acc --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidator.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./utility/LayerZeroPacket.sol"; +import "./utility/Buffer.sol"; +import "../interfaces/ILayerZeroValidationLibrary.sol"; +import "./utility/UltraLightNodeEVMDecoder.sol"; + +contract MPTValidator is ILayerZeroValidationLibrary { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + using Buffer for Buffer.buffer; + using SafeMath for uint; + + bytes32 public constant PACKET_SIGNATURE = 0xe8d23d927749ec8e512eb885679c2977d57068839d8cca1a85685dbbea0648f6; + + struct ULNLog { + bytes32 contractAddress; + bytes32 topicZeroSig; + bytes data; + } + + function validateProof( + bytes32 _receiptsRoot, + bytes calldata _transactionProof, + uint _remoteAddressSize + ) external pure override returns (LayerZeroPacket.Packet memory packet) { + (uint16 remoteChainId, bytes[] memory proof, uint[] memory pointers, uint receiptIndex, uint logIndex) = abi + .decode(_transactionProof, (uint16, bytes[], uint[], uint, uint)); + + ULNLog memory log = _getVerifiedLog(_receiptsRoot, receiptIndex, logIndex, proof, pointers); + require(log.topicZeroSig == PACKET_SIGNATURE, "LayerZero: packet not recognized"); //data + + return getPacket(log.data, remoteChainId, _remoteAddressSize, log.contractAddress); + } + + function _getVerifiedLog( + bytes32 hashRoot, + uint receiptSlotIndex, + uint logIndex, + bytes[] memory proof, + uint[] memory pointers + ) internal pure returns (ULNLog memory) { + // walk and assert the hash links of MPT + uint pointer; + bytes memory proofBytes; + for (uint i = 0; i < proof.length; i++) { + proofBytes = proof[i]; + require(hashRoot == keccak256(proofBytes), "LayerZero: invalid hashlink"); + if (i < pointers.length) { + pointer = pointers[i]; + assembly { + hashRoot := mload(add(add(proofBytes, pointer), 32)) + } + } + } + + // build the iterator for the proofBytes + RLPDecode.Iterator memory it = RLPDecode.toRlpItem(proofBytes).iterator(); + + // get the receipt item from either branch or leaf node + RLPDecode.RLPItem memory receiptItem = it.item.getItemByIndex(receiptSlotIndex); + // it = targetReceiptIter + it = receiptItem.typeOffset().iterator(); + it.next(); // status + it.next(); // gasUsed + it.next(); // logBloom + + // it = targetLogIter + it = it.next().getItemByIndex(logIndex).iterator(); + ULNLog memory log; + log.contractAddress = bytes32(it.next().toUint()); + log.topicZeroSig = bytes32(it.next().getItemByIndex(0).toUint()); + log.data = it.next().toBytes(); + + return log; + } + + // profiling and test + function getVerifyLog( + bytes32 hashRoot, + uint receiptSlotIndex, + uint logIndex, + bytes[] memory proof, + uint[] memory pointers + ) external pure returns (ULNLog memory) { + return _getVerifiedLog(hashRoot, receiptSlotIndex, logIndex, proof, pointers); + } + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + uint16 dstChainId; + address dstAddress; + uint size; + uint64 nonce; + + // The log consists of the destination chain id and then a bytes payload + // 0--------------------------------------------31 + // 0 | destination chain id + // 32 | defines bytes array + // 64 | + // 96 | bytes array size + // 128 | payload + assembly { + dstChainId := mload(add(data, 32)) + size := mload(add(data, 96)) /// size of the byte array + nonce := mload(add(data, 104)) // offset to convert to uint64 128 is index -24 + dstAddress := mload(add(data, sub(add(128, sizeOfSrcAddress), 4))) // offset to convert to address 12 -8 + } + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 136, sizeOfSrcAddress); // 128 + 8 + + uint payloadSize = size.sub(20).sub(sizeOfSrcAddress); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, sizeOfSrcAddress.add(156), payloadSize); // 148 + 8 + return + LayerZeroPacket.Packet( + srcChain, + dstChainId, + nonce, + address(dstAddress), + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/MPTValidator01.sol b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidator01.sol new file mode 100644 index 0000000..68b9f6d --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidator01.sol @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./utility/LayerZeroPacket.sol"; +import "./utility/UltraLightNodeEVMDecoder.sol"; +import "../interfaces/IValidationLibraryHelperV2.sol"; +import "../interfaces/ILayerZeroValidationLibrary.sol"; + +interface IStargate { + // Stargate objects for abi encoding / decoding + struct SwapObj { + uint amount; + uint eqFee; + uint eqReward; + uint lpFee; + uint protocolFee; + uint lkbRemove; + } + + struct CreditObj { + uint credits; + uint idealBalance; + } +} + +contract MPTValidator01 is ILayerZeroValidationLibrary, IValidationLibraryHelperV2 { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + + uint8 public proofType = 1; + uint8 public utilsVersion = 4; + bytes32 public constant PACKET_SIGNATURE = 0xe9bded5f24a4168e4f3bf44e00298c993b22376aad8c58c7dda9718a54cbea82; + + address public immutable stargateBridgeAddress; + address public immutable stargateTokenAddress; + + constructor(address _stargateBridgeAddress, address _stargateTokenAddress) { + stargateBridgeAddress = _stargateBridgeAddress; + stargateTokenAddress = _stargateTokenAddress; + } + + function validateProof( + bytes32 _receiptsRoot, + bytes calldata _transactionProof, + uint _remoteAddressSize + ) external view override returns (LayerZeroPacket.Packet memory packet) { + require(_remoteAddressSize > 0, "ProofLib: invalid address size"); + (bytes[] memory proof, uint[] memory receiptSlotIndex, uint logIndex) = abi.decode( + _transactionProof, + (bytes[], uint[], uint) + ); + + ULNLog memory log = _getVerifiedLog(_receiptsRoot, receiptSlotIndex, logIndex, proof); + require(log.topicZeroSig == PACKET_SIGNATURE, "ProofLib: packet not recognized"); //data + + packet = LayerZeroPacket.getPacketV2(log.data, _remoteAddressSize, log.contractAddress); + + if (packet.dstAddress == stargateBridgeAddress) packet.payload = _secureStgPayload(packet.payload); + + if (packet.dstAddress == stargateTokenAddress) packet.payload = _secureStgTokenPayload(packet.payload); + + return packet; + } + + function _secureStgTokenPayload(bytes memory _payload) internal pure returns (bytes memory) { + (bytes memory toAddressBytes, uint qty) = abi.decode(_payload, (bytes, uint)); + + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + if (toAddress == address(0)) { + address deadAddress = address(0x000000000000000000000000000000000000dEaD); + bytes memory newToAddressBytes = abi.encodePacked(deadAddress); + return abi.encode(newToAddressBytes, qty); + } + + // default to return the original payload + return _payload; + } + + function _secureStgPayload(bytes memory _payload) internal view returns (bytes memory) { + // functionType is uint8 even though the encoding will take up the side of uint256 + uint8 functionType; + assembly { + functionType := mload(add(_payload, 32)) + } + + // TYPE_SWAP_REMOTE == 1 && only if the payload has a payload + // only swapRemote inside of stargate can call sgReceive on an user supplied to address + // thus we do not care about the other type functions even if the toAddress is overly long. + if (functionType == 1) { + // decode the _payload with its types + ( + , + uint srcPoolId, + uint dstPoolId, + uint dstGasForCall, + IStargate.CreditObj memory c, + IStargate.SwapObj memory s, + bytes memory toAddressBytes, + bytes memory contractCallPayload + ) = abi.decode(_payload, (uint8, uint, uint, uint, IStargate.CreditObj, IStargate.SwapObj, bytes, bytes)); + + // if contractCallPayload.length > 0 need to check if the to address is a contract or not + if (contractCallPayload.length > 0) { + // otherwise, need to check if the payload can be delivered to the toAddress + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + // check if the toAddress is a contract. We are not concerned about addresses that pretend to be wallets. because worst case we just delete their payload if being malicious + // we can guarantee that if a size > 0, then the contract is definitely a contract address in this context + uint size; + assembly { + size := extcodesize(toAddress) + } + + if (size == 0) { + // size == 0 indicates its not a contract, payload wont be delivered + // secure the _payload to make sure funds can be delivered to the toAddress + bytes memory newToAddressBytes = abi.encodePacked(toAddress); + bytes memory securePayload = abi.encode( + functionType, + srcPoolId, + dstPoolId, + dstGasForCall, + c, + s, + newToAddressBytes, + bytes("") + ); + return securePayload; + } + } + } + + // default to return the original payload + return _payload; + } + + function secureStgTokenPayload(bytes memory _payload) external pure returns (bytes memory) { + return _secureStgTokenPayload(_payload); + } + + function secureStgPayload(bytes memory _payload) external view returns (bytes memory) { + return _secureStgPayload(_payload); + } + + function _getVerifiedLog( + bytes32 hashRoot, + uint[] memory paths, + uint logIndex, + bytes[] memory proof + ) internal pure returns (ULNLog memory) { + require(paths.length == proof.length, "ProofLib: invalid proof size"); + require(proof.length > 0, "ProofLib: proof size must > 0"); + RLPDecode.RLPItem memory item; + bytes memory proofBytes; + + for (uint i = 0; i < proof.length; i++) { + proofBytes = proof[i]; + require(hashRoot == keccak256(proofBytes), "ProofLib: invalid hashlink"); + item = RLPDecode.toRlpItem(proofBytes).safeGetItemByIndex(paths[i]); + if (i < proof.length - 1) hashRoot = bytes32(item.toUint()); + } + + // burning status + gasUsed + logBloom + RLPDecode.RLPItem memory logItem = item.typeOffset().safeGetItemByIndex(3); + RLPDecode.Iterator memory it = logItem.safeGetItemByIndex(logIndex).iterator(); + ULNLog memory log; + log.contractAddress = bytes32(it.next().toUint()); + log.topicZeroSig = bytes32(it.next().safeGetItemByIndex(0).toUint()); + log.data = it.next().toBytes(); + + return log; + } + + function getUtilsVersion() external view override returns (uint8) { + return utilsVersion; + } + + function getProofType() external view override returns (uint8) { + return proofType; + } + + function getVerifyLog( + bytes32 hashRoot, + uint[] memory receiptSlotIndex, + uint logIndex, + bytes[] memory proof + ) external pure override returns (ULNLog memory) { + return _getVerifiedLog(hashRoot, receiptSlotIndex, logIndex, proof); + } + + function getPacket( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure override returns (LayerZeroPacket.Packet memory) { + return LayerZeroPacket.getPacketV2(data, sizeOfSrcAddress, ulnAddress); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorStgV3.sol b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorStgV3.sol new file mode 100644 index 0000000..7da59b6 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorStgV3.sol @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./utility/LayerZeroPacket.sol"; +import "./utility/Buffer.sol"; +import "../interfaces/ILayerZeroValidationLibrary.sol"; +import "./utility/UltraLightNodeEVMDecoder.sol"; + +contract MPTValidatorStgV3 is ILayerZeroValidationLibrary { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + using Buffer for Buffer.buffer; + using SafeMath for uint; + + bytes32 public constant PACKET_SIGNATURE = 0xe8d23d927749ec8e512eb885679c2977d57068839d8cca1a85685dbbea0648f6; + + address public immutable stargateBridgeAddress; + address public immutable stgTokenAddress; + uint16 public immutable localChainId; + + constructor(address _stargateBridgeAddress, address _stgTokenAddress, uint16 _localChainId) { + stargateBridgeAddress = _stargateBridgeAddress; + stgTokenAddress = _stgTokenAddress; + localChainId = _localChainId; + } + + struct ULNLog { + bytes32 contractAddress; + bytes32 topicZeroSig; + bytes data; + } + + // Stargate objects for abi encoding / decoding + struct SwapObj { + uint amount; + uint eqFee; + uint eqReward; + uint lpFee; + uint protocolFee; + uint lkbRemove; + } + + struct CreditObj { + uint credits; + uint idealBalance; + } + + function validateProof( + bytes32 _receiptsRoot, + bytes calldata _transactionProof, + uint _remoteAddressSize + ) external view override returns (LayerZeroPacket.Packet memory) { + (uint16 remoteChainId, bytes[] memory proof, uint[] memory receiptSlotIndex, uint logIndex) = abi.decode( + _transactionProof, + (uint16, bytes[], uint[], uint) + ); + + ULNLog memory log = _getVerifiedLog(_receiptsRoot, receiptSlotIndex, logIndex, proof); + require(log.topicZeroSig == PACKET_SIGNATURE, "ProofLib: packet not recognized"); //data + + LayerZeroPacket.Packet memory packet = _getPacket( + log.data, + remoteChainId, + _remoteAddressSize, + log.contractAddress + ); + + require(packet.dstChainId == localChainId, "ProofLib: invalid destination chain ID"); + + if (packet.dstAddress == stargateBridgeAddress) packet.payload = _secureStgPayload(packet.payload); + + if (packet.dstAddress == stgTokenAddress) packet.payload = _secureStgTokenPayload(packet.payload); + + return packet; + } + + function _secureStgTokenPayload(bytes memory _payload) internal pure returns (bytes memory) { + (bytes memory toAddressBytes, uint qty) = abi.decode(_payload, (bytes, uint)); + + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + if (toAddress == address(0)) { + address deadAddress = address(0x000000000000000000000000000000000000dEaD); + bytes memory newToAddressBytes = abi.encodePacked(deadAddress); + return abi.encode(newToAddressBytes, qty); + } + + // default to return the original payload + return _payload; + } + + function _secureStgPayload(bytes memory _payload) internal view returns (bytes memory) { + // functionType is uint8 even though the encoding will take up the side of uint256 + uint8 functionType; + assembly { + functionType := mload(add(_payload, 32)) + } + + // TYPE_SWAP_REMOTE == 1 && only if the payload has a payload + // only swapRemote inside of stargate can call sgReceive on an user supplied to address + // thus we do not care about the other type functions even if the toAddress is overly long. + if (functionType == 1) { + // decode the _payload with its types + ( + , + uint srcPoolId, + uint dstPoolId, + uint dstGasForCall, + CreditObj memory c, + SwapObj memory s, + bytes memory toAddressBytes, + bytes memory contractCallPayload + ) = abi.decode(_payload, (uint8, uint, uint, uint, CreditObj, SwapObj, bytes, bytes)); + + // if contractCallPayload.length > 0 need to check if the to address is a contract or not + if (contractCallPayload.length > 0) { + // otherwise, need to check if the payload can be delivered to the toAddress + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + // check if the toAddress is a contract. We are not concerned about addresses that pretend to be wallets. because worst case we just delete their payload if being malicious + // we can guarantee that if a size > 0, then the contract is definitely a contract address in this context + uint size; + assembly { + size := extcodesize(toAddress) + } + + if (size == 0) { + // size == 0 indicates its not a contract, payload wont be delivered + // secure the _payload to make sure funds can be delivered to the toAddress + bytes memory newToAddressBytes = abi.encodePacked(toAddress); + bytes memory securePayload = abi.encode( + functionType, + srcPoolId, + dstPoolId, + dstGasForCall, + c, + s, + newToAddressBytes, + bytes("") + ); + return securePayload; + } + } + } + + // default to return the original payload + return _payload; + } + + function secureStgTokenPayload(bytes memory _payload) external pure returns (bytes memory) { + return _secureStgTokenPayload(_payload); + } + + function secureStgPayload(bytes memory _payload) external view returns (bytes memory) { + return _secureStgPayload(_payload); + } + + function _getVerifiedLog( + bytes32 hashRoot, + uint[] memory paths, + uint logIndex, + bytes[] memory proof + ) internal pure returns (ULNLog memory) { + require(paths.length == proof.length, "ProofLib: invalid proof size"); + + RLPDecode.RLPItem memory item; + bytes memory proofBytes; + + for (uint i = 0; i < proof.length; i++) { + proofBytes = proof[i]; + require(hashRoot == keccak256(proofBytes), "ProofLib: invalid hashlink"); + item = RLPDecode.toRlpItem(proofBytes).safeGetItemByIndex(paths[i]); + if (i < proof.length - 1) hashRoot = bytes32(item.toUint()); + } + + // burning status + gasUsed + logBloom + RLPDecode.RLPItem memory logItem = item.typeOffset().safeGetItemByIndex(3); + RLPDecode.Iterator memory it = logItem.safeGetItemByIndex(logIndex).iterator(); + ULNLog memory log; + log.contractAddress = bytes32(it.next().toUint()); + log.topicZeroSig = bytes32(it.next().getItemByIndex(0).toUint()); + log.data = it.next().toBytes(); + + return log; + } + + // profiling and test + function getVerifyLog( + bytes32 hashRoot, + uint[] memory receiptSlotIndex, + uint logIndex, + bytes[] memory proof + ) external pure returns (ULNLog memory) { + return _getVerifiedLog(hashRoot, receiptSlotIndex, logIndex, proof); + } + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure returns (LayerZeroPacket.Packet memory) { + return _getPacket(data, srcChain, sizeOfSrcAddress, ulnAddress); + } + + function _getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + uint16 dstChainId; + address dstAddress; + uint size; + uint64 nonce; + + // The log consists of the destination chain id and then a bytes payload + // 0--------------------------------------------31 + // 0 | destination chain id + // 32 | defines bytes array + // 64 | + // 96 | bytes array size + // 128 | payload + assembly { + dstChainId := mload(add(data, 32)) + size := mload(add(data, 96)) /// size of the byte array + nonce := mload(add(data, 104)) // offset to convert to uint64 128 is index -24 + dstAddress := mload(add(data, sub(add(128, sizeOfSrcAddress), 4))) // offset to convert to address 12 -8 + } + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 136, sizeOfSrcAddress); // 128 + 8 + + uint payloadSize = size.sub(20).sub(sizeOfSrcAddress); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, sizeOfSrcAddress.add(156), payloadSize); // 148 + 8 + return + LayerZeroPacket.Packet( + srcChain, + dstChainId, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV2.sol b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV2.sol new file mode 100644 index 0000000..e26b017 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV2.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./utility/LayerZeroPacket.sol"; +import "./utility/Buffer.sol"; +import "../interfaces/ILayerZeroValidationLibrary.sol"; +import "./utility/UltraLightNodeEVMDecoder.sol"; + +contract MPTValidatorV2 is ILayerZeroValidationLibrary { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + using Buffer for Buffer.buffer; + using SafeMath for uint; + + bytes32 public constant PACKET_SIGNATURE = 0xe8d23d927749ec8e512eb885679c2977d57068839d8cca1a85685dbbea0648f6; + + struct ULNLog { + bytes32 contractAddress; + bytes32 topicZeroSig; + bytes data; + } + + function validateProof( + bytes32 _receiptsRoot, + bytes calldata _transactionProof, + uint _remoteAddressSize + ) external pure override returns (LayerZeroPacket.Packet memory packet) { + (uint16 remoteChainId, bytes[] memory proof, uint[] memory receiptSlotIndex, uint logIndex) = abi.decode( + _transactionProof, + (uint16, bytes[], uint[], uint) + ); + + ULNLog memory log = _getVerifiedLog(_receiptsRoot, receiptSlotIndex, logIndex, proof); + require(log.topicZeroSig == PACKET_SIGNATURE, "ProofLib: packet not recognized"); //data + + return _getPacket(log.data, remoteChainId, _remoteAddressSize, log.contractAddress); + } + + function _getVerifiedLog( + bytes32 hashRoot, + uint[] memory paths, + uint logIndex, + bytes[] memory proof + ) internal pure returns (ULNLog memory) { + require(paths.length == proof.length, "ProofLib: invalid proof size"); + + RLPDecode.RLPItem memory item; + bytes memory proofBytes; + + for (uint i = 0; i < proof.length; i++) { + proofBytes = proof[i]; + require(hashRoot == keccak256(proofBytes), "ProofLib: invalid hashlink"); + item = RLPDecode.toRlpItem(proofBytes).safeGetItemByIndex(paths[i]); + if (i < proof.length - 1) hashRoot = bytes32(item.toUint()); + } + + // burning status + gasUsed + logBloom + RLPDecode.RLPItem memory logItem = item.typeOffset().safeGetItemByIndex(3); + RLPDecode.Iterator memory it = logItem.safeGetItemByIndex(logIndex).iterator(); + ULNLog memory log; + log.contractAddress = bytes32(it.next().toUint()); + log.topicZeroSig = bytes32(it.next().getItemByIndex(0).toUint()); + log.data = it.next().toBytes(); + + return log; + } + + // profiling and test + function getVerifyLog( + bytes32 hashRoot, + uint[] memory receiptSlotIndex, + uint logIndex, + bytes[] memory proof + ) external pure returns (ULNLog memory) { + return _getVerifiedLog(hashRoot, receiptSlotIndex, logIndex, proof); + } + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure returns (LayerZeroPacket.Packet memory) { + return _getPacket(data, srcChain, sizeOfSrcAddress, ulnAddress); + } + + function _getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + uint16 dstChainId; + address dstAddress; + uint size; + uint64 nonce; + + // The log consists of the destination chain id and then a bytes payload + // 0--------------------------------------------31 + // 0 | destination chain id + // 32 | defines bytes array + // 64 | + // 96 | bytes array size + // 128 | payload + assembly { + dstChainId := mload(add(data, 32)) + size := mload(add(data, 96)) /// size of the byte array + nonce := mload(add(data, 104)) // offset to convert to uint64 128 is index -24 + dstAddress := mload(add(data, sub(add(128, sizeOfSrcAddress), 4))) // offset to convert to address 12 -8 + } + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 136, sizeOfSrcAddress); // 128 + 8 + + uint payloadSize = size.sub(20).sub(sizeOfSrcAddress); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, sizeOfSrcAddress.add(156), payloadSize); // 148 + 8 + return + LayerZeroPacket.Packet( + srcChain, + dstChainId, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV4.sol b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV4.sol new file mode 100644 index 0000000..47c5e18 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV4.sol @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "../interfaces/IValidationLibraryHelper.sol"; +import "./utility/LayerZeroPacket.sol"; +import "./utility/UltraLightNodeEVMDecoder.sol"; + +interface IUltraLightNode { + struct BlockData { + uint confirmations; + bytes32 data; + } + + struct ApplicationConfiguration { + uint16 inboundProofLibraryVersion; + uint64 inboundBlockConfirmations; + address relayer; + uint16 outboundProofType; + uint64 outboundBlockConfirmations; + address oracle; + } + + function getAppConfig( + uint16 _chainId, + address userApplicationAddress + ) external view returns (ApplicationConfiguration memory); + + function getBlockHeaderData( + address _oracle, + uint16 _remoteChainId, + bytes32 _lookupHash + ) external view returns (BlockData memory blockData); +} + +interface IStargate { + // Stargate objects for abi encoding / decoding + struct SwapObj { + uint amount; + uint eqFee; + uint eqReward; + uint lpFee; + uint protocolFee; + uint lkbRemove; + } + + struct CreditObj { + uint credits; + uint idealBalance; + } +} + +contract MPTValidatorV4 is ILayerZeroValidationLibrary, IValidationLibraryHelper { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + using LayerZeroPacket for bytes; + + uint8 public utilsVersion = 3; + bytes32 public constant PACKET_SIGNATURE = 0xe8d23d927749ec8e512eb885679c2977d57068839d8cca1a85685dbbea0648f6; + + address public immutable stargateBridgeAddress; + address public immutable stgTokenAddress; + uint16 public immutable localChainId; + IUltraLightNode public immutable uln; + + constructor(address _stargateBridgeAddress, address _stgTokenAddress, uint16 _localChainId, address _ulnAddress) { + stargateBridgeAddress = _stargateBridgeAddress; + stgTokenAddress = _stgTokenAddress; + localChainId = _localChainId; + uln = IUltraLightNode(_ulnAddress); + } + + function validateProof( + bytes32 _receiptsRoot, + bytes calldata _transactionProof, + uint _remoteAddressSize + ) external view override returns (LayerZeroPacket.Packet memory) { + require(_remoteAddressSize > 0, "ProofLib: invalid address size"); + + ( + uint16 remoteChainId, + bytes32 blockHash, + bytes[] memory proof, + uint[] memory receiptSlotIndex, + uint logIndex + ) = abi.decode(_transactionProof, (uint16, bytes32, bytes[], uint[], uint)); + + ULNLog memory log = _getVerifiedLog(_receiptsRoot, receiptSlotIndex, logIndex, proof); + require(log.topicZeroSig == PACKET_SIGNATURE, "ProofLib: packet not recognized"); //data + + LayerZeroPacket.Packet memory packet = log.data.getPacket( + remoteChainId, + _remoteAddressSize, + log.contractAddress + ); + + _assertMessagePath(packet, blockHash, _receiptsRoot); + + if (packet.dstAddress == stargateBridgeAddress) packet.payload = _secureStgPayload(packet.payload); + + if (packet.dstAddress == stgTokenAddress) packet.payload = _secureStgTokenPayload(packet.payload); + + return packet; + } + + function _assertMessagePath( + LayerZeroPacket.Packet memory packet, + bytes32 blockHash, + bytes32 receiptsRoot + ) internal view { + require(packet.dstChainId == localChainId, "ProofLib: invalid destination chain ID"); + + IUltraLightNode.ApplicationConfiguration memory appConfig = uln.getAppConfig( + packet.srcChainId, + packet.dstAddress + ); + IUltraLightNode.BlockData memory blockData = uln.getBlockHeaderData( + appConfig.oracle, + packet.srcChainId, + blockHash + ); + + require(blockData.data == receiptsRoot, "ProofLib: invalid receipt root"); + + require( + blockData.confirmations >= appConfig.inboundBlockConfirmations, + "ProofLib: not enough block confirmations" + ); + } + + function _secureStgTokenPayload(bytes memory _payload) internal pure returns (bytes memory) { + (bytes memory toAddressBytes, uint qty) = abi.decode(_payload, (bytes, uint)); + + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + if (toAddress == address(0)) { + address deadAddress = address(0x000000000000000000000000000000000000dEaD); + bytes memory newToAddressBytes = abi.encodePacked(deadAddress); + return abi.encode(newToAddressBytes, qty); + } + + // default to return the original payload + return _payload; + } + + function _secureStgPayload(bytes memory _payload) internal view returns (bytes memory) { + // functionType is uint8 even though the encoding will take up the side of uint256 + uint8 functionType; + assembly { + functionType := mload(add(_payload, 32)) + } + + // TYPE_SWAP_REMOTE == 1 && only if the payload has a payload + // only swapRemote inside of stargate can call sgReceive on an user supplied to address + // thus we do not care about the other type functions even if the toAddress is overly long. + if (functionType == 1) { + // decode the _payload with its types + ( + , + uint srcPoolId, + uint dstPoolId, + uint dstGasForCall, + IStargate.CreditObj memory c, + IStargate.SwapObj memory s, + bytes memory toAddressBytes, + bytes memory contractCallPayload + ) = abi.decode(_payload, (uint8, uint, uint, uint, IStargate.CreditObj, IStargate.SwapObj, bytes, bytes)); + + // if contractCallPayload.length > 0 need to check if the to address is a contract or not + if (contractCallPayload.length > 0) { + // otherwise, need to check if the payload can be delivered to the toAddress + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + // check if the toAddress is a contract. We are not concerned about addresses that pretend to be wallets. because worst case we just delete their payload if being malicious + // we can guarantee that if a size > 0, then the contract is definitely a contract address in this context + uint size; + assembly { + size := extcodesize(toAddress) + } + + if (size == 0) { + // size == 0 indicates its not a contract, payload wont be delivered + // secure the _payload to make sure funds can be delivered to the toAddress + bytes memory newToAddressBytes = abi.encodePacked(toAddress); + bytes memory securePayload = abi.encode( + functionType, + srcPoolId, + dstPoolId, + dstGasForCall, + c, + s, + newToAddressBytes, + bytes("") + ); + return securePayload; + } + } + } + + // default to return the original payload + return _payload; + } + + function secureStgTokenPayload(bytes memory _payload) external pure returns (bytes memory) { + return _secureStgTokenPayload(_payload); + } + + function secureStgPayload(bytes memory _payload) external view returns (bytes memory) { + return _secureStgPayload(_payload); + } + + function _getVerifiedLog( + bytes32 hashRoot, + uint[] memory paths, + uint logIndex, + bytes[] memory proof + ) internal pure returns (ULNLog memory) { + require(paths.length == proof.length, "ProofLib: invalid proof size"); + require(proof.length > 0, "ProofLib: proof size must > 0"); + RLPDecode.RLPItem memory item; + bytes memory proofBytes; + + for (uint i = 0; i < proof.length; i++) { + proofBytes = proof[i]; + require(hashRoot == keccak256(proofBytes), "ProofLib: invalid hashlink"); + item = RLPDecode.toRlpItem(proofBytes).safeGetItemByIndex(paths[i]); + if (i < proof.length - 1) hashRoot = bytes32(item.toUint()); + } + + // burning status + gasUsed + logBloom + RLPDecode.RLPItem memory logItem = item.typeOffset().safeGetItemByIndex(3); + RLPDecode.Iterator memory it = logItem.safeGetItemByIndex(logIndex).iterator(); + ULNLog memory log; + log.contractAddress = bytes32(it.next().toUint()); + log.topicZeroSig = bytes32(it.next().safeGetItemByIndex(0).toUint()); + log.data = it.next().toBytes(); + + return log; + } + + function getUtilsVersion() external view override returns (uint8) { + return utilsVersion; + } + + function getVerifyLog( + bytes32 hashRoot, + uint[] memory receiptSlotIndex, + uint logIndex, + bytes[] memory proof + ) external pure override returns (ULNLog memory) { + return _getVerifiedLog(hashRoot, receiptSlotIndex, logIndex, proof); + } + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure override returns (LayerZeroPacket.Packet memory) { + return data.getPacket(srcChain, sizeOfSrcAddress, ulnAddress); + } + + // profiling and test + function assertMessagePath( + LayerZeroPacket.Packet memory packet, + bytes32 blockHash, + bytes32 receiptsRoot + ) external view { + _assertMessagePath(packet, blockHash, receiptsRoot); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV5.sol b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV5.sol new file mode 100644 index 0000000..cb7d777 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/MPTValidatorV5.sol @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; +pragma abicoder v2; + +import "../interfaces/IValidationLibraryHelper.sol"; +import "./utility/LayerZeroPacket.sol"; +import "./utility/UltraLightNodeEVMDecoder.sol"; + +interface IUltraLightNode { + struct BlockData { + uint confirmations; + bytes32 data; + } + + struct ApplicationConfiguration { + uint16 inboundProofLibraryVersion; + uint64 inboundBlockConfirmations; + address relayer; + uint16 outboundProofType; + uint64 outboundBlockConfirmations; + address oracle; + } + + function getAppConfig( + uint16 _chainId, + address userApplicationAddress + ) external view returns (ApplicationConfiguration memory); + + function getBlockHeaderData( + address _oracle, + uint16 _remoteChainId, + bytes32 _lookupHash + ) external view returns (BlockData memory blockData); +} + +interface IStargate { + // Stargate objects for abi encoding / decoding + struct SwapObj { + uint amount; + uint eqFee; + uint eqReward; + uint lpFee; + uint protocolFee; + uint lkbRemove; + } + + struct CreditObj { + uint credits; + uint idealBalance; + } +} + +contract MPTValidatorV5 is ILayerZeroValidationLibrary, IValidationLibraryHelper { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + using LayerZeroPacket for bytes; + + uint8 public utilsVersion = 3; + bytes32 public constant PACKET_SIGNATURE = 0xe8d23d927749ec8e512eb885679c2977d57068839d8cca1a85685dbbea0648f6; + + address public immutable stargateBridgeAddress; + address public immutable stgTokenAddress; + address public immutable relayerAddress; + uint16 public immutable localChainId; + IUltraLightNode public immutable uln; + + constructor( + address _stargateBridgeAddress, + address _stgTokenAddress, + uint16 _localChainId, + address _ulnAddress, + address _relayerAddress + ) { + stargateBridgeAddress = _stargateBridgeAddress; + stgTokenAddress = _stgTokenAddress; + localChainId = _localChainId; + uln = IUltraLightNode(_ulnAddress); + relayerAddress = _relayerAddress; + } + + function validateProof( + bytes32 _receiptsRoot, + bytes calldata _transactionProof, + uint _remoteAddressSize + ) external view override returns (LayerZeroPacket.Packet memory) { + require(_remoteAddressSize > 0, "ProofLib: invalid address size"); + + ( + uint16 remoteChainId, + bytes32 blockHash, + bytes[] memory proof, + uint[] memory receiptSlotIndex, + uint logIndex + ) = abi.decode(_transactionProof, (uint16, bytes32, bytes[], uint[], uint)); + + ULNLog memory log = _getVerifiedLog(_receiptsRoot, receiptSlotIndex, logIndex, proof); + require(log.topicZeroSig == PACKET_SIGNATURE, "ProofLib: packet not recognized"); //data + + LayerZeroPacket.Packet memory packet = log.data.getPacket( + remoteChainId, + _remoteAddressSize, + log.contractAddress + ); + + _assertMessagePath(packet, blockHash, _receiptsRoot); + + if (packet.dstAddress == stargateBridgeAddress) packet.payload = _secureStgPayload(packet.payload); + + if (packet.dstAddress == stgTokenAddress) packet.payload = _secureStgTokenPayload(packet.payload); + + return packet; + } + + function _assertMessagePath( + LayerZeroPacket.Packet memory packet, + bytes32 blockHash, + bytes32 receiptsRoot + ) internal view { + require(packet.dstChainId == localChainId, "ProofLib: invalid destination chain ID"); + + IUltraLightNode.ApplicationConfiguration memory appConfig = uln.getAppConfig( + packet.srcChainId, + packet.dstAddress + ); + IUltraLightNode.BlockData memory blockData = uln.getBlockHeaderData( + appConfig.oracle, + packet.srcChainId, + blockHash + ); + require(appConfig.relayer == relayerAddress, "ProofLib: invalid relayer"); + + require(blockData.data == receiptsRoot, "ProofLib: invalid receipt root"); + + require( + blockData.confirmations >= appConfig.inboundBlockConfirmations, + "ProofLib: not enough block confirmations" + ); + } + + function _secureStgTokenPayload(bytes memory _payload) internal pure returns (bytes memory) { + (bytes memory toAddressBytes, uint qty) = abi.decode(_payload, (bytes, uint)); + + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + if (toAddress == address(0)) { + address deadAddress = address(0x000000000000000000000000000000000000dEaD); + bytes memory newToAddressBytes = abi.encodePacked(deadAddress); + return abi.encode(newToAddressBytes, qty); + } + + // default to return the original payload + return _payload; + } + + function _secureStgPayload(bytes memory _payload) internal view returns (bytes memory) { + // functionType is uint8 even though the encoding will take up the side of uint256 + uint8 functionType; + assembly { + functionType := mload(add(_payload, 32)) + } + + // TYPE_SWAP_REMOTE == 1 && only if the payload has a payload + // only swapRemote inside of stargate can call sgReceive on an user supplied to address + // thus we do not care about the other type functions even if the toAddress is overly long. + if (functionType == 1) { + // decode the _payload with its types + ( + , + uint srcPoolId, + uint dstPoolId, + uint dstGasForCall, + IStargate.CreditObj memory c, + IStargate.SwapObj memory s, + bytes memory toAddressBytes, + bytes memory contractCallPayload + ) = abi.decode(_payload, (uint8, uint, uint, uint, IStargate.CreditObj, IStargate.SwapObj, bytes, bytes)); + + // if contractCallPayload.length > 0 need to check if the to address is a contract or not + if (contractCallPayload.length > 0) { + // otherwise, need to check if the payload can be delivered to the toAddress + address toAddress = address(0); + if (toAddressBytes.length > 0) { + assembly { + toAddress := mload(add(toAddressBytes, 20)) + } + } + + // check if the toAddress is a contract. We are not concerned about addresses that pretend to be wallets. because worst case we just delete their payload if being malicious + // we can guarantee that if a size > 0, then the contract is definitely a contract address in this context + uint size; + assembly { + size := extcodesize(toAddress) + } + + if (size == 0) { + // size == 0 indicates its not a contract, payload wont be delivered + // secure the _payload to make sure funds can be delivered to the toAddress + bytes memory newToAddressBytes = abi.encodePacked(toAddress); + bytes memory securePayload = abi.encode( + functionType, + srcPoolId, + dstPoolId, + dstGasForCall, + c, + s, + newToAddressBytes, + bytes("") + ); + return securePayload; + } + } + } + + // default to return the original payload + return _payload; + } + + function secureStgTokenPayload(bytes memory _payload) external pure returns (bytes memory) { + return _secureStgTokenPayload(_payload); + } + + function secureStgPayload(bytes memory _payload) external view returns (bytes memory) { + return _secureStgPayload(_payload); + } + + function _getVerifiedLog( + bytes32 hashRoot, + uint[] memory paths, + uint logIndex, + bytes[] memory proof + ) internal pure returns (ULNLog memory) { + require(paths.length == proof.length, "ProofLib: invalid proof size"); + require(proof.length > 0, "ProofLib: proof size must > 0"); + RLPDecode.RLPItem memory item; + bytes memory proofBytes; + + for (uint i = 0; i < proof.length; i++) { + proofBytes = proof[i]; + require(hashRoot == keccak256(proofBytes), "ProofLib: invalid hashlink"); + item = RLPDecode.toRlpItem(proofBytes).safeGetItemByIndex(paths[i]); + if (i < proof.length - 1) hashRoot = bytes32(item.toUint()); + } + + // burning status + gasUsed + logBloom + RLPDecode.RLPItem memory logItem = item.typeOffset().safeGetItemByIndex(3); + RLPDecode.Iterator memory it = logItem.safeGetItemByIndex(logIndex).iterator(); + ULNLog memory log; + log.contractAddress = bytes32(it.next().toUint()); + log.topicZeroSig = bytes32(it.next().safeGetItemByIndex(0).toUint()); + log.data = it.next().toBytes(); + + return log; + } + + function getUtilsVersion() external view override returns (uint8) { + return utilsVersion; + } + + function getVerifyLog( + bytes32 hashRoot, + uint[] memory receiptSlotIndex, + uint logIndex, + bytes[] memory proof + ) external pure override returns (ULNLog memory) { + return _getVerifiedLog(hashRoot, receiptSlotIndex, logIndex, proof); + } + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure override returns (LayerZeroPacket.Packet memory) { + return data.getPacket(srcChain, sizeOfSrcAddress, ulnAddress); + } + + // profiling and test + function assertMessagePath( + LayerZeroPacket.Packet memory packet, + bytes32 blockHash, + bytes32 receiptsRoot + ) external view { + _assertMessagePath(packet, blockHash, receiptsRoot); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/utility/Buffer.sol b/endpoint/contracts/V1Contracts/contracts/proof/utility/Buffer.sol new file mode 100644 index 0000000..ea6af00 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/utility/Buffer.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: BUSL-1.1 + +// https://github.com/ensdomains/buffer + +pragma solidity ^0.7.0; + +/** + * @dev A library for working with mutable byte buffers in Solidity. + * + * Byte buffers are mutable and expandable, and provide a variety of primitives + * for writing to them. At any time you can fetch a bytes object containing the + * current contents of the buffer. The bytes object should not be stored between + * operations, as it may change due to resizing of the buffer. + */ +library Buffer { + /** + * @dev Represents a mutable buffer. Buffers have a current value (buf) and + * a capacity. The capacity may be longer than the current value, in + * which case it can be extended without the need to allocate more memory. + */ + struct buffer { + bytes buf; + uint capacity; + } + + /** + * @dev Initializes a buffer with an initial capacity.a co + * @param buf The buffer to initialize. + * @param capacity The number of bytes of space to allocate the buffer. + * @return The buffer, for chaining. + */ + function init(buffer memory buf, uint capacity) internal pure returns (buffer memory) { + if (capacity % 32 != 0) { + capacity += 32 - (capacity % 32); + } + // Allocate space for the buffer data + buf.capacity = capacity; + assembly { + let ptr := mload(0x40) + mstore(buf, ptr) + mstore(ptr, 0) + mstore(0x40, add(32, add(ptr, capacity))) + } + return buf; + } + + /** + * @dev Writes a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The start offset to write to. + * @param rawData The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function writeRawBytes( + buffer memory buf, + uint off, + bytes memory rawData, + uint offData, + uint len + ) internal pure returns (buffer memory) { + if (off + len > buf.capacity) { + resize(buf, max(buf.capacity, len + off) * 2); + } + + uint dest; + uint src; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Start address = buffer address + offset + sizeof(buffer length) + dest := add(add(bufptr, 32), off) + // Update buffer length if we're extending it + if gt(add(len, off), buflen) { + mstore(bufptr, add(len, off)) + } + src := add(rawData, offData) + } + + // Copy word-length chunks while possible + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint mask = 256 ** (32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + + return buf; + } + + /** + * @dev Writes a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The start offset to write to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns (buffer memory) { + require(len <= data.length); + + if (off + len > buf.capacity) { + resize(buf, max(buf.capacity, len + off) * 2); + } + + uint dest; + uint src; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Start address = buffer address + offset + sizeof(buffer length) + dest := add(add(bufptr, 32), off) + // Update buffer length if we're extending it + if gt(add(len, off), buflen) { + mstore(bufptr, add(len, off)) + } + src := add(data, 32) + } + + // Copy word-length chunks while possible + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint mask = 256 ** (32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + + return buf; + } + + function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, data.length); + } + + function resize(buffer memory buf, uint capacity) private pure { + bytes memory oldbuf = buf.buf; + init(buf, capacity); + append(buf, oldbuf); + } + + function max(uint a, uint b) private pure returns (uint) { + if (a > b) { + return a; + } + return b; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/utility/LayerZeroPacket.sol b/endpoint/contracts/V1Contracts/contracts/proof/utility/LayerZeroPacket.sol new file mode 100644 index 0000000..2837872 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/utility/LayerZeroPacket.sol @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "./Buffer.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; + +library LayerZeroPacket { + using Buffer for Buffer.buffer; + using SafeMath for uint; + + struct Packet { + uint16 srcChainId; + uint16 dstChainId; + uint64 nonce; + address dstAddress; + bytes srcAddress; + bytes32 ulnAddress; + bytes payload; + } + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + uint16 dstChainId; + address dstAddress; + uint size; + uint64 nonce; + + // The log consists of the destination chain id and then a bytes payload + // 0--------------------------------------------31 + // 0 | total bytes size + // 32 | destination chain id + // 64 | bytes offset + // 96 | bytes array size + // 128 | payload + assembly { + dstChainId := mload(add(data, 32)) + size := mload(add(data, 96)) /// size of the byte array + nonce := mload(add(data, 104)) // offset to convert to uint64 128 is index -24 + dstAddress := mload(add(data, sub(add(128, sizeOfSrcAddress), 4))) // offset to convert to address 12 -8 + } + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 136, sizeOfSrcAddress); // 128 + 8 + + uint payloadSize = size.sub(28).sub(sizeOfSrcAddress); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, sizeOfSrcAddress.add(156), payloadSize); // 148 + 8 + return + LayerZeroPacket.Packet( + srcChain, + dstChainId, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } + + function getPacketV2( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + // packet def: abi.encodePacked(nonce, srcChain, srcAddress, dstChain, dstAddress, payload); + // data def: abi.encode(packet) = offset(32) + length(32) + packet + // if from EVM + // 0 - 31 0 - 31 | total bytes size + // 32 - 63 32 - 63 | location + // 64 - 95 64 - 95 | size of the packet + // 96 - 103 96 - 103 | nonce + // 104 - 105 104 - 105 | srcChainId + // 106 - P 106 - 125 | srcAddress, where P = 106 + sizeOfSrcAddress - 1, + // P+1 - P+2 126 - 127 | dstChainId + // P+3 - P+22 128 - 147 | dstAddress + // P+23 - END 148 - END | payload + + // decode the packet + uint256 realSize; + uint64 nonce; + uint16 srcChain; + uint16 dstChain; + address dstAddress; + assembly { + realSize := mload(add(data, 64)) + nonce := mload(add(data, 72)) // 104 - 32 + srcChain := mload(add(data, 74)) // 106 - 32 + dstChain := mload(add(data, add(76, sizeOfSrcAddress))) // P + 3 - 32 = 105 + size + 3 - 32 = 76 + size + dstAddress := mload(add(data, add(96, sizeOfSrcAddress))) // P + 23 - 32 = 105 + size + 23 - 32 = 96 + size + } + + require(srcChain != 0, "LayerZeroPacket: invalid packet"); + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 106, sizeOfSrcAddress); + + uint nonPayloadSize = sizeOfSrcAddress.add(32); // 2 + 2 + 8 + 20, 32 + 20 = 52 if sizeOfSrcAddress == 20 + uint payloadSize = realSize.sub(nonPayloadSize); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, nonPayloadSize.add(96), payloadSize); + + return + LayerZeroPacket.Packet( + srcChain, + dstChain, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } + + function getPacketV3( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + // data def: abi.encodePacked(nonce, srcChain, srcAddress, dstChain, dstAddress, payload); + // if from EVM + // 0 - 31 0 - 31 | total bytes size + // 32 - 39 32 - 39 | nonce + // 40 - 41 40 - 41 | srcChainId + // 42 - P 42 - 61 | srcAddress, where P = 41 + sizeOfSrcAddress, + // P+1 - P+2 62 - 63 | dstChainId + // P+3 - P+22 64 - 83 | dstAddress + // P+23 - END 84 - END | payload + + // decode the packet + uint256 realSize = data.length; + uint nonPayloadSize = sizeOfSrcAddress.add(32); // 2 + 2 + 8 + 20, 32 + 20 = 52 if sizeOfSrcAddress == 20 + require(realSize >= nonPayloadSize, "LayerZeroPacket: invalid packet"); + uint payloadSize = realSize - nonPayloadSize; + + uint64 nonce; + uint16 srcChain; + uint16 dstChain; + address dstAddress; + assembly { + nonce := mload(add(data, 8)) // 40 - 32 + srcChain := mload(add(data, 10)) // 42 - 32 + dstChain := mload(add(data, add(12, sizeOfSrcAddress))) // P + 3 - 32 = 41 + size + 3 - 32 = 12 + size + dstAddress := mload(add(data, add(32, sizeOfSrcAddress))) // P + 23 - 32 = 41 + size + 23 - 32 = 32 + size + } + + require(srcChain != 0, "LayerZeroPacket: invalid packet"); + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 42, sizeOfSrcAddress); + + Buffer.buffer memory payloadBuffer; + if (payloadSize > 0) { + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, nonPayloadSize.add(32), payloadSize); + } + + return + LayerZeroPacket.Packet( + srcChain, + dstChain, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/utility/RLPDecode.sol b/endpoint/contracts/V1Contracts/contracts/proof/utility/RLPDecode.sol new file mode 100644 index 0000000..2a8fbb5 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/utility/RLPDecode.sol @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: BUSL-1.1 + +// https://github.com/hamdiallam/solidity-rlp + +pragma solidity ^0.7.0; + +library RLPDecode { + uint8 constant STRING_SHORT_START = 0x80; + uint8 constant STRING_LONG_START = 0xb8; + uint8 constant LIST_SHORT_START = 0xc0; + uint8 constant LIST_LONG_START = 0xf8; + uint8 constant WORD_SIZE = 32; + + struct RLPItem { + uint len; + uint memPtr; + } + + struct Iterator { + RLPItem item; // Item that's being iterated over. + uint nextPtr; // Position of the next item in the list. + } + + /* + * @dev Returns the next element in the iteration. Reverts if it has not next element. + * @param self The iterator. + * @return The next element in the iteration. + */ + function next(Iterator memory self) internal pure returns (RLPItem memory) { + require(hasNext(self), "RLPDecoder iterator has no next"); + + uint ptr = self.nextPtr; + uint itemLength = _itemLength(ptr); + self.nextPtr = ptr + itemLength; + + return RLPItem(itemLength, ptr); + } + + /* + * @dev Returns true if the iteration has more elements. + * @param self The iterator. + * @return true if the iteration has more elements. + */ + function hasNext(Iterator memory self) internal pure returns (bool) { + RLPItem memory item = self.item; + return self.nextPtr < item.memPtr + item.len; + } + + /* + * @param item RLP encoded bytes + */ + + function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { + uint memPtr; + assembly { + memPtr := add(item, 0x20) + } + // offset the pointer if the first byte + + uint8 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + uint len = item.length; + if (len > 0 && byte0 < LIST_SHORT_START) { + assembly { + memPtr := add(memPtr, 0x01) + } + len -= 1; + } + return RLPItem(len, memPtr); + } + + /* + * @dev Create an iterator. Reverts if item is not a list. + * @param self The RLP item. + * @return An 'Iterator' over the item. + */ + function iterator(RLPItem memory self) internal pure returns (Iterator memory) { + require(isList(self), "RLPDecoder iterator is not list"); + + uint ptr = self.memPtr + _payloadOffset(self.memPtr); + return Iterator(self, ptr); + } + + /* + * @param item RLP encoded bytes + */ + function rlpLen(RLPItem memory item) internal pure returns (uint) { + return item.len; + } + + /* + * @param item RLP encoded bytes + */ + function payloadLen(RLPItem memory item) internal pure returns (uint) { + uint offset = _payloadOffset(item.memPtr); + require(item.len >= offset, "RLPDecoder: invalid uint RLP item offset size"); + return item.len - offset; + } + + /* + * @param item RLP encoded list in bytes + */ + function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { + require(isList(item), "RLPDecoder iterator is not a list"); + + uint items = numItems(item); + RLPItem[] memory result = new RLPItem[](items); + + uint memPtr = item.memPtr + _payloadOffset(item.memPtr); + uint dataLen; + for (uint i = 0; i < items; i++) { + dataLen = _itemLength(memPtr); + result[i] = RLPItem(dataLen, memPtr); + memPtr = memPtr + dataLen; + } + + return result; + } + + /* + * @param get the RLP item by index. save gas. + */ + function getItemByIndex(RLPItem memory item, uint idx) internal pure returns (RLPItem memory) { + require(isList(item), "RLPDecoder iterator is not a list"); + + uint memPtr = item.memPtr + _payloadOffset(item.memPtr); + uint dataLen; + for (uint i = 0; i < idx; i++) { + dataLen = _itemLength(memPtr); + memPtr = memPtr + dataLen; + } + dataLen = _itemLength(memPtr); + return RLPItem(dataLen, memPtr); + } + + /* + * @param get the RLP item by index. save gas. + */ + function safeGetItemByIndex(RLPItem memory item, uint idx) internal pure returns (RLPItem memory) { + require(isList(item), "RLPDecoder iterator is not a list"); + require(idx < numItems(item), "RLP item out of bounds"); + uint endPtr = item.memPtr + item.len; + + uint memPtr = item.memPtr + _payloadOffset(item.memPtr); + uint dataLen; + for (uint i = 0; i < idx; i++) { + dataLen = _itemLength(memPtr); + memPtr = memPtr + dataLen; + } + dataLen = _itemLength(memPtr); + + require(memPtr + dataLen <= endPtr, "RLP item overflow"); + return RLPItem(dataLen, memPtr); + } + + /* + * @param offset the receipt bytes item + */ + function typeOffset(RLPItem memory item) internal pure returns (RLPItem memory) { + uint offset = _payloadOffset(item.memPtr); + uint8 byte0; + uint memPtr = item.memPtr; + uint len = item.len; + assembly { + memPtr := add(memPtr, offset) + byte0 := byte(0, mload(memPtr)) + } + if (len > 0 && byte0 < LIST_SHORT_START) { + assembly { + memPtr := add(memPtr, 0x01) + } + len -= 1; + } + return RLPItem(len, memPtr); + } + + // @return indicator whether encoded payload is a list. negate this function call for isData. + function isList(RLPItem memory item) internal pure returns (bool) { + if (item.len == 0) return false; + + uint8 byte0; + uint memPtr = item.memPtr; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < LIST_SHORT_START) return false; + return true; + } + + /** RLPItem conversions into data types **/ + + // @returns raw rlp encoding in bytes + function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { + bytes memory result = new bytes(item.len); + if (result.length == 0) return result; + + uint ptr; + assembly { + ptr := add(0x20, result) + } + + copy(item.memPtr, ptr, item.len); + return result; + } + + // any non-zero byte except "0x80" is considered true + function toBoolean(RLPItem memory item) internal pure returns (bool) { + require(item.len == 1, "RLPDecoder toBoolean invalid length"); + uint result; + uint memPtr = item.memPtr; + assembly { + result := byte(0, mload(memPtr)) + } + + // SEE Github Issue #5. + // Summary: Most commonly used RLP libraries (i.e Geth) will encode + // "0" as "0x80" instead of as "0". We handle this edge case explicitly + // here. + if (result == 0 || result == STRING_SHORT_START) { + return false; + } else { + return true; + } + } + + function toAddress(RLPItem memory item) internal pure returns (address) { + // 1 byte for the length prefix + require(item.len == 21, "RLPDecoder toAddress invalid length"); + + return address(toUint(item)); + } + + function toUint(RLPItem memory item) internal pure returns (uint) { + require(item.len > 0 && item.len <= 33, "RLPDecoder toUint invalid length"); + + uint offset = _payloadOffset(item.memPtr); + require(item.len >= offset, "RLPDecoder: invalid RLP item offset size"); + uint len = item.len - offset; + + uint result; + uint memPtr = item.memPtr + offset; + assembly { + result := mload(memPtr) + + // shift to the correct location if necessary + if lt(len, 32) { + result := div(result, exp(256, sub(32, len))) + } + } + + return result; + } + + // enforces 32 byte length + function toUintStrict(RLPItem memory item) internal pure returns (uint) { + // one byte prefix + require(item.len == 33, "RLPDecoder toUintStrict invalid length"); + + uint result; + uint memPtr = item.memPtr + 1; + assembly { + result := mload(memPtr) + } + + return result; + } + + function toBytes(RLPItem memory item) internal pure returns (bytes memory) { + require(item.len > 0, "RLPDecoder toBytes invalid length"); + + uint offset = _payloadOffset(item.memPtr); + require(item.len >= offset, "RLPDecoder: invalid RLP item offset size"); + uint len = item.len - offset; // data length + bytes memory result = new bytes(len); + + uint destPtr; + assembly { + destPtr := add(0x20, result) + } + + copy(item.memPtr + offset, destPtr, len); + return result; + } + + /* + * Private Helpers + */ + + // @return number of payload items inside an encoded list. + function numItems(RLPItem memory item) internal pure returns (uint) { + if (item.len == 0) return 0; + + uint count = 0; + uint currPtr = item.memPtr + _payloadOffset(item.memPtr); + uint endPtr = item.memPtr + item.len; + while (currPtr < endPtr) { + currPtr = currPtr + _itemLength(currPtr); // skip over an item + count++; + } + + return count; + } + + // @return entire rlp item byte length + function _itemLength(uint memPtr) private pure returns (uint) { + uint itemLen; + uint byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) itemLen = 1; + else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1; + else if (byte0 < LIST_SHORT_START) { + assembly { + let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is + memPtr := add(memPtr, 1) // skip over the first byte + + /* 32 byte word size */ + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len + itemLen := add(dataLen, add(byteLen, 1)) + } + } else if (byte0 < LIST_LONG_START) { + itemLen = byte0 - LIST_SHORT_START + 1; + } else { + assembly { + let byteLen := sub(byte0, 0xf7) + memPtr := add(memPtr, 1) + + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length + itemLen := add(dataLen, add(byteLen, 1)) + } + } + + return itemLen; + } + + // @return number of bytes until the data + function _payloadOffset(uint memPtr) private pure returns (uint) { + uint byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) return 0; + else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1; + else if (byte0 < LIST_SHORT_START) + // being explicit + return byte0 - (STRING_LONG_START - 1) + 1; + else return byte0 - (LIST_LONG_START - 1) + 1; + } + + /* + * @param src Pointer to source + * @param dest Pointer to destination + * @param len Amount of memory to copy from the source + */ + function copy(uint src, uint dest, uint len) private pure { + if (len == 0) return; + + // copy as many word sizes as possible + for (; len >= WORD_SIZE; len -= WORD_SIZE) { + assembly { + mstore(dest, mload(src)) + } + + src += WORD_SIZE; + dest += WORD_SIZE; + } + + // left over bytes. Mask is used to remove unwanted bytes from the word + uint mask = 256 ** (WORD_SIZE - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) // zero out src + let destpart := and(mload(dest), mask) // retrieve the bytes + mstore(dest, or(destpart, srcpart)) + } + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proof/utility/UltraLightNodeEVMDecoder.sol b/endpoint/contracts/V1Contracts/contracts/proof/utility/UltraLightNodeEVMDecoder.sol new file mode 100644 index 0000000..39688e6 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proof/utility/UltraLightNodeEVMDecoder.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.0; +pragma abicoder v2; + +import "./RLPDecode.sol"; + +library UltraLightNodeEVMDecoder { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + + struct Log { + address contractAddress; + bytes32 topicZero; + bytes data; + } + + function getReceiptLog(bytes memory data, uint logIndex) internal pure returns (Log memory) { + RLPDecode.Iterator memory it = RLPDecode.toRlpItem(data).iterator(); + uint idx; + while (it.hasNext()) { + if (idx == 3) { + return toReceiptLog(it.next().getItemByIndex(logIndex).toRlpBytes()); + } else it.next(); + idx++; + } + revert("no log index in receipt"); + } + + function toReceiptLog(bytes memory data) internal pure returns (Log memory) { + RLPDecode.Iterator memory it = RLPDecode.toRlpItem(data).iterator(); + Log memory log; + + uint idx; + while (it.hasNext()) { + if (idx == 0) { + log.contractAddress = it.next().toAddress(); + } else if (idx == 1) { + RLPDecode.RLPItem memory item = it.next().getItemByIndex(0); + log.topicZero = bytes32(item.toUint()); + } else if (idx == 2) log.data = it.next().toBytes(); + else it.next(); + idx++; + } + return log; + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proxy/DefaultProxyAdmin.sol b/endpoint/contracts/V1Contracts/contracts/proxy/DefaultProxyAdmin.sol new file mode 100644 index 0000000..161ed5a --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proxy/DefaultProxyAdmin.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.0; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "./TransparentUpgradeableProxy.sol"; + +/** + * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an + * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. + */ +contract DefaultProxyAdmin is Ownable { + constructor(address initialOwner) { + transferOwnership(initialOwner); + } + + /** + * @dev Returns the current implementation of `proxy`. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) { + // We need to manually run the static call since the getter cannot be flagged as view + // bytes4(keccak256("implementation()")) == 0x5c60da1b + (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b"); + require(success); + return abi.decode(returndata, (address)); + } + + /** + * @dev Returns the current admin of `proxy`. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) { + // We need to manually run the static call since the getter cannot be flagged as view + // bytes4(keccak256("admin()")) == 0xf851a440 + (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); + require(success); + return abi.decode(returndata, (address)); + } + + /** + * @dev Changes the admin of `proxy` to `newAdmin`. + * + * Requirements: + * + * - This contract must be the current admin of `proxy`. + */ + function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner { + proxy.changeAdmin(newAdmin); + } + + /** + * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner { + proxy.upgradeTo(implementation); + } + + /** + * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See + * {TransparentUpgradeableProxy-upgradeToAndCall}. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function upgradeAndCall( + TransparentUpgradeableProxy proxy, + address implementation, + bytes memory data + ) public payable virtual onlyOwner { + proxy.upgradeToAndCall{value: msg.value}(implementation, data); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/proxy/TransparentUpgradeableProxy.sol b/endpoint/contracts/V1Contracts/contracts/proxy/TransparentUpgradeableProxy.sol new file mode 100644 index 0000000..0220b87 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/proxy/TransparentUpgradeableProxy.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.7.0; + +import "@openzeppelin/contracts/proxy/UpgradeableProxy.sol"; + +/** + * @dev This contract implements a proxy that is upgradeable by an admin. + * + * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector + * clashing], which can potentially be used in an attack, this contract uses the + * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two + * things that go hand in hand: + * + * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if + * that call matches one of the admin functions exposed by the proxy itself. + * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the + * implementation. If the admin tries to call a function on the implementation it will fail with an error that says + * "admin cannot fallback to proxy target". + * + * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing + * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due + * to sudden errors when trying to call a function from the proxy implementation. + * + * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, + * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy. + */ +contract TransparentUpgradeableProxy is UpgradeableProxy { + /** + * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and + * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}. + */ + constructor(address _logic, address admin_, bytes memory _data) payable UpgradeableProxy(_logic, _data) { + assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); + _setAdmin(admin_); + } + + /** + * @dev Emitted when the admin account has changed. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. + */ + modifier ifAdmin() { + if (msg.sender == _admin()) { + _; + } else { + _fallback(); + } + } + + /** + * @dev Returns the current admin. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` + */ + function admin() external ifAdmin returns (address admin_) { + admin_ = _admin(); + } + + /** + * @dev Returns the current implementation. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + */ + function implementation() external ifAdmin returns (address implementation_) { + implementation_ = _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}. + */ + function changeAdmin(address newAdmin) external virtual ifAdmin { + require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address"); + emit AdminChanged(_admin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev Upgrade the implementation of the proxy. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. + */ + function upgradeTo(address newImplementation) external virtual ifAdmin { + _upgradeTo(newImplementation); + } + + /** + * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified + * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the + * proxied contract. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}. + */ + function upgradeToAndCall(address newImplementation, bytes calldata data) external payable virtual ifAdmin { + _upgradeTo(newImplementation); + Address.functionDelegateCall(newImplementation, data); + } + + /** + * @dev Returns the current admin. + */ + function _admin() internal view virtual returns (address adm) { + bytes32 slot = _ADMIN_SLOT; + // solhint-disable-next-line no-inline-assembly + assembly { + adm := sload(slot) + } + } + + /** + * @dev Stores a new address in the EIP1967 admin slot. + */ + function _setAdmin(address newAdmin) private { + bytes32 slot = _ADMIN_SLOT; + + // solhint-disable-next-line no-inline-assembly + assembly { + sstore(slot, newAdmin) + } + } + + /** + * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. + */ + function _beforeFallback() internal virtual override { + require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); + super._beforeFallback(); + } +} diff --git a/endpoint/contracts/V1Contracts/contracts/utility/LayerZeroPacket.sol b/endpoint/contracts/V1Contracts/contracts/utility/LayerZeroPacket.sol new file mode 100644 index 0000000..2837872 --- /dev/null +++ b/endpoint/contracts/V1Contracts/contracts/utility/LayerZeroPacket.sol @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "./Buffer.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; + +library LayerZeroPacket { + using Buffer for Buffer.buffer; + using SafeMath for uint; + + struct Packet { + uint16 srcChainId; + uint16 dstChainId; + uint64 nonce; + address dstAddress; + bytes srcAddress; + bytes32 ulnAddress; + bytes payload; + } + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + uint16 dstChainId; + address dstAddress; + uint size; + uint64 nonce; + + // The log consists of the destination chain id and then a bytes payload + // 0--------------------------------------------31 + // 0 | total bytes size + // 32 | destination chain id + // 64 | bytes offset + // 96 | bytes array size + // 128 | payload + assembly { + dstChainId := mload(add(data, 32)) + size := mload(add(data, 96)) /// size of the byte array + nonce := mload(add(data, 104)) // offset to convert to uint64 128 is index -24 + dstAddress := mload(add(data, sub(add(128, sizeOfSrcAddress), 4))) // offset to convert to address 12 -8 + } + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 136, sizeOfSrcAddress); // 128 + 8 + + uint payloadSize = size.sub(28).sub(sizeOfSrcAddress); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, sizeOfSrcAddress.add(156), payloadSize); // 148 + 8 + return + LayerZeroPacket.Packet( + srcChain, + dstChainId, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } + + function getPacketV2( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + // packet def: abi.encodePacked(nonce, srcChain, srcAddress, dstChain, dstAddress, payload); + // data def: abi.encode(packet) = offset(32) + length(32) + packet + // if from EVM + // 0 - 31 0 - 31 | total bytes size + // 32 - 63 32 - 63 | location + // 64 - 95 64 - 95 | size of the packet + // 96 - 103 96 - 103 | nonce + // 104 - 105 104 - 105 | srcChainId + // 106 - P 106 - 125 | srcAddress, where P = 106 + sizeOfSrcAddress - 1, + // P+1 - P+2 126 - 127 | dstChainId + // P+3 - P+22 128 - 147 | dstAddress + // P+23 - END 148 - END | payload + + // decode the packet + uint256 realSize; + uint64 nonce; + uint16 srcChain; + uint16 dstChain; + address dstAddress; + assembly { + realSize := mload(add(data, 64)) + nonce := mload(add(data, 72)) // 104 - 32 + srcChain := mload(add(data, 74)) // 106 - 32 + dstChain := mload(add(data, add(76, sizeOfSrcAddress))) // P + 3 - 32 = 105 + size + 3 - 32 = 76 + size + dstAddress := mload(add(data, add(96, sizeOfSrcAddress))) // P + 23 - 32 = 105 + size + 23 - 32 = 96 + size + } + + require(srcChain != 0, "LayerZeroPacket: invalid packet"); + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 106, sizeOfSrcAddress); + + uint nonPayloadSize = sizeOfSrcAddress.add(32); // 2 + 2 + 8 + 20, 32 + 20 = 52 if sizeOfSrcAddress == 20 + uint payloadSize = realSize.sub(nonPayloadSize); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, nonPayloadSize.add(96), payloadSize); + + return + LayerZeroPacket.Packet( + srcChain, + dstChain, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } + + function getPacketV3( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + // data def: abi.encodePacked(nonce, srcChain, srcAddress, dstChain, dstAddress, payload); + // if from EVM + // 0 - 31 0 - 31 | total bytes size + // 32 - 39 32 - 39 | nonce + // 40 - 41 40 - 41 | srcChainId + // 42 - P 42 - 61 | srcAddress, where P = 41 + sizeOfSrcAddress, + // P+1 - P+2 62 - 63 | dstChainId + // P+3 - P+22 64 - 83 | dstAddress + // P+23 - END 84 - END | payload + + // decode the packet + uint256 realSize = data.length; + uint nonPayloadSize = sizeOfSrcAddress.add(32); // 2 + 2 + 8 + 20, 32 + 20 = 52 if sizeOfSrcAddress == 20 + require(realSize >= nonPayloadSize, "LayerZeroPacket: invalid packet"); + uint payloadSize = realSize - nonPayloadSize; + + uint64 nonce; + uint16 srcChain; + uint16 dstChain; + address dstAddress; + assembly { + nonce := mload(add(data, 8)) // 40 - 32 + srcChain := mload(add(data, 10)) // 42 - 32 + dstChain := mload(add(data, add(12, sizeOfSrcAddress))) // P + 3 - 32 = 41 + size + 3 - 32 = 12 + size + dstAddress := mload(add(data, add(32, sizeOfSrcAddress))) // P + 23 - 32 = 41 + size + 23 - 32 = 32 + size + } + + require(srcChain != 0, "LayerZeroPacket: invalid packet"); + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 42, sizeOfSrcAddress); + + Buffer.buffer memory payloadBuffer; + if (payloadSize > 0) { + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, nonPayloadSize.add(32), payloadSize); + } + + return + LayerZeroPacket.Packet( + srcChain, + dstChain, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } +} diff --git a/endpoint/contracts/V1Contracts/interfaces/ILayerZeroValidationLibrary.sol b/endpoint/contracts/V1Contracts/interfaces/ILayerZeroValidationLibrary.sol new file mode 100644 index 0000000..8bc01a7 --- /dev/null +++ b/endpoint/contracts/V1Contracts/interfaces/ILayerZeroValidationLibrary.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +import "../proof/utility/LayerZeroPacket.sol"; + +interface ILayerZeroValidationLibrary { + function validateProof( + bytes32 blockData, + bytes calldata _data, + uint _remoteAddressSize + ) external returns (LayerZeroPacket.Packet memory packet); +} diff --git a/endpoint/contracts/V1Contracts/interfaces/IValidationLibraryHelperV2.sol b/endpoint/contracts/V1Contracts/interfaces/IValidationLibraryHelperV2.sol new file mode 100644 index 0000000..67d119c --- /dev/null +++ b/endpoint/contracts/V1Contracts/interfaces/IValidationLibraryHelperV2.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.7.0; +pragma abicoder v2; + +import "../proof/utility/LayerZeroPacket.sol"; + +interface IValidationLibraryHelperV2 { + struct ULNLog { + bytes32 contractAddress; + bytes32 topicZeroSig; + bytes data; + } + + function getVerifyLog( + bytes32 hashRoot, + uint[] calldata receiptSlotIndex, + uint logIndex, + bytes[] calldata proof + ) external pure returns (ULNLog memory); + + function getPacket( + bytes calldata data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) external pure returns (LayerZeroPacket.Packet memory); + + function getUtilsVersion() external view returns (uint8); + + function getProofType() external view returns (uint8); +} diff --git a/endpoint/contracts/V1Contracts/proof/utility/LayerZeroPacket.sol b/endpoint/contracts/V1Contracts/proof/utility/LayerZeroPacket.sol new file mode 100644 index 0000000..2837872 --- /dev/null +++ b/endpoint/contracts/V1Contracts/proof/utility/LayerZeroPacket.sol @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity 0.7.6; + +import "./Buffer.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; + +library LayerZeroPacket { + using Buffer for Buffer.buffer; + using SafeMath for uint; + + struct Packet { + uint16 srcChainId; + uint16 dstChainId; + uint64 nonce; + address dstAddress; + bytes srcAddress; + bytes32 ulnAddress; + bytes payload; + } + + function getPacket( + bytes memory data, + uint16 srcChain, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + uint16 dstChainId; + address dstAddress; + uint size; + uint64 nonce; + + // The log consists of the destination chain id and then a bytes payload + // 0--------------------------------------------31 + // 0 | total bytes size + // 32 | destination chain id + // 64 | bytes offset + // 96 | bytes array size + // 128 | payload + assembly { + dstChainId := mload(add(data, 32)) + size := mload(add(data, 96)) /// size of the byte array + nonce := mload(add(data, 104)) // offset to convert to uint64 128 is index -24 + dstAddress := mload(add(data, sub(add(128, sizeOfSrcAddress), 4))) // offset to convert to address 12 -8 + } + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 136, sizeOfSrcAddress); // 128 + 8 + + uint payloadSize = size.sub(28).sub(sizeOfSrcAddress); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, sizeOfSrcAddress.add(156), payloadSize); // 148 + 8 + return + LayerZeroPacket.Packet( + srcChain, + dstChainId, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } + + function getPacketV2( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + // packet def: abi.encodePacked(nonce, srcChain, srcAddress, dstChain, dstAddress, payload); + // data def: abi.encode(packet) = offset(32) + length(32) + packet + // if from EVM + // 0 - 31 0 - 31 | total bytes size + // 32 - 63 32 - 63 | location + // 64 - 95 64 - 95 | size of the packet + // 96 - 103 96 - 103 | nonce + // 104 - 105 104 - 105 | srcChainId + // 106 - P 106 - 125 | srcAddress, where P = 106 + sizeOfSrcAddress - 1, + // P+1 - P+2 126 - 127 | dstChainId + // P+3 - P+22 128 - 147 | dstAddress + // P+23 - END 148 - END | payload + + // decode the packet + uint256 realSize; + uint64 nonce; + uint16 srcChain; + uint16 dstChain; + address dstAddress; + assembly { + realSize := mload(add(data, 64)) + nonce := mload(add(data, 72)) // 104 - 32 + srcChain := mload(add(data, 74)) // 106 - 32 + dstChain := mload(add(data, add(76, sizeOfSrcAddress))) // P + 3 - 32 = 105 + size + 3 - 32 = 76 + size + dstAddress := mload(add(data, add(96, sizeOfSrcAddress))) // P + 23 - 32 = 105 + size + 23 - 32 = 96 + size + } + + require(srcChain != 0, "LayerZeroPacket: invalid packet"); + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 106, sizeOfSrcAddress); + + uint nonPayloadSize = sizeOfSrcAddress.add(32); // 2 + 2 + 8 + 20, 32 + 20 = 52 if sizeOfSrcAddress == 20 + uint payloadSize = realSize.sub(nonPayloadSize); + Buffer.buffer memory payloadBuffer; + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, nonPayloadSize.add(96), payloadSize); + + return + LayerZeroPacket.Packet( + srcChain, + dstChain, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } + + function getPacketV3( + bytes memory data, + uint sizeOfSrcAddress, + bytes32 ulnAddress + ) internal pure returns (LayerZeroPacket.Packet memory) { + // data def: abi.encodePacked(nonce, srcChain, srcAddress, dstChain, dstAddress, payload); + // if from EVM + // 0 - 31 0 - 31 | total bytes size + // 32 - 39 32 - 39 | nonce + // 40 - 41 40 - 41 | srcChainId + // 42 - P 42 - 61 | srcAddress, where P = 41 + sizeOfSrcAddress, + // P+1 - P+2 62 - 63 | dstChainId + // P+3 - P+22 64 - 83 | dstAddress + // P+23 - END 84 - END | payload + + // decode the packet + uint256 realSize = data.length; + uint nonPayloadSize = sizeOfSrcAddress.add(32); // 2 + 2 + 8 + 20, 32 + 20 = 52 if sizeOfSrcAddress == 20 + require(realSize >= nonPayloadSize, "LayerZeroPacket: invalid packet"); + uint payloadSize = realSize - nonPayloadSize; + + uint64 nonce; + uint16 srcChain; + uint16 dstChain; + address dstAddress; + assembly { + nonce := mload(add(data, 8)) // 40 - 32 + srcChain := mload(add(data, 10)) // 42 - 32 + dstChain := mload(add(data, add(12, sizeOfSrcAddress))) // P + 3 - 32 = 41 + size + 3 - 32 = 12 + size + dstAddress := mload(add(data, add(32, sizeOfSrcAddress))) // P + 23 - 32 = 41 + size + 23 - 32 = 32 + size + } + + require(srcChain != 0, "LayerZeroPacket: invalid packet"); + + Buffer.buffer memory srcAddressBuffer; + srcAddressBuffer.init(sizeOfSrcAddress); + srcAddressBuffer.writeRawBytes(0, data, 42, sizeOfSrcAddress); + + Buffer.buffer memory payloadBuffer; + if (payloadSize > 0) { + payloadBuffer.init(payloadSize); + payloadBuffer.writeRawBytes(0, data, nonPayloadSize.add(32), payloadSize); + } + + return + LayerZeroPacket.Packet( + srcChain, + dstChain, + nonce, + dstAddress, + srcAddressBuffer.buf, + ulnAddress, + payloadBuffer.buf + ); + } +} diff --git a/endpoint/hardhat.config.ts b/endpoint/hardhat.config.ts index cf20c1f..92ae457 100644 --- a/endpoint/hardhat.config.ts +++ b/endpoint/hardhat.config.ts @@ -52,21 +52,11 @@ const config: HardhatUserConfig = { ], }, networks: { - 'sepolia-testnet': { - eid: EndpointId.SEPOLIA_V2_TESTNET, - url: process.env.RPC_URL_SEPOLIA || 'https://rpc.sepolia.org/', - accounts, - }, 'avalanche-testnet': { eid: EndpointId.AVALANCHE_V2_TESTNET, url: process.env.RPC_URL_FUJI || 'https://rpc.ankr.com/avalanche_fuji', accounts, }, - 'amoy-testnet': { - eid: EndpointId.AMOY_V2_TESTNET, - url: process.env.RPC_URL_AMOY || 'https://polygon-amoy-bor-rpc.publicnode.com', - accounts, - }, }, namedAccounts: { deployer: { diff --git a/endpoint/package.json b/endpoint/package.json index a53e915..d46340a 100644 --- a/endpoint/package.json +++ b/endpoint/package.json @@ -1,73 +1,121 @@ { - "name": "@layerzerolabs/oapp-example", - "version": "0.2.13", - "private": true, - "license": "MIT", + "name": "@layerzerolabs/lz-evm-v1-0.7", + "version": "2.3.38", + "license": "BUSL-1.1", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + }, + "./ops": { + "types": "./dist/ops.d.ts", + "import": "./dist/ops.mjs", + "require": "./dist/ops.cjs" + }, + "./package.json": "./package.json", + "./contracts/*": "./contracts/*", + "./artifacts/*": "./artifacts/*", + "./utils/*": { + "import": "./utils/*.js", + "require": "./utils/*.js" + } + }, + "main": "./dist/index.cjs", + "types": "./dist/index.d.ts", + "files": [ + "./dist/**/*", + "contracts/interfaces/**/*", + "contracts/precrime/interfaces/**/*", + "contracts/precrime/PreCrimeBase.sol", + "contracts/precrime/PreCrimeView.sol" + ], "scripts": { - "clean": "rm -rf artifacts cache out", - "compile": "$npm_execpath run compile:forge && $npm_execpath run compile:hardhat", - "compile:forge": "forge build", - "compile:hardhat": "hardhat compile", - "lint": "$npm_execpath run lint:js && $npm_execpath run lint:sol", - "lint:fix": "eslint --fix '**/*.{js,ts,json}' && prettier --write . && solhint 'contracts/**/*.sol' --fix --noPrompt", - "lint:js": "eslint '**/*.{js,ts,json}' && prettier --check .", - "lint:sol": "solhint 'contracts/**/*.sol'", - "test": "$npm_execpath run test:forge && $npm_execpath run test:hardhat", - "test:forge": "forge test", - "test:hardhat": "hardhat test" + "build": "$npm_execpath clean-prebuild && $npm_execpath build-forge && $npm_execpath build-hardhat && $npm_execpath build-tron && $npm_execpath build-ts", + "build-forge": "forge build", + "build-hardhat": "$npm_execpath hardhat compile", + "build-tron": "$npm_execpath hardhat compile --network tron-sandbox-local --no-copy-artifacts --no-copy-deployments", + "build-ts": "$npm_execpath tsc --noEmit && $npm_execpath tsup --entry.index ./src/index.ts && $npm_execpath tsup-node --entry.ops ./ops/index.ts", + "clean": "$npm_execpath clean-prebuild && rimraf .turbo src/typechain-types out && $npm_execpath clean-hardhat", + "clean-hardhat": "rimraf -g '*artifacts*' '*cache*'", + "clean-prebuild": "rimraf dist", + "prepack": "node ./scripts/pack.mjs --prepack", + "postpack": "node ./scripts/pack.mjs --postpack", + "test": "$npm_execpath test-forge && $npm_execpath hardhat test --parallel", + "test-forge": "forge test" }, "resolutions": { + "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@^0.3.0-beta.13" + }, + "dependencies": { + "abi-decoder": "^2.4.0", + "dotenv": "^16.3.1", "ethers": "^5.7.2", - "hardhat-deploy": "^0.12.1" + "lodash": "^4.17.21", + "pino": "^8.16.2", + "tiny-invariant": "^1.3.1", + "zksync-ethers": "^5.0.0", + "zksync-web3": "^0.14.3" }, "devDependencies": { - "@babel/core": "^7.23.9", - "@layerzerolabs/eslint-config-next": "~2.3.34", - "@layerzerolabs/lz-definitions": "^2.3.34", - "@layerzerolabs/lz-evm-messagelib-v2": "^2.3.34", - "@layerzerolabs/lz-evm-protocol-v2": "^2.3.34", - "@layerzerolabs/lz-evm-v1-0.7": "^2.3.34", - "@layerzerolabs/lz-v2-utilities": "^2.3.34", - "@layerzerolabs/oapp-evm": "^0.0.3", - "@layerzerolabs/prettier-config-next": "^2.3.34", - "@layerzerolabs/solhint-config": "^2.3.34", - "@layerzerolabs/test-devtools-evm-foundry": "~0.2.11", - "@layerzerolabs/toolbox-foundry": "~0.1.9", - "@layerzerolabs/toolbox-hardhat": "~0.3.6", - "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@ethersproject/abi": "^5.7.0", + "@ethersproject/providers": "^5.7.0", + "@layerzerolabs/hardhat-collect-outcomes": "^2.3.29", + "@layerzerolabs/hardhat-config": "workspace:^", + "@layerzerolabs/hardhat-tron": "workspace:^", + "@layerzerolabs/lz-definitions": "workspace:^", + "@layerzerolabs/lz-proof-utility": "workspace:^", + "@layerzerolabs/lz-utilities": "workspace:^", + "@layerzerolabs/monorepo-utilities": "workspace:^", + "@layerzerolabs/ops-core": "workspace:^", + "@layerzerolabs/ops-definitions-layerzero": "workspace:^", + "@layerzerolabs/ops-plugin-core": "workspace:^", + "@layerzerolabs/ops-utilities": "workspace:^", + "@layerzerolabs/tsup-config-next": "workspace:^", + "@layerzerolabs/typescript-config-next": "workspace:^", + "@matterlabs/hardhat-zksync-deploy": "^0.6.5", + "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", + "@nomicfoundation/hardhat-network-helpers": "^1.0.9", + "@nomicfoundation/hardhat-verify": "^2.0.2", "@nomiclabs/hardhat-ethers": "^2.2.3", - "@openzeppelin/contracts": "^5.0.2", - "@openzeppelin/contracts-upgradeable": "^5.0.2", - "@rushstack/eslint-patch": "^1.7.0", + "@openzeppelin/contracts": "3.4.2-solc-0.7", + "@openzeppelin/contracts-upgradeable": "3.4.2-solc-0.7", + "@stargatefinance/stg-evm-v1": "^1.0.33", + "@typechain/ethers-v5": "^10.2.1", + "@typechain/hardhat": "^6.1.6", "@types/chai": "^4.3.11", + "@types/jest": "^29.5.10", + "@types/lodash": "^4.14.202", "@types/mocha": "^10.0.6", - "@types/node": "~18.18.14", - "chai": "^4.4.1", - "dotenv": "^16.4.1", - "eslint": "^8.55.0", - "eslint-plugin-jest-extended": "~2.0.0", - "ethers": "^5.7.2", - "hardhat": "^2.22.3", + "@types/node": "^20.10.5", + "chai": "^4.3.10", + "chai-ethers": "^0.0.1", + "cross-cat": "1.0.0", + "hardhat": "^2.19.0", "hardhat-contract-sizer": "^2.10.0", - "hardhat-deploy": "^0.12.1", + "hardhat-deploy": "^0.12.4", + "hardhat-deploy-ethers": "^0.3.0-beta.13", + "hardhat-gas-reporter": "^1.0.9", "mocha": "^10.2.0", - "prettier": "^3.2.5", - "solhint": "^4.1.1", - "solidity-bytes-utils": "^0.8.2", - "ts-node": "^10.9.2", - "typescript": "^5.4.4" + "rimraf": "^5.0.5", + "smol-toml": "^1.2.0", + "solidity-coverage": "^0.8.5", + "ts-node": "^10.9.1", + "tsup": "^8.0.1", + "typechain": "^8.3.2", + "typescript": "~5.2.2" }, - "engines": { - "node": ">=18.16.0" + "peerDependencies": { + "@openzeppelin/contracts": "3.4.2-solc-0.7 || ^3.4.2 || ^4.0.0 || ^5.0.0", + "@openzeppelin/contracts-upgradeable": "3.4.2-solc-0.7 || ^3.4.2 || ^4.0.0 || ^5.0.0", + "hardhat-deploy": "^0.12.4" }, - "pnpm": { - "overrides": { - "ethers": "^5.7.2", - "hardhat-deploy": "^0.12.1" - } + "publishConfig": { + "access": "public" }, - "overrides": { - "ethers": "^5.7.2", - "hardhat-deploy": "^0.12.1" + "lzVersions": { + "default": [ + "v1" + ] } } diff --git a/endpoint/pnpm-lock.yaml b/endpoint/pnpm-lock.yaml index a8acf19..d0ec417 100644 --- a/endpoint/pnpm-lock.yaml +++ b/endpoint/pnpm-lock.yaml @@ -17,34 +17,34 @@ importers: version: 7.25.2 '@layerzerolabs/eslint-config-next': specifier: ~2.3.34 - version: 2.3.38(typescript@5.5.4) + version: 2.3.39(typescript@5.5.4) '@layerzerolabs/lz-definitions': specifier: ^2.3.34 - version: 2.3.38 + version: 2.3.39 '@layerzerolabs/lz-evm-messagelib-v2': specifier: ^2.3.34 - version: 2.3.38(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + version: 2.3.39(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) '@layerzerolabs/lz-evm-protocol-v2': specifier: ^2.3.34 - version: 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + version: 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) '@layerzerolabs/lz-evm-v1-0.7': specifier: ^2.3.34 - version: 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + version: 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@layerzerolabs/lz-v2-utilities': specifier: ^2.3.34 - version: 2.3.38 + version: 2.3.39 '@layerzerolabs/oapp-evm': specifier: ^0.0.3 - version: 0.0.3(rzjgcitmshqiknfnsxwkpnnicm) + version: 0.0.3(36b2aggl3sxcxwtbalsnsirne4) '@layerzerolabs/prettier-config-next': specifier: ^2.3.34 - version: 2.3.38 + version: 2.3.39 '@layerzerolabs/solhint-config': specifier: ^2.3.34 - version: 2.3.38(typescript@5.5.4) + version: 2.3.39(typescript@5.5.4) '@layerzerolabs/test-devtools-evm-foundry': specifier: ~0.2.11 - version: 0.2.11(jr4wkoefb5a2xvwxl6dcarsev4) + version: 0.2.11(6ahe3m33dbzdvy3gmc7ckilnlq) '@layerzerolabs/toolbox-foundry': specifier: ~0.1.9 version: 0.1.9 @@ -58,10 +58,10 @@ importers: specifier: ^2.2.3 version: 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)) '@openzeppelin/contracts': - specifier: ^5.0.2 + specifier: ^5.0.0 version: 5.0.2 '@openzeppelin/contracts-upgradeable': - specifier: ^5.0.2 + specifier: ^5.0.0 version: 5.0.2(@openzeppelin/contracts@5.0.2) '@rushstack/eslint-patch': specifier: ^1.7.0 @@ -99,6 +99,9 @@ importers: hardhat-deploy: specifier: ^0.12.1 version: 0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + hardhat-deploy-ethers: + specifier: ^0.3.0-beta.13 + version: 0.3.0-beta.13(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)) mocha: specifier: ^10.2.0 version: 10.7.3 @@ -432,11 +435,11 @@ packages: '@layerzerolabs/lz-definitions': ^2.3.3 zod: ^3.22.4 - '@layerzerolabs/eslint-config-next@2.3.38': - resolution: {integrity: sha512-IRXSwDf1NqlUQsV/0Wd9i2G0SZmovBPqZmwkM4oRRhjmwywL2ee10ilT7gMVCUAfWCJ+4d1Q0tevuK0zVfgwjw==} + '@layerzerolabs/eslint-config-next@2.3.39': + resolution: {integrity: sha512-J3XX3pHMWyZff8DQzk3fjlcxJ/FgvT3Id8TFvFxuNG8MDOcvRLs2XQXhrujlpWtFnmKoMvgcRApH/GqLmIW9Kw==} - '@layerzerolabs/evm-sdks-core@2.3.38': - resolution: {integrity: sha512-Com9ZlW9hqn8btyNZ5GTmlOin+zuI9R46FqKS7+mByvAWwer03brvmz4sO4AVWUE3VKCzU5ioSYcNuWn6Ahzhg==} + '@layerzerolabs/evm-sdks-core@2.3.39': + resolution: {integrity: sha512-62PX28K5+fyufBlMqvTdnIS/j14zh4LgN/DAhvMpxr41rrM8F3cUX0/+0iAa54lRK1cA2bOToYyMi1YG+HKrZw==} '@layerzerolabs/export-deployments@0.0.11': resolution: {integrity: sha512-VhsAMRLqFJSp6s5WnZzEA0CbIW5TE5OTCRLxY1Hf8yhEAIqzWpUdkqnms65QeRJ+82Mkx6YoR27rBA9v/bgStg==} @@ -463,18 +466,18 @@ packages: yoga-layout-prebuilt: optional: true - '@layerzerolabs/lz-definitions@2.3.38': - resolution: {integrity: sha512-aNt+/qb+AI9tIfCxh8JuehhUakG3e6HsJuEaB/GzJakbCI9LmKucge1klDPse1fPoaWJ/akE+t+6fEleoMJvjw==} + '@layerzerolabs/lz-definitions@2.3.39': + resolution: {integrity: sha512-PcRrn7/ZNEcNoa23gkfJ0GNLVFYl/OVj5h9ZOTLNCTXzI2qPAcA2n2VQI9t4qMHX+3c+V7pZhvQMmD9qt4fY4g==} - '@layerzerolabs/lz-evm-messagelib-v2@2.3.38': - resolution: {integrity: sha512-92Iq//ckeI/jGzTaNK4UNdUKL4N3Mz7FBAv2/Sd8VYrQC7D1WpUOZY5TzjxQI8YTK6MaeTAERIbBX5ZUbP8/pg==} + '@layerzerolabs/lz-evm-messagelib-v2@2.3.39': + resolution: {integrity: sha512-DlWvoy8xjDygZVsBtLC87lLtbdNINdvTHG1RP6UTFyRuHv81MUjg7VQbdcSdFTds5VWM48I64ay7q1lSoviyHg==} peerDependencies: '@arbitrum/nitro-contracts': ^1.1.0 '@axelar-network/axelar-gmp-sdk-solidity': ^5.6.4 '@chainlink/contracts-ccip': ^0.7.6 '@eth-optimism/contracts': ^0.6.0 - '@layerzerolabs/lz-evm-protocol-v2': ^2.3.38 - '@layerzerolabs/lz-evm-v1-0.7': ^2.3.38 + '@layerzerolabs/lz-evm-protocol-v2': ^2.3.39 + '@layerzerolabs/lz-evm-v1-0.7': ^2.3.39 '@openzeppelin/contracts': ^4.8.1 || ^5.0.0 '@openzeppelin/contracts-upgradeable': ^4.8.1 || ^5.0.0 hardhat-deploy: ^0.12.1 @@ -483,29 +486,29 @@ packages: '@arbitrum/nitro-contracts': optional: true - '@layerzerolabs/lz-evm-protocol-v2@2.3.38': - resolution: {integrity: sha512-MF13xZGo9xkRRkubCrqXwAIJV5DHlB85mvDaBbi5T5TlCltIxzoSQu0b8FCH3AniEFBvqo1nc4dJ1E8KSfi0BA==} + '@layerzerolabs/lz-evm-protocol-v2@2.3.39': + resolution: {integrity: sha512-w9/S0KttXLi3WhF003Bft26HrfCvsw2EHqG8Rpe8zk+FTL9KRrI6pTmUzNCDZfsyeOyUCBKNrmJ6qbSRqxp8mg==} peerDependencies: '@openzeppelin/contracts': ^4.8.1 || ^5.0.0 '@openzeppelin/contracts-upgradeable': ^4.8.1 || ^5.0.0 hardhat-deploy: ^0.12.1 solidity-bytes-utils: ^0.8.0 - '@layerzerolabs/lz-evm-sdk-v1@2.3.38': - resolution: {integrity: sha512-xihVwdSSeqg+c64V84eC1nfUWIj/qhZUifQKrgheDLImP4/tzSX9CVukdN3IBedbafGWV9bPNsq5oWZWrhK6Fg==} + '@layerzerolabs/lz-evm-sdk-v1@2.3.39': + resolution: {integrity: sha512-gXk7atp2h42o4zN2J96fVPhXfLEHxx39FC7iRY8rYwQxh39xRWrENj1KLTUxlrC1zjF31PUo0/ifdYox4oraUw==} - '@layerzerolabs/lz-evm-sdk-v2@2.3.38': - resolution: {integrity: sha512-VBFmeclYvz83SGaJKwxvSYgm8jW0pBAYj99YWolhIMgyR0+xiXqwfbJlFptyOlQYVuj+SgJV8SNi6vxR6c0GZA==} + '@layerzerolabs/lz-evm-sdk-v2@2.3.39': + resolution: {integrity: sha512-O1RuWh5t5147pOOXkKzr76oxjjiVW+jm0Z60ZUDBRKZCMhwyYWZdhab9q709+bwSOR80UXNSEfHZL9Z950j2/w==} - '@layerzerolabs/lz-evm-v1-0.7@2.3.38': - resolution: {integrity: sha512-ATMBzDvAeKlx/R2bzcL/fPXRPZCKSC3mUxEMquk0Haj8B/4tYreh6tAiuj2hGDgC7SHOj/AY3IHf6pWzL2jOYQ==} + '@layerzerolabs/lz-evm-v1-0.7@2.3.39': + resolution: {integrity: sha512-ItDUFJoJ/y1Bd5/fl3zhc3VhmsWkv/wy4TBeoSouvrGiMY6yAp0d4MkfnKdyFK5EhiHmdRcGi7dZO2rWkMzkXg==} peerDependencies: '@openzeppelin/contracts': 3.4.2-solc-0.7 || ^3.4.2 || ^4.0.0 || ^5.0.0 '@openzeppelin/contracts-upgradeable': 3.4.2-solc-0.7 || ^3.4.2 || ^4.0.0 || ^5.0.0 hardhat-deploy: ^0.12.1 - '@layerzerolabs/lz-v2-utilities@2.3.38': - resolution: {integrity: sha512-azpVYt930XsCybuMVY5SFSWCgcOLkU5Trnvi/4L5w0AsTpCWJ7NsxKbI3diSdKSepaHQDgQ1khv135KXarukYw==} + '@layerzerolabs/lz-v2-utilities@2.3.39': + resolution: {integrity: sha512-sue9hz030+alJu29gPSjQXTDKJF5z8KjeJ9JWNgUqIFosgCs44C1ok4sVse+7uaNP//fIokuRcV4hvUw5mRSZg==} '@layerzerolabs/oapp-evm@0.0.3': resolution: {integrity: sha512-ZTPPRWSt4YxLvEHijKqmK7CbTdG2x6le8oNhlGF9CvvAx7VLMJr/HDjhZorU8fkde1g8MdLhnhSzM9pi+GIYSA==} @@ -526,8 +529,8 @@ packages: '@openzeppelin/contracts': ^4.8.1 || ^5.0.0 '@openzeppelin/contracts-upgradeable': ^4.8.1 || ^5.0.0 - '@layerzerolabs/prettier-config-next@2.3.38': - resolution: {integrity: sha512-BnnD9ikHObUMLBChcHsT7CvDDs4hvi4lVABWgtbaLbORkic1BIPBnd0tbXH+qmErGJ979Z/hi0Kp3tb925lXXw==} + '@layerzerolabs/prettier-config-next@2.3.39': + resolution: {integrity: sha512-IrOi1kjxIZEYYtbeS0zIUe816MbornatPKl6cRIdS/JD5dz/sc+SqbZxl66/iNhkfe/PeFVWSxmNRiu/xofxUA==} '@layerzerolabs/protocol-devtools-evm@1.2.0': resolution: {integrity: sha512-jZDfUDuxd3dJG8Sh6RxdZo9hep8y4IqpAC5h99coZcJYFp3WsXC5KF2C1++D20JLjdHVJXHziTUSnPUQOI0rTg==} @@ -552,8 +555,8 @@ packages: '@layerzerolabs/lz-definitions': ^2.3.3 zod: ^3.22.4 - '@layerzerolabs/solhint-config@2.3.38': - resolution: {integrity: sha512-yV7tuD3bF9/6XLdGiw2lmf+ytcoLLbR86YcKygwaakDyK/1mKOp8ZNkrBJORoJQDYCJ+2ZgeZmgjkq/ihxdqOQ==} + '@layerzerolabs/solhint-config@2.3.39': + resolution: {integrity: sha512-4pkM80nPhT9vGRjo4KvBMGRrN513DyG0k+4kaANNKPzAwJWGpBvb371JvxPm+GNleDSW5LKfMxlQm52wiK8vlg==} '@layerzerolabs/test-devtools-evm-foundry@0.2.11': resolution: {integrity: sha512-tBAzfrAVu1Hn0aPUfzXhDrP63KgTrVj2KLz+MwymD8qcEvwFLQiBWz7EVUBMuy8M0Yy1mTUvsbmcaA3mZeym2Q==} @@ -2240,6 +2243,12 @@ packages: peerDependencies: hardhat: ^2.0.0 + hardhat-deploy-ethers@0.3.0-beta.13: + resolution: {integrity: sha512-PdWVcKB9coqWV1L7JTpfXRCI91Cgwsm7KLmBcwZ8f0COSm1xtABHZTyz3fvF6p42cTnz1VM0QnfDvMFlIRkSNw==} + peerDependencies: + ethers: ^5.7.2 + hardhat: ^2.0.0 + hardhat-deploy@0.12.4: resolution: {integrity: sha512-bYO8DIyeGxZWlhnMoCBon9HNZb6ji0jQn7ngP1t5UmGhC8rQYhji7B73qETMOFhzt5ECZPr+U52duj3nubsqdQ==} @@ -2792,8 +2801,8 @@ packages: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true - mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true @@ -4515,17 +4524,17 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@layerzerolabs/devtools-evm-hardhat@1.1.0(5eplbhqkmr4ft6444bdwdnhg2y)': + '@layerzerolabs/devtools-evm-hardhat@1.1.0(zly5aefqk3cifafmx2dftvoi3y)': dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/abstract-signer': 5.7.0 '@ethersproject/contracts': 5.7.0 '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/devtools-evm': 0.4.1(2r7ft22zawgnxzihoucwo7zmpm) + '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/devtools-evm': 0.4.1(nfdzw3pf63s3ap3qdaq3qscnbe) '@layerzerolabs/export-deployments': 0.0.11 '@layerzerolabs/io-devtools': 0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 + '@layerzerolabs/lz-definitions': 2.3.39 '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)) '@safe-global/protocol-kit': 1.3.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) fp-ts: 2.16.9 @@ -4541,7 +4550,7 @@ snapshots: - supports-color - utf-8-validate - '@layerzerolabs/devtools-evm@0.4.1(2r7ft22zawgnxzihoucwo7zmpm)': + '@layerzerolabs/devtools-evm@0.4.1(nfdzw3pf63s3ap3qdaq3qscnbe)': dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/abstract-provider': 5.7.0 @@ -4551,9 +4560,9 @@ snapshots: '@ethersproject/constants': 5.7.0 '@ethersproject/contracts': 5.7.0 '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) + '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) '@layerzerolabs/io-devtools': 0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 + '@layerzerolabs/lz-definitions': 2.3.39 '@safe-global/api-kit': 1.3.1 '@safe-global/protocol-kit': 1.3.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -4566,17 +4575,17 @@ snapshots: - supports-color - utf-8-validate - '@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8)': + '@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8)': dependencies: '@ethersproject/bytes': 5.7.0 '@layerzerolabs/io-devtools': 0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 + '@layerzerolabs/lz-definitions': 2.3.39 bs58: 6.0.0 exponential-backoff: 3.1.1 js-yaml: 4.1.0 zod: 3.23.8 - '@layerzerolabs/eslint-config-next@2.3.38(typescript@5.5.4)': + '@layerzerolabs/eslint-config-next@2.3.39(typescript@5.5.4)': dependencies: '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.5.4) @@ -4597,7 +4606,7 @@ snapshots: - supports-color - typescript - '@layerzerolabs/evm-sdks-core@2.3.38(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@layerzerolabs/evm-sdks-core@2.3.39(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: @@ -4623,54 +4632,54 @@ snapshots: react: 17.0.2 yoga-layout-prebuilt: 1.10.0 - '@layerzerolabs/lz-definitions@2.3.38': + '@layerzerolabs/lz-definitions@2.3.39': dependencies: tiny-invariant: 1.3.3 - '@layerzerolabs/lz-evm-messagelib-v2@2.3.38(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2)': + '@layerzerolabs/lz-evm-messagelib-v2@2.3.39(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2)': dependencies: '@axelar-network/axelar-gmp-sdk-solidity': 5.10.0 '@chainlink/contracts-ccip': 0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) '@eth-optimism/contracts': 0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) - '@layerzerolabs/lz-evm-protocol-v2': 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-v1-0.7': 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@layerzerolabs/lz-evm-protocol-v2': 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@openzeppelin/contracts': 5.0.2 '@openzeppelin/contracts-upgradeable': 5.0.2(@openzeppelin/contracts@5.0.2) hardhat-deploy: 0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) solidity-bytes-utils: 0.8.2 - '@layerzerolabs/lz-evm-protocol-v2@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2)': + '@layerzerolabs/lz-evm-protocol-v2@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2)': dependencies: '@openzeppelin/contracts': 5.0.2 '@openzeppelin/contracts-upgradeable': 5.0.2(@openzeppelin/contracts@5.0.2) hardhat-deploy: 0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) solidity-bytes-utils: 0.8.2 - '@layerzerolabs/lz-evm-sdk-v1@2.3.38(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@layerzerolabs/lz-evm-sdk-v1@2.3.39(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@layerzerolabs/evm-sdks-core': 2.3.38(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@layerzerolabs/evm-sdks-core': 2.3.39(bufferutil@4.0.8)(utf-8-validate@5.0.10) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate - '@layerzerolabs/lz-evm-sdk-v2@2.3.38(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@layerzerolabs/lz-evm-sdk-v2@2.3.39(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@layerzerolabs/evm-sdks-core': 2.3.38(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@layerzerolabs/evm-sdks-core': 2.3.39(bufferutil@4.0.8)(utf-8-validate@5.0.10) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate - '@layerzerolabs/lz-evm-v1-0.7@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@layerzerolabs/lz-evm-v1-0.7@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: '@openzeppelin/contracts': 5.0.2 '@openzeppelin/contracts-upgradeable': 5.0.2(@openzeppelin/contracts@5.0.2) hardhat-deploy: 0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@layerzerolabs/lz-v2-utilities@2.3.38': + '@layerzerolabs/lz-v2-utilities@2.3.39': dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/address': 5.7.0 @@ -4681,11 +4690,11 @@ snapshots: bs58: 5.0.0 tiny-invariant: 1.3.3 - '@layerzerolabs/oapp-evm@0.0.3(rzjgcitmshqiknfnsxwkpnnicm)': + '@layerzerolabs/oapp-evm@0.0.3(36b2aggl3sxcxwtbalsnsirne4)': dependencies: - '@layerzerolabs/lz-evm-messagelib-v2': 2.3.38(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-protocol-v2': 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-v1-0.7': 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@layerzerolabs/lz-evm-messagelib-v2': 2.3.39(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-protocol-v2': 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@openzeppelin/contracts': 5.0.2 '@openzeppelin/contracts-upgradeable': 5.0.2(@openzeppelin/contracts@5.0.2) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -4693,22 +4702,22 @@ snapshots: - bufferutil - utf-8-validate - '@layerzerolabs/oft-evm@0.0.10(4nefoeqpcdzybqt5d7v6gwybrm)': + '@layerzerolabs/oft-evm@0.0.10(s5a3slqs5xtuxv62xvfdcznkta)': dependencies: - '@layerzerolabs/lz-evm-messagelib-v2': 2.3.38(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-protocol-v2': 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-v1-0.7': 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) - '@layerzerolabs/oapp-evm': 0.0.3(rzjgcitmshqiknfnsxwkpnnicm) + '@layerzerolabs/lz-evm-messagelib-v2': 2.3.39(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-protocol-v2': 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@layerzerolabs/oapp-evm': 0.0.3(36b2aggl3sxcxwtbalsnsirne4) '@openzeppelin/contracts': 5.0.2 '@openzeppelin/contracts-upgradeable': 5.0.2(@openzeppelin/contracts@5.0.2) - '@layerzerolabs/prettier-config-next@2.3.38': + '@layerzerolabs/prettier-config-next@2.3.39': dependencies: prettier: 3.3.3 prettier-plugin-packagejson: 2.5.2(prettier@3.3.3) prettier-plugin-solidity: 1.4.1(prettier@3.3.3) - '@layerzerolabs/protocol-devtools-evm@1.2.0(dak5dzz5rwnkirqgninuledy2i)': + '@layerzerolabs/protocol-devtools-evm@1.2.0(2gxch2oy7ajqqujguuxv2s36pu)': dependencies: '@ethersproject/abstract-provider': 5.7.0 '@ethersproject/abstract-signer': 5.7.0 @@ -4716,33 +4725,33 @@ snapshots: '@ethersproject/constants': 5.7.0 '@ethersproject/contracts': 5.7.0 '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/devtools-evm': 0.4.1(2r7ft22zawgnxzihoucwo7zmpm) + '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/devtools-evm': 0.4.1(nfdzw3pf63s3ap3qdaq3qscnbe) '@layerzerolabs/io-devtools': 0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 - '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) + '@layerzerolabs/lz-definitions': 2.3.39 + '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) p-memoize: 4.0.4 zod: 3.23.8 - '@layerzerolabs/protocol-devtools@0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8)': + '@layerzerolabs/protocol-devtools@0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8)': dependencies: - '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 + '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/lz-definitions': 2.3.39 zod: 3.23.8 - '@layerzerolabs/solhint-config@2.3.38(typescript@5.5.4)': + '@layerzerolabs/solhint-config@2.3.39(typescript@5.5.4)': dependencies: solhint: 4.5.4(typescript@5.5.4) transitivePeerDependencies: - typescript - '@layerzerolabs/test-devtools-evm-foundry@0.2.11(jr4wkoefb5a2xvwxl6dcarsev4)': + '@layerzerolabs/test-devtools-evm-foundry@0.2.11(6ahe3m33dbzdvy3gmc7ckilnlq)': dependencies: - '@layerzerolabs/lz-evm-messagelib-v2': 2.3.38(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-protocol-v2': 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-v1-0.7': 2.3.38(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) - '@layerzerolabs/oapp-evm': 0.0.3(rzjgcitmshqiknfnsxwkpnnicm) - '@layerzerolabs/oft-evm': 0.0.10(4nefoeqpcdzybqt5d7v6gwybrm) + '@layerzerolabs/lz-evm-messagelib-v2': 2.3.39(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@eth-optimism/contracts@0.6.0(bufferutil@4.0.8)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@layerzerolabs/lz-evm-protocol-v2@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2))(@layerzerolabs/lz-evm-v1-0.7@2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-protocol-v2': 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': 2.3.39(@openzeppelin/contracts-upgradeable@5.0.2(@openzeppelin/contracts@5.0.2))(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@layerzerolabs/oapp-evm': 0.0.3(36b2aggl3sxcxwtbalsnsirne4) + '@layerzerolabs/oft-evm': 0.0.10(s5a3slqs5xtuxv62xvfdcznkta) '@openzeppelin/contracts': 5.0.2 '@openzeppelin/contracts-upgradeable': 5.0.2(@openzeppelin/contracts@5.0.2) @@ -4760,20 +4769,20 @@ snapshots: '@ethersproject/bytes': 5.7.0 '@ethersproject/contracts': 5.7.0 '@ethersproject/hash': 5.7.0 - '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/devtools-evm': 0.4.1(2r7ft22zawgnxzihoucwo7zmpm) - '@layerzerolabs/devtools-evm-hardhat': 1.1.0(5eplbhqkmr4ft6444bdwdnhg2y) + '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/devtools-evm': 0.4.1(nfdzw3pf63s3ap3qdaq3qscnbe) + '@layerzerolabs/devtools-evm-hardhat': 1.1.0(zly5aefqk3cifafmx2dftvoi3y) '@layerzerolabs/io-devtools': 0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 - '@layerzerolabs/lz-evm-sdk-v1': 2.3.38(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@layerzerolabs/lz-evm-sdk-v2': 2.3.38(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@layerzerolabs/lz-v2-utilities': 2.3.38 - '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/protocol-devtools-evm': 1.2.0(dak5dzz5rwnkirqgninuledy2i) + '@layerzerolabs/lz-definitions': 2.3.39 + '@layerzerolabs/lz-evm-sdk-v1': 2.3.39(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@layerzerolabs/lz-evm-sdk-v2': 2.3.39(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@layerzerolabs/lz-v2-utilities': 2.3.39 + '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/protocol-devtools-evm': 1.2.0(2gxch2oy7ajqqujguuxv2s36pu) '@layerzerolabs/test-devtools-evm-hardhat': 0.2.6(hardhat@2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(solidity-bytes-utils@0.8.2) - '@layerzerolabs/ua-devtools': 1.0.4(x34n35fepqsxdkxtzw6k3ez3tq) - '@layerzerolabs/ua-devtools-evm': 3.0.0(ae2nc37tjcrfrqskuqwsuwucpq) - '@layerzerolabs/ua-devtools-evm-hardhat': 3.0.0(tnfehutspvxgnr4pupv4sv4ydu) + '@layerzerolabs/ua-devtools': 1.0.4(6xtymgicwzew2e73pmlxq3a56e) + '@layerzerolabs/ua-devtools-evm': 3.0.0(ki3wit34t4j5pfwzwhwkx3gzza) + '@layerzerolabs/ua-devtools-evm-hardhat': 3.0.0(qmwkjvjam6kdwikbn23cmmgqsm) '@nomicfoundation/hardhat-ethers': 3.0.7(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) fp-ts: 2.16.9 @@ -4799,49 +4808,49 @@ snapshots: - supports-color - utf-8-validate - '@layerzerolabs/ua-devtools-evm-hardhat@3.0.0(tnfehutspvxgnr4pupv4sv4ydu)': + '@layerzerolabs/ua-devtools-evm-hardhat@3.0.0(qmwkjvjam6kdwikbn23cmmgqsm)': dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/bytes': 5.7.0 '@ethersproject/contracts': 5.7.0 '@ethersproject/hash': 5.7.0 - '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/devtools-evm': 0.4.1(2r7ft22zawgnxzihoucwo7zmpm) - '@layerzerolabs/devtools-evm-hardhat': 1.1.0(5eplbhqkmr4ft6444bdwdnhg2y) + '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/devtools-evm': 0.4.1(nfdzw3pf63s3ap3qdaq3qscnbe) + '@layerzerolabs/devtools-evm-hardhat': 1.1.0(zly5aefqk3cifafmx2dftvoi3y) '@layerzerolabs/io-devtools': 0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 - '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/protocol-devtools-evm': 1.2.0(dak5dzz5rwnkirqgninuledy2i) - '@layerzerolabs/ua-devtools': 1.0.4(x34n35fepqsxdkxtzw6k3ez3tq) - '@layerzerolabs/ua-devtools-evm': 3.0.0(ae2nc37tjcrfrqskuqwsuwucpq) + '@layerzerolabs/lz-definitions': 2.3.39 + '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/protocol-devtools-evm': 1.2.0(2gxch2oy7ajqqujguuxv2s36pu) + '@layerzerolabs/ua-devtools': 1.0.4(6xtymgicwzew2e73pmlxq3a56e) + '@layerzerolabs/ua-devtools-evm': 3.0.0(ki3wit34t4j5pfwzwhwkx3gzza) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) hardhat: 2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10) hardhat-deploy: 0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) p-memoize: 4.0.4 typescript: 5.5.4 - '@layerzerolabs/ua-devtools-evm@3.0.0(ae2nc37tjcrfrqskuqwsuwucpq)': + '@layerzerolabs/ua-devtools-evm@3.0.0(ki3wit34t4j5pfwzwhwkx3gzza)': dependencies: '@ethersproject/constants': 5.7.0 '@ethersproject/contracts': 5.7.0 - '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/devtools-evm': 0.4.1(2r7ft22zawgnxzihoucwo7zmpm) + '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/devtools-evm': 0.4.1(nfdzw3pf63s3ap3qdaq3qscnbe) '@layerzerolabs/io-devtools': 0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 - '@layerzerolabs/lz-v2-utilities': 2.3.38 - '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) - '@layerzerolabs/protocol-devtools-evm': 1.2.0(dak5dzz5rwnkirqgninuledy2i) - '@layerzerolabs/ua-devtools': 1.0.4(x34n35fepqsxdkxtzw6k3ez3tq) + '@layerzerolabs/lz-definitions': 2.3.39 + '@layerzerolabs/lz-v2-utilities': 2.3.39 + '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) + '@layerzerolabs/protocol-devtools-evm': 1.2.0(2gxch2oy7ajqqujguuxv2s36pu) + '@layerzerolabs/ua-devtools': 1.0.4(6xtymgicwzew2e73pmlxq3a56e) p-memoize: 4.0.4 zod: 3.23.8 - '@layerzerolabs/ua-devtools@1.0.4(x34n35fepqsxdkxtzw6k3ez3tq)': + '@layerzerolabs/ua-devtools@1.0.4(6xtymgicwzew2e73pmlxq3a56e)': dependencies: - '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) + '@layerzerolabs/devtools': 0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) '@layerzerolabs/io-devtools': 0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8) - '@layerzerolabs/lz-definitions': 2.3.38 - '@layerzerolabs/lz-v2-utilities': 2.3.38 - '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.38)(zod@3.23.8) + '@layerzerolabs/lz-definitions': 2.3.39 + '@layerzerolabs/lz-v2-utilities': 2.3.39 + '@layerzerolabs/protocol-devtools': 0.4.2(@layerzerolabs/devtools@0.3.24(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.12(ink-gradient@2.0.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink-table@3.1.0(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2))(ink@3.2.0(bufferutil@4.0.8)(react@17.0.2)(utf-8-validate@5.0.10))(react@17.0.2)(yoga-layout-prebuilt@1.10.0)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8))(@layerzerolabs/lz-definitions@2.3.39)(zod@3.23.8) zod: 3.23.8 '@mdn/browser-compat-data@5.5.49': {} @@ -6835,6 +6844,11 @@ snapshots: hardhat: 2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10) strip-ansi: 6.0.1 + hardhat-deploy-ethers@0.3.0-beta.13(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)): + dependencies: + ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + hardhat: 2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@18.18.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10) + hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0 @@ -7413,13 +7427,13 @@ snapshots: mkdirp-promise@5.0.1: dependencies: - mkdirp: 3.0.1 + mkdirp: 1.0.4 mkdirp@0.5.6: dependencies: minimist: 1.2.8 - mkdirp@3.0.1: {} + mkdirp@1.0.4: {} mnemonist@0.38.5: dependencies: diff --git a/oldpackage.json b/oldpackage.json new file mode 100644 index 0000000..69a2e20 --- /dev/null +++ b/oldpackage.json @@ -0,0 +1,83 @@ +{ + "name": "@layerzerolabs/oapp-example", + "version": "0.2.13", + "private": true, + "license": "MIT", + "scripts": { + "clean": "rm -rf artifacts cache out", + "compile": "$npm_execpath run compile:forge && $npm_execpath run compile:hardhat", + "compile:forge": "forge build", + "compile:hardhat": "hardhat compile", + "lint": "$npm_execpath run lint:js && $npm_execpath run lint:sol", + "lint:fix": "eslint --fix '**/*.{js,ts,json}' && prettier --write . && solhint 'contracts/**/*.sol' --fix --noPrompt", + "lint:js": "eslint '**/*.{js,ts,json}' && prettier --check .", + "lint:sol": "solhint 'contracts/**/*.sol'", + "test": "$npm_execpath run test:forge && $npm_execpath run test:hardhat", + "test:forge": "forge test", + "test:hardhat": "hardhat test" + }, + "resolutions": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + }, + "devDependencies": { + "@layerzerolabs/lz-evm-v1-0.7": "^2.3.34", + "hardhat-deploy": "^0.12.1", + "hardhat-deploy-ethers": "^0.3.0-beta.13", + "@babel/core": "^7.23.9", + "@layerzerolabs/eslint-config-next": "~2.3.34", + "@layerzerolabs/lz-definitions": "^2.3.34", + "@layerzerolabs/lz-evm-messagelib-v2": "^2.3.34", + "@layerzerolabs/lz-evm-protocol-v2": "^2.3.34", + "@layerzerolabs/lz-evm-v1-0.7": "^2.3.34", + "@layerzerolabs/lz-v2-utilities": "^2.3.34", + "@layerzerolabs/oapp-evm": "^0.0.3", + "@layerzerolabs/prettier-config-next": "^2.3.34", + "@layerzerolabs/solhint-config": "^2.3.34", + "@layerzerolabs/test-devtools-evm-foundry": "~0.2.11", + "@layerzerolabs/toolbox-foundry": "~0.1.9", + "@layerzerolabs/toolbox-hardhat": "~0.3.6", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomiclabs/hardhat-ethers": "^2.2.3", + "@openzeppelin/contracts": "^5.0.0", + "@openzeppelin/contracts-upgradeable": "^5.0.0", + "@rushstack/eslint-patch": "^1.7.0", + "@types/chai": "^4.3.11", + "@types/mocha": "^10.0.6", + "@types/node": "~18.18.14", + "chai": "^4.4.1", + "dotenv": "^16.4.1", + "eslint": "^8.55.0", + "eslint-plugin-jest-extended": "~2.0.0", + "ethers": "^5.7.2", + "hardhat": "^2.22.3", + "hardhat-contract-sizer": "^2.10.0", + "hardhat-deploy": "^0.12.1", + "mocha": "^10.2.0", + "prettier": "^3.2.5", + "solhint": "^4.1.1", + "solidity-bytes-utils": "^0.8.2", + "ts-node": "^10.9.2", + "typescript": "^5.4.4" + }, + "engines": { + "node": ">=18.16.0" + }, + "pnpm": { + "overrides": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + } + }, + "overrides": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + }, + "peerDependencies": { + "@layerzerolabs/lz-evm-v1-0.7": "workspace:^", + "@openzeppelin/contracts": "^4.8.1 || ^5.0.0", + "hardhat-deploy": "^0.12.4", + "solidity-bytes-utils": "^0.8.0" + } + } + \ No newline at end of file