diff --git a/helix-contract/address/ln-dev.json b/helix-contract/address/ln-dev.json new file mode 100644 index 00000000..16a9e259 --- /dev/null +++ b/helix-contract/address/ln-dev.json @@ -0,0 +1,63 @@ +{ + "messagers": { + "zksync-goerli": { + "layerzeroMessager": "0x7e303b0A3F08F9fa5F5629Abb998B8Deba89049B" + }, + "goerli": { + "Eth2ArbSendService": "0xa4eE139bE76d277D997aCf9D58053D8DaF7E050a", + "Eth2LineaSendService": "0x9878e74634544d92a043f1826a94465035FA51f4", + "layerzeroMessager": "0xca4490875739BEb1c4ec9ee5d6814774114e1973", + "axelarMessager": "0x037c7b64c80251Cf5C64Ed8f731c799Dc1856701", + "debugMessager": "0x2e8D237226041FAFe3F66b6cfc54b064923D454E" + }, + "arbitrum-goerli": { + "Eth2ArbReceiveService": "0x102F8D7Cfe692AA79c17E3958aB00D060Df0B88f", + "layerzeroMessager": "0x953bE65E685099277F1f09Ebe10746810dC0593D", + "axelarMessager": "0xBc30913CC01A2eC70483681841bbb43D2f77caEd", + "debugMessager": "0x7f431D5ba484Eb96811C469BE3DcbB23c67ae4a8" + }, + "linea-goerli": { + "Eth2LineaReceiveService": "0x8200b3130416F633A696FB9bb0e689a356625075", + "layerzeroMessager": "0xfB09042050868594a54a59EdEAEa96e2765dAd0B", + "axelarMessager": "0x14DB1d462ED061b037C7920857Fc66522ed5bf85", + "debugMessager": "0x25Ce9C92526D002a11aBA105563a713357429A99" + }, + "mantle-goerli": { + "layerzeroMessager": "0xBE4a32f37d11e8227444837DFb3c634d189ccEDc", + "axelarMessager": "0xbb593913a4f3E4eE77861f743c697A4cb95837eF", + "debugMessager": "0x84f7a56483C100ECb12CbB4A31b7873dAE0d8E9B" + } + }, + "ProxyAdmin": { + "zkSync": "0xd7b3aC0c9E99e9B2EF1C9D2a5ff397867c8c8A3E", + "others": "0xE3979fFa68BBa1F53c6F502c8F5788B370d28730" + }, + "LnDefaultBridgeLogic": { + "zkSync": "0x6213E3bc566f7d7A73Fd7565c97ac5Ffb8624674", + "others": "0x310bbebF08cbCC1DB41299E602Ef0319b9D1d979" + }, + "LnDefaultBridgeProxy": { + "zkSync": "0xe8d55759c32fb608fD092aB2C0ef8A1F52B254d4", + "others": "0x7e101911E5FB461d78FBde3992f76F3Bf8BbA829" + }, + "LnOppositeBridgeLogic": "0x3CFe649a4d5530AA2c716F0ca279b937687684f9", + "LnOppositeBridgeProxy": "0x4C538EfA6e3f9Dfb939AA4F0B224577DA665923a", + "deployer": "0xbe6b2860d3c17a719be0A4911EA0EE689e8357f3", + "usdt": { + "goerli": "0xa39cffE89567eBfb5c306a07dfb6e5B3ba41F358", + "mantle-goerli": "0xDb06D904AC5Bdff3b8E6Ac96AFedd3381d94CFDD", + "arbitrum-goerli": "0x543bf1AC41485dc78039b9351563E4Dd13A288cb", + "linea-goerli": "0x8f3663930211f3DE17619FEB2eeB44c9c3F44a06", + "zksync-goerli": "0xb5372ed3bb2CbA63e7908066ac10ee94d30eA839" + }, + "usdc": { + "goerli": "0xe9784E0d9A939dbe966b021DE3cd877284DB1B99", + "mantle-goerli": "0xD610DE267f7590D5bCCE89489ECd2C1A4AfdF76B", + "arbitrum-goerli": "0xBAD026e314a77e727dF643B02f63adA573a3757c", + "linea-goerli": "0xeC89AF5FF618bbF667755BE9d63C69F21F1c00C8", + "zksync-goerli": "0xAe60e005C560E869a2bad271e38e3C9D78381aFF" + }, + "keyTool": { + "goerli": "0xCD45731FFF1b250e9433A1F1F5A609F3A19557A5" + } +} diff --git a/helix-contract/contracts/ln/Arb2EthSource.sol b/helix-contract/contracts/ln/Arb2EthSource.sol deleted file mode 100644 index 58d5d72d..00000000 --- a/helix-contract/contracts/ln/Arb2EthSource.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol"; -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnOppositeBridgeSource.sol"; - -contract Arb2EthSource is Initializable, LnAccessController, LnOppositeBridgeSource { - address public remoteBridge; - address public remoteBridgeAlias; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == remoteBridgeAlias, "invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - _setFeeReceiver(dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function updateProtocolFee(address token, uint112 _protocolFee) external onlyDao { - _updateProtocolFee(token, _protocolFee); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - remoteBridgeAlias = AddressAliasHelper.applyL1ToL2Alias(remoteBridge); - } - - function setRemoteBridgeAlias(address _remoteBridgeAlias) external onlyDao { - remoteBridgeAlias = _remoteBridgeAlias; - } - - function registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) external onlyOperator { - _registerToken(sourceToken, targetToken, protocolFee, penaltyLnCollateral, sourceDecimals, targetDecimals); - } - - function slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external onlyRemoteBridge whenNotPaused { - _slash(latestSlashTransferId, transferId, sourceToken, provider, slasher); - } - - function withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdrawMargin(latestSlashTransferId, lastTransferId, provider, sourceToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/Arb2EthTarget.sol b/helix-contract/contracts/ln/Arb2EthTarget.sol deleted file mode 100644 index b40fa88a..00000000 --- a/helix-contract/contracts/ln/Arb2EthTarget.sol +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@arbitrum/nitro-contracts/src/bridge/IInbox.sol"; -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnOppositeBridgeTarget.sol"; - -contract Arb2EthTarget is Initializable, LnAccessController, LnOppositeBridgeTarget { - IInbox public inbox; - address public remoteBridge; - - event WithdrawMargin(bytes32 lastTransferId, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _inbox) public initializer { - inbox = IInbox(_inbox); - _initialize(_dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function submissionRefundFee( - uint256 baseFee, - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory slashCall = _encodeSlashCall( - latestSlashTransferId, - transferId, - provider, - sourceToken, - slasher - ); - uint256 fee = inbox.calculateRetryableSubmissionFee(slashCall.length, baseFee); - return fee + fee * percentIncrease / 100; - } - - function submissionWithdrawFee( - uint256 baseFee, - bytes32 lastTransferId, - address sourceToken, - uint112 amount, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory withdrawCall = _requestWithdrawMargin( - lastTransferId, - sourceToken, - amount - ); - uint256 fee = inbox.calculateRetryableSubmissionFee(withdrawCall.length, baseFee); - return fee + fee * percentIncrease / 100; - } - - function _sendMessage( - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid, - bytes memory message, - uint256 prepaid - ) internal returns(uint256) { - return inbox.createRetryableTicket{ value: prepaid }( - remoteBridge, - 0, - maxSubmissionCost, - msg.sender, - msg.sender, - maxGas, - gasPriceBid, - message - ); - } - - function slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory refundCallMessage = _slashAndRemoteRefund( - params, - expectedTransferId - ); - uint256 valueUsed = address(0) == params.targetToken ? params.amount : 0; - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, refundCallMessage, msg.value - valueUsed); - } - - function retryRemoteRefund( - bytes32 transferId, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory refundCallMessage = _retrySlashAndRemoteRefund(transferId); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, refundCallMessage, msg.value); - } - - function requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory cancelWithdrawMarginCall = _requestWithdrawMargin( - lastTransferId, - sourceToken, - amount - ); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, cancelWithdrawMarginCall, msg.value); - emit WithdrawMargin(lastTransferId, amount); - } -} - diff --git a/helix-contract/contracts/ln/Eth2ArbSource.sol b/helix-contract/contracts/ln/Eth2ArbSource.sol deleted file mode 100644 index b9cedcdb..00000000 --- a/helix-contract/contracts/ln/Eth2ArbSource.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@arbitrum/nitro-contracts/src/bridge/IInbox.sol"; -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnDefaultBridgeSource.sol"; - -contract Eth2ArbSource is Initializable, LnAccessController, LnDefaultBridgeSource { - IInbox public inbox; - address public remoteBridge; - - event WithdrawMargin(address sourceToken, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _inbox) public initializer { - inbox = IInbox(_inbox); - _initialize(_dao); - _setFeeReceiver(_dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function submissionSlashFee( - uint256 baseFee, - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory slashCall = _encodeSlashCall( - params, - slasher, - fee, - penalty - ); - uint256 submissionFee = inbox.calculateRetryableSubmissionFee(slashCall.length, baseFee); - return submissionFee + submissionFee * percentIncrease / 100; - } - - function submissionWithdrawFee( - uint256 baseFee, - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory withdrawCall = _encodeWithdrawCall( - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - uint256 fee = inbox.calculateRetryableSubmissionFee(withdrawCall.length, baseFee); - return fee + fee * percentIncrease / 100; - } - - function _sendMessage( - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid, - bytes memory message, - uint256 prepaid - ) internal returns(uint256) { - return inbox.createRetryableTicket{ value: prepaid }( - remoteBridge, - 0, - maxSubmissionCost, - msg.sender, - msg.sender, - maxGas, - gasPriceBid, - message - ); - } - - // this function can retry - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/Eth2ArbTarget.sol b/helix-contract/contracts/ln/Eth2ArbTarget.sol deleted file mode 100644 index ac510ebf..00000000 --- a/helix-contract/contracts/ln/Eth2ArbTarget.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol"; -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnDefaultBridgeTarget.sol"; - -contract Eth2ArbTarget is Initializable, LnAccessController, LnDefaultBridgeTarget { - address public remoteBridge; - address public remoteBridgeAlias; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == remoteBridgeAlias, "invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - remoteBridgeAlias = AddressAliasHelper.applyL1ToL2Alias(remoteBridge); - } - - function setRemoteBridgeAlias(address _remoteBridgeAlias) external onlyDao { - remoteBridgeAlias = _remoteBridgeAlias; - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external onlyRemoteBridge whenNotPaused { - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/Eth2LineaSource.sol b/helix-contract/contracts/ln/Eth2LineaSource.sol deleted file mode 100644 index f9ae8035..00000000 --- a/helix-contract/contracts/ln/Eth2LineaSource.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnDefaultBridgeSource.sol"; -import "./interface/ILineaMessageService.sol"; - -contract Eth2LineaSource is Initializable, LnAccessController, LnDefaultBridgeSource { - ILineaMessageService public messageService; - address public remoteBridge; - - event WithdrawMargin(address sourceToken, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _messageService) public initializer { - messageService = ILineaMessageService(_messageService); - _initialize(_dao); - _setFeeReceiver(_dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function _sendMessage( - bytes memory message, - uint256 fee - ) internal { - messageService.sendMessage{ value: fee }( - remoteBridge, - fee, - message - ); - } - - // this function can retry - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/Eth2LineaTarget.sol b/helix-contract/contracts/ln/Eth2LineaTarget.sol deleted file mode 100644 index b37a7cdd..00000000 --- a/helix-contract/contracts/ln/Eth2LineaTarget.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnDefaultBridgeTarget.sol"; -import "./interface/ILineaMessageService.sol"; - -contract Eth2LineaTarget is Initializable, LnAccessController, LnDefaultBridgeTarget { - address public remoteBridge; - address public messageService; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == messageService, "invalid msg.sender"); - require(ILineaMessageService(messageService).sender() == remoteBridge, "invalid remote caller"); - _; - } - - function initialize(address _dao, address _messageService) public initializer { - _initialize(_dao); - messageService = _messageService; - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external onlyRemoteBridge whenNotPaused { - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/Eth2ZkSyncSource.sol b/helix-contract/contracts/ln/Eth2ZkSyncSource.sol deleted file mode 100644 index 28b4089f..00000000 --- a/helix-contract/contracts/ln/Eth2ZkSyncSource.sol +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnDefaultBridgeSource.sol"; -import "./interface/IZksyncMailbox.sol"; - -contract Eth2ZkSyncSource is Initializable, LnAccessController, LnDefaultBridgeSource { - IMailbox public mailbox; - address public remoteBridge; - - event WithdrawMargin(address sourceToken, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _mailbox) public initializer { - mailbox = IMailbox(_mailbox); - _initialize(_dao); - _setFeeReceiver(_dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function l2Fee( - uint256 gasPrice, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) external view returns(uint256) { - return mailbox.l2TransactionBaseCost(gasPrice, l2GasLimit, l2GasPerPubdataByteLimit); - } - - function _sendMessage( - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit, - bytes memory message, - uint256 prepaid - ) internal returns(bytes32) { - return mailbox.requestL2Transaction{ value: prepaid }( - remoteBridge, - 0, - message, - l2GasLimit, - l2GasPerPubdataByteLimit, - new bytes[](0), - msg.sender - ); - } - - // this function can retry - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/Eth2ZkSyncTarget.sol b/helix-contract/contracts/ln/Eth2ZkSyncTarget.sol deleted file mode 100644 index f1c0f4ca..00000000 --- a/helix-contract/contracts/ln/Eth2ZkSyncTarget.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnDefaultBridgeTarget.sol"; - -contract Eth2ZkSyncTarget is Initializable, LnAccessController, LnDefaultBridgeTarget { - uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); - address public remoteBridge; - address public remoteBridgeAlias; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == remoteBridgeAlias, "invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - // l1 address to l2 address - remoteBridgeAlias = address(uint160(_remoteBridge) + offset); - } - - function setRemoteBridgeAlias(address _remoteBridgeAlias) external onlyDao { - remoteBridgeAlias = _remoteBridgeAlias; - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external onlyRemoteBridge whenNotPaused { - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/Linea2EthSource.sol b/helix-contract/contracts/ln/Linea2EthSource.sol deleted file mode 100644 index fdc2da6a..00000000 --- a/helix-contract/contracts/ln/Linea2EthSource.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnOppositeBridgeSource.sol"; -import "./interface/ILineaMessageService.sol"; - -contract Linea2EthSource is Initializable, LnAccessController, LnOppositeBridgeSource { - address public remoteBridge; - // linea message service address - address public messageService; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == messageService, "invalid msg.sender"); - require(ILineaMessageService(messageService).sender() == remoteBridge, "invalid remote caller"); - _; - } - - function initialize(address _dao, address _messageService) public initializer { - _initialize(_dao); - _setFeeReceiver(_dao); - messageService = _messageService; - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function updateProtocolFee(address token, uint112 _protocolFee) external onlyDao { - _updateProtocolFee(token, _protocolFee); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) external onlyOperator { - _registerToken(sourceToken, targetToken, protocolFee, penaltyLnCollateral, sourceDecimals, targetDecimals); - } - - function slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external onlyRemoteBridge whenNotPaused { - _slash(latestSlashTransferId, transferId, sourceToken, provider, slasher); - } - - function withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdrawMargin(latestSlashTransferId, lastTransferId, provider, sourceToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/Linea2EthTarget.sol b/helix-contract/contracts/ln/Linea2EthTarget.sol deleted file mode 100644 index e8ce09a8..00000000 --- a/helix-contract/contracts/ln/Linea2EthTarget.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnOppositeBridgeTarget.sol"; -import "./interface/ILineaMessageService.sol"; - -contract Linea2EthTarget is Initializable, LnAccessController, LnOppositeBridgeTarget { - ILineaMessageService public messageService; - address public remoteBridge; - - event WithdrawMargin(bytes32 lastTransferId, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _messageService) public initializer { - messageService = ILineaMessageService(_messageService); - _initialize(_dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function _sendMessage( - bytes memory message, - uint256 fee - ) internal { - messageService.sendMessage{ value: fee }( - remoteBridge, - fee, - message - ); - } - - function slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external whenNotPaused { - bytes memory refundCallMessage = _slashAndRemoteRefund( - params, - expectedTransferId - ); - uint256 valueUsed = address(0) == params.targetToken ? params.amount : 0; - _sendMessage(refundCallMessage, msg.value - valueUsed); - } - - function retryRemoteRefund( - bytes32 transferId - ) payable external whenNotPaused { - bytes memory refundCallMessage = _retrySlashAndRemoteRefund(transferId); - _sendMessage(refundCallMessage, msg.value); - } - - function requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount - ) payable external whenNotPaused { - bytes memory cancelWithdrawMarginCall = _requestWithdrawMargin( - lastTransferId, - sourceToken, - amount - ); - _sendMessage(cancelWithdrawMarginCall, msg.value); - emit WithdrawMargin(lastTransferId, amount); - } -} - diff --git a/helix-contract/contracts/ln/LnBridgeBaseLZ.sol b/helix-contract/contracts/ln/LnBridgeBaseLZ.sol deleted file mode 100644 index b55e0053..00000000 --- a/helix-contract/contracts/ln/LnBridgeBaseLZ.sol +++ /dev/null @@ -1,185 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnDefaultBridgeSource.sol"; -import "./base/LnDefaultBridgeTarget.sol"; -import "./interface/ILayerZeroEndpoint.sol"; - -contract LnBridgeBaseLZ is Initializable, LnAccessController, LnDefaultBridgeSource, LnDefaultBridgeTarget { - ILayerZeroEndpoint public endpoint; - address public remoteBridge; - bytes32 public trustedRemote; - uint16 public remoteChainId; - - event WithdrawMargin(address sourceToken, uint112 amount); - event CallResult(bytes srcAddress, bool successed); - - receive() external payable {} - - modifier onlyRemoteBridge(bytes calldata srcAddress) { - require(msg.sender == address(endpoint), "invalid caller"); - require(trustedRemote == keccak256(srcAddress), "invalid remote caller"); - _; - } - - function initialize(address _dao, address _endpoint, uint16 _remoteChainId) public initializer { - _initialize(_dao); - endpoint = ILayerZeroEndpoint(_endpoint); - _setFeeReceiver(_dao); - remoteChainId = _remoteChainId; - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - trustedRemote = keccak256(abi.encodePacked(_remoteBridge, address(this))); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function estimateSlashFee( - TransferParameter calldata params - ) external view returns(uint256 nativeFee, uint256 zroFee) { - bytes memory slashCallMessage = _encodeSlashCall( - params, - msg.sender, - 0, - 0 - ); - return endpoint.estimateFees( - remoteChainId, - remoteBridge, - slashCallMessage, - false, - bytes("") - ); - } - - function estimateWithdrawFee( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external view returns(uint256 nativeFee, uint256 zroFee) { - bytes memory withdrawCallMessage = _encodeWithdrawCall( - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - return endpoint.estimateFees( - remoteChainId, - remoteBridge, - withdrawCallMessage, - false, - bytes("") - ); - } - - function _sendMessage( - bytes memory message, - uint256 prepaid - ) internal { - bytes memory destination = abi.encodePacked( - remoteBridge, - address(this) - ); - endpoint.send{ value: prepaid }( - remoteChainId, - destination, - message, - payable(msg.sender), - // zro payment, future parameter - address(0x0), - bytes("") - ); - } - - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } - - function lzReceive( - uint16 _srcChainId, - bytes calldata _srcAddress, - uint64, //nonce unused - bytes calldata _payload) onlyRemoteBridge(_srcAddress) whenNotPaused external { - require(_srcChainId == remoteChainId, "invalid src chainid"); - // call - (bool success,) = address(this).call(_payload); - // don't revert to prevent message block - emit CallResult(_srcAddress, success); - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external { - require(msg.sender == address(this), "only self"); - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external { - require(msg.sender == address(this), "only self"); - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/LnDefaultBridge.sol b/helix-contract/contracts/ln/LnDefaultBridge.sol new file mode 100644 index 00000000..ab56a387 --- /dev/null +++ b/helix-contract/contracts/ln/LnDefaultBridge.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; +import "./base/LnAccessController.sol"; +import "./base/LnDefaultBridgeSource.sol"; +import "./base/LnDefaultBridgeTarget.sol"; +import "./interface/ILowLevelMessager.sol"; + +contract LnDefaultBridge is Initializable, LnAccessController, LnDefaultBridgeSource, LnDefaultBridgeTarget { + struct MessagerService { + address sendService; + address receiveService; + } + + // remoteChainId => messager + mapping(uint256=>MessagerService) public messagers; + + receive() external payable {} + + function initialize(address dao) public initializer { + _initialize(dao); + _updateFeeReceiver(dao); + } + + function unpause() external onlyOperator { + _unpause(); + } + + function pause() external onlyOperator { + _pause(); + } + + // the remote endpoint is unique, if we want multi-path to remote endpoint, then the messager should support multi-path + function setSendService(uint256 _remoteChainId, address _remoteBridge, address _service) external onlyDao { + messagers[_remoteChainId].sendService = _service; + ILowLevelMessageSender(_service).registerRemoteReceiver(_remoteChainId, _remoteBridge); + } + + function setReceiveService(uint256 _remoteChainId, address _remoteBridge, address _service) external onlyDao { + messagers[_remoteChainId].receiveService = _service; + ILowLevelMessageReceiver(_service).registerRemoteSender(_remoteChainId, _remoteBridge); + } + + function updateFeeReceiver(address _receiver) external onlyDao { + _updateFeeReceiver(_receiver); + } + + function setTokenInfo( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _protocolFee, + uint112 _penaltyLnCollateral, + uint8 _sourceDecimals, + uint8 _targetDecimals + ) external onlyDao { + _setTokenInfo( + _remoteChainId, + _sourceToken, + _targetToken, + _protocolFee, + _penaltyLnCollateral, + _sourceDecimals, + _targetDecimals + ); + } + + function _sendMessageToTarget(uint256 _remoteChainId, bytes memory _payload, bytes memory _extParams) whenNotPaused internal override { + address sendService = messagers[_remoteChainId].sendService; + require(sendService != address(0), "invalid messager"); + ILowLevelMessageSender(sendService).sendMessage{value: msg.value}(_remoteChainId, _payload, _extParams); + } + + function _verifyRemote(uint256 _remoteChainId) whenNotPaused internal view override { + address receiveService = messagers[_remoteChainId].receiveService; + require(receiveService == msg.sender, "invalid messager"); + } +} + diff --git a/helix-contract/contracts/ln/LnOppositeBridge.sol b/helix-contract/contracts/ln/LnOppositeBridge.sol new file mode 100644 index 00000000..3c959cbd --- /dev/null +++ b/helix-contract/contracts/ln/LnOppositeBridge.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; +import "./base/LnAccessController.sol"; +import "./base/LnOppositeBridgeSource.sol"; +import "./base/LnOppositeBridgeTarget.sol"; +import "./interface/ILowLevelMessager.sol"; + +contract LnOppositeBridge is Initializable, LnAccessController, LnOppositeBridgeSource, LnOppositeBridgeTarget { + struct MessagerService { + address sendService; + address receiveService; + } + mapping(uint256=>MessagerService) public messagers; + + receive() external payable {} + + function initialize(address _dao) public initializer { + _initialize(_dao); + _updateFeeReceiver(_dao); + } + + function unpause() external onlyOperator { + _unpause(); + } + + function pause() external onlyOperator { + _pause(); + } + + function setSendService(uint256 _remoteChainId, address _remoteBridge, address _service) external onlyDao { + messagers[_remoteChainId].sendService = _service; + ILowLevelMessageSender(_service).registerRemoteReceiver(_remoteChainId, _remoteBridge); + } + + function setReceiveService(uint256 _remoteChainId, address _remoteBridge, address _service) external onlyDao { + messagers[_remoteChainId].receiveService = _service; + ILowLevelMessageReceiver(_service).registerRemoteSender(_remoteChainId, _remoteBridge); + } + + function updateFeeReceiver(address _receiver) external onlyDao { + _updateFeeReceiver(_receiver); + } + + function setTokenInfo( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _protocolFee, + uint112 _penaltyLnCollateral, + uint8 _sourceDecimals, + uint8 _targetDecimals + ) external onlyDao { + _setTokenInfo( + _remoteChainId, + _sourceToken, + _targetToken, + _protocolFee, + _penaltyLnCollateral, + _sourceDecimals, + _targetDecimals + ); + } + + function _sendMessageToTarget(uint256 _remoteChainId, bytes memory _payload, bytes memory _extParams) whenNotPaused internal override { + address sendService = messagers[_remoteChainId].sendService; + require(sendService != address(0), "invalid messager"); + ILowLevelMessageSender(sendService).sendMessage{value: msg.value}(_remoteChainId, _payload, _extParams); + } + + function _verifyRemote(uint256 _remoteChainId) whenNotPaused internal view override { + address receiveService = messagers[_remoteChainId].receiveService; + require(receiveService == msg.sender, "invalid messager"); + } +} + diff --git a/helix-contract/contracts/ln/ZkSync2EthSource.sol b/helix-contract/contracts/ln/ZkSync2EthSource.sol deleted file mode 100644 index 97a5e4ed..00000000 --- a/helix-contract/contracts/ln/ZkSync2EthSource.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnOppositeBridgeSource.sol"; - -contract ZkSync2EthSource is Initializable, LnAccessController, LnOppositeBridgeSource { - uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); - address public remoteBridge; - address public remoteBridgeAlias; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == remoteBridgeAlias, "invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - _setFeeReceiver(dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function updateProtocolFee(address token, uint112 _protocolFee) external onlyDao { - _updateProtocolFee(token, _protocolFee); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - remoteBridgeAlias = address(uint160(_remoteBridge) + offset); - } - - function setRemoteBridgeAlias(address _remoteBridgeAlias) external onlyDao { - remoteBridgeAlias = _remoteBridgeAlias; - } - - function registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) external onlyOperator { - _registerToken(sourceToken, targetToken, protocolFee, penaltyLnCollateral, sourceDecimals, targetDecimals); - } - - function slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external onlyRemoteBridge whenNotPaused { - _slash(latestSlashTransferId, transferId, sourceToken, provider, slasher); - } - - function withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdrawMargin(latestSlashTransferId, lastTransferId, provider, sourceToken, amount); - } -} - diff --git a/helix-contract/contracts/ln/ZkSync2EthTarget.sol b/helix-contract/contracts/ln/ZkSync2EthTarget.sol deleted file mode 100644 index a562e0a3..00000000 --- a/helix-contract/contracts/ln/ZkSync2EthTarget.sol +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/proxy/utils/Initializable.sol"; -import "./base/LnAccessController.sol"; -import "./base/LnOppositeBridgeTarget.sol"; -import "./interface/IZksyncMailbox.sol"; - -contract ZkSync2EthTarget is Initializable, LnAccessController, LnOppositeBridgeTarget { - IMailbox public mailbox; - address public remoteBridge; - - event WithdrawMargin(bytes32 lastTransferId, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _mailBox) public initializer { - mailbox = IMailbox(_mailBox); - _initialize(_dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function l2Fee( - uint256 gasPrice, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) external view returns(uint256) { - return mailbox.l2TransactionBaseCost(gasPrice, l2GasLimit, l2GasPerPubdataByteLimit); - } - - function _sendMessage( - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit, - bytes memory message, - uint256 prepaid - ) internal returns(bytes32) { - return mailbox.requestL2Transaction{ value: prepaid }( - remoteBridge, - 0, - message, - l2GasLimit, - l2GasPerPubdataByteLimit, - new bytes[](0), - msg.sender - ); - } - - function slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory refundCallMessage = _slashAndRemoteRefund( - params, - expectedTransferId - ); - uint256 valueUsed = address(0) == params.targetToken ? params.amount : 0; - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, refundCallMessage, msg.value - valueUsed); - } - - function retryRemoteRefund( - bytes32 transferId, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory refundCallMessage = _retrySlashAndRemoteRefund(transferId); - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, refundCallMessage, msg.value); - } - - function requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory cancelWithdrawMarginCall = _requestWithdrawMargin( - lastTransferId, - sourceToken, - amount - ); - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, cancelWithdrawMarginCall, msg.value); - emit WithdrawMargin(lastTransferId, amount); - } -} - diff --git a/helix-contract/contracts/ln/base/LnAccessController.sol b/helix-contract/contracts/ln/base/LnAccessController.sol index da336a3e..8f2cb263 100644 --- a/helix-contract/contracts/ln/base/LnAccessController.sol +++ b/helix-contract/contracts/ln/base/LnAccessController.sol @@ -1,39 +1,45 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity/contracts/access/AccessControlEnumerable.sol"; -import "@zeppelin-solidity/contracts/security/Pausable.sol"; +pragma solidity ^0.8.17; /// @title LnAccessController /// @notice LnAccessController is a contract to control the access permission /// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); +contract LnAccessController { + address public dao; + address public operator; + + mapping(address=>bool) public callerWhiteList; modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); + require(msg.sender == dao, "!dao"); _; } modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); + require(msg.sender == operator, "!operator"); _; } - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); + modifier onlyWhiteListCaller() { + require(callerWhiteList[msg.sender], "caller not in white list"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; } - function unpause() external onlyOperator { - _unpause(); + function authoriseAppCaller(address appAddress, bool enable) onlyDao external { + callerWhiteList[appAddress] = enable; } - function pause() external onlyOperator { - _pause(); + function transferOwnership(address _dao) onlyDao external { + dao = _dao; } } diff --git a/helix-contract/contracts/ln/base/LnBridgeHelper.sol b/helix-contract/contracts/ln/base/LnBridgeHelper.sol index cadcc37e..76fad68b 100644 --- a/helix-contract/contracts/ln/base/LnBridgeHelper.sol +++ b/helix-contract/contracts/ln/base/LnBridgeHelper.sol @@ -1,10 +1,16 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; import "@zeppelin-solidity/contracts/token/ERC20/IERC20.sol"; -contract LnBridgeHelper { +library LnBridgeHelper { + // the time(seconds) for liquidity provider to delivery message + // if timeout, slasher can work. + uint256 constant public SLASH_EXPIRE_TIME = 30 * 60; bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); + // liquidity fee base rate + // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount + uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; struct TransferParameter { bytes32 previousTransferId; @@ -12,11 +18,39 @@ contract LnBridgeHelper { address sourceToken; address targetToken; uint112 amount; - uint64 timestamp; + uint256 timestamp; address receiver; } - function _safeTransfer( + // sourceToken and targetToken is the pair of erc20 token(or native) addresses + // if sourceToken == address(0), then it's native token + // if targetToken == address(0), then remote is native token + // * `protocolFee` is the protocol fee charged by system + // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. + struct TokenInfo { + uint112 protocolFee; + uint112 penaltyLnCollateral; + uint8 sourceDecimals; + uint8 targetDecimals; + bool isRegistered; + } + + function sourceAmountToTargetAmount( + TokenInfo memory tokenInfo, + uint112 amount + ) internal pure returns(uint112) { + uint256 targetAmount = uint256(amount) * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; + require(targetAmount < type(uint112).max, "overflow amount"); + return uint112(targetAmount); + } + + function calculateProviderFee(uint112 baseFee, uint16 liquidityFeeRate, uint112 amount) internal pure returns(uint112) { + uint256 fee = uint256(baseFee) + uint256(liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; + require(fee < type(uint112).max, "overflow fee"); + return uint112(fee); + } + + function safeTransfer( address token, address receiver, uint256 amount @@ -29,7 +63,7 @@ contract LnBridgeHelper { require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); } - function _safeTransferFrom( + function safeTransferFrom( address token, address sender, address receiver, @@ -44,7 +78,7 @@ contract LnBridgeHelper { require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); } - function _safeTransferNative( + function safeTransferNative( address receiver, uint256 amount ) internal { @@ -52,16 +86,18 @@ contract LnBridgeHelper { require(success, "lnBridgeHelper:transfer native token failed"); } - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { + function getProviderKey(uint256 remoteChainId, address provider, address sourceToken, address targetToken) pure internal returns(bytes32) { return keccak256(abi.encodePacked( + remoteChainId, provider, - sourceToken + sourceToken, + targetToken )); } - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { + function getTokenKey(uint256 remoteChainId, address sourceToken, address targetToken) pure internal returns(bytes32) { return keccak256(abi.encodePacked( - provider, + remoteChainId, sourceToken, targetToken )); diff --git a/helix-contract/contracts/ln/base/LnDefaultBridgeSource.sol b/helix-contract/contracts/ln/base/LnDefaultBridgeSource.sol index 03a8c73b..0419cf45 100644 --- a/helix-contract/contracts/ln/base/LnDefaultBridgeSource.sol +++ b/helix-contract/contracts/ln/base/LnDefaultBridgeSource.sol @@ -1,51 +1,29 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; -import "@zeppelin-solidity/contracts/token/ERC20/IERC20.sol"; +import "@zeppelin-solidity/contracts/security/Pausable.sol"; import "./LnBridgeHelper.sol"; import "../interface/ILnDefaultBridgeTarget.sol"; -/// @title LnPositiveBridgeSource -/// @notice LnPositiveBridgeSource is a contract to help user transfer token to liquidity node and generate proof, +/// @title LnDefaultBridgeSource +/// @notice LnDefaultBridgeSource is a contract to help user transfer token to liquidity node and generate proof, /// then the liquidity node must transfer the same amount of the token to the user on target chain. /// Otherwise if timeout the slasher can send a slash request message to target chain, then force transfer from lnProvider's margin to the user. /// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnDefaultBridgeSource is LnBridgeHelper { - // the time(seconds) for liquidity provider to delivery message - // if timeout, slasher can work. - uint256 constant private MIN_SLASH_TIMESTAMP = 30 * 60; - // liquidity fee base rate - // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - // max transfer amount one time - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - +contract LnDefaultBridgeSource is Pausable { // provider fee is paid to liquidity node's account // the fee is charged by the same token that user transfered // providerFee = baseFee + liquidityFeeRate/LIQUIDITY_FEE_RATE_BASE * sendAmount - struct LnProviderFee { + struct SourceProviderConfigure { uint112 baseFee; - uint8 liquidityFeeRate; + uint16 liquidityFeeRate; + uint64 withdrawNonce; + bool pause; } - struct LnProviderInfo { - LnProviderFee fee; + struct SourceProviderInfo { + SourceProviderConfigure config; // we use this nonce to generate the unique withdraw id - uint64 withdrawNonce; bytes32 lastTransferId; } // the Snapshot is the state of the token bridge when user prepare to transfer across chains. @@ -54,8 +32,10 @@ contract LnDefaultBridgeSource is LnBridgeHelper { // 2. if totalFee increase, revert // 3. if totalFee decrease, success struct Snapshot { + uint256 remoteChainId; address provider; address sourceToken; + address targetToken; bytes32 transferId; uint112 totalFee; uint64 withdrawNonce; @@ -66,36 +46,46 @@ contract LnDefaultBridgeSource is LnBridgeHelper { struct LockInfo { uint112 fee; uint112 penalty; - bool isLocked; + // the timestamp when token locked, if zero, the lockinfo not exist + uint32 timestamp; } - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // providerKey => provider info - mapping(bytes32=>LnProviderInfo) public lnProviders; + + // hash(remoteChainId, sourceToken, targetToken) => token info + mapping(bytes32=>LnBridgeHelper.TokenInfo) public tokenInfos; + // hash(remoteChainId, provider, sourceToken, targetToken) => provider info + mapping(bytes32=>SourceProviderInfo) public srcProviders; // transferId => lock info mapping(bytes32=>LockInfo) public lockInfos; address public protocolFeeReceiver; event TokenLocked( + uint256 remoteChainId, bytes32 transferId, address provider, address sourceToken, + address targetToken, uint112 amount, uint112 fee, - uint64 timestamp, + uint32 timestamp, address receiver); - event LnProviderUpdated(address provider, address sourceToken, uint112 baseFee, uint8 liquidityfeeRate); + event LnProviderUpdated(uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint112 baseFee, uint8 liquidityfeeRate); + + event WithdrawMarginRequest(uint256 remoteChainId, address sourceToken, address targetToken, uint112 amount); + event SlashRequest(uint256 remoteChainId, address sourceToken, address targetToken, bytes32 expectedTransferId); // protocolFeeReceiver is the protocol fee reciever, we don't use the contract itself as the receiver - function _setFeeReceiver(address _feeReceiver) internal { + /// @notice should be called by special privileges + function _updateFeeReceiver(address _feeReceiver) internal { require(_feeReceiver != address(this), "invalid system fee receiver"); protocolFeeReceiver = _feeReceiver; } // register or update token info, it can be only called by contract owner // source token can only map a unique target token on target chain + /// @notice should be called by special privileges function _setTokenInfo( + uint256 _remoteChainId, address _sourceToken, address _targetToken, uint112 _protocolFee, @@ -103,8 +93,8 @@ contract LnDefaultBridgeSource is LnBridgeHelper { uint8 _sourceDecimals, uint8 _targetDecimals ) internal { - tokenInfos[_sourceToken] = TokenInfo( - _targetToken, + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _sourceToken, _targetToken)); + tokenInfos[key] = LnBridgeHelper.TokenInfo( _protocolFee, _penaltyLnCollateral, _sourceDecimals, @@ -113,37 +103,57 @@ contract LnDefaultBridgeSource is LnBridgeHelper { ); } + function providerPause(uint256 _remoteChainId, address _sourceToken, address _targetToken) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + srcProviders[providerKey].config.pause = true; + } + + function providerUnpause(uint256 _remoteChainId, address _sourceToken, address _targetToken) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + srcProviders[providerKey].config.pause = false; + } + // lnProvider register // 1. set fee on source chain // 2. deposit margin on target chain function setProviderFee( - address sourceToken, - uint112 baseFee, - uint8 liquidityFeeRate + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _baseFee, + uint8 _liquidityFeeRate ) external { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _sourceToken, _targetToken)); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[key]; require(tokenInfo.isRegistered, "token not registered"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderFee memory providerFee = LnProviderFee(baseFee, liquidityFeeRate); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + + require(_liquidityFeeRate < LnBridgeHelper.LIQUIDITY_FEE_RATE_BASE, "liquidity fee too large"); + SourceProviderConfigure memory providerConfigure = srcProviders[providerKey].config; + providerConfigure.baseFee = _baseFee; + providerConfigure.liquidityFeeRate = _liquidityFeeRate; // we only update the field fee of the provider info // if the provider has not been registered, then this line will register, otherwise update fee - lnProviders[providerKey].fee = providerFee; - - emit LnProviderUpdated(msg.sender, sourceToken, baseFee, liquidityFeeRate); - } + srcProviders[providerKey].config = providerConfigure; - function calculateProviderFee(LnProviderFee memory fee, uint112 amount) internal pure returns(uint256) { - return uint256(fee.baseFee) + uint256(fee.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; + emit LnProviderUpdated(_remoteChainId, msg.sender, _sourceToken, _targetToken, _baseFee, _liquidityFeeRate); } // the fee user should paid when transfer. // totalFee = providerFee + protocolFee - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); + function totalFee( + uint256 _remoteChainId, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external view returns(uint256) { + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _sourceToken, _targetToken)); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[key]; + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + uint112 providerFee = LnBridgeHelper.calculateProviderFee(providerInfo.config.baseFee, providerInfo.config.liquidityFeeRate, _amount); return providerFee + tokenInfo.protocolFee; } @@ -153,64 +163,68 @@ contract LnDefaultBridgeSource is LnBridgeHelper { // 1. the state(lastTransferId, fee, withdrawNonce) must match snapshot // 2. transferId not exist function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; + Snapshot calldata _snapshot, + uint112 _amount, + address _receiver + ) whenNotPaused external payable { + require(_amount > 0, "invalid amount"); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[ + LnBridgeHelper.getTokenKey(_snapshot.remoteChainId, _snapshot.sourceToken, _snapshot.targetToken) + ]; require(tokenInfo.isRegistered, "token not registered"); - bytes32 providerKey = getDefaultProviderKey(snapshot.provider, snapshot.sourceToken, tokenInfo.targetToken); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_snapshot.remoteChainId, _snapshot.provider, _snapshot.sourceToken, _snapshot.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + require(!providerInfo.config.pause, "provider paused"); + uint112 providerFee = LnBridgeHelper.calculateProviderFee(providerInfo.config.baseFee, providerInfo.config.liquidityFeeRate, _amount); // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired:transfer"); - require(snapshot.withdrawNonce == providerInfo.withdrawNonce, "snapshot expired:withdraw"); - require(snapshot.totalFee >= providerFee + tokenInfo.protocolFee && providerFee > 0, "fee is invalid"); + require(providerInfo.lastTransferId == _snapshot.transferId, "snapshot expired:transfer"); + require(_snapshot.withdrawNonce == providerInfo.config.withdrawNonce, "snapshot expired:withdraw"); + require(_snapshot.totalFee >= providerFee + tokenInfo.protocolFee && providerFee > 0, "fee is invalid"); - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(amount)); - uint64 timestamp = uint64(block.timestamp); + uint112 targetAmount = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, _amount); + require(targetAmount > 0, "invalid target amount"); + require(block.timestamp < type(uint32).max, "timestamp overflow"); bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, + block.chainid, + _snapshot.remoteChainId, + _snapshot.transferId, + _snapshot.provider, + _snapshot.sourceToken, + _snapshot.targetToken, + _receiver, targetAmount )); - require(!lockInfos[transferId].isLocked, "transferId exist"); + require(lockInfos[transferId].timestamp == 0, "transferId exist"); // if the transfer refund, then the fee and penalty should be given to slasher, but the protocol fee is ignored // and we use the penalty value configure at the moment transfer confirmed - lockInfos[transferId] = LockInfo(snapshot.totalFee, tokenInfo.penaltyLnCollateral, true); + lockInfos[transferId] = LockInfo(providerFee, tokenInfo.penaltyLnCollateral, uint32(block.timestamp)); // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; + srcProviders[providerKey].lastTransferId = transferId; - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); + if (_snapshot.sourceToken == address(0)) { + require(_amount + _snapshot.totalFee == msg.value, "amount unmatched"); + LnBridgeHelper.safeTransferNative(_snapshot.provider, _amount + providerFee); if (tokenInfo.protocolFee > 0) { - _safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); + LnBridgeHelper.safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; + uint256 refund = _snapshot.totalFee - tokenInfo.protocolFee - providerFee; if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); + LnBridgeHelper.safeTransferNative(msg.sender, refund); } } else { - _safeTransferFrom( - snapshot.sourceToken, + LnBridgeHelper.safeTransferFrom( + _snapshot.sourceToken, msg.sender, - snapshot.provider, - amount + providerFee + _snapshot.provider, + _amount + providerFee ); if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, + LnBridgeHelper.safeTransferFrom( + _snapshot.sourceToken, msg.sender, protocolFeeReceiver, tokenInfo.protocolFee @@ -218,109 +232,150 @@ contract LnDefaultBridgeSource is LnBridgeHelper { } } emit TokenLocked( + _snapshot.remoteChainId, transferId, - snapshot.provider, - snapshot.sourceToken, + _snapshot.provider, + _snapshot.sourceToken, + _snapshot.targetToken, targetAmount, - uint112(providerFee), - timestamp, - receiver); + providerFee, + uint32(block.timestamp), + _receiver); } - function _sourceAmountToTargetAmount( - TokenInfo memory tokenInfo, - uint256 amount - ) internal pure returns(uint112) { - uint256 targetAmount = amount * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - return uint112(targetAmount); - } - - function _slashAndRemoteRelease( - TransferParameter memory params, - bytes32 expectedTransferId + function _slashAndRemoteReleaseCall( + LnBridgeHelper.TransferParameter memory _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId ) internal view returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "invalid timestamp"); - TokenInfo memory tokenInfo = tokenInfos[params.sourceToken]; + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _params.sourceToken, _params.targetToken)); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[key]; require(tokenInfo.isRegistered, "token not registered"); - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(params.amount)); + uint112 targetAmount = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, _params.amount); bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, + block.chainid, + _remoteChainId, + _params.previousTransferId, + _params.provider, + _params.sourceToken, + _params.targetToken, + _params.receiver, targetAmount )); - require(expectedTransferId == transferId, "expected transfer id not match"); + require(_expectedTransferId == transferId, "expected transfer id not match"); LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.isLocked, "lock info not match"); - uint112 targetFee = _sourceAmountToTargetAmount(tokenInfo, lockInfo.fee); - uint112 targetPenalty = _sourceAmountToTargetAmount(tokenInfo, lockInfo.penalty); + require(lockInfo.timestamp == _params.timestamp, "invalid timestamp"); + require(block.timestamp > lockInfo.timestamp + LnBridgeHelper.SLASH_EXPIRE_TIME, "invalid timestamp"); + uint112 targetFee = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, lockInfo.fee); + uint112 targetPenalty = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, lockInfo.penalty); - message = _encodeSlashCall( - params, - msg.sender, - targetFee, - targetPenalty + message = abi.encodeWithSelector( + ILnDefaultBridgeTarget.slash.selector, + _params, + block.chainid, + msg.sender, // slasher + targetFee, + targetPenalty ); } - function _withdrawMargin( - address sourceToken, - uint112 amount + function _withdrawMarginCall( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _amount ) internal returns(bytes memory message) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _sourceToken, _targetToken)); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[key]; require(tokenInfo.isRegistered, "token not registered"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - lnProviders[providerKey].withdrawNonce = providerInfo.withdrawNonce + 1; - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, amount); - message = _encodeWithdrawCall( + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + srcProviders[providerKey].config.withdrawNonce = providerInfo.config.withdrawNonce + 1; + uint112 targetAmount = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, _amount); + message = abi.encodeWithSelector( + ILnDefaultBridgeTarget.withdraw.selector, + block.chainid, providerInfo.lastTransferId, - providerInfo.withdrawNonce + 1, - msg.sender, - sourceToken, - tokenInfo.targetToken, + providerInfo.config.withdrawNonce + 1, + msg.sender, //provider, + _sourceToken, + _targetToken, targetAmount ); } - function _encodeSlashCall( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty + function encodeSlashCall( + LnBridgeHelper.TransferParameter memory _params, + uint256 _localChainId, + address _slasher, + uint112 _fee, + uint112 _penalty ) public pure returns(bytes memory message) { return abi.encodeWithSelector( ILnDefaultBridgeTarget.slash.selector, - params, - slasher, - fee, - penalty + _params, + _localChainId, + _slasher, + _fee, + _penalty ); } - function _encodeWithdrawCall( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount + function encodeWithdrawCall( + bytes32 _lastTransferId, + uint256 _localChainId, + uint64 _withdrawNonce, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount ) public pure returns(bytes memory message) { return abi.encodeWithSelector( ILnDefaultBridgeTarget.withdraw.selector, - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount + _localChainId, + _lastTransferId, + _withdrawNonce, + _provider, + _sourceToken, + _targetToken, + _amount + ); + } + + function _sendMessageToTarget(uint256 _remoteChainId, bytes memory _payload, bytes memory _extParams) internal virtual {} + + function requestSlashAndRemoteRelease( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId, + bytes memory _extParams + ) payable external { + bytes memory slashCallMessage = _slashAndRemoteReleaseCall( + _params, + _remoteChainId, + _expectedTransferId + ); + _sendMessageToTarget(_remoteChainId, slashCallMessage, _extParams); + emit SlashRequest(_remoteChainId, _params.sourceToken, _params.targetToken, _expectedTransferId); + } + + function requestWithdrawMargin( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _amount, + bytes memory _extParams + ) payable external { + bytes memory withdrawCallMessage = _withdrawMarginCall( + _remoteChainId, + _sourceToken, + _targetToken, + _amount ); + _sendMessageToTarget(_remoteChainId, withdrawCallMessage, _extParams); + emit WithdrawMarginRequest(_remoteChainId, _sourceToken, _targetToken, _amount); } } diff --git a/helix-contract/contracts/ln/base/LnDefaultBridgeTarget.sol b/helix-contract/contracts/ln/base/LnDefaultBridgeTarget.sol index b46d2dae..dc569d72 100644 --- a/helix-contract/contracts/ln/base/LnDefaultBridgeTarget.sol +++ b/helix-contract/contracts/ln/base/LnDefaultBridgeTarget.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; import "./LnBridgeHelper.sol"; -contract LnDefaultBridgeTarget is LnBridgeHelper { - uint256 constant private MIN_SLASH_TIMESTAMP = 30 * 60; - - struct ProviderInfo { +contract LnDefaultBridgeTarget { + struct TargetProviderInfo { uint256 margin; // use this slash gas reserve to pay the slash fee if transfer filled but timeout uint256 slashReserveFund; @@ -15,8 +13,8 @@ contract LnDefaultBridgeTarget is LnBridgeHelper { } // providerKey => margin - // providerKey = hash(provider, sourceToken, targetToken) - mapping(bytes32=>ProviderInfo) public lnProviderInfos; + // providerKey = hash(remoteChainId, provider, sourceToken, targetToken) + mapping(bytes32=>TargetProviderInfo) public tgtProviders; // if timestamp > 0, the Transfer has been relayed or slashed // if slasher == address(0), this FillTransfer is relayed by lnProvider @@ -29,188 +27,203 @@ contract LnDefaultBridgeTarget is LnBridgeHelper { // transferId => FillTransfer mapping(bytes32 => FillTransfer) public fillTransfers; - event TransferFilled(address provider, bytes32 transferId); - event Slash(bytes32 transferId, address provider, address token, uint256 margin, address slasher); - event MarginUpdated(address provider, address token, uint256 amount, uint64 withdrawNonce); - event SlashReserveUpdated(address provider, address token, uint256 amount); + event TransferFilled(bytes32 transferId, address provider); + event Slash(bytes32 transferId, uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint256 margin, address slasher); + event MarginUpdated(uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint256 amount, uint64 withdrawNonce); + event SlashReserveUpdated(address provider, address sourceToken, address targetToken, uint256 amount); + + modifier allowRemoteCall(uint256 _remoteChainId) { + _verifyRemote(_remoteChainId); + _; + } + + function _verifyRemote(uint256 _remoteChainId) internal virtual {} function depositProviderMargin( - address sourceToken, - address targetToken, - uint256 margin + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint256 _margin ) external payable { - require(margin > 0, "invalid margin"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin + margin; - lnProviderInfos[providerKey].margin = updatedMargin; - if (targetToken == address(0)) { - require(msg.value == margin, "invalid margin value"); + require(_margin > 0, "invalid margin"); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; + uint256 updatedMargin = providerInfo.margin + _margin; + tgtProviders[providerKey].margin = updatedMargin; + if (_targetToken == address(0)) { + require(msg.value == _margin, "invalid margin value"); } else { - _safeTransferFrom(targetToken, msg.sender, address(this), margin); + LnBridgeHelper.safeTransferFrom(_targetToken, msg.sender, address(this), _margin); } - emit MarginUpdated(msg.sender, sourceToken, updatedMargin, providerInfo.withdrawNonce); + emit MarginUpdated(_remoteChainId, msg.sender, _sourceToken, _targetToken, updatedMargin, providerInfo.withdrawNonce); } function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId ) external payable { - require(params.provider == msg.sender, "invalid provider"); - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); + require(_params.provider == msg.sender, "invalid provider"); + require(_params.previousTransferId == bytes32(0) || fillTransfers[_params.previousTransferId].timestamp > 0, "last transfer not filled"); bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount + _remoteChainId, + block.chainid, + _params.previousTransferId, + _params.provider, + _params.sourceToken, + _params.targetToken, + _params.receiver, + _params.amount )); - require(expectedTransferId == transferId, "check expected transferId failed"); + require(_expectedTransferId == transferId, "check expected transferId failed"); FillTransfer memory fillTransfer = fillTransfers[transferId]; // Make sure this transfer was never filled before require(fillTransfer.timestamp == 0, "transfer has been filled"); fillTransfers[transferId].timestamp = uint64(block.timestamp); - if (block.timestamp - MIN_SLASH_TIMESTAMP > params.timestamp) { - bytes32 providerKey = getDefaultProviderKey(msg.sender, params.sourceToken, params.targetToken); - lnProviderInfos[providerKey].lastExpireFillTime = uint64(block.timestamp); + if (block.timestamp - LnBridgeHelper.SLASH_EXPIRE_TIME > _params.timestamp) { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _params.sourceToken, _params.targetToken); + tgtProviders[providerKey].lastExpireFillTime = uint64(block.timestamp); } - if (params.targetToken == address(0)) { - require(msg.value == params.amount, "lnBridgeTarget:invalid amount"); - _safeTransferNative(params.receiver, params.amount); + if (_params.targetToken == address(0)) { + require(msg.value == _params.amount, "lnBridgeTarget:invalid amount"); + LnBridgeHelper.safeTransferNative(_params.receiver, _params.amount); } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); + LnBridgeHelper.safeTransferFrom(_params.targetToken, msg.sender, _params.receiver, uint256(_params.amount)); } - emit TransferFilled(params.provider, transferId); + emit TransferFilled(transferId, _params.provider); } function depositSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint256 _amount ) external payable { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedAmount = providerInfo.slashReserveFund + amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - require(msg.value == amount, "amount invalid"); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; + uint256 updatedAmount = providerInfo.slashReserveFund + _amount; + tgtProviders[providerKey].slashReserveFund = updatedAmount; + if (_targetToken == address(0)) { + require(msg.value == _amount, "amount invalid"); } else { - _safeTransferFrom(targetToken, msg.sender, address(this), amount); + LnBridgeHelper.safeTransferFrom(_targetToken, msg.sender, address(this), _amount); } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); + emit SlashReserveUpdated(msg.sender, _sourceToken, _targetToken, updatedAmount); } // withdraw slash fund // provider can't withdraw until the block.timestamp overtime lastExpireFillTime for a period of time function withdrawSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint256 _amount ) external { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - require(amount <= providerInfo.slashReserveFund, "reserve not enough"); - require(block.timestamp - MIN_SLASH_TIMESTAMP >= providerInfo.lastExpireFillTime, "time not expired"); - uint256 updatedAmount = providerInfo.slashReserveFund - amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - _safeTransferNative(msg.sender, amount); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; + require(_amount <= providerInfo.slashReserveFund, "reserve not enough"); + require(block.timestamp - LnBridgeHelper.SLASH_EXPIRE_TIME >= providerInfo.lastExpireFillTime, "time not expired"); + uint256 updatedAmount = providerInfo.slashReserveFund - _amount; + tgtProviders[providerKey].slashReserveFund = updatedAmount; + if (_targetToken == address(0)) { + LnBridgeHelper.safeTransferNative(msg.sender, _amount); } else { - _safeTransfer(targetToken, msg.sender, amount); + LnBridgeHelper.safeTransfer(_targetToken, msg.sender, _amount); } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); + emit SlashReserveUpdated(msg.sender, _sourceToken, _targetToken, updatedAmount); } - function _withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal { + function withdraw( + uint256 _remoteChainId, + bytes32 _lastTransferId, + uint64 _withdrawNonce, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external allowRemoteCall(_remoteChainId) { // ensure all transfer has finished - require(lastTransferId == bytes32(0) || fillTransfers[lastTransferId].timestamp > 0, "last transfer not filled"); + require(_lastTransferId == bytes32(0) || fillTransfers[_lastTransferId].timestamp > 0, "last transfer not filled"); - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; // all the early withdraw info ignored - require(providerInfo.withdrawNonce < withdrawNonce, "withdraw nonce expired"); + require(providerInfo.withdrawNonce < _withdrawNonce, "withdraw nonce expired"); // transfer token - require(providerInfo.margin >= amount, "margin not enough"); - uint256 updatedMargin = providerInfo.margin - amount; - lnProviderInfos[providerKey].margin = updatedMargin; - lnProviderInfos[providerKey].withdrawNonce = withdrawNonce; + require(providerInfo.margin >= _amount, "margin not enough"); + uint256 updatedMargin = providerInfo.margin - _amount; + tgtProviders[providerKey].margin = updatedMargin; + tgtProviders[providerKey].withdrawNonce = _withdrawNonce; - if (targetToken == address(0)) { - _safeTransferNative(provider, amount); + if (_targetToken == address(0)) { + LnBridgeHelper.safeTransferNative(_provider, _amount); } else { - _safeTransfer(targetToken, provider, amount); + LnBridgeHelper.safeTransfer(_targetToken, _provider, _amount); } - emit MarginUpdated(provider, sourceToken, updatedMargin, withdrawNonce); + emit MarginUpdated(_remoteChainId, _provider, _sourceToken, _targetToken, updatedMargin, _withdrawNonce); } - function _slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal { - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); + function slash( + LnBridgeHelper.TransferParameter memory _params, + uint256 _remoteChainId, + address _slasher, + uint112 _fee, + uint112 _penalty + ) external allowRemoteCall(_remoteChainId) { + require(_params.previousTransferId == bytes32(0) || fillTransfers[_params.previousTransferId].timestamp > 0, "last transfer not filled"); bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount + _remoteChainId, + block.chainid, + _params.previousTransferId, + _params.provider, + _params.sourceToken, + _params.targetToken, + _params.receiver, + _params.amount )); FillTransfer memory fillTransfer = fillTransfers[transferId]; require(fillTransfer.slasher == address(0), "transfer has been slashed"); - bytes32 providerKey = getDefaultProviderKey(params.provider, params.sourceToken, params.targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _params.provider, _params.sourceToken, _params.targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; uint256 updatedMargin = providerInfo.margin; // transfer is not filled if (fillTransfer.timestamp == 0) { - require(params.timestamp < block.timestamp - MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId] = FillTransfer(uint64(block.timestamp), slasher); + require(_params.timestamp < block.timestamp - LnBridgeHelper.SLASH_EXPIRE_TIME, "time not expired"); + fillTransfers[transferId] = FillTransfer(uint64(block.timestamp), _slasher); // 1. transfer token to receiver // 2. trnasfer fee and penalty to slasher // update margin - uint256 marginCost = params.amount + fee + penalty; + uint256 marginCost = _params.amount + _fee + _penalty; require(providerInfo.margin >= marginCost, "margin not enough"); updatedMargin = providerInfo.margin - marginCost; - lnProviderInfos[providerKey].margin = updatedMargin; + tgtProviders[providerKey].margin = updatedMargin; - if (params.targetToken == address(0)) { - _safeTransferNative(params.receiver, params.amount); - _safeTransferNative(slasher, fee + penalty); + if (_params.targetToken == address(0)) { + LnBridgeHelper.safeTransferNative(_params.receiver, _params.amount); + LnBridgeHelper.safeTransferNative(_slasher, _fee + _penalty); } else { - _safeTransfer(params.targetToken, params.receiver, uint256(params.amount)); - _safeTransfer(params.targetToken, slasher, fee + penalty); + LnBridgeHelper.safeTransfer(_params.targetToken, _params.receiver, uint256(_params.amount)); + LnBridgeHelper.safeTransfer(_params.targetToken, _slasher, _fee + _penalty); } } else { - require(fillTransfer.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId].slasher = slasher; - uint112 slashRefund = penalty / 5; + require(fillTransfer.timestamp > _params.timestamp + LnBridgeHelper.SLASH_EXPIRE_TIME, "time not expired"); + fillTransfers[transferId].slasher = _slasher; + uint112 slashRefund = _penalty / 5; // transfer slashRefund to slasher require(providerInfo.slashReserveFund >= slashRefund, "slashReserveFund not enough"); - lnProviderInfos[providerKey].slashReserveFund = providerInfo.slashReserveFund - slashRefund; - if (params.targetToken == address(0)) { - _safeTransferNative(slasher, slashRefund); + tgtProviders[providerKey].slashReserveFund = providerInfo.slashReserveFund - slashRefund; + if (_params.targetToken == address(0)) { + LnBridgeHelper.safeTransferNative(_slasher, slashRefund); } else { - _safeTransfer(params.targetToken, slasher, slashRefund); + LnBridgeHelper.safeTransfer(_params.targetToken, _slasher, slashRefund); } } - emit Slash(transferId, params.provider, params.sourceToken, updatedMargin, slasher); + emit Slash(transferId, _remoteChainId, _params.provider, _params.sourceToken, _params.targetToken, updatedMargin, _slasher); } } diff --git a/helix-contract/contracts/ln/base/LnOppositeBridgeSource.sol b/helix-contract/contracts/ln/base/LnOppositeBridgeSource.sol index ca9a859f..dcba7de7 100644 --- a/helix-contract/contracts/ln/base/LnOppositeBridgeSource.sol +++ b/helix-contract/contracts/ln/base/LnOppositeBridgeSource.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; -import "@zeppelin-solidity/contracts/token/ERC20/IERC20.sol"; +import "@zeppelin-solidity/contracts/security/Pausable.sol"; import "./LnBridgeHelper.sol"; /// @title LnBridgeSource @@ -9,36 +9,19 @@ import "./LnBridgeHelper.sol"; /// then the liquidity node must transfer the same amount of the token to the user on target chain. /// Otherwise if timeout the slasher can paid for relayer and slash the transfer, then request slash from lnProvider's margin. /// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnOppositeBridgeSource is LnBridgeHelper { - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } +contract LnOppositeBridgeSource is Pausable { // the Liquidity Node provider info // Liquidity Node need register first - struct LnProviderConfigure { + struct SourceProviderConfigure { uint112 margin; uint112 baseFee; // liquidityFeeRate / 100,000 * amount = liquidityFee // the max liquidity fee rate is 0.255% - uint8 liquidityFeeRate; - } - struct LnProviderInfo { - LnProviderConfigure config; + uint16 liquidityFeeRate; bool pause; + } + struct SourceProviderInfo { + SourceProviderConfigure config; bytes32 lastTransferId; } @@ -48,133 +31,146 @@ contract LnOppositeBridgeSource is LnBridgeHelper { // 2. if margin decrease or totalFee increase, revert // 3. if margin increase or totalFee decrease, success struct Snapshot { + uint256 remoteChainId; address provider; address sourceToken; + address targetToken; bytes32 transferId; - uint112 depositedMargin; uint112 totalFee; + uint112 depositedMargin; } // registered token info - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // registered lnProviders - mapping(bytes32=>LnProviderInfo) public lnProviders; + // tokenKey => token info + mapping(bytes32=>LnBridgeHelper.TokenInfo) public tokenInfos; + // registered srcProviders + mapping(bytes32=>SourceProviderInfo) public srcProviders; // each time cross chain transfer, amount and fee can't be larger than type(uint112).max struct LockInfo { // amount + providerFee + penaltyLnCollateral // the Indexer should be care about this value, it will frozen lnProvider's margin when the transfer not finished. // and when the slasher slash success, this amount of token will be transfer from lnProvider's margin to slasher. - uint112 amountWithFeeAndPenalty; + uint112 amount; + uint112 feeAndPenalty; + uint32 timestamp; bool hasSlashed; } - // key: transferId = hash(proviousTransferId, timestamp, targetToken, receiver, targetAmount) + // key: transferId = hash(proviousTransferId, targetToken, receiver, targetAmount) // * `proviousTransferId` is used to ensure the continuous of the transfer // * `timestamp` is the block.timestmap to judge timeout on target chain(here we support source and target chain has the same world clock) // * `targetToken`, `receiver` and `targetAmount` are used on target chain to transfer target token. mapping(bytes32 => LockInfo) public lockInfos; - address public feeReceiver; + + address public protocolFeeReceiver; event TokenLocked( + uint256 remoteChainId, bytes32 transferId, address provider, address sourceToken, + address targetToken, uint112 amount, uint112 fee, - uint64 timestamp, + uint32 timestamp, address receiver); - event LiquidityWithdrawn(address provider, address token, uint112 amount); - event Slash(bytes32 transferId, address provider, address token, uint112 margin, address slasher); + event LiquidityWithdrawn(uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint112 amount); + event Slash(uint256 remoteChainId, bytes32 transferId, address provider, address sourceToken, address targetToken, uint112 margin, address slasher); // relayer - event LnProviderUpdated(address provider, address token, uint112 margin, uint112 baseFee, uint8 liquidityfeeRate); + event LnProviderUpdated(uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint112 margin, uint112 baseFee, uint16 liquidityfeeRate); - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - feeReceiver = _feeReceiver; + modifier allowRemoteCall(uint256 _remoteChainId) { + _verifyRemote(_remoteChainId); + _; } - function _updateProtocolFee(address _token, uint112 _protocolFee) internal { - require(tokenInfos[_token].isRegistered, "token not registered"); - tokenInfos[_token].protocolFee = _protocolFee; + function _verifyRemote(uint256 _remoteChainId) internal virtual {} + + function _updateFeeReceiver(address _feeReceiver) internal { + require(_feeReceiver != address(this), "invalid system fee receiver"); + protocolFeeReceiver = _feeReceiver; } - function _updatePenaltyLnCollateral(address _token, uint112 _penaltyLnCollateral) internal { - require(tokenInfos[_token].isRegistered, "token not registered"); - tokenInfos[_token].penaltyLnCollateral = _penaltyLnCollateral; + function _setTokenInfo( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _protocolFee, + uint112 _penaltyLnCollateral, + uint8 _sourceDecimals, + uint8 _targetDecimals + ) internal { + bytes32 tokenKey = LnBridgeHelper.getTokenKey(_remoteChainId, _sourceToken, _targetToken); + tokenInfos[tokenKey] = LnBridgeHelper.TokenInfo( + _protocolFee, + _penaltyLnCollateral, + _sourceDecimals, + _targetDecimals, + true + ); } - function providerPause(address sourceToken) external { - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - lnProviders[providerKey].pause = true; + function providerPause(uint256 _remoteChainId, address _sourceToken, address _targetToken) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + srcProviders[providerKey].config.pause = true; } - function providerUnpause(address sourceToken) external { - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - lnProviders[providerKey].pause = false; + function providerUnpause(uint256 _remoteChainId, address _sourceToken, address _targetToken) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + srcProviders[providerKey].config.pause = false; } // lnProvider can register or update its configure by using this function // * `margin` is the increased value of the deposited margin function updateProviderFeeAndMargin( - address sourceToken, - uint112 margin, - uint112 baseFee, - uint8 liquidityFeeRate + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _margin, + uint112 _baseFee, + uint16 _liquidityFeeRate ) external payable { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; + require(_liquidityFeeRate < LnBridgeHelper.LIQUIDITY_FEE_RATE_BASE, "liquidity fee too large"); + bytes32 tokenKey = LnBridgeHelper.getTokenKey(_remoteChainId, _sourceToken, _targetToken); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[tokenKey]; require(tokenInfo.isRegistered, "token is not registered"); - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; - LnProviderConfigure memory config = LnProviderConfigure( + SourceProviderConfigure memory config = SourceProviderConfigure( // the margin can be only increased here - margin + providerInfo.config.margin, - baseFee, - liquidityFeeRate + _margin + providerInfo.config.margin, + _baseFee, + _liquidityFeeRate, + providerInfo.config.pause ); - lnProviders[providerKey].config = config; + srcProviders[providerKey].config = config; - if (sourceToken == address(0)) { - require(msg.value == margin, "invalid margin value"); + if (_sourceToken == address(0)) { + require(msg.value == _margin, "invalid margin value"); } else { - if (margin > 0) { - _safeTransferFrom(sourceToken, msg.sender, address(this), margin); + if (_margin > 0) { + LnBridgeHelper.safeTransferFrom(_sourceToken, msg.sender, address(this), _margin); } } - emit LnProviderUpdated(msg.sender, sourceToken, config.margin, baseFee, liquidityFeeRate); - } - - function _registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) internal { - tokenInfos[sourceToken] = TokenInfo( - targetToken, - protocolFee, - penaltyLnCollateral, - sourceDecimals, - targetDecimals, - true - ); - } - - function calculateProviderFee(LnProviderConfigure memory config, uint112 amount) internal pure returns(uint256) { - return uint256(config.baseFee) + uint256(config.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; + emit LnProviderUpdated(_remoteChainId, msg.sender, _sourceToken, _targetToken, config.margin, _baseFee, _liquidityFeeRate); } // the fee user should paid when transfer. // totalFee = providerFee + protocolFee // providerFee = provider.baseFee + provider.liquidityFeeRate * amount - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - bytes32 providerKey = getProviderKey(provider, sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.config, amount); - return providerFee + tokenInfos[sourceToken].protocolFee; + function totalFee( + uint256 _remoteChainId, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external view returns(uint256) { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + uint112 providerFee = LnBridgeHelper.calculateProviderFee(providerInfo.config.baseFee, providerInfo.config.liquidityFeeRate, _amount); + bytes32 tokenKey = LnBridgeHelper.getTokenKey(_remoteChainId, _sourceToken, _targetToken); + return providerFee + tokenInfos[tokenKey].protocolFee; } // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. @@ -183,152 +179,161 @@ contract LnOppositeBridgeSource is LnBridgeHelper { // 1. the state(lastTransferId, fee, margin) must match snapshot // 2. transferId not exist function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); + Snapshot calldata _snapshot, + uint112 _amount, + address _receiver + ) whenNotPaused external payable { + require(_amount > 0, "invalid amount"); - bytes32 providerKey = getProviderKey(snapshot.provider, snapshot.sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; + bytes32 providerKey = LnBridgeHelper.getProviderKey(_snapshot.remoteChainId, _snapshot.provider, _snapshot.sourceToken, _snapshot.targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; - require(!providerInfo.pause, "provider paused"); + require(!providerInfo.config.pause, "provider paused"); - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[ + LnBridgeHelper.getTokenKey(_snapshot.remoteChainId, _snapshot.sourceToken, _snapshot.targetToken) + ]; - uint256 providerFee = calculateProviderFee(providerInfo.config, amount); + uint112 providerFee = LnBridgeHelper.calculateProviderFee(providerInfo.config.baseFee, providerInfo.config.liquidityFeeRate, _amount); - // Note: this requirement is not enough to ensure that the lnProvider's margin is enough because there maybe some frozen margins in other transfers - require(providerInfo.config.margin >= amount + tokenInfo.penaltyLnCollateral + uint112(providerFee), "amount not valid"); - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired"); - require(snapshot.totalFee >= tokenInfo.protocolFee + providerFee, "fee is invalid"); - require(snapshot.depositedMargin <= providerInfo.config.margin, "margin updated"); + require(providerInfo.lastTransferId == _snapshot.transferId, "snapshot expired"); + // Note: this requirement is not enough to ensure that the lnProvider's margin is enough because there maybe some frozen margins in other transfers + require(providerInfo.config.margin >= _amount + tokenInfo.penaltyLnCollateral + providerFee, "amount not valid"); + require(_snapshot.depositedMargin <= providerInfo.config.margin, "margin updated"); + require(_snapshot.totalFee >= tokenInfo.protocolFee + providerFee, "fee is invalid"); - uint256 targetAmount = uint256(amount) * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - uint64 timestamp = uint64(block.timestamp); + uint112 targetAmount = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, _amount); + require(targetAmount > 0, "invalid amount"); + require(block.timestamp < type(uint32).max, "timestamp overflow"); bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - uint112(targetAmount))); - require(lockInfos[transferId].amountWithFeeAndPenalty == 0, "transferId exist"); - lockInfos[transferId] = LockInfo(amount + tokenInfo.penaltyLnCollateral + uint112(providerFee), false); + block.chainid, + _snapshot.remoteChainId, + _snapshot.transferId, + _snapshot.provider, + _snapshot.sourceToken, + _snapshot.targetToken, + _receiver, + targetAmount)); + require(lockInfos[transferId].timestamp == 0, "transferId exist"); + lockInfos[transferId] = LockInfo(_amount, tokenInfo.penaltyLnCollateral + providerFee, uint32(block.timestamp), false); // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; + srcProviders[providerKey].lastTransferId = transferId; - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); + if (_snapshot.sourceToken == address(0)) { + require(_amount + _snapshot.totalFee == msg.value, "amount unmatched"); + LnBridgeHelper.safeTransferNative(_snapshot.provider, _amount + providerFee); if (tokenInfo.protocolFee > 0) { - _safeTransferNative(feeReceiver, tokenInfo.protocolFee); + LnBridgeHelper.safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; + uint256 refund = _snapshot.totalFee - tokenInfo.protocolFee - providerFee; if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); + LnBridgeHelper.safeTransferNative(msg.sender, refund); } } else { - _safeTransferFrom( - snapshot.sourceToken, + LnBridgeHelper.safeTransferFrom( + _snapshot.sourceToken, msg.sender, - snapshot.provider, - amount + providerFee + _snapshot.provider, + _amount + providerFee ); if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, + LnBridgeHelper.safeTransferFrom( + _snapshot.sourceToken, msg.sender, - feeReceiver, + protocolFeeReceiver, tokenInfo.protocolFee ); } } emit TokenLocked( + _snapshot.remoteChainId, transferId, - snapshot.provider, - snapshot.sourceToken, - amount, - uint112(providerFee), - timestamp, - receiver); + _snapshot.provider, + _snapshot.sourceToken, + _snapshot.targetToken, + targetAmount, + providerFee, + uint32(block.timestamp), + _receiver); } // this slash is called by remote message // the token should be sent to the slasher who slash and finish the transfer on target chain. // latestSlashTransferId is the latest slashed transfer trusted from the target chain, and the current slash transfer cannot be executed before the latestSlash transfer. // after slash, the margin of lnProvider need to be updated - function _slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address sourceToken, - address provider, - address slasher - ) internal { + function slash( + bytes32 _latestSlashTransferId, + bytes32 _transferId, + uint256 _remoteChainId, + uint256 _timestamp, + address _sourceToken, + address _targetToken, + address _provider, + address _slasher + ) external allowRemoteCall(_remoteChainId) { // check lastTransfer // ensure last slash transfer(checked on target chain) has been slashed - LockInfo memory lastLockInfo = lockInfos[latestSlashTransferId]; - require(lastLockInfo.hasSlashed || latestSlashTransferId == INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); - LockInfo memory lockInfo = lockInfos[transferId]; + LockInfo memory lastLockInfo = lockInfos[_latestSlashTransferId]; + require(lastLockInfo.hasSlashed || _latestSlashTransferId == LnBridgeHelper.INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); + LockInfo memory lockInfo = lockInfos[_transferId]; // ensure transfer exist and not slashed yet require(!lockInfo.hasSlashed, "transfer has been slashed"); - require(lockInfo.amountWithFeeAndPenalty > 0, "lnBridgeSource:invalid transferId"); + require(lockInfo.timestamp > 0, "lnBridgeSource:invalid timestamp"); - bytes32 providerKey = getProviderKey(provider, sourceToken); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); - LnProviderInfo memory lnProvider = lnProviders[providerKey]; - lockInfos[transferId].hasSlashed = true; + SourceProviderInfo memory lnProvider = srcProviders[providerKey]; + lockInfos[_transferId].hasSlashed = true; // transfer token to the slasher - uint256 slashAmount = lockInfo.amountWithFeeAndPenalty; + uint112 slashAmount = (lockInfo.timestamp == _timestamp ? lockInfo.amount + lockInfo.feeAndPenalty : lockInfo.amount); require(lnProvider.config.margin >= slashAmount, "margin not enough"); - uint112 updatedMargin = lnProvider.config.margin - uint112(slashAmount); - lnProviders[providerKey].config.margin = updatedMargin; + uint112 updatedMargin = lnProvider.config.margin - slashAmount; + srcProviders[providerKey].config.margin = updatedMargin; - if (sourceToken == address(0)) { - _safeTransferNative(slasher, slashAmount); + if (_sourceToken == address(0)) { + LnBridgeHelper.safeTransferNative(_slasher, slashAmount); } else { - _safeTransfer(sourceToken, slasher, slashAmount); + LnBridgeHelper.safeTransfer(_sourceToken, _slasher, slashAmount); } - emit Slash(transferId, provider, sourceToken, updatedMargin, slasher); + emit Slash(_remoteChainId, _transferId, _provider, _sourceToken, _targetToken, updatedMargin, _slasher); } // lastTransfer is the latest slash transfer, all transfer must be relayed or slashed - // if user use the snapshot before this transaction to send cross-chain transfer, it should be reverted because this `_withdrawMargin` will decrease margin. - function _withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) internal { + // if user use the snapshot before this transaction to send cross-chain transfer, it should be reverted because this `withdrawMargin` will decrease margin. + function withdrawMargin( + bytes32 _latestSlashTransferId, + bytes32 _lastTransferId, + uint256 _remoteChainId, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external allowRemoteCall(_remoteChainId) { // check the latest slash transfer // ensure latest slash tranfer(verified on target chain) has been slashed on source chain - LockInfo memory lastRefundLockInfo = lockInfos[latestSlashTransferId]; - require(lastRefundLockInfo.hasSlashed || latestSlashTransferId == INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); + LockInfo memory lastRefundLockInfo = lockInfos[_latestSlashTransferId]; + require(lastRefundLockInfo.hasSlashed || _latestSlashTransferId == LnBridgeHelper.INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); // use this condition to ensure that the withdraw message is sent by the provider // the parameter provider is the message sender of this remote withdraw call - bytes32 providerKey = getProviderKey(provider, sourceToken); - LnProviderInfo memory lnProvider = lnProviders[providerKey]; + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + SourceProviderInfo memory lnProvider = srcProviders[providerKey]; // ensure all transfer has finished - require(lnProvider.lastTransferId == lastTransferId, "invalid last transferid"); - require(lnProvider.config.margin >= amount, "margin not enough"); - uint112 updatedMargin = lnProvider.config.margin - amount; - lnProviders[providerKey].config.margin = updatedMargin; - if (sourceToken == address(0)) { - _safeTransferNative(provider, amount); + require(lnProvider.lastTransferId == _lastTransferId, "invalid last transferid"); + require(lnProvider.config.margin >= _amount, "margin not enough"); + uint112 updatedMargin = lnProvider.config.margin - _amount; + srcProviders[providerKey].config.margin = updatedMargin; + if (_sourceToken == address(0)) { + LnBridgeHelper.safeTransferNative(_provider, _amount); } else { - _safeTransfer(sourceToken, provider, amount); + LnBridgeHelper.safeTransfer(_sourceToken, _provider, _amount); } - emit LiquidityWithdrawn(provider, sourceToken, updatedMargin); + emit LiquidityWithdrawn(_remoteChainId, _provider, _sourceToken, _targetToken, updatedMargin); } } diff --git a/helix-contract/contracts/ln/base/LnOppositeBridgeTarget.sol b/helix-contract/contracts/ln/base/LnOppositeBridgeTarget.sol index ce035a09..e616c500 100644 --- a/helix-contract/contracts/ln/base/LnOppositeBridgeTarget.sol +++ b/helix-contract/contracts/ln/base/LnOppositeBridgeTarget.sol @@ -1,19 +1,19 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; import "../interface/ILnOppositeBridgeSource.sol"; import "./LnBridgeHelper.sol"; -contract LnOppositeBridgeTarget is LnBridgeHelper { - uint256 constant public MIN_REFUND_TIMESTAMP = 30 * 60; - +contract LnOppositeBridgeTarget { // if slasher == address(0), this FillTransfer is relayed by lnProvider // otherwise, this FillTransfer is slashed by slasher // if there is no slash transfer before, then it's latestSlashTransferId is assigned by INIT_SLASH_TRANSFER_ID, a special flag struct SlashInfo { address provider; address sourceToken; + address targetToken; address slasher; + uint256 timestamp; } // transferId => latest slash transfer Id @@ -22,27 +22,29 @@ contract LnOppositeBridgeTarget is LnBridgeHelper { mapping(bytes32 => SlashInfo) public slashInfos; event TransferFilled(bytes32 transferId, address slasher); + event SlashRequest(uint256 remoteChainId, address sourceToken, address targetToken, bytes32 transferId); + event WithdrawMarginRequest(uint256 remoteChainId, address sourceToken, address targetToken, uint112 amount); // if slasher is nonzero, then it's a slash fill transfer function _checkPreviousAndFillTransfer( - bytes32 transferId, - bytes32 previousTransferId + bytes32 _transferId, + bytes32 _previousTransferId ) internal { // the first fill transfer, we fill the INIT_SLASH_TRANSFER_ID as the latest slash transferId - if (previousTransferId == bytes32(0)) { - fillTransfers[transferId] = INIT_SLASH_TRANSFER_ID; + if (_previousTransferId == bytes32(0)) { + fillTransfers[_transferId] = LnBridgeHelper.INIT_SLASH_TRANSFER_ID; } else { // Find the previous slash fill, it is a slash fill if the slasher is not zero address. - bytes32 previousLatestSlashTransferId = fillTransfers[previousTransferId]; + bytes32 previousLatestSlashTransferId = fillTransfers[_previousTransferId]; require(previousLatestSlashTransferId != bytes32(0), "previous fill not exist"); - SlashInfo memory previousSlashInfo = slashInfos[previousTransferId]; + SlashInfo memory previousSlashInfo = slashInfos[_previousTransferId]; // we use latestSlashTransferId to store the latest slash transferId // if previous.slasher != 0, then previous is slashed // if previous.slasher == 0, then previous is not slashed - bytes32 latestSlashTransferId = previousSlashInfo.slasher != address(0) ? previousTransferId : previousLatestSlashTransferId; + bytes32 latestSlashTransferId = previousSlashInfo.slasher != address(0) ? _previousTransferId : previousLatestSlashTransferId; - fillTransfers[transferId] = latestSlashTransferId; + fillTransfers[_transferId] = latestSlashTransferId; } } @@ -62,117 +64,180 @@ contract LnOppositeBridgeTarget is LnBridgeHelper { // II. transferId is trusted => previousTransferId is trusted => latestSlashTransferId is trusted if previousTransfer is a slash transfer // III. Both I and II => latestSlashTransferId is trusted if previousTransfer is normal relayed tranfer function _fillTransfer( - TransferParameter calldata params, - bytes32 expectedTransferId + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId ) internal { bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount)); - require(expectedTransferId == transferId, "check expected transferId failed"); + _remoteChainId, + block.chainid, + _params.previousTransferId, + _params.provider, + _params.sourceToken, + _params.targetToken, + _params.receiver, + _params.amount)); + require(_expectedTransferId == transferId, "check expected transferId failed"); // Make sure this transfer was never filled before require(fillTransfers[transferId] == bytes32(0), "fill exist"); - _checkPreviousAndFillTransfer(transferId, params.previousTransferId); + _checkPreviousAndFillTransfer(transferId, _params.previousTransferId); - if (params.targetToken == address(0)) { - require(msg.value >= params.amount, "invalid amount"); - _safeTransferNative(params.receiver, params.amount); + if (_params.targetToken == address(0)) { + require(msg.value >= _params.amount, "invalid amount"); + LnBridgeHelper.safeTransferNative(_params.receiver, _params.amount); } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); + LnBridgeHelper.safeTransferFrom(_params.targetToken, msg.sender, _params.receiver, uint256(_params.amount)); } } function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId ) payable external { // normal relay message, fill slasher as zero - require(params.provider == msg.sender, "invalid provider"); - _fillTransfer(params, expectedTransferId); + require(_params.provider == msg.sender, "invalid provider"); + _fillTransfer(_params, _remoteChainId, _expectedTransferId); - emit TransferFilled(expectedTransferId, address(0)); + emit TransferFilled(_expectedTransferId, address(0)); } // The condition for slash is that the transfer has timed out // Meanwhile we need to request a slash transaction to the source chain to withdraw the LnProvider's margin // On the source chain, we need to verify all the transfers before has been relayed or slashed. // So we needs to carry the the previous shash transferId to ensure that the slash is continuous. - function _slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId + function _slashAndRemoteReleaseCall( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId ) internal returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_REFUND_TIMESTAMP, "slash time not expired"); - _fillTransfer(params, expectedTransferId); + require(block.timestamp > _params.timestamp + LnBridgeHelper.SLASH_EXPIRE_TIME, "slash time not expired"); + _fillTransfer(_params, _remoteChainId, _expectedTransferId); // slasher = msg.sender - slashInfos[expectedTransferId] = SlashInfo(params.provider, params.sourceToken, msg.sender); + slashInfos[_expectedTransferId] = SlashInfo(_params.provider, _params.sourceToken, _params.targetToken, msg.sender, _params.timestamp); // Do not slash `transferId` in source chain unless `latestSlashTransferId` has been slashed - message = _encodeSlashCall( - fillTransfers[expectedTransferId], - expectedTransferId, - params.provider, - params.sourceToken, + message = encodeSlashCall( + fillTransfers[_expectedTransferId], + _expectedTransferId, + _params.timestamp, + _params.sourceToken, + _params.targetToken, + _params.provider, msg.sender ); - emit TransferFilled(expectedTransferId, msg.sender); + emit TransferFilled(_expectedTransferId, msg.sender); } // we use this to verify that the transfer has been slashed by user and it can resend the slash request - function _retrySlashAndRemoteRefund(bytes32 transferId) internal view returns(bytes memory message) { - bytes32 latestSlashTransferId = fillTransfers[transferId]; + function _retrySlashAndRemoteReleaseCall(bytes32 _transferId) internal view returns(bytes memory message) { + bytes32 latestSlashTransferId = fillTransfers[_transferId]; // transfer must be filled require(latestSlashTransferId != bytes32(0), "invalid transfer id"); // transfer must be slashed - SlashInfo memory slashInfo = slashInfos[transferId]; + SlashInfo memory slashInfo = slashInfos[_transferId]; require(slashInfo.slasher != address(0), "slasher not exist"); - message = _encodeSlashCall( + message = encodeSlashCall( latestSlashTransferId, - transferId, - slashInfo.provider, + _transferId, + slashInfo.timestamp, slashInfo.sourceToken, + slashInfo.targetToken, + slashInfo.provider, slashInfo.slasher ); } - function _encodeSlashCall( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) internal pure returns(bytes memory) { + function encodeSlashCall( + bytes32 _latestSlashTransferId, + bytes32 _transferId, + uint256 _timestamp, + address _sourceToken, + address _targetToken, + address _provider, + address _slasher + ) public view returns(bytes memory) { return abi.encodeWithSelector( ILnOppositeBridgeSource.slash.selector, - latestSlashTransferId, - transferId, - provider, - sourceToken, - slasher + _latestSlashTransferId, + _transferId, + block.chainid, + _timestamp, + _sourceToken, + _targetToken, + _provider, + _slasher ); } - function _requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount - ) internal view returns(bytes memory message) { - bytes32 latestSlashTransferId = fillTransfers[lastTransferId]; - require(latestSlashTransferId != bytes32(0), "invalid last transfer"); + function _sendMessageToTarget(uint256 _remoteChainId, bytes memory _payload, bytes memory _extParams) internal virtual {} + + function requestSlashAndRemoteRelease( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId, + bytes memory _extParams + ) payable external { + bytes memory slashCallMessage = _slashAndRemoteReleaseCall( + _params, + _remoteChainId, + _expectedTransferId + ); + _sendMessageToTarget(_remoteChainId, slashCallMessage, _extParams); + emit SlashRequest(_remoteChainId, _params.sourceToken, _params.targetToken, _expectedTransferId); + } + + function requestRetrySlashAndRemoteRelease( + uint256 remoteChainId, + bytes32 _transferId, + bytes memory _extParams + ) payable external { + bytes memory retryCallMessage = _retrySlashAndRemoteReleaseCall(_transferId); + _sendMessageToTarget(remoteChainId, retryCallMessage, _extParams); + } + + function encodeWithdrawMargin( + bytes32 _lastTransferId, + address _sourceToken, + address _targetToken, + uint112 _amount + ) public view returns(bytes memory message) { + bytes32 latestSlashTransferId = LnBridgeHelper.INIT_SLASH_TRANSFER_ID; + if (_lastTransferId != bytes32(0)) { + latestSlashTransferId = fillTransfers[_lastTransferId]; + require(latestSlashTransferId != bytes32(0), "invalid last transfer"); + } return abi.encodeWithSelector( ILnOppositeBridgeSource.withdrawMargin.selector, latestSlashTransferId, - lastTransferId, + _lastTransferId, + block.chainid, msg.sender, - sourceToken, - amount + _sourceToken, + _targetToken, + _amount + ); + } + + function requestWithdrawMargin( + uint256 _remoteChainId, + bytes32 _lastTransferId, + address _sourceToken, + address _targetToken, + uint112 _amount, + bytes memory _extParams + ) payable external { + bytes memory withdrawCallMessage = encodeWithdrawMargin( + _lastTransferId, + _sourceToken, + _targetToken, + _amount ); + _sendMessageToTarget(_remoteChainId, withdrawCallMessage, _extParams); + emit WithdrawMarginRequest(_remoteChainId, _sourceToken, _targetToken, _amount); } } diff --git a/helix-contract/contracts/ln/interface/ILnDefaultBridgeTarget.sol b/helix-contract/contracts/ln/interface/ILnDefaultBridgeTarget.sol index 93a6b0c6..b6258847 100644 --- a/helix-contract/contracts/ln/interface/ILnDefaultBridgeTarget.sol +++ b/helix-contract/contracts/ln/interface/ILnDefaultBridgeTarget.sol @@ -1,17 +1,19 @@ // SPDX-License-Identifier: MIT +pragma solidity >=0.8.17; -pragma solidity >=0.8.10; import "../base/LnBridgeHelper.sol"; interface ILnDefaultBridgeTarget { function slash( LnBridgeHelper.TransferParameter memory params, + uint256 remoteChainId, address slasher, uint112 fee, uint112 penalty ) external; function withdraw( + uint256 _sourceChainId, bytes32 lastTransferId, uint64 withdrawNonce, address provider, diff --git a/helix-contract/contracts/ln/interface/ILnOppositeBridgeSource.sol b/helix-contract/contracts/ln/interface/ILnOppositeBridgeSource.sol index 72a40766..00881ecb 100644 --- a/helix-contract/contracts/ln/interface/ILnOppositeBridgeSource.sol +++ b/helix-contract/contracts/ln/interface/ILnOppositeBridgeSource.sol @@ -1,21 +1,25 @@ // SPDX-License-Identifier: MIT - -pragma solidity >=0.8.10; +pragma solidity >=0.8.17; interface ILnOppositeBridgeSource { function slash( bytes32 lastRefundTransferId, bytes32 transferId, - address provider, + uint256 remoteChainId, + uint256 timestamp, address sourceToken, + address targetToken, + address provider, address slasher ) external; function withdrawMargin( bytes32 lastRefundTransferId, bytes32 lastTransferId, + uint256 remoteChainId, address provider, address sourceToken, + address targetToken, uint112 amount ) external; } diff --git a/helix-contract/contracts/ln/interface/ILowLevelMessager.sol b/helix-contract/contracts/ln/interface/ILowLevelMessager.sol new file mode 100644 index 00000000..c79fad74 --- /dev/null +++ b/helix-contract/contracts/ln/interface/ILowLevelMessager.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} + +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; +} diff --git a/helix-contract/contracts/ln/messager/AxelarMessager.sol b/helix-contract/contracts/ln/messager/AxelarMessager.sol new file mode 100644 index 00000000..27fa9fbe --- /dev/null +++ b/helix-contract/contracts/ln/messager/AxelarMessager.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { StringToAddress, AddressToString } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressString.sol'; +import "../interface/ILowLevelMessager.sol"; +import "../base/LnAccessController.sol"; +import "./interface/IAxelarGateway.sol"; +import "./interface/IAxelarGasReceiver.sol"; + +contract AxelarMessager is LnAccessController { + using StringToAddress for string; + using AddressToString for address; + + IAxelarGateway public immutable gateway; + IAxelarGasReceiver public immutable gasReceiver; + + struct RemoteMessager { + string axRemoteChainName; + address messager; + } + + // app remoteChainId => axelar remote messager + mapping(uint256=>RemoteMessager) public remoteMessagers; + // axRemoteChainName => trustedRemotes + mapping(string=>address) public trustedRemotes; + + // token bridge pair + // hash(axRemoteChainName, localAppAddress) => remoteAppAddress + mapping(bytes32=>address) public remoteAppReceivers; + mapping(bytes32=>address) public remoteAppSenders; + + event CallResult(string sourceChain, string srcAddress, bool successed); + + constructor(address _dao, address _gateway, address _gasReceiver) { + _initialize(_dao); + gateway = IAxelarGateway(_gateway); + gasReceiver = IAxelarGasReceiver(_gasReceiver); + } + + // receive remote message + function execute( + bytes32 _commandId, + string calldata _sourceChain, + string calldata _sourceAddress, + bytes calldata _payload + ) external { + // check message comes from remote messager + require(gateway.validateContractCall(_commandId, _sourceChain, _sourceAddress, keccak256(_payload)), "invalid contract call"); + require(_sourceAddress.toAddress() == trustedRemotes[_sourceChain], "invalid remote messager"); + // check message comes from remote app + (address remoteAppAddress, address localAppAddress, bytes memory message) = abi.decode(_payload, (address, address, bytes)); + bytes32 key = keccak256(abi.encodePacked(_sourceChain, localAppAddress)); + require(remoteAppAddress == remoteAppSenders[key], "invalid remote address"); + // call local app + (bool success,) = localAppAddress.call(message); + emit CallResult(_sourceChain, _sourceAddress, success); + } + + function setRemoteMessager(uint256 _appRemoteChainId, string calldata _remoteChainName, address _remoteMessager) onlyDao external { + remoteMessagers[_appRemoteChainId] = RemoteMessager(_remoteChainName, _remoteMessager); + trustedRemotes[_remoteChainName] = _remoteMessager; + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.axRemoteChainName, msg.sender)); + remoteAppReceivers[key] = _remoteBridge; + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.axRemoteChainName, msg.sender)); + remoteAppSenders[key] = _remoteBridge; + } + + function sendMessage(uint256 _remoteChainId, bytes memory _message, bytes memory _params) onlyWhiteListCaller external payable { + address refunder = address(bytes20(_params)); + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.axRemoteChainName, msg.sender)); + address remoteAppAddress = remoteAppReceivers[key]; + require(remoteAppAddress != address(0), "app pair not registered"); + bytes memory axPayload = abi.encode(msg.sender, remoteAppAddress, _message); + + string memory stringAddress = remoteMessager.messager.toString(); + gasReceiver.payNativeGasForContractCall{value: msg.value}( + address(this), + remoteMessager.axRemoteChainName, + stringAddress, + axPayload, + refunder + ); + gateway.callContract( + remoteMessager.axRemoteChainName, + stringAddress, + axPayload + ); + } +} + diff --git a/helix-contract/contracts/ln/messager/DebugMessager.sol b/helix-contract/contracts/ln/messager/DebugMessager.sol new file mode 100644 index 00000000..42447323 --- /dev/null +++ b/helix-contract/contracts/ln/messager/DebugMessager.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "../base/LnBridgeHelper.sol"; +import "../interface/ILnDefaultBridgeTarget.sol"; +import "../interface/ILnOppositeBridgeSource.sol"; + +contract DebugMessager { + address public owner; + address public app; + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner, "invalid owner"); + _; + } + + function slashDefault( + LnBridgeHelper.TransferParameter memory _params, + uint256 _remoteChainId, + address _slasher, + uint112 _fee, + uint112 _penalty + ) external onlyOwner { + ILnDefaultBridgeTarget(app).slash(_params, _remoteChainId, _slasher, _fee, _penalty); + } + + function slashOpposite( + bytes32 _latestSlashTransferId, + bytes32 _transferId, + uint256 _remoteChainId, + uint256 _timestamp, + address _sourceToken, + address _targetToken, + address _provider, + address _slasher + ) external onlyOwner { + ILnOppositeBridgeSource(app).slash( + _latestSlashTransferId, + _transferId, + _remoteChainId, + _timestamp, + _sourceToken, + _targetToken, + _provider, + _slasher + ); + } + + function withdrawDefault( + uint256 _remoteChainId, + bytes32 _lastTransferId, + uint64 _withdrawNonce, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external onlyOwner { + ILnDefaultBridgeTarget(app).withdraw( + _remoteChainId, + _lastTransferId, + _withdrawNonce, + _provider, + _sourceToken, + _targetToken, + _amount + ); + } + + function withdrawOpposite( + bytes32 _latestSlashTransferId, + bytes32 _lastTransferId, + uint256 _remoteChainId, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external onlyOwner { + ILnOppositeBridgeSource(app).withdrawMargin( + _latestSlashTransferId, + _lastTransferId, + _remoteChainId, + _provider, + _sourceToken, + _targetToken, + _amount + ); + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) external { + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) external { + app = msg.sender; + } +} + diff --git a/helix-contract/contracts/ln/messager/Eth2ArbReceiveService.sol b/helix-contract/contracts/ln/messager/Eth2ArbReceiveService.sol new file mode 100644 index 00000000..25cc3542 --- /dev/null +++ b/helix-contract/contracts/ln/messager/Eth2ArbReceiveService.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol"; +import "../base/LnAccessController.sol"; +import "../interface/ILowLevelMessager.sol"; + +// from ethereum to arbitrum messager +contract Eth2ArbReceiveService is ILowLevelMessageReceiver, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + address public remoteMessagerAlias; + + mapping(address=>address) public appPairs; + + modifier onlyRemoteBridge() { + require(msg.sender == remoteMessagerAlias, "invalid remote caller"); + _; + } + + constructor(address _dao, uint256 _remoteChainId) { + _initialize(_dao); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessagerAlias = AddressAliasHelper.applyL1ToL2Alias(_remoteMessager); + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function recvMessage(address _remoteApp, address _localApp, bytes memory _message) onlyRemoteBridge external { + address remoteAppAddress = appPairs[_localApp]; + require(remoteAppAddress == _remoteApp, "invalid remote app"); + (bool result,) = _localApp.call(_message); + require(result == true, "local call failed"); + } +} + diff --git a/helix-contract/contracts/ln/messager/Eth2ArbSendService.sol b/helix-contract/contracts/ln/messager/Eth2ArbSendService.sol new file mode 100644 index 00000000..d35e39b7 --- /dev/null +++ b/helix-contract/contracts/ln/messager/Eth2ArbSendService.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@arbitrum/nitro-contracts/src/bridge/IInbox.sol"; +import "../base/LnAccessController.sol"; +import "../interface/ILowLevelMessager.sol"; + +// from ethereum to arbitrum messager +contract Eth2ArbSendService is ILowLevelMessageSender, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + IInbox public inbox; + address public remoteMessager; + mapping(address=>address) public appPairs; + + constructor(address _dao, address _inbox, uint256 _remoteChainId) { + _initialize(_dao); + inbox = IInbox(_inbox); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessager = _remoteMessager; + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function sendMessage(uint256 _remoteChainId, bytes memory _message, bytes memory _params) onlyWhiteListCaller external payable { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + address remoteAppAddress = appPairs[msg.sender]; + require(remoteAppAddress != address(0), "app not registered"); + + (uint256 maxSubmissionCost, uint256 l2GasPrice, uint256 l2GasLimit, address refunder) = abi.decode(_params, (uint256, uint256, uint256, address)); + + bytes memory remoteReceiveCall = abi.encodeWithSelector( + ILowLevelMessageReceiver.recvMessage.selector, + msg.sender, + remoteAppAddress, + _message + ); + inbox.createRetryableTicket{value: msg.value}( + remoteMessager, + 0, + maxSubmissionCost, + refunder, + refunder, + l2GasLimit, + l2GasPrice, + remoteReceiveCall + ); + } + + function fee( + uint256 _callSize, + uint256 _l1GasPrice, + uint256 _l2GasPrice, + uint256 _l2GasLimit, + uint256 _percentIncrease + ) external view returns(uint256, uint256) { + uint256 submissionFee = inbox.calculateRetryableSubmissionFee(_callSize, _l1GasPrice); + uint256 scaleSubmissionFee = submissionFee + submissionFee * _percentIncrease / 100; + return (scaleSubmissionFee, scaleSubmissionFee + _l2GasPrice * _l2GasLimit); + } + + function encodeParams( + uint256 _maxSubmissionCost, + uint256 _l2GasPrice, + uint256 _l2GasLimit, + address _refunder + ) external pure returns(bytes memory) { + return abi.encode(_maxSubmissionCost, _l2GasPrice, _l2GasLimit, _refunder); + } +} + diff --git a/helix-contract/contracts/ln/messager/Eth2LineaReceiveService.sol b/helix-contract/contracts/ln/messager/Eth2LineaReceiveService.sol new file mode 100644 index 00000000..cbad876a --- /dev/null +++ b/helix-contract/contracts/ln/messager/Eth2LineaReceiveService.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "./interface/ILineaMessageService.sol"; +import "../base/LnAccessController.sol"; +import "../interface/ILowLevelMessager.sol"; + +// from ethereum to linea messager +contract Eth2LineaReceiveService is ILowLevelMessageReceiver, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + ILineaMessageService public messageService; + address public remoteMessager; + + mapping(address=>address) public appPairs; + + modifier onlyRemoteBridge() { + require(msg.sender == address(messageService), "invalid msg.sender"); + require(messageService.sender() == remoteMessager, "invalid remote caller"); + _; + } + + constructor(address _dao, address _messageService, uint256 _remoteChainId) { + _initialize(_dao); + messageService = ILineaMessageService(_messageService); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessager = _remoteMessager; + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function recvMessage(address _remoteApp, address _localApp, bytes memory _message) onlyRemoteBridge external { + address remoteAppAddress = appPairs[_localApp]; + require(remoteAppAddress == _remoteApp, "invalid remote app"); + (bool result,) = _localApp.call(_message); + require(result == true, "local call failed"); + } +} + diff --git a/helix-contract/contracts/ln/messager/Eth2LineaSendService.sol b/helix-contract/contracts/ln/messager/Eth2LineaSendService.sol new file mode 100644 index 00000000..ea925b69 --- /dev/null +++ b/helix-contract/contracts/ln/messager/Eth2LineaSendService.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "./interface/ILineaMessageService.sol"; +import "../base/LnAccessController.sol"; +import "../interface/ILowLevelMessager.sol"; + +// from ethereum to linea messager +contract Eth2LineaSendService is ILowLevelMessageSender, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + ILineaMessageService public messageService; + address public remoteMessager; + + mapping(address=>address) public appPairs; + + constructor(address _dao, address _messageService, uint256 _remoteChainId) { + _initialize(_dao); + messageService = ILineaMessageService(_messageService); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessager = _remoteMessager; + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function sendMessage(uint256 _remoteChainId, bytes memory _message, bytes memory) onlyWhiteListCaller external payable { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + address remoteAppAddress = appPairs[msg.sender]; + require(remoteAppAddress != address(0), "app not registered"); + + bytes memory remoteReceiveCall = abi.encodeWithSelector( + ILowLevelMessageReceiver.recvMessage.selector, + msg.sender, + remoteAppAddress, + _message + ); + messageService.sendMessage{value: msg.value}( + remoteMessager, + msg.value, + remoteReceiveCall + ); + } +} diff --git a/helix-contract/contracts/ln/messager/Eth2ZkSyncReceiveService.sol b/helix-contract/contracts/ln/messager/Eth2ZkSyncReceiveService.sol new file mode 100644 index 00000000..66fc42ce --- /dev/null +++ b/helix-contract/contracts/ln/messager/Eth2ZkSyncReceiveService.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "./interface/IZksyncMailbox.sol"; +import "../base/LnAccessController.sol"; +import "../interface/ILowLevelMessager.sol"; + +// from ethereum to zkSync messager +contract Eth2ZkSyncReceiveService is ILowLevelMessageReceiver, LnAccessController { + uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); + uint256 immutable public REMOTE_CHAINID; + IMailbox public mailbox; + address public remoteMessagerAlias; + + mapping(address=>address) public appPairs; + + modifier onlyRemoteBridge() { + require(msg.sender == remoteMessagerAlias, "invalid remote caller"); + _; + } + + constructor(address _dao, address _mailbox, uint256 _remoteChainId) { + _initialize(_dao); + mailbox = IMailbox(_mailbox); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessagerAlias = address(uint160(_remoteMessager) + offset); + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function recvMessage(address _remoteApp, address _localApp, bytes memory _message) onlyRemoteBridge external { + address remoteAppAddress = appPairs[_localApp]; + require(remoteAppAddress == _remoteApp, "invalid remote app"); + (bool result,) = _localApp.call(_message); + require(result == true, "local call failed"); + } +} + diff --git a/helix-contract/contracts/ln/messager/Eth2ZkSyncSendService.sol b/helix-contract/contracts/ln/messager/Eth2ZkSyncSendService.sol new file mode 100644 index 00000000..032ef912 --- /dev/null +++ b/helix-contract/contracts/ln/messager/Eth2ZkSyncSendService.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "./interface/IZksyncMailbox.sol"; +import "../base/LnAccessController.sol"; +import "../interface/ILowLevelMessager.sol"; + +// from ethereum to zkSync messager +contract Eth2ZkSyncSendService is ILowLevelMessageSender, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + IMailbox public mailbox; + address public remoteMessager; + + mapping(address=>address) public appPairs; + + constructor(address _dao, address _mailbox, uint256 _remoteChainId) { + _initialize(_dao); + mailbox = IMailbox(_mailbox); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessager = _remoteMessager; + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function sendMessage(uint256 _remoteChainId, bytes memory _message, bytes memory _params) onlyWhiteListCaller external payable { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + address remoteAppAddress = appPairs[msg.sender]; + require(remoteAppAddress != address(0), "app not registered"); + + (uint256 l2GasLimit, uint256 l2GasPerPubdataByteLimit, address refunder) = abi.decode(_params, (uint256, uint256, address)); + + bytes memory remoteReceiveCall = abi.encodeWithSelector( + ILowLevelMessageReceiver.recvMessage.selector, + msg.sender, + remoteAppAddress, + _message + ); + mailbox.requestL2Transaction{value: msg.value}( + remoteMessager, + 0, + remoteReceiveCall, + l2GasLimit, + l2GasPerPubdataByteLimit, + new bytes[](0), + refunder + ); + } + + function fee( + uint256 _gasPrice, + uint256 _l2GasLimit, + uint256 _l2GasPerPubdataByteLimit + ) external view returns(uint256) { + return mailbox.l2TransactionBaseCost(_gasPrice, _l2GasLimit, _l2GasPerPubdataByteLimit); + } + + function encodeParams( + uint256 _l2GasLimit, + uint256 _l2GasPerPubdataByteLimit, + address _refunder + ) external pure returns(bytes memory) { + return abi.encode(_l2GasLimit, _l2GasPerPubdataByteLimit, _refunder); + } +} + diff --git a/helix-contract/contracts/ln/messager/LayerZeroMessager.sol b/helix-contract/contracts/ln/messager/LayerZeroMessager.sol new file mode 100644 index 00000000..9f204809 --- /dev/null +++ b/helix-contract/contracts/ln/messager/LayerZeroMessager.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "./interface/ILayerZeroEndpoint.sol"; +import "../interface/ILowLevelMessager.sol"; +import "../base/LnAccessController.sol"; + +contract LayerZeroMessager is LnAccessController { + ILayerZeroEndpoint public endpoint; + + struct RemoteMessager { + uint16 lzRemoteChainId; + address messager; + } + + // app remoteChainId => layerzero remote messager + mapping(uint256=>RemoteMessager) public remoteMessagers; + // lz remoteChainId => trustedRemotes + mapping(uint16=>bytes32) public trustedRemotes; + + // token bridge pair + // hash(lzRemoteChainId, localAppAddress) => remoteAppAddress + mapping(bytes32=>address) public remoteAppReceivers; + mapping(bytes32=>address) public remoteAppSenders; + + event CallResult(uint16 lzRemoteChainId, bytes srcAddress, bool successed); + event CallerUnMatched(uint16 lzRemoteChainId, bytes srcAddress, address remoteAppAddress); + + constructor(address _dao, address _endpoint) { + _initialize(_dao); + endpoint = ILayerZeroEndpoint(_endpoint); + } + + modifier onlyRemoteBridge(uint16 lzRemoteChainId, bytes calldata srcAddress) { + require(msg.sender == address(endpoint), "invalid caller"); + require(trustedRemotes[lzRemoteChainId] == keccak256(srcAddress), "invalid remote caller"); + _; + } + + function setRemoteMessager(uint256 _appRemoteChainId, uint16 _lzRemoteChainId, address _remoteMessager) onlyDao external { + remoteMessagers[_appRemoteChainId] = RemoteMessager(_lzRemoteChainId, _remoteMessager); + trustedRemotes[_lzRemoteChainId] = keccak256(abi.encodePacked(_remoteMessager, address(this))); + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.lzRemoteChainId, msg.sender)); + remoteAppReceivers[key] = _remoteBridge; + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.lzRemoteChainId, msg.sender)); + remoteAppSenders[key] = _remoteBridge; + } + + function sendMessage(uint256 _remoteChainId, bytes memory _message, bytes memory _params) onlyWhiteListCaller external payable { + address refunder = address(bytes20(_params)); + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes memory destination = abi.encodePacked( + remoteMessager.messager, + address(this) + ); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.lzRemoteChainId, msg.sender)); + address remoteAppAddress = remoteAppReceivers[key]; + require(remoteAppAddress != address(0), "app pair not registered"); + bytes memory lzPayload = abi.encode(msg.sender, remoteAppAddress, _message); + endpoint.send{ value: msg.value }( + remoteMessager.lzRemoteChainId, + destination, + lzPayload, + payable(refunder), + // zro payment, future parameter + address(0x0), + bytes("") + ); + } + + function lzReceive( + uint16 _srcChainId, + bytes calldata _srcAddress, + uint64, //nonce unused + bytes calldata _payload) onlyRemoteBridge(_srcChainId, _srcAddress) external { + // call + (address remoteAppAddress, address localAppAddress, bytes memory message) = abi.decode(_payload, (address, address, bytes)); + bytes32 key = keccak256(abi.encodePacked(_srcChainId, localAppAddress)); + if (remoteAppAddress != remoteAppSenders[key]) { + emit CallerUnMatched(_srcChainId, _srcAddress, remoteAppAddress); + return; + } + (bool success,) = localAppAddress.call(message); + // don't revert to prevent message block + emit CallResult(_srcChainId, _srcAddress, success); + } + + function fee( + uint256 _remoteChainId, + bytes memory _message + ) external view returns(uint256 nativeFee, uint256 zroFee) { + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "messager not configured"); + return endpoint.estimateFees( + remoteMessager.lzRemoteChainId, + remoteMessager.messager, + _message, + false, + bytes("") + ); + } +} + diff --git a/helix-contract/contracts/ln/messager/interface/IAxelarGasReceiver.sol b/helix-contract/contracts/ln/messager/interface/IAxelarGasReceiver.sol new file mode 100644 index 00000000..0c66d785 --- /dev/null +++ b/helix-contract/contracts/ln/messager/interface/IAxelarGasReceiver.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +interface IAxelarGasReceiver { + function payNativeGasForContractCall( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + address refundAddress + ) external payable; +} + diff --git a/helix-contract/contracts/ln/messager/interface/IAxelarGateway.sol b/helix-contract/contracts/ln/messager/interface/IAxelarGateway.sol new file mode 100644 index 00000000..95b38152 --- /dev/null +++ b/helix-contract/contracts/ln/messager/interface/IAxelarGateway.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +interface IAxelarGateway { + function callContract( + string calldata destinationChain, + string calldata contractAddress, + bytes calldata payload + ) external; + + function validateContractCall( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes32 payloadHash + ) external returns (bool); +} diff --git a/helix-contract/contracts/ln/interface/ILayerZeroEndpoint.sol b/helix-contract/contracts/ln/messager/interface/ILayerZeroEndpoint.sol similarity index 95% rename from helix-contract/contracts/ln/interface/ILayerZeroEndpoint.sol rename to helix-contract/contracts/ln/messager/interface/ILayerZeroEndpoint.sol index ff124d8b..1918ba8d 100644 --- a/helix-contract/contracts/ln/interface/ILayerZeroEndpoint.sol +++ b/helix-contract/contracts/ln/messager/interface/ILayerZeroEndpoint.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; interface ILayerZeroEndpoint { function send( diff --git a/helix-contract/contracts/ln/interface/ILineaMessageService.sol b/helix-contract/contracts/ln/messager/interface/ILineaMessageService.sol similarity index 89% rename from helix-contract/contracts/ln/interface/ILineaMessageService.sol rename to helix-contract/contracts/ln/messager/interface/ILineaMessageService.sol index 090038fc..ded01c65 100644 --- a/helix-contract/contracts/ln/interface/ILineaMessageService.sol +++ b/helix-contract/contracts/ln/messager/interface/ILineaMessageService.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; interface ILineaMessageService { function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable; diff --git a/helix-contract/contracts/ln/interface/IZksyncMailbox.sol b/helix-contract/contracts/ln/messager/interface/IZksyncMailbox.sol similarity index 96% rename from helix-contract/contracts/ln/interface/IZksyncMailbox.sol rename to helix-contract/contracts/ln/messager/interface/IZksyncMailbox.sol index 2264bf6d..29c3765a 100644 --- a/helix-contract/contracts/ln/interface/IZksyncMailbox.sol +++ b/helix-contract/contracts/ln/messager/interface/IZksyncMailbox.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; interface IMailbox { function requestL2Transaction( diff --git a/helix-contract/contracts/ln/test/MockEth2ArbReceiveService.sol b/helix-contract/contracts/ln/test/MockEth2ArbReceiveService.sol new file mode 100644 index 00000000..9b2439cf --- /dev/null +++ b/helix-contract/contracts/ln/test/MockEth2ArbReceiveService.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "../messager/Eth2ArbReceiveService.sol"; + +contract MockEth2ArbReceiveService is Eth2ArbReceiveService { + constructor(address _dao, uint256 _remoteChainId) Eth2ArbReceiveService(_dao, _remoteChainId) {} + + function setRemoteMessagerAlias(address _remoteMessagerAlias) external { + remoteMessagerAlias = _remoteMessagerAlias; + } +} diff --git a/helix-contract/contracts/ln/test/MockLayerZero.sol b/helix-contract/contracts/ln/test/MockLayerZero.sol index 7c0920e5..d6e6cd46 100644 --- a/helix-contract/contracts/ln/test/MockLayerZero.sol +++ b/helix-contract/contracts/ln/test/MockLayerZero.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; -import "../interface/ILayerZeroEndpoint.sol"; +import "../messager/interface/ILayerZeroEndpoint.sol"; interface ILayerZeroReceiver { function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external; diff --git a/helix-contract/contracts/ln/test/TestToken.sol b/helix-contract/contracts/ln/test/TestToken.sol new file mode 100644 index 00000000..c444876c --- /dev/null +++ b/helix-contract/contracts/ln/test/TestToken.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@zeppelin-solidity/contracts/token/ERC20/IERC20.sol"; +import "@zeppelin-solidity/contracts/utils/math/SafeMath.sol"; +import "@zeppelin-solidity/contracts/access/Ownable.sol"; + +contract HelixTestErc20 is IERC20, Ownable { + using SafeMath for uint256; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowances; + + mapping (address => uint256) public allowFaucet; + + uint256 private _totalSupply; + + uint256 public maxFaucetAllowed = 100000; + + string public name; + string public symbol; + uint8 public decimals; + + constructor(string memory _name, string memory _symbol, uint8 _decimals) { + name = _name; + symbol = _symbol; + decimals = _decimals; + _transferOwnership(_msgSender()); + } + + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } + + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } + + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(msg.sender, recipient, amount); + return true; + } + + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(msg.sender, spender, amount); + return true; + } + + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance")); + return true; + } + + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); + return true; + } + + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); + return true; + } + + function _transfer(address sender, address recipient, uint256 amount) internal virtual { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + // only factory contract can mint with the lock proof from ethereum + function mint(address account, uint256 amount) external onlyOwner { + _mint(account, amount); + } + + function setMaxFaucetAllowed(uint256 allowed) external onlyOwner { + maxFaucetAllowed = allowed; + } + + function faucet(uint256 amount) external { + uint256 alreadyFaucet = allowFaucet[msg.sender]; + require(maxFaucetAllowed * 10**decimals - alreadyFaucet > amount, "exceed faucet amount"); + allowFaucet[msg.sender] = alreadyFaucet + amount; + _mint(msg.sender, amount); + } + + function burn(address account, uint256 amount) external { + if (account != msg.sender && owner() != msg.sender && _allowances[account][msg.sender] != type(uint256).max) { + _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount, "ERC20: decreased allowance below zero")); + } + _burn(account, amount); + } + + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} diff --git a/helix-contract/contracts/ln/tool/Create2Deployer.sol b/helix-contract/contracts/ln/tool/Create2Deployer.sol new file mode 100644 index 00000000..39f9ae19 --- /dev/null +++ b/helix-contract/contracts/ln/tool/Create2Deployer.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract Create2Deployer { + event Deployed(address addr, uint256 salt); + + function deploy(bytes memory code, uint256 salt) public { + address addr; + assembly { + addr := create2(0, add(code, 0x20), mload(code), salt) + if iszero(extcodesize(addr)) { + revert(0, 0) + } + } + + emit Deployed(addr, salt); + } +} diff --git a/helix-contract/contracts/ln/tool/GetKey.sol b/helix-contract/contracts/ln/tool/GetKey.sol new file mode 100644 index 00000000..59a57d9a --- /dev/null +++ b/helix-contract/contracts/ln/tool/GetKey.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract GetKey { + function getProviderKey(uint256 remoteChainId, address provider, address sourceToken, address targetToken) external pure returns(bytes32) { + return keccak256(abi.encodePacked( + remoteChainId, + provider, + sourceToken, + targetToken + )); + } + + function getTokenKey(uint256 remoteChainId, address sourceToken, address targetToken) external pure returns(bytes32) { + return keccak256(abi.encodePacked( + remoteChainId, + sourceToken, + targetToken + )); + } +} diff --git a/helix-contract/contracts/ln/ProxyAdmin.sol b/helix-contract/contracts/ln/tool/ProxyAdmin.sol similarity index 80% rename from helix-contract/contracts/ln/ProxyAdmin.sol rename to helix-contract/contracts/ln/tool/ProxyAdmin.sol index 8df6b07b..013b8913 100644 --- a/helix-contract/contracts/ln/ProxyAdmin.sol +++ b/helix-contract/contracts/ln/tool/ProxyAdmin.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; import "@zeppelin-solidity/contracts/proxy/transparent/ProxyAdmin.sol"; diff --git a/helix-contract/contracts/ln/TransparentUpgradeableProxy.sol b/helix-contract/contracts/ln/tool/TransparentUpgradeableProxy.sol similarity index 82% rename from helix-contract/contracts/ln/TransparentUpgradeableProxy.sol rename to helix-contract/contracts/ln/tool/TransparentUpgradeableProxy.sol index 9f0fe26f..e2f58e45 100644 --- a/helix-contract/contracts/ln/TransparentUpgradeableProxy.sol +++ b/helix-contract/contracts/ln/tool/TransparentUpgradeableProxy.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; +pragma solidity ^0.8.17; import "@zeppelin-solidity/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/helix-contract/contracts/mapping-token/test/MockArbitrumInbox.sol b/helix-contract/contracts/mapping-token/test/MockArbitrumInbox.sol index 73ea2179..2540b93f 100644 --- a/helix-contract/contracts/mapping-token/test/MockArbitrumInbox.sol +++ b/helix-contract/contracts/mapping-token/test/MockArbitrumInbox.sol @@ -18,6 +18,7 @@ contract MockArbitrumInbox { bytes calldata data ) external payable returns (uint256) { // we use this gas price to mock failed remote call + console.log("mock arbitrum inbox call", to); if (maxFeePerGas > 100) { (bool result, ) = to.call(data); require(result == true, "arbitrum mock call failed"); diff --git a/helix-contract/deploy/create2.js b/helix-contract/deploy/create2.js new file mode 100644 index 00000000..98d8262e --- /dev/null +++ b/helix-contract/deploy/create2.js @@ -0,0 +1,20 @@ + +var Create2 = { + getDeployedBytecode: function(factory, constructorTypes, constructorArgs) { + const abiCoder = ethers.utils.defaultAbiCoder; + const encodedParams = abiCoder.encode(constructorTypes, constructorArgs); + const bytecode = `${factory.bytecode}${encodedParams.slice(2)}`; + return bytecode; + }, + deploy: async function(deployAddress, wallet, bytecode, salt) { + const hexSalt = ethers.utils.id(salt.toString()); + const deployer = await ethers.getContractAt("Create2Deployer", deployAddress, wallet); + const result = await (await deployer.deploy(bytecode, hexSalt)).wait(); + const targetEvent = result.events.find((e) => e.event == 'Deployed'); + const abiCoder = ethers.utils.defaultAbiCoder; + const eventParams = abiCoder.decode(["address", "uint256"], targetEvent.data); + return eventParams[0]; + } +} + +module.exports = Create2 diff --git a/helix-contract/deploy/deploy.js b/helix-contract/deploy/deploy.js deleted file mode 100644 index 72a12b01..00000000 --- a/helix-contract/deploy/deploy.js +++ /dev/null @@ -1,46 +0,0 @@ -var ProxyDeployer = require("./proxy.js"); - -var MappingTokenDeployer = { - deploycBridgeHelixHandler: async function(messageBus) { - const cBridgeContract = await ethers.getContractFactory("cBridgeMessageHandle"); - const cBridgeHandler = await cBridgeContract.deploy(); - await cBridgeHandler.deployed(); - console.log("helix contract address:", cBridgeHandler.address); - await cBridgeHandler.setMessageBus(messageBus); - return cBridgeHandler; - }, - deployErc721Backing: async function(proxyAdminAddr, helixHandler) { - console.log("deploy backing contract, it's a proxy contract"); - const backingContract = await ethers.getContractFactory("Erc721BackingUnsupportingConfirm"); - const backingLogic = await backingContract.deploy(); - await backingLogic.deployed(); - console.log("deploy backing logic", backingLogic.address); - return { - "logic": backingLogic, - "proxy": await ProxyDeployer.deployProxyContract( - proxyAdminAddr, - backingContract, - backingLogic.address, - [helixHandler] - ) - }; - }, - deployErc721MappingTokenFactory: async function(proxyAdminAddr, helixHandler) { - console.log("deploy mapping token factory contract, it's a proxy contract"); - const mtfContract = await ethers.getContractFactory("Erc721MappingTokenFactoryUnsupportingConfirm"); - const mtfLogic = await mtfContract.deploy(); - console.log("deploy mtf logic", mtfLogic.address); - await mtfLogic.deployed(); - return { - "logic": mtfLogic, - "proxy": await ProxyDeployer.deployProxyContract( - proxyAdminAddr, - mtfContract, - mtfLogic.address, - [helixHandler] - ) - }; - } -} - -module.exports = MappingTokenDeployer diff --git a/helix-contract/deploy/deploy_arbi2eth_ln.js b/helix-contract/deploy/deploy_arbi2eth_ln.js deleted file mode 100644 index abab4492..00000000 --- a/helix-contract/deploy/deploy_arbi2eth_ln.js +++ /dev/null @@ -1,433 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY - -// goerli test <> arbitrum goerli test -const ethereumUrl = "https://rpc.ankr.com/eth_goerli"; -const arbitrumUrl = "https://goerli-rollup.arbitrum.io/rpc"; -const ringArbitrumAddress = "0xFBAD806Bdf9cEC2943be281FB355Da05068DE925"; -const ringEthereumAddress = "0x1836BAFa3016Dd5Ce543D0F7199cB858ec69F41E"; -const ethereumProxyAdmin = "0x3F3eDBda6124462a09E071c5D90e072E0d5d4ed4"; -const arbitrumProxyAdmin = "0x66d86a686e50c98bac236105efafb99ee7605dc5"; -const inboxEthereumAddress = "0x6BEbC4925716945D46F0Ec336D5C2564F419682C"; -const arbitrumChainId = 421613; -const ethereumChainId = 5; -const daoOnArbitrum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -const daoOnEthereum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; - -const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -async function getLnBridgeTargetInitData(wallet, dao, inbox) { - const bridgeContract = await ethers.getContractFactory("Arb2EthTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, inbox], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function getLnBridgeSourceInitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Arb2EthSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function transferAndLockMargin( - wallet, - bridgeAddress, - provider, - tokenAddress, - amount, - receiver) { - const bridge = await ethers.getContractAt("LnOppositeBridgeSource", bridgeAddress, wallet); - const expectedFee = await bridge.totalFee( - provider, - tokenAddress, - amount); - console.log("expect fee is", expectedFee); - const providerInfo = await bridge.lnProviders(await bridge.getProviderKey(provider, tokenAddress)); - const expectedMargin = providerInfo.config.margin; - console.log("expect margin is", expectedMargin); - //const tx = await bridge.callStatic.transferAndLockMargin( - const tx = await bridge.transferAndLockMargin( - [ - provider, - tokenAddress, - providerInfo.lastTransferId, - expectedMargin, - expectedFee, - ], - amount, - wallet.address, - ); - console.log(tx); -} - -async function relay( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnOppositeBridgeTarget", bridgeAddress, wallet); - //const tx = await bridge.callStatic.relay( - await bridge.transferAndReleaseMargin( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - ); - //console.log(tx); -} - -async function slash( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("Arb2EthTarget", bridgeAddress, wallet); - const maxSubmissionCost = await bridge.submissionRefundFee( - 30000000000, - previousTransferId, - previousTransferId, - provider, - sourceToken, - wallet.address, - 10, - ); - const maxGas = 1000000; - const gasPriceBid = 20000000000; - const cost = maxSubmissionCost.add("0x470de4df820000"); - //return; - - //const tx = await bridge.callStatic.slashAndRemoteRefund( - await bridge.slashAndRemoteRefund( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - maxSubmissionCost, - maxGas, - gasPriceBid, - {value: cost }, - ); - //console.log(tx); -} - -async function requestWithdrawMargin( - wallet, - bridgeAddress, - lastTransferId, - sourceToken, - amount, -) { - const bridge = await ethers.getContractAt("Arb2EthTarget", bridgeAddress, wallet); - const maxSubmissionCost = await bridge.submissionWithdrawFee( - 30000000000, - lastTransferId, - sourceToken, - amount, - 10, - ); - const maxGas = 1000000; - const gasPriceBid = 20000000000; - const cost = maxSubmissionCost.add("0x470de4df820000"); - //return; - - //const tx = await bridge.callStatic.requestWithdrawMargin( - await bridge.requestWithdrawMargin( - lastTransferId, - sourceToken, - amount, - maxSubmissionCost, - maxGas, - gasPriceBid, - {value: cost }, - ); - //console.log(tx); -} - -function wallet() { - const ethereumProvider = new ethers.providers.JsonRpcProvider(ethereumUrl); - const ethereumWallet = new ethers.Wallet(privateKey, ethereumProvider); - const arbitrumProvider = new ethers.providers.JsonRpcProvider(arbitrumUrl); - const arbitrumWallet = new ethers.Wallet(privateKey, arbitrumProvider); - return [arbitrumWallet, ethereumWallet]; -} - -async function getLnBridgeOnL1InitData(wallet, dao, inbox) { - const bridgeContract = await ethers.getContractFactory("Arb2EthTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, inbox], - "initialize", - ); - console.log("ln bridge on l1 init data:", initdata); -} - -async function getLnBridgeOnL2InitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Arb2EthSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("ln bridge on l2 init data:", initdata); -} - -async function deployLnArbitrumBridgeOnL2(wallet, dao, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Arb2EthSource", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln bridge logic on L2, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao], - wallet); - console.log("finish to deploy ln bridge proxy on L2, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deployLnArbitrumBridgeOnL1(wallet, dao, inbox, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Arb2EthTarget", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln bridge logic on L1, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, inbox], - wallet); - console.log("finish to deploy ln bridge proxy on L1, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deploy(arbitrumWallet, ethereumWallet) { - const arbitrumLnBridgeAddress = await deployLnArbitrumBridgeOnL2( - arbitrumWallet, - daoOnArbitrum, - arbitrumProxyAdmin - ); - const ethereumLnBridgeAddress = await deployLnArbitrumBridgeOnL1( - ethereumWallet, - daoOnEthereum, - inboxEthereumAddress, - ethereumProxyAdmin - ); - - const arbitrumLnBridge = await ethers.getContractAt("Arb2EthSource", arbitrumLnBridgeAddress, arbitrumWallet); - const ethereumLnBridge = await ethers.getContractAt("Arb2EthTarget", ethereumLnBridgeAddress, ethereumWallet); - await arbitrumLnBridge.updateFeeReceiver(daoOnArbitrum); - await arbitrumLnBridge.setRemoteBridge(ethereumLnBridgeAddress); - await ethereumLnBridge.setRemoteBridge(arbitrumLnBridgeAddress); - - // register special erc20 token - // native token weth - // we need replace this wring address by exist one - const ringOnArbitrum = await ethers.getContractAt("Erc20", ringArbitrumAddress, arbitrumWallet); - const ringOnEthereum = await ethers.getContractAt("Erc20", ringEthereumAddress, ethereumWallet); - - // register token - await arbitrumLnBridge.registerToken( - ringArbitrumAddress, - ringEthereumAddress, - // helix fee - ethers.utils.parseEther("1.5"), - // penaltyLnCollateral - ethers.utils.parseEther("20"), - 18, // local decimals - 18, // remote decimals - ); - - // register provider - await ringOnArbitrum.approve(arbitrumLnBridge.address, ethers.utils.parseEther("10000000")); - await arbitrumLnBridge.updateProviderFeeAndMargin( - ringArbitrumAddress, - ethers.utils.parseEther("1000"), - ethers.utils.parseEther("100"), - 100 // liquidityFee - ); - return { - "LnBridgeOnArbitrum": arbitrumLnBridgeAddress, - "LnBridgeOnEthereum": ethereumLnBridgeAddress, - }; -} - -async function updateProviderInfo( - arbitrumLnBridgeAddress, - arbitrumWallet, - margin, - baseFee, - liquidityFeeRate, -) { - const arbitrumLnBridge = await ethers.getContractAt("Arb2EthSource", arbitrumLnBridgeAddress, arbitrumWallet); - await arbitrumLnBridge.updateProviderFeeAndMargin( - ringArbitrumAddress, - margin, - baseFee, - liquidityFeeRate, - ); -} - -// 2. deploy mapping token factory -async function main() { - const wallets = wallet(); - const arbitrumWallet = wallets[0]; - const ethereumWallet = wallets[1]; - - //await getLnBridgeTargetInitData(arbitrumWallet, "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", "0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f"); - await getLnBridgeSourceInitData(arbitrumWallet, "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"); - return; - - /* - const deployed = await deploy(arbitrumWallet, ethereumWallet); - console.log(deployed); - return; - */ - - - const arbitrumLnBridgeAddress = "0x7B8413FA1c1033844ac813A2E6475E15FB0fb3BA"; - const ethereumLnBridgeAddress = "0x3B1A953bFa72Af4ae3494b08e453BFF30a06A550"; - - // update margin and fee - /* - const arbitrumLnBridge = await ethers.getContractAt("Arb2EthSource", arbitrumLnBridgeAddress, arbitrumWallet); - await arbitrumLnBridge.updateProviderFeeAndMargin( - ringArbitrumAddress, - ethers.utils.parseEther("500"), - ethers.utils.parseEther("10"), - 100 // liquidityFee - ); - return; - */ - - const ringOnArbitrum = await ethers.getContractAt("Erc20", ringArbitrumAddress, arbitrumWallet); - //await ringOnArbitrum.approve(arbitrumLnBridgeAddress, ethers.utils.parseEther("10000000")); - const ringOnEthereum = await ethers.getContractAt("Erc20", ringEthereumAddress, ethereumWallet); - //await ringOnEthereum.approve(ethereumLnBridgeAddress, ethers.utils.parseEther("10000000")); - - const amount1 = ethers.utils.parseEther("23"); - - // lock - /* - await transferAndLockMargin( - arbitrumWallet, - arbitrumLnBridgeAddress, - arbitrumWallet.address, - ringArbitrumAddress, - amount1, - arbitrumWallet.address - ); - console.log("transfer and lock margin 1 successed"); - return; - */ - - // relay - // query: lastTransferId on arbitrum - const lastTransferId = "0x356AB27AE7C69D5BDB71C97B96EF45362E71EADE1A5EFE1ADF5706FC0DFC8625"; - const timestamp = 1688961375; - const expectedTransferId = "0xD1207442C3AC4BABC7500E06C2C08E3E5A46A452D92A7936A9B90ECE22C55E5E"; - - /* - await relay( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - ringArbitrumAddress, - ringEthereumAddress, - lastTransferId, - timestamp, - arbitrumWallet.address, - amount1, - expectedTransferId, - ) - console.log("relay 1 successed"); - return; - */ - - - // slasher - /* - await slash( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - ringArbitrumAddress, - ringEthereumAddress, - lastTransferId, - timestamp, - arbitrumWallet.address, - amount1, - expectedTransferId, - ); - console.log("slash successed"); - return; - */ - - // withdraw - - await requestWithdrawMargin( - ethereumWallet, - ethereumLnBridgeAddress, - "0xDD5703D47E4494FFC87660F3CBF2AFBA7A137755A91C81DC7ED120BB18E33A83", //lastTransferId - ringArbitrumAddress, - ethers.utils.parseEther("3"), // amount - ); - - console.log("withdraw successed"); - -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - -/* -arbitrumLnBridgeAddressLogic = 0xBFA90e358a9B2218ceb900afD9ac78691C92ABa6 -arbitrumLnBridgeAddressProxy = 0x7B8413FA1c1033844ac813A2E6475E15FB0fb3BA -ethereumLnBridgeAddressLogic = 0x0BA214a9Ab958C1A19D913f2Ac00119d27f196bB -ethereumLnBridgeAddressProxy = 0x3B1A953bFa72Af4ae3494b08e453BFF30a06A550 -*/ - diff --git a/helix-contract/deploy/deploy_crab2darwinia_lp.js b/helix-contract/deploy/deploy_crab2darwinia_lp.js deleted file mode 100644 index 82f4ec9a..00000000 --- a/helix-contract/deploy/deploy_crab2darwinia_lp.js +++ /dev/null @@ -1,301 +0,0 @@ -var ProxyDeployer = require("./proxy.js"); -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); - -const precompileStorageAddress = "0x0000000000000000000000000000000000000400"; -const precompileDispatchAddress = "0x0000000000000000000000000000000000000401"; - -const crabNetworkId = "0x0000002c"; //44 -const crabBridgeNetworkId = "0x63726162"; //crab -const crabTransactCallIndex = 10241;//0x2801 -const crabSendmsgIndex = 12291;//0x3003 -const crabOutboundLaneId = "0x00000000"; -const crabStorageKeyForMarketFee = "0xe0c938a0fbc88db6078b53e160c7c3ed2edb70953213f33a6ef6b8a5e3ffcab2"; -const crabStorageKeyForLatestNonce = "0xf1501030816118b9129255f5096aa9b296c246acb9b55077390e3ca723a0ca1f"; -const crabStorageKeyForLastDeliveredNonce = "0xf1501030816118b9129255f5096aa9b2e5f83cf83f2127eb47afdc35d6e43fab"; - -const darwiniaNetworkId = "0x0000002e"; //46 -const darwiniaBridgeNetworkId = "0x64617277"; //darw -const darwiniaTransactCallIndex = 12289;//0x3001 -const darwiniaSendmsgIndex = 11267;//0x2C03 -const darwiniaOutboundLaneId = "0x00000000"; -const darwiniaStorageKeyForMarketFee = "0x190d00dd4103825c78f55e5b5dbf8bfe2edb70953213f33a6ef6b8a5e3ffcab2"; -const darwiniaStorageKeyForLatestNonce = "0xf4e61b17ce395203fe0f3c53a0d3986096c246acb9b55077390e3ca723a0ca1f"; -const darwiniaStorageKeyForLastDeliveredNonce = "0xf4e61b17ce395203fe0f3c53a0d39860e5f83cf83f2127eb47afdc35d6e43fab"; - -const WCrabAddress = "0x2D2b97EA380b0185e9fDF8271d1AFB5d2Bf18329"; -const xWCrabAddress = "0x656567Eb75b765FC320783cc6EDd86bD854b2305"; -const WRingAddress = "0xE7578598Aac020abFB918f33A20faD5B71d670b4"; -const xWRingAddress = "0x273131F7CB50ac002BDd08cA721988731F7e1092"; - -const dao = "0xd2c7008400F54aA70Af01CF8C747a4473246593E"; - -const privateKey = process.env.PRIKEY -const relayPrivateKey = process.env.RELAYPRIKEY; - -async function deployMessageEndpoint(wallet) { - const handleContract = await ethers.getContractFactory("DarwiniaSub2SubMessageEndpoint", wallet); - const handle = await handleContract.deploy(); - await handle.deployed(); - return handle -} - -async function lockAndRemoteIssuing(nonce, tokenIndex, bridgeAddress, wallet, amount, fee, remoteIssuingNative) { - const bridge = await ethers.getContractAt("LpSub2SubBridge", bridgeAddress, wallet); - //const tx = await bridge.callStatic.lockAndRemoteIssuing( - const tx = await bridge.lockAndRemoteIssuing( - nonce, - wallet.address, - amount, - fee, - tokenIndex, - remoteIssuingNative, - { - gasLimit: 150000, - } - ); - //console.log(tx); -} - -async function lockNativeAndRemoteIssue(nonce, bridgeAddress, wallet, amount, fee) { - const bridge = await ethers.getContractAt("LpSub2SubBridge", bridgeAddress, wallet); - //const tx = await bridge.callStatic.lockNativeAndRemoteIssuing( - const tx = await bridge.lockNativeAndRemoteIssuing( - amount, - fee, - wallet.address, - nonce, - false, - { - value: amount.add(fee), - gasLimit: 120000 - }, - ); - //console.log(tx); -} - -async function relay(nonce, token, sender, receiver, amount, sourceChainId, issuingNative, wallet, bridgeAddress) { - const bridge = await ethers.getContractAt("LpSub2SubBridge", bridgeAddress, wallet); - //const tx = await bridge.callStatic.relay( - await bridge.relay( - nonce, - token, - sender, - receiver, - amount, - sourceChainId, - issuingNative, - { - value: issuingNative ? amount : 0, - gasLimit: 120000 - } - ); - //console.log(tx); -} - -function wait(ms) { - return new Promise(resolve => setTimeout(() => resolve(), ms)); -}; - -function wallet() { - // crab - const crabUrl = "https://darwinia-crab.api.onfinality.io/public-https"; - const darwiniaUrl = "https://rpc.darwinia.network"; - - const crabProvider = new ethers.providers.JsonRpcProvider(crabUrl); - const crabWallet = new ethers.Wallet(privateKey, crabProvider); - const darwiniaProvider = new ethers.providers.JsonRpcProvider(darwiniaUrl); - const darwiniaWallet = new ethers.Wallet(privateKey, darwiniaProvider); - - const crabRelayWallet = new ethers.Wallet(relayPrivateKey, crabProvider); - const darwiniaRelayWallet = new ethers.Wallet(relayPrivateKey, darwiniaProvider); - - return [crabWallet, darwiniaWallet, crabRelayWallet, darwiniaRelayWallet]; -} - -async function deploy(crabWallet, darwiniaWallet) { - - // deploy - const crabMessageEndpoint = await deployMessageEndpoint(crabWallet); - console.log("deploy crab message handle finished, address: ", crabMessageEndpoint.address); - const darwiniaMessageEndpoint = await deployMessageEndpoint(darwiniaWallet); - console.log("deploy darwinia message handle finished, address: ", darwiniaMessageEndpoint.address); - - // configure message handle - await crabMessageEndpoint.setRemoteHelix(darwiniaBridgeNetworkId, darwiniaNetworkId, darwiniaMessageEndpoint.address); - await crabMessageEndpoint.setRemoteCallIndex(darwiniaTransactCallIndex); - await crabMessageEndpoint.setLocalAddress(precompileStorageAddress, precompileDispatchAddress); - await crabMessageEndpoint.setLocalCallInfo(crabSendmsgIndex, crabOutboundLaneId, darwiniaOutboundLaneId); - await crabMessageEndpoint.setLocalStorageKey(crabStorageKeyForMarketFee, crabStorageKeyForLatestNonce, crabStorageKeyForLastDeliveredNonce); - console.log("finish configure crab message handle"); - await darwiniaMessageEndpoint.setRemoteHelix(crabBridgeNetworkId, crabNetworkId, crabMessageEndpoint.address); - await darwiniaMessageEndpoint.setRemoteCallIndex(crabTransactCallIndex); - await darwiniaMessageEndpoint.setLocalAddress(precompileStorageAddress, precompileDispatchAddress); - await darwiniaMessageEndpoint.setLocalCallInfo(darwiniaSendmsgIndex, darwiniaOutboundLaneId, crabOutboundLaneId); - await darwiniaMessageEndpoint.setLocalStorageKey(darwiniaStorageKeyForMarketFee, darwiniaStorageKeyForLatestNonce, darwiniaStorageKeyForLastDeliveredNonce); - console.log("finish configure mapping token factory message handle"); - - const crabLpBridgeContractLogic = await ethers.getContractFactory("LpSub2SubBridge", crabWallet); - const crabLpBridgeLogic = await crabLpBridgeContractLogic.deploy(); - await crabLpBridgeLogic.deployed(); - console.log("finish to deploy crab lp bridge logic contract, address: ", crabLpBridgeLogic.address); - - const crabAdmin = await ProxyDeployer.deployProxyAdmin(crabWallet); - console.log("finish to deploy crab lp bridge admin, address: ", crabAdmin.address); - const crabProxy = await ProxyDeployer.deployProxyContract( - crabAdmin.address, - crabLpBridgeContractLogic, - crabLpBridgeLogic.address, - [crabMessageEndpoint.address, darwiniaMessageEndpoint.address, dao], - crabWallet); - console.log("finish to deploy crab lp bridge proxy, address: ", crabProxy.address); - - const darwiniaLpBridgeContractLogic = await ethers.getContractFactory("LpSub2SubBridge", darwiniaWallet); - const darwiniaLpBridgeLogic = await darwiniaLpBridgeContractLogic.deploy(); - await darwiniaLpBridgeLogic.deployed(); - console.log("finish to deploy darwinia lp bridge logic, address: ", darwiniaLpBridgeLogic.address); - - const darwiniaAdmin = await ProxyDeployer.deployProxyAdmin(darwiniaWallet); - console.log("finish to deploy darwinia lp bridge admin, address: ", darwiniaAdmin.address); - const darwiniaProxy = await ProxyDeployer.deployProxyContract( - darwiniaAdmin.address, - darwiniaLpBridgeContractLogic, - darwiniaLpBridgeLogic.address, - [darwiniaMessageEndpoint.address, crabLpBridgeLogic.address, dao], - darwiniaWallet); - console.log("finish to deploy darwinia lp bridge proxy, address: ", darwiniaProxy.address); - - const crabLpBridge = await ethers.getContractAt("LpSub2SubBridge", crabProxy.address, crabWallet); - console.log("finish to configure crab"); - - const darwiniaLpBridge = await ethers.getContractAt("LpSub2SubBridge", darwiniaProxy.address, darwiniaWallet); - console.log("finish to configure mapping token factory"); - - await crabLpBridge.updateFeeReceiver(dao); - await darwiniaLpBridge.updateFeeReceiver(dao); - await crabLpBridge.setRemoteBridge(darwiniaLpBridge.address); - await darwiniaLpBridge.setRemoteBridge(crabLpBridge.address); - - await crabMessageEndpoint.grantRole(await crabMessageEndpoint.CALLER_ROLE(), crabLpBridge.address); - await crabMessageEndpoint.grantRole(await crabMessageEndpoint.CALLEE_ROLE(), crabLpBridge.address); - await darwiniaMessageEndpoint.grantRole(await darwiniaMessageEndpoint.CALLER_ROLE(), darwiniaLpBridge.address); - await darwiniaMessageEndpoint.grantRole(await darwiniaMessageEndpoint.CALLEE_ROLE(), darwiniaLpBridge.address); - console.log("grant role permission finished"); - - // register special erc20 token - // register - //const tx = await crab.callStatic.register( - let tx = await crabLpBridge.registerToken( - WCrabAddress, // local token address - xWCrabAddress, // remote token address - ethers.utils.parseEther("1"), // helix fee - 46, // remote chain id - 18, // local decimals - 18, // remote decimals - false, // remote is native - ); - await crabLpBridge.setwTokenIndex(0); - console.log("transaction is ", tx); - tx = await crabLpBridge.registerToken( - xWRingAddress, // local token address - WRingAddress, // remote token address - ethers.utils.parseEther("1"), // helix fee - 46, // remote chain id - 18, // local decimals - 18, // remote decimals - true, // remote is native - ); - - tx = await darwiniaLpBridge.registerToken( - xWCrabAddress, // local token address - WCrabAddress, // remote token address - ethers.utils.parseEther("1"), // helix fee - 44, // remote chain id - 18, // local decimals - 18, // remote decimals - true, // remote is native: wcrab is the remote wrapped native token - ); - tx = await darwiniaLpBridge.registerToken( - WRingAddress, // local token address - xWRingAddress, // remote token address - ethers.utils.parseEther("1"), // helix fee - 44, // remote chain id - 18, // local decimals - 18, // remote decimals - false, // remote is native - ); - await darwiniaLpBridge.setwTokenIndex(1); - - return { - crabLpBridgeAddress: crabLpBridge.address, - darwiniaLpBridgeAddress: darwiniaLpBridge.address, - } -} - -// 2. deploy mapping token factory -async function main() { - const wallets = wallet(); - const crabWallet = wallets[0]; - const darwiniaWallet = wallets[1]; - const crabRelayWallet = wallets[2]; - const darwiniaRelayWallet = wallets[3]; - - let timestamp = Date.now(); - - //const deployed = await deploy(crabWallet, darwiniaWallet); - //console.log(deployed); - //return; - const crabLpBridgeAddress = "0x0C2E72C10D2db4BD00960151B114d56E2a2daEc7"; - const darwiniaLpBridgeAddress = "0x71388920e33021E871b322a50859691a3332A5a3"; - - // lock wcrab and remote issue xwcrab - //const amount01 = ethers.utils.parseEther("1.014"); - //const fee01 = ethers.utils.parseEther("13"); - //console.log("lock 001", timestamp); - const wcrab = await ethers.getContractAt("WToken", WCrabAddress, crabRelayWallet); - await wcrab.deposit({value: ethers.utils.parseEther("10")}); - await wcrab.approve(crabLpBridgeAddress, ethers.utils.parseEther("100000")); - const xwcrab = await ethers.getContractAt("Erc20", xWCrabAddress, darwiniaRelayWallet); - await xwcrab.approve(darwiniaLpBridgeAddress, ethers.utils.parseEther("100000")); - - const wring = await ethers.getContractAt("WToken", WRingAddress, darwiniaRelayWallet); - await wring.deposit({value: ethers.utils.parseEther("10")}); - await wring.approve(darwiniaLpBridgeAddress, ethers.utils.parseEther("100000")); - const xwring = await ethers.getContractAt("Erc20", xWRingAddress, crabRelayWallet); - await xwring.approve(crabLpBridgeAddress, ethers.utils.parseEther("100000")); - - await lockAndRemoteIssuing(timestamp, 0, crabLpBridgeAddress, crabWallet, amount01, fee01, false); - console.log("lock and remote issuing"); - - await relay( - timestamp, // nonce - xWCrabAddress, // token - darwiniaWallet.address, // sender - darwiniaWallet.address, // receiver - amount01, // amount - 44, // sourceChainId - false, // issuingNative - darwiniaRelayWallet, // wallet - darwiniaLpBridgeAddress); // bridgeAddress - console.log("relay 001"); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - -/* -deploy crab message handle finished, address: 0xDC03582F4e8b49B9F3220C857b09dbd57aE817cC -deploy darwinia message handle finished, address: 0xffA1bC7A77E9778f3cAd03d10d3734198397DB76 -finish configure crab message handle -finish configure mapping token factory message handle -finish to deploy crab lp bridge logic contract, address: 0x6387Db51f4E17Bb78cC471E343D36765bc3f98E9 -finish to deploy crab lp bridge admin, address: 0x07ccf6373cABC15976628316Ac02aa41d938FD46 -finish to deploy crab lp bridge proxy, address: 0x0f1Fd0E0963242715E63C5dC60F510aB85Ad3367 -finish to deploy darwinia lp bridge logic, address: 0x9FA16146BA5b7d144927E46acd9ba143f7205Fc8 -finish to deploy darwinia lp bridge admin, address: 0x8738A64392b71617aF4C685d0E827855c741fDF7 -finish to deploy darwinia lp bridge proxy, address: 0xEf8868d2faE4e16882285BB1026df3D0398a48b7 -*/ diff --git a/helix-contract/deploy/deploy_eth2arbi_ln.js b/helix-contract/deploy/deploy_eth2arbi_ln.js deleted file mode 100644 index 3afc8c7e..00000000 --- a/helix-contract/deploy/deploy_eth2arbi_ln.js +++ /dev/null @@ -1,451 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY - -// goerli test <> arbitrum goerli test -const ethereumUrl = "https://rpc.ankr.com/eth_goerli"; -const arbitrumUrl = "https://goerli-rollup.arbitrum.io/rpc"; -const ringArbitrumAddress = "0xFBAD806Bdf9cEC2943be281FB355Da05068DE925"; -const ringEthereumAddress = "0x1836BAFa3016Dd5Ce543D0F7199cB858ec69F41E"; -const ethereumProxyAdmin = "0x3F3eDBda6124462a09E071c5D90e072E0d5d4ed4"; -const arbitrumProxyAdmin = "0x66d86a686e50c98bac236105efafb99ee7605dc5"; -const inboxEthereumAddress = "0x6BEbC4925716945D46F0Ec336D5C2564F419682C"; -const arbitrumChainId = 421613; -const ethereumChainId = 5; -const daoOnArbitrum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -const daoOnEthereum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; - -const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -async function getLnBridgeTargetInitData(wallet, dao, inbox) { - const bridgeContract = await ethers.getContractFactory("Eth2ArbTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, inbox], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function getLnBridgeSourceInitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Eth2ArbSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function transferAndLockMargin( - wallet, - bridgeAddress, - provider, - sourceTokenAddress, - targetTokenAddress, - amount, - receiver, - withdrawNonce, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeSource", bridgeAddress, wallet); - const expectedFee = await bridge.totalFee( - provider, - sourceTokenAddress, - amount); - console.log("expect fee is", expectedFee); - const providerInfo = await bridge.lnProviders(await bridge.getDefaultProviderKey(provider, sourceTokenAddress, targetTokenAddress)); - //const tx = await bridge.callStatic.transferAndLockMargin( - const tx = await bridge.transferAndLockMargin( - [ - provider, - sourceTokenAddress, - providerInfo.lastTransferId, - expectedFee, - withdrawNonce, - ], - amount, - wallet.address, - ); - console.log(tx); -} - -async function relay( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeTarget", bridgeAddress, wallet); - //const tx = await bridge.callStatic.transferAndReleaseMargin( - await bridge.transferAndReleaseMargin( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - ); - //console.log(tx); -} - -async function slash( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("Eth2ArbSource", bridgeAddress, wallet); - const maxSubmissionCost = await bridge.submissionSlashFee( - 30000000000, - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - receiver, - 0, - 0, - 10, - ); - const maxGas = 1000000; - const gasPriceBid = 20000000000; - const cost = maxSubmissionCost.add("0x470de4df820000"); - //return; - - //const tx = await bridge.callStatic.slashAndRemoteRefund( - await bridge.slashAndRemoteRelease( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - maxSubmissionCost, - maxGas, - gasPriceBid, - {value: cost }, - ); - //console.log(tx); -} - -async function requestWithdrawMargin( - wallet, - bridgeAddress, - lastTransferId, - provider, - sourceToken, - targetToken, - amount, -) { - const bridge = await ethers.getContractAt("Eth2ArbSource", bridgeAddress, wallet); - const maxSubmissionCost = await bridge.submissionWithdrawFee( - 30000000000, - lastTransferId, - 0, - provider, - sourceToken, - targetToken, - amount, - 10, - ); - const maxGas = 1000000; - const gasPriceBid = 20000000000; - const cost = maxSubmissionCost.add("0x470de4df820000"); - //return; - - //const tx = await bridge.callStatic.requestWithdrawMargin( - await bridge.requestWithdrawMargin( - sourceToken, - amount, - maxSubmissionCost, - maxGas, - gasPriceBid, - {value: cost }, - ); - //console.log(tx); -} - -function wallet() { - const ethereumProvider = new ethers.providers.JsonRpcProvider(ethereumUrl); - const ethereumWallet = new ethers.Wallet(privateKey, ethereumProvider); - const arbitrumProvider = new ethers.providers.JsonRpcProvider(arbitrumUrl); - const arbitrumWallet = new ethers.Wallet(privateKey, arbitrumProvider); - return [arbitrumWallet, ethereumWallet]; -} - -async function getLnBridgeOnL1InitData(wallet, dao, inbox) { - const bridgeContract = await ethers.getContractFactory("Arb2EthTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, inbox], - "initialize", - ); - console.log("ln bridge on l1 init data:", initdata); -} - -async function getLnBridgeOnL2InitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Arb2EthSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("ln bridge on l2 init data:", initdata); -} - -async function deployLnTarget(wallet, dao, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Eth2ArbTarget", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln target bridge logic, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao], - wallet); - console.log("finish to deploy ln bridge proxy on L2, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deployLnSource(wallet, dao, inbox, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Eth2ArbSource", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln source bridge logic, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, inbox], - wallet); - console.log("finish to deploy ln bridge proxy on ethereum, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deploy(arbitrumWallet, ethereumWallet) { - const ethereumLnBridgeAddress = await deployLnSource( - ethereumWallet, - daoOnEthereum, - inboxEthereumAddress, - ethereumProxyAdmin - ); - const arbitrumLnBridgeAddress = await deployLnTarget( - arbitrumWallet, - daoOnArbitrum, - arbitrumProxyAdmin - ); - - const arbitrumLnBridge = await ethers.getContractAt("Eth2ArbTarget", arbitrumLnBridgeAddress, arbitrumWallet); - const ethereumLnBridge = await ethers.getContractAt("Eth2ArbSource", ethereumLnBridgeAddress, ethereumWallet); - await ethereumLnBridge.updateFeeReceiver(daoOnEthereum); - await arbitrumLnBridge.setRemoteBridge(ethereumLnBridgeAddress); - await ethereumLnBridge.setRemoteBridge(arbitrumLnBridgeAddress); - - // register special erc20 token - // native token weth - // we need replace this wring address by exist one - const ringOnArbitrum = await ethers.getContractAt("Erc20", ringArbitrumAddress, arbitrumWallet); - const ringOnEthereum = await ethers.getContractAt("Erc20", ringEthereumAddress, ethereumWallet); - - // register token - await ethereumLnBridge.setTokenInfo( - ringEthereumAddress, - ringArbitrumAddress, - // helix fee - ethers.utils.parseEther("1.5"), - // penaltyLnCollateral - ethers.utils.parseEther("20"), - 18, // local decimals - 18, // remote decimals - ); - - // register provider - await ethereumLnBridge.setProviderFee( - ringEthereumAddress, - ethers.utils.parseEther("2.5"), - 10, - ); - await ringOnArbitrum.approve(arbitrumLnBridge.address, ethers.utils.parseEther("10000000")); - await arbitrumLnBridge.depositProviderMargin( - ringEthereumAddress, - ringArbitrumAddress, - ethers.utils.parseEther("1000"), - ); - await arbitrumLnBridge.depositSlashFundReserve( - ringEthereumAddress, - ringArbitrumAddress, - ethers.utils.parseEther("100"), - ); - return { - "LnBridgeOnArbitrum": arbitrumLnBridgeAddress, - "LnBridgeOnEthereum": ethereumLnBridgeAddress, - }; -} - -// 2. deploy mapping token factory -async function main() { - const wallets = wallet(); - const arbitrumWallet = wallets[0]; - const ethereumWallet = wallets[1]; - - /* - const bridgeContract = await ethers.getContractFactory("Eth2ArbTarget", arbitrumWallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln source bridge logic, address: ", lnBridgeLogic.address); - return; - */ - //await getLnBridgeTargetInitData(arbitrumWallet, "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", "0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f"); - //await getLnBridgeSourceInitData(arbitrumWallet, "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"); - //return; - - /* - const deployed = await deploy(arbitrumWallet, ethereumWallet); - console.log(deployed); - return; - */ - - const ethereumLnBridgeAddress = "0xcD86cf37a4Dc6f78B4899232E7dD1b5c8130EFDA"; - const arbitrumLnBridgeAddress = "0x4112c9d474951246fBC2B4D868D247e714698aE1"; - - // update margin and fee - /* - const arbitrumLnBridge = await ethers.getContractAt("Eth2ArbTarget", arbitrumLnBridgeAddress, arbitrumWallet); - await arbitrumLnBridge.depositSlashFundReserve( - ringEthereumAddress, - ringArbitrumAddress, - ethers.utils.parseEther("100"), - ); - return; - await arbitrumLnBridge.updateProviderFeeAndMargin( - ringArbitrumAddress, - ethers.utils.parseEther("500"), - ethers.utils.parseEther("10"), - 100 // liquidityFee - ); - return; - */ - - const ringOnArbitrum = await ethers.getContractAt("Erc20", ringArbitrumAddress, arbitrumWallet); - //await ringOnArbitrum.approve(arbitrumLnBridgeAddress, ethers.utils.parseEther("10000000")); - const ringOnEthereum = await ethers.getContractAt("Erc20", ringEthereumAddress, ethereumWallet); - //await ringOnEthereum.approve(ethereumLnBridgeAddress, ethers.utils.parseEther("10000000")); - - const amount1 = ethers.utils.parseEther("60"); - - // lock - await transferAndLockMargin( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - ringEthereumAddress, - ringArbitrumAddress, - amount1, - ethereumWallet.address, - 5, - ); - console.log("transfer and lock margin 1 successed"); - return; - - // relay - // query: lastTransferId on arbitrum - const lastTransferId = "0x8B500E2285E419B16C38634EFD51D4C276EEB3EDCAFC167DEB76499137873232"; - const timestamp = 1690514832; - const expectedTransferId = "0x198B51E6BFE20CE224C577423D91DDC43EEB77DDC15C2348FB011C8C937BEFA1"; - - /* - await relay( - arbitrumWallet, - arbitrumLnBridgeAddress, - arbitrumWallet.address, - ringEthereumAddress, - ringArbitrumAddress, - lastTransferId, - timestamp, - arbitrumWallet.address, - amount1, - expectedTransferId, - ) - console.log("relay 1 successed"); - return; - */ - - // slasher - /* - await slash( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - ringEthereumAddress, - ringArbitrumAddress, - lastTransferId, - timestamp, - arbitrumWallet.address, - amount1, - expectedTransferId, - ); - console.log("slash successed"); - return; - */ - - // withdraw - - await requestWithdrawMargin( - ethereumWallet, - ethereumLnBridgeAddress, - "0x198B51E6BFE20CE224C577423D91DDC43EEB77DDC15C2348FB011C8C937BEFA1", //lastTransferId - ethereumWallet.address, - ringEthereumAddress, - ringArbitrumAddress, - ethers.utils.parseEther("3.2"), // amount - ); - - console.log("withdraw successed"); - -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - -/* -ethereumLnBridgeAddressLogic = 0x26F3925F0cbcf79bF8Ca9041347327b82c55916b -ethereumLnBridgeAddressProxy = 0xcD86cf37a4Dc6f78B4899232E7dD1b5c8130EFDA -arbitrumLnBridgeAddressLogic = 0xC91aff6adA5e743Ae89589126AE4521eB2ec47f2 -arbitrumLnBridgeAddressProxy = 0x4112c9d474951246fBC2B4D868D247e714698aE1 -*/ - diff --git a/helix-contract/deploy/deploy_eth2linea_ln.js b/helix-contract/deploy/deploy_eth2linea_ln.js deleted file mode 100644 index cd4cab8c..00000000 --- a/helix-contract/deploy/deploy_eth2linea_ln.js +++ /dev/null @@ -1,380 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY - -// goerli test <> linea goerli test -const ethereumUrl = "https://rpc.ankr.com/eth_goerli"; -const lineaUrl = "https://rpc.goerli.linea.build"; -const usdcLineaAddress = "0xB4257F31750961C8e536f5cfCBb3079437700416"; -const usdcEthereumAddress = "0x07865c6E87B9F70255377e024ace6630C1Eaa37F"; -const ethereumProxyAdmin = "0x3F3eDBda6124462a09E071c5D90e072E0d5d4ed4"; -const lineaProxyAdmin = "0x9bc1C7567DDBcaF2212185b6665D755d842d01E4"; -const messageServiceEthereumAddress = "0x70BaD09280FD342D02fe64119779BC1f0791BAC2"; -const messageServiceLineaAddress = "0xC499a572640B64eA1C8c194c43Bc3E19940719dC"; -const daoOnLinea = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -const daoOnEthereum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; - -const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -async function getLnBridgeTargetInitData(wallet, dao, messageService) { - const bridgeContract = await ethers.getContractFactory("Eth2LineaTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, messageService], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function getLnBridgeSourceInitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Eth2LineaSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function transferAndLockMargin( - wallet, - bridgeAddress, - provider, - sourceTokenAddress, - targetTokenAddress, - amount, - receiver, - withdrawNonce, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeSource", bridgeAddress, wallet); - const expectedFee = await bridge.totalFee( - provider, - sourceTokenAddress, - amount); - console.log("expect fee is", expectedFee); - const providerInfo = await bridge.lnProviders(await bridge.getDefaultProviderKey(provider, sourceTokenAddress, targetTokenAddress)); - //const tx = await bridge.callStatic.transferAndLockMargin( - const tx = await bridge.transferAndLockMargin( - [ - provider, - sourceTokenAddress, - providerInfo.lastTransferId, - expectedFee, - withdrawNonce, - ], - amount, - wallet.address, - ); - console.log(tx); -} - -async function relay( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeTarget", bridgeAddress, wallet); - //const tx = await bridge.callStatic.transferAndReleaseMargin( - await bridge.transferAndReleaseMargin( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - ); - //console.log(tx); -} - -async function slash( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("Eth2LineaSource", bridgeAddress, wallet); - const cost = 0; - //return; - - //const tx = await bridge.callStatic.slashAndRemoteRefund( - await bridge.slashAndRemoteRelease( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - {value: cost }, - ); - //console.log(tx); -} - -async function requestWithdrawMargin( - wallet, - bridgeAddress, - sourceToken, - amount, -) { - const bridge = await ethers.getContractAt("Eth2LineaSource", bridgeAddress, wallet); - const cost = 0; - - //const tx = await bridge.callStatic.requestWithdrawMargin( - await bridge.requestWithdrawMargin( - sourceToken, - amount, - {value: cost }, - ); - //console.log(tx); -} - -function wallet() { - const ethereumProvider = new ethers.providers.JsonRpcProvider(ethereumUrl); - const ethereumWallet = new ethers.Wallet(privateKey, ethereumProvider); - const lineaProvider = new ethers.providers.JsonRpcProvider(lineaUrl); - const lineaWallet = new ethers.Wallet(privateKey, lineaProvider); - return [lineaWallet, ethereumWallet]; -} - -async function getLnBridgeOnL1InitData(wallet, dao, messageService) { - const bridgeContract = await ethers.getContractFactory("Linea2EthTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, messageService], - "initialize", - ); - console.log("ln bridge on l1 init data:", initdata); -} - -async function getLnBridgeOnL2InitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Linea2EthSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("ln bridge on l2 init data:", initdata); -} - -async function deployLnTarget(wallet, dao, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Eth2LineaTarget", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln target bridge logic, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, messageServiceLineaAddress], - wallet); - console.log("finish to deploy ln bridge proxy on L2, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deployLnSource(wallet, dao, messageService, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Eth2LineaSource", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln source bridge logic, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, messageService], - wallet); - console.log("finish to deploy ln bridge proxy on ethereum, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deploy(lineaWallet, ethereumWallet) { - const ethereumLnBridgeAddress = await deployLnSource( - ethereumWallet, - daoOnEthereum, - messageServiceEthereumAddress, - ethereumProxyAdmin - ); - const lineaLnBridgeAddress = await deployLnTarget( - lineaWallet, - daoOnLinea, - lineaProxyAdmin - ); - - const lineaLnBridge = await ethers.getContractAt("Eth2LineaTarget", lineaLnBridgeAddress, lineaWallet); - const ethereumLnBridge = await ethers.getContractAt("Eth2LineaSource", ethereumLnBridgeAddress, ethereumWallet); - await ethereumLnBridge.updateFeeReceiver(daoOnEthereum); - await lineaLnBridge.setRemoteBridge(ethereumLnBridgeAddress); - await ethereumLnBridge.setRemoteBridge(lineaLnBridgeAddress); - - // register special erc20 token - // native token weth - // we need replace this wusdc address by exist one - const usdcOnLinea = await ethers.getContractAt("Erc20", usdcLineaAddress, lineaWallet); - const usdcOnEthereum = await ethers.getContractAt("Erc20", usdcEthereumAddress, ethereumWallet); - - // register token - await ethereumLnBridge.setTokenInfo( - usdcEthereumAddress, - usdcLineaAddress, - // helix fee - 1500000, - // penaltyLnCollateral - 2000000, - 6, // local decimals - 6, // remote decimals - ); - - // register provider - await ethereumLnBridge.setProviderFee( - usdcEthereumAddress, - 2500000, - 10, - ); - await usdcOnLinea.approve(lineaLnBridge.address, ethers.utils.parseEther("10000000")); - await lineaLnBridge.depositProviderMargin( - usdcEthereumAddress, - usdcLineaAddress, - 100000000, - ); - await lineaLnBridge.depositSlashFundReserve( - usdcEthereumAddress, - usdcLineaAddress, - 10000000, - ); - return { - "LnBridgeOnLinea": lineaLnBridgeAddress, - "LnBridgeOnEthereum": ethereumLnBridgeAddress, - }; -} - -// 2. deploy mapping token factory -async function main() { - const wallets = wallet(); - const lineaWallet = wallets[0]; - const ethereumWallet = wallets[1]; - - /* - const deployed = await deploy(lineaWallet, ethereumWallet); - console.log(deployed); - return; - */ - - const ethereumLnBridgeAddress = "0x5A351EA4F4128F58EA13DDa52E3d1842c0b3B690"; - const lineaLnBridgeAddress = "0xeA5f0a09A8723444965FDd6f76523C338faB00f7"; - - const usdcOnLinea = await ethers.getContractAt("Erc20", usdcLineaAddress, lineaWallet); - //await usdcOnLinea.approve(lineaLnBridgeAddress, ethers.utils.parseEther("10000000")); - const usdcOnEthereum = await ethers.getContractAt("Erc20", usdcEthereumAddress, ethereumWallet); - //await usdcOnEthereum.approve(ethereumLnBridgeAddress, ethers.utils.parseEther("10000000")); - - const amount1 = 3000000; - - // lock - /* - await transferAndLockMargin( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - usdcEthereumAddress, - usdcLineaAddress, - amount1, - ethereumWallet.address, - 0, - ); - console.log("transfer and lock margin 1 successed"); - return; - */ - - // relay - // query: lastTransferId on linea - const lastTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - const timestamp = 1692691272; - const expectedTransferId = "0x851E1255171825594B313D8AE96F277C0DBAAB5246B9BC661BA98538F675425C"; - - /* - await relay( - lineaWallet, - lineaLnBridgeAddress, - lineaWallet.address, - usdcEthereumAddress, - usdcLineaAddress, - lastTransferId, - timestamp, - lineaWallet.address, - amount1, - expectedTransferId, - ) - console.log("relay 1 successed"); - return; - */ - - // slasher - /* - await slash( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - usdcEthereumAddress, - usdcLineaAddress, - lastTransferId, - timestamp, - lineaWallet.address, - amount1, - expectedTransferId, - ); - console.log("slash successed"); - return; - */ - - // withdraw - - await requestWithdrawMargin( - ethereumWallet, - ethereumLnBridgeAddress, - usdcEthereumAddress, - 3200000 - ); - - console.log("withdraw successed"); - -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - -/* -ethereumLnBridgeAddressLogic = 0x92cc77912625eb88c139086f9cB59Bf97dae5943 -ethereumLnBridgeAddressProxy = 0x5A351EA4F4128F58EA13DDa52E3d1842c0b3B690 -lineaLnBridgeAddressLogic = 0x188Ec0CEB000444E14f8BB29b9113794B25A571B -lineaLnBridgeAddressProxy = 0xeA5f0a09A8723444965FDd6f76523C338faB00f7 -*/ - diff --git a/helix-contract/deploy/deploy_eth2zksync_source.js b/helix-contract/deploy/deploy_eth2zksync_source.js deleted file mode 100644 index 47372997..00000000 --- a/helix-contract/deploy/deploy_eth2zksync_source.js +++ /dev/null @@ -1,457 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); -const { Provider } = require("zksync-web3"); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY - -// goerli test <> zkSync goerli test -const ethereumUrl = "https://rpc.ankr.com/eth_goerli"; -const zkSyncUrl = "https://zksync2-testnet.zksync.dev"; -const ethereumProxyAdmin = "0x3F3eDBda6124462a09E071c5D90e072E0d5d4ed4"; -const zkSyncProxyAdmin = "0x96892F3EaD26515592Da38432cFABad991BBd69d"; -const mailboxEthereumAddress = "0x1908e2BF4a88F91E4eF0DC72f02b8Ea36BEa2319"; -const daoOnZkSync = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -const daoOnEthereum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -const zkSyncLnBridgeAddress = "0xe0be4a2b4B0846fB088F720D3D0DFE725b9c91ee"; - -const tokenInfos = { - RING: { - sourceAddress: "0x1836BAFa3016Dd5Ce543D0F7199cB858ec69F41E", - targetAddress: "0x61C31A1fA4a8D765e63D4285f368aA2f4d912DbB", - protocolFee: ethers.utils.parseEther("1.5"), - penalty: ethers.utils.parseEther("20"), - providerFee: ethers.utils.parseEther("2.5"), - providerLiquidityRate: 10, - sourceDecimals: 18, - targetDecimals: 18, - margin: ethers.utils.parseEther("10000"), - slashFund: ethers.utils.parseEther("10"), - }, - USDC: { - sourceAddress: "0xd35CCeEAD182dcee0F148EbaC9447DA2c4D449c4", - targetAddress: "0x0faF6df7054946141266420b43783387A78d82A9", - protocolFee: 1500000, - penalty: 10000000, - providerFee: 2500000, - providerLiquidityRate: 10, - sourceDecimals: 6, - targetDecimals: 6, - margin: 10000000000, - slashFund: 10000000, - }, - ETH: { - sourceAddress: "0x0000000000000000000000000000000000000000", - targetAddress: "0x0000000000000000000000000000000000000000", - protocolFee: ethers.utils.parseEther("0.0001"), - penalty: ethers.utils.parseEther("0.001"), - providerFee: ethers.utils.parseEther("0.00015"), - providerLiquidityRate: 10, - sourceDecimals: 18, - targetDecimals: 18, - margin: ethers.utils.parseEther("0.1"), - slashFund: ethers.utils.parseEther("0.0001"), - }, -}; - -async function getLnBridgeTargetInitData(wallet, dao, inbox) { - const bridgeContract = await ethers.getContractFactory("Eth2ZkSyncTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, inbox], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function getLnBridgeSourceInitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Eth2ZkSyncSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function transferAndLockMargin( - wallet, - bridgeAddress, - provider, - sourceTokenAddress, - targetTokenAddress, - amount, - receiver, - withdrawNonce, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeSource", bridgeAddress, wallet); - const expectedFee = await bridge.totalFee( - provider, - sourceTokenAddress, - amount); - console.log("expect fee is", expectedFee); - const providerInfo = await bridge.lnProviders(await bridge.getDefaultProviderKey(provider, sourceTokenAddress, targetTokenAddress)); - let value = expectedFee.add(amount); - if (sourceTokenAddress !== "0x0000000000000000000000000000000000000000") { - value = 0; - } - //const tx = await bridge.callStatic.transferAndLockMargin( - const tx = await bridge.transferAndLockMargin( - [ - provider, - sourceTokenAddress, - providerInfo.lastTransferId, - expectedFee, - withdrawNonce, - ], - amount, - wallet.address, - { value: value }, - ); - console.log(tx); -} - -async function relay( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeTarget", bridgeAddress, wallet); - //const tx = await bridge.callStatic.transferAndReleaseMargin( - const tx = await bridge.transferAndReleaseMargin( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - ); - console.log(tx); -} - -async function slash( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("Eth2ZkSyncSource", bridgeAddress, wallet); - const cost = await bridge.l2Fee( - 10000000000, - 1000000, - 800, - ); - //const tx = await bridge.callStatic.slashAndRemoteRelease( - await bridge.slashAndRemoteRelease( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - 1000000, - 800, - {value: cost }, - ); - //console.log(tx); -} - -async function requestWithdrawMargin( - wallet, - bridgeAddress, - sourceToken, - amount, -) { - const bridge = await ethers.getContractAt("Eth2ZkSyncSource", bridgeAddress, wallet); - const cost = await bridge.l2Fee( - 100000000000, - 1000000, - 800, - ); - - //const tx = await bridge.callStatic.requestWithdrawMargin( - await bridge.requestWithdrawMargin( - sourceToken, - amount, - 1000000, - 800, - {value: cost }, - ); - //console.log(tx); -} - -function wallet() { - const ethereumProvider = new ethers.providers.JsonRpcProvider(ethereumUrl); - const ethereumWallet = new ethers.Wallet(privateKey, ethereumProvider); - const zkSyncProvider = new ethers.providers.JsonRpcProvider(zkSyncUrl); - const zkSyncWallet = new ethers.Wallet(privateKey, zkSyncProvider); - return [zkSyncWallet, ethereumWallet]; -} - -async function getLnBridgeOnL1InitData(wallet, dao, inbox) { - const bridgeContract = await ethers.getContractFactory("Eth2ZkSyncTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, inbox], - "initialize", - ); - console.log("ln bridge on l1 init data:", initdata); -} - -async function getLnBridgeOnL2InitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Eth2ZkSyncSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("ln bridge on l2 init data:", initdata); -} - -async function deployLnSource(wallet, dao, mailbox, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Eth2ZkSyncSource", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln source bridge logic, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, mailbox], - wallet); - console.log("finish to deploy ln bridge proxy on ethereum, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deploy(zkSyncWallet, ethereumWallet) { - const ethereumLnBridgeAddress = await deployLnSource( - ethereumWallet, - daoOnEthereum, - mailboxEthereumAddress, - ethereumProxyAdmin - ); - - const zkSyncLnBridge = await ethers.getContractAt("Eth2ZkSyncTarget", zkSyncLnBridgeAddress, zkSyncWallet); - const ethereumLnBridge = await ethers.getContractAt("Eth2ZkSyncSource", ethereumLnBridgeAddress, ethereumWallet); - await ethereumLnBridge.updateFeeReceiver(daoOnEthereum); - await zkSyncLnBridge.setRemoteBridge(ethereumLnBridgeAddress); - await ethereumLnBridge.setRemoteBridge(zkSyncLnBridgeAddress); - - return { - "LnBridgeOnZkSync": zkSyncLnBridgeAddress, - "LnBridgeOnEthereum": ethereumLnBridgeAddress, - }; -} - -async function registerToken(token, sourceLnBridgeAddress, wallet) { - const tokenInfo = tokenInfos[token]; - const ethereumLnBridge = await ethers.getContractAt("Eth2ZkSyncSource", sourceLnBridgeAddress, wallet); - // register token - await ethereumLnBridge.setTokenInfo( - tokenInfo.sourceAddress, - tokenInfo.targetAddress, - tokenInfo.protocolFee, - tokenInfo.penalty, - tokenInfo.sourceDecimals, - tokenInfo.targetDecimals, - ); - console.log("register token finished", token); -} - -async function registerProvider(token, sourceLnBridgeAddress, targetLnBridgeAddress, sourceWallet, targetWallet) { - const tokenInfo = tokenInfos[token]; - const sourceLnBridge = await ethers.getContractAt("Eth2ZkSyncSource", sourceLnBridgeAddress, sourceWallet); - const targetLnBridge = await ethers.getContractAt("Eth2ZkSyncTarget", targetLnBridgeAddress, targetWallet); - // register provider - await sourceLnBridge.setProviderFee( - tokenInfo.sourceAddress, - tokenInfo.providerFee, - tokenInfo.providerLiquidityRate, - ); - console.log("set provider fee finished"); - let marginValue = tokenInfo.margin; - let slashValue = tokenInfo.slashFund; - if (tokenInfo.targetAddress !== "0x0000000000000000000000000000000000000000") { - const targetToken = await ethers.getContractAt("Erc20", tokenInfo.targetAddress, targetWallet); - await targetToken.approve(targetLnBridge.address, ethers.utils.parseEther("10000000")); - marginValue = 0; - slashValue = 0; - } - await targetLnBridge.depositProviderMargin( - tokenInfo.sourceAddress, - tokenInfo.targetAddress, - tokenInfo.margin, - {value: marginValue }, - ); - console.log("deposit margin finished"); - await targetLnBridge.depositSlashFundReserve( - tokenInfo.sourceAddress, - tokenInfo.targetAddress, - tokenInfo.slashFund, - {value: slashValue }, - ); - console.log("register provider finished", token); -} - -async function lockToken( - token, - bridgeAddress, - amount, - withdrawNonce, - needApprove, - sourceWallet, - targetWallet, -) { - const tokenInfo = tokenInfos[token]; - - if (needApprove && tokenInfo.sourceAddress !== "0x0000000000000000000000000000000000000000") { - const sourceToken = await ethers.getContractAt("Erc20", tokenInfo.sourceAddress, sourceWallet); - await sourceToken.approve(bridgeAddress, ethers.utils.parseEther("10000000")); - } - - // lock - await transferAndLockMargin( - sourceWallet, - bridgeAddress, - sourceWallet.address, - tokenInfo.sourceAddress, - tokenInfo.targetAddress, - amount, - sourceWallet.address, - withdrawNonce, - ); - console.log("transfer and lock margin 1 successed"); -} - -// 2. deploy mapping token factory -async function main() { - /* - const l2Provider = new Provider("https://testnet.era.zksync.dev"); - const zkSyncAddress = await l2Provider.getMainContractAddress(); - */ - - const wallets = wallet(); - const zkSyncWallet = wallets[0]; - const ethereumWallet = wallets[1]; - - //await getLnBridgeTargetInitData(zkSyncWallet, "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", "0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f"); - //await getLnBridgeSourceInitData(zkSyncWallet, "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"); - //return; - - // only deploy ethereum contract - /* - const deployed = await deploy(zkSyncWallet, ethereumWallet); - console.log(deployed); - return; - */ - - const ethereumLnBridgeAddress = "0xc05ca63DAB6b48bcd82320d29Ad44BD6A3C21160"; - - /* - await registerToken('ETH', ethereumLnBridgeAddress, ethereumWallet); - await registerProvider('ETH', ethereumLnBridgeAddress, zkSyncLnBridgeAddress, ethereumWallet, zkSyncWallet); - return; - */ - - const amount = ethers.utils.parseEther("0.001"); - //const amount = 12000000; - await lockToken( - 'ETH', - ethereumLnBridgeAddress, - amount, - 0, // withdrawNonce - true, // needApprove - ethereumWallet, - zkSyncWallet, - ); - return; - - // relay - // query: lastTransferId on zkSync - const lastTransferId = "0x349F327DD96E2FC5940EC3D1A75EEBC32FBAAB98C099CFCD0BBFB06A94CC0CE3"; - const timestamp = 1691561400; - const expectedTransferId = "0x648B13FF4E4F75B5683DDD08D23F275DFCC4893945458AE9516AC014110EB14E"; - - /* - await relay( - zkSyncWallet, - zkSyncLnBridgeAddress, - zkSyncWallet.address, - ringEthereumAddress, - ringZkSyncAddress, - lastTransferId, - timestamp, - zkSyncWallet.address, - amount1, - expectedTransferId, - ) - console.log("relay 1 successed"); - return; - */ - - // slasher - /* - await slash( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - ringEthereumAddress, - ringZkSyncAddress, - lastTransferId, - timestamp, - zkSyncWallet.address, - amount1, - expectedTransferId, - ); - console.log("slash successed"); - return; - */ - - // withdraw - /* - await requestWithdrawMargin( - ethereumWallet, - ethereumLnBridgeAddress, - ringEthereumAddress, - ethers.utils.parseEther("3.2"), // amount - ); - */ - - console.log("withdraw successed"); - -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - - diff --git a/helix-contract/deploy/deploy_layerzero_ln.js b/helix-contract/deploy/deploy_layerzero_ln.js deleted file mode 100644 index 0606e9fc..00000000 --- a/helix-contract/deploy/deploy_layerzero_ln.js +++ /dev/null @@ -1,420 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY - -const lineaNetwork = { - url: "https://rpc.goerli.linea.build", - proxyAdmin: "0x9bc1C7567DDBcaF2212185b6665D755d842d01E4", - dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", - endpoint: "0x6aB5Ae6822647046626e83ee6dB8187151E1d5ab", - usdc: "0xB4257F31750961C8e536f5cfCBb3079437700416", - chainId: 10157, -}; - -const zkSyncNetwork = { - url: "https://zksync2-testnet.zksync.dev", - proxyAdmin: "0x96892F3EaD26515592Da38432cFABad991BBd69d", - dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", - endpoint: "0x093D2CF57f764f09C3c2Ac58a42A2601B8C79281", - usdc: "0x0faF6df7054946141266420b43783387A78d82A9", - chainId: 10165, -}; - -const arbitrumNetwork = { - url: "https://goerli-rollup.arbitrum.io/rpc", - proxyAdmin: "0x66d86a686e50c98bac236105efafb99ee7605dc5", - dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", - endpoint: "0x6aB5Ae6822647046626e83ee6dB8187151E1d5ab", - usdc: "0xEA70a40Df1432A1b38b916A51Fb81A4cc805a963", - chainId: 10143, -}; - -const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -async function getLnBridgeTargetInitData(wallet, dao, endpoint, chainId) { - const bridgeContract = await ethers.getContractFactory("LnBridgeBaseLZ", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, endpoint, chainId], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function transferAndLockMargin( - wallet, - bridgeAddress, - provider, - sourceTokenAddress, - targetTokenAddress, - amount, - receiver, - withdrawNonce, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeSource", bridgeAddress, wallet); - const expectedFee = await bridge.totalFee( - provider, - sourceTokenAddress, - amount); - console.log("expect fee is", expectedFee); - const providerInfo = await bridge.lnProviders(await bridge.getDefaultProviderKey(provider, sourceTokenAddress, targetTokenAddress)); - //const tx = await bridge.callStatic.transferAndLockMargin( - const tx = await bridge.transferAndLockMargin( - [ - provider, - sourceTokenAddress, - providerInfo.lastTransferId, - expectedFee, - withdrawNonce, - ], - amount, - wallet.address, - ); - console.log(tx); -} - -async function relay( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeTarget", bridgeAddress, wallet); - //const tx = await bridge.callStatic.transferAndReleaseMargin( - await bridge.transferAndReleaseMargin( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - ); - //console.log(tx); -} - -async function slash( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnBridgeBaseLZ", bridgeAddress, wallet); - const cost = await bridge.estimateSlashFee( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ] - ); - //return; - - //const tx = await bridge.callStatic.slashAndRemoteRefund( - await bridge.slashAndRemoteRelease( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - {value: cost }, - ); - //console.log(tx); -} - -async function requestWithdrawMargin( - wallet, - bridgeAddress, - sourceToken, - amount, -) { - const bridge = await ethers.getContractAt("LnBridgeBaseLZ", bridgeAddress, wallet); - const cost = await bridge.estimateWithdrawFee( - "0x851E1255171825594B313D8AE96F277C0DBAAB5246B9BC661BA98538F675425C", - 1, - "0x1000000000000000000000000000000000000001", - "0x1000000000000000000000000000000000000001", - "0x1000000000000000000000000000000000000001", - 100 - ); - console.log(cost); - - //const tx = await bridge.callStatic.requestWithdrawMargin( - await bridge.requestWithdrawMargin( - sourceToken, - amount, - {value: cost.nativeFee }, - ); - //console.log(tx); -} - -function wallet(sourceUrl, targetUrl) { - const sourceProvider = new ethers.providers.JsonRpcProvider(sourceUrl); - const sourceWallet = new ethers.Wallet(privateKey, sourceProvider); - const targetProvider = new ethers.providers.JsonRpcProvider(targetUrl); - const targetWallet = new ethers.Wallet(privateKey, targetProvider); - return [sourceWallet, targetWallet]; -} - -async function deployLnBridge(wallet, dao, proxyAdminAddress, endpoint, chainId) { - const bridgeContract = await ethers.getContractFactory("LnBridgeBaseLZ", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln bridge logic, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, endpoint, chainId], - wallet); - console.log("finish to deploy ln bridge proxy, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deploy(wallet01, wallet02, network01, network02) { - const bridgeAddress01 = await deployLnBridge( - wallet01, - network01.dao, - network01.proxyAdmin, - network01.endpoint, - network02.chainId - ); - const bridgeAddress02 = await deployLnBridge( - wallet02, - network02.dao, - network02.proxyAdmin, - network02.endpoint, - network01.chainId - ); - - const bridge01 = await ethers.getContractAt("LnBridgeBaseLZ", bridgeAddress01, wallet01); - const bridge02 = await ethers.getContractAt("LnBridgeBaseLZ", bridgeAddress02, wallet02); - await bridge01.updateFeeReceiver(network01.dao); - await bridge02.updateFeeReceiver(network02.dao); - await bridge01.setRemoteBridge(bridgeAddress02); - await bridge02.setRemoteBridge(bridgeAddress01); - - // register special erc20 token - // native token weth - // we need replace this wusdc address by exist one - const usdc01 = await ethers.getContractAt("Erc20", network01.usdc, wallet01); - const usdc02 = await ethers.getContractAt("Erc20", network02.usdc, wallet02); - - // register token - await bridge01.setTokenInfo( - usdc01.address, - usdc02.address, - // helix fee - 1500000, - // penaltyLnCollateral - 2000000, - 6, // local decimals - 6, // remote decimals - ); - await bridge02.setTokenInfo( - usdc02.address, - usdc01.address, - // helix fee - 1500000, - // penaltyLnCollateral - 2000000, - 6, // local decimals - 6, // remote decimals - ); - - // register provider - await bridge01.setProviderFee( - usdc01.address, - 2500000, - 10, - ); - await bridge02.setProviderFee( - usdc02.address, - 2500000, - 10, - ); - await usdc01.approve(bridge01.address, ethers.utils.parseEther("10000000")); - await usdc02.approve(bridge02.address, ethers.utils.parseEther("10000000")); - await bridge01.depositProviderMargin( - usdc02.address, - usdc01.address, - 10000000000, - ); - await bridge02.depositProviderMargin( - usdc01.address, - usdc02.address, - 10000000000, - ); - await bridge01.depositSlashFundReserve( - usdc02.address, - usdc01.address, - 10000000, - ); - await bridge02.depositSlashFundReserve( - usdc01.address, - usdc02.address, - 10000000, - ); - return { - "bridge01": bridge01.address, - "bridge02": bridge02.address, - }; -} - -// 2. deploy mapping token factory -async function main() { - const network01 = arbitrumNetwork; - const network02 = lineaNetwork; - const wallets = wallet(network01.url, network02.url); - const wallet01 = wallets[0]; - const wallet02 = wallets[1]; - - /* - const deployed = await deploy(wallet01, wallet02, network01, network02); - console.log(deployed); - return; - */ - - const bridgeAddress01 = "0x504F597CfB0A32704AA6533Fb75dCD60dB982836"; - const bridgeAddress02 = "0xE4B4b7707450b60421b5d7DE372fA5920F2bBDa8"; - - const usdc01 = await ethers.getContractAt("Erc20", network01.usdc, wallet01); - //await usdc01.approve(bridgeAddress01, ethers.utils.parseEther("10000000")); - const usdc02 = await ethers.getContractAt("Erc20", network02.usdc, wallet02); - //await usdc02.approve(bridgeAddress02, ethers.utils.parseEther("10000000")); - - const amount1 = 3300000; - - // lock - /* - await transferAndLockMargin( - wallet01, - bridgeAddress01, - wallet01.address, - usdc01.address, - usdc02.address, - amount1, - wallet01.address, - 0, - ); - */ - - await transferAndLockMargin( - wallet02, - bridgeAddress02, - wallet02.address, - usdc02.address, - usdc01.address, - amount1, - wallet02.address, - 0, - ); - console.log("transfer and lock margin 1 successed"); - return; - - // relay - // query: lastTransferId on linea - const lastTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - const timestamp = 1692867700; - const expectedTransferId = "0xFC62B416DE2A4182296774CFC5673507415FEA2BD2B5EAAA50F5A9CA387846CA"; - - /* - await relay( - wallet02, - bridgeAddress02, - wallet02.address, - usdc01.address, - usdc02.address, - lastTransferId, - timestamp, - wallet02.address, - amount1, - expectedTransferId, - ) - console.log("relay 1 successed"); - return; - */ - - // slasher - /* - await slash( - wallet01, - bridgeAddress01, - wallet01.address, - usdc01.address, - usdc02.address, - lastTransferId, - timestamp, - wallet01.address, - amount1, - expectedTransferId, - ); - console.log("slash successed"); - return; - */ - - // withdraw - /* - await requestWithdrawMargin( - wallet02, - bridgeAddress02, - usdc02.address, - 1200000 - ); - */ - - /* - await requestWithdrawMargin( - wallet01, - bridgeAddress01, - usdc01.address, - 320000 - ); - */ - - console.log("withdraw successed"); - -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - -/* -bridge01Logic = 0xCF8923ebF4244cedC647936a0281dd10bDFCBF18 -bridge01Proxy = 0x78a6831Da2293fbEFd0d8aFB4D1f7CBB751e0119 -bridge02Logic = 0x7C46146F50757Dc5A244C68C35FA2341351D5410 -bridge02Proxy = 0x17bAfDDB48b2bD2424da843df67ACfe9183E087E -*/ - diff --git a/helix-contract/deploy/deploy_layerzero_zksync.js b/helix-contract/deploy/deploy_layerzero_zksync.js deleted file mode 100644 index 27cc0cd9..00000000 --- a/helix-contract/deploy/deploy_layerzero_zksync.js +++ /dev/null @@ -1,417 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY - -const lineaNetwork = { - url: "https://rpc.goerli.linea.build", - proxyAdmin: "0x9bc1C7567DDBcaF2212185b6665D755d842d01E4", - dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", - endpoint: "0x6aB5Ae6822647046626e83ee6dB8187151E1d5ab", - usdc: "0xB4257F31750961C8e536f5cfCBb3079437700416", - chainId: 10157, -}; - -const zkSyncNetwork = { - url: "https://zksync2-testnet.zksync.dev", - proxyAdmin: "0x96892F3EaD26515592Da38432cFABad991BBd69d", - dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", - endpoint: "0x093D2CF57f764f09C3c2Ac58a42A2601B8C79281", - usdc: "0x0faF6df7054946141266420b43783387A78d82A9", - chainId: 10165, - // to arbitrum - //logicAddress: "0x60C7406916e015Acce5260B5c21a169eDdC4761e", - //proxyAddress: "0xa88b0119753A0dC9cB27e54Ab9F333DAd80D6141", - // to linea - logicAddress: "0x33D05496F6323861F9e76228F72700C7b7C5766C", - proxyAddress: "0xAe753Ae021cf1Eb3ec55C5c8798C9015Acde6281", -}; - -const arbitrumNetwork = { - url: "https://goerli-rollup.arbitrum.io/rpc", - proxyAdmin: "0x66d86a686e50c98bac236105efafb99ee7605dc5", - dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", - endpoint: "0x6aB5Ae6822647046626e83ee6dB8187151E1d5ab", - usdc: "0xEA70a40Df1432A1b38b916A51Fb81A4cc805a963", - chainId: 10143, -}; - -const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -async function getLnBridgeTargetInitData(wallet, dao, endpoint, chainId) { - const bridgeContract = await ethers.getContractFactory("LnBridgeBaseLZ", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, endpoint, chainId], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function transferAndLockMargin( - wallet, - bridgeAddress, - provider, - sourceTokenAddress, - targetTokenAddress, - amount, - receiver, - withdrawNonce, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeSource", bridgeAddress, wallet); - const expectedFee = await bridge.totalFee( - provider, - sourceTokenAddress, - amount); - console.log("expect fee is", expectedFee); - const providerInfo = await bridge.lnProviders(await bridge.getDefaultProviderKey(provider, sourceTokenAddress, targetTokenAddress)); - //const tx = await bridge.callStatic.transferAndLockMargin( - const tx = await bridge.transferAndLockMargin( - [ - provider, - sourceTokenAddress, - providerInfo.lastTransferId, - expectedFee, - withdrawNonce, - ], - amount, - wallet.address, - ); - console.log(tx); -} - -async function relay( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnDefaultBridgeTarget", bridgeAddress, wallet); - //const tx = await bridge.callStatic.transferAndReleaseMargin( - await bridge.transferAndReleaseMargin( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - ); - //console.log(tx); -} - -async function slash( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnBridgeBaseLZ", bridgeAddress, wallet); - const cost = await bridge.estimateSlashFee( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ] - ); - //return; - - //const tx = await bridge.callStatic.slashAndRemoteRefund( - await bridge.slashAndRemoteRelease( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - {value: cost }, - ); - //console.log(tx); -} - -async function requestWithdrawMargin( - wallet, - bridgeAddress, - sourceToken, - amount, -) { - const bridge = await ethers.getContractAt("LnBridgeBaseLZ", bridgeAddress, wallet); - const cost = await bridge.estimateWithdrawFee( - "0x851E1255171825594B313D8AE96F277C0DBAAB5246B9BC661BA98538F675425C", - 1, - "0x1000000000000000000000000000000000000001", - "0x1000000000000000000000000000000000000001", - "0x1000000000000000000000000000000000000001", - 100 - ); - console.log(cost); - - //const tx = await bridge.callStatic.requestWithdrawMargin( - await bridge.requestWithdrawMargin( - sourceToken, - amount, - {value: cost.nativeFee }, - ); - //console.log(tx); -} - -function wallet(sourceUrl, targetUrl) { - const sourceProvider = new ethers.providers.JsonRpcProvider(sourceUrl); - const sourceWallet = new ethers.Wallet(privateKey, sourceProvider); - const targetProvider = new ethers.providers.JsonRpcProvider(targetUrl); - const targetWallet = new ethers.Wallet(privateKey, targetProvider); - return [sourceWallet, targetWallet]; -} - -async function deployLnBridge(wallet, dao, proxyAdminAddress, endpoint, chainId) { - const bridgeContract = await ethers.getContractFactory("LnBridgeBaseLZ", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln bridge logic, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, endpoint, chainId], - wallet); - console.log("finish to deploy ln bridge proxy, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -// network01 is zksync, which has been deployed -async function deploy(wallet01, wallet02, network01, network02) { - const bridgeAddress01 = network01.proxyAddress; - const bridgeAddress02 = await deployLnBridge( - wallet02, - network02.dao, - network02.proxyAdmin, - network02.endpoint, - network01.chainId - ); - - const bridge01 = await ethers.getContractAt("LnBridgeBaseLZ", bridgeAddress01, wallet01); - const bridge02 = await ethers.getContractAt("LnBridgeBaseLZ", bridgeAddress02, wallet02); - await bridge01.updateFeeReceiver(network01.dao); - await bridge02.updateFeeReceiver(network02.dao); - await bridge01.setRemoteBridge(bridgeAddress02); - await bridge02.setRemoteBridge(bridgeAddress01); - - // register special erc20 token - // native token weth - // we need replace this wusdc address by exist one - const usdc01 = await ethers.getContractAt("Erc20", network01.usdc, wallet01); - const usdc02 = await ethers.getContractAt("Erc20", network02.usdc, wallet02); - - // register token - await bridge01.setTokenInfo( - usdc01.address, - usdc02.address, - // helix fee - 1500000, - // penaltyLnCollateral - 2000000, - 6, // local decimals - 6, // remote decimals - ); - await bridge02.setTokenInfo( - usdc02.address, - usdc01.address, - // helix fee - 1500000, - // penaltyLnCollateral - 2000000, - 6, // local decimals - 6, // remote decimals - ); - - // register provider - await bridge01.setProviderFee( - usdc01.address, - 2500000, - 10, - ); - await bridge02.setProviderFee( - usdc02.address, - 2500000, - 10, - ); - await usdc01.approve(bridge01.address, ethers.utils.parseEther("10000000")); - await usdc02.approve(bridge02.address, ethers.utils.parseEther("10000000")); - await bridge01.depositProviderMargin( - usdc02.address, - usdc01.address, - 10000000000, - ); - await bridge02.depositProviderMargin( - usdc01.address, - usdc02.address, - 10000000000, - ); - await bridge01.depositSlashFundReserve( - usdc02.address, - usdc01.address, - 10000000, - ); - await bridge02.depositSlashFundReserve( - usdc01.address, - usdc02.address, - 10000000, - ); - return { - "bridge01": bridge01.address, - "bridge02": bridge02.address, - }; -} - -// 2. deploy mapping token factory -async function main() { - const network01 = zkSyncNetwork; - const network02 = lineaNetwork; - const wallets = wallet(network01.url, network02.url); - const wallet01 = wallets[0]; - const wallet02 = wallets[1]; - - const deployed = await deploy(wallet01, wallet02, network01, network02); - console.log(deployed); - return; - - const bridgeAddress01 = "0x965d47903b616e92f9a9659537Ed0d39E7752E82"; - const bridgeAddress02 = "0x2B0aC3b9a8e89Ba2e6e806f59272bb1c718e1E2a"; - - const usdc01 = await ethers.getContractAt("Erc20", network01.usdc, wallet01); - //await usdc01.approve(bridgeAddress01, ethers.utils.parseEther("10000000")); - const usdc02 = await ethers.getContractAt("Erc20", network02.usdc, wallet02); - //await usdc02.approve(bridgeAddress02, ethers.utils.parseEther("10000000")); - - const amount1 = 3000000; - - // lock - await transferAndLockMargin( - wallet01, - bridgeAddress01, - wallet01.address, - usdc01.address, - usdc02.address, - amount1, - wallet01.address, - 0, - ); - /* - await transferAndLockMargin( - wallet02, - bridgeAddress02, - wallet02.address, - usdc02.address, - usdc01.address, - amount1, - wallet02.address, - 0, - ); - console.log("transfer and lock margin 1 successed"); - return; - */ - - // relay - // query: lastTransferId on linea - const lastTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - const timestamp = 1692867700; - const expectedTransferId = "0xFC62B416DE2A4182296774CFC5673507415FEA2BD2B5EAAA50F5A9CA387846CA"; - - /* - await relay( - wallet02, - bridgeAddress02, - wallet02.address, - usdc01.address, - usdc02.address, - lastTransferId, - timestamp, - wallet02.address, - amount1, - expectedTransferId, - ) - console.log("relay 1 successed"); - return; - */ - - // slasher - /* - await slash( - wallet01, - bridgeAddress01, - wallet01.address, - usdc01.address, - usdc02.address, - lastTransferId, - timestamp, - wallet01.address, - amount1, - expectedTransferId, - ); - console.log("slash successed"); - return; - */ - - // withdraw - - await requestWithdrawMargin( - wallet02, - bridgeAddress02, - usdc02.address, - 1200000 - ); - - /* - await requestWithdrawMargin( - wallet01, - bridgeAddress01, - usdc01.address, - 320000 - ); - */ - - console.log("withdraw successed"); - -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - -/* -bridge01Logic = 0xCF8923ebF4244cedC647936a0281dd10bDFCBF18 -bridge01Proxy = 0x78a6831Da2293fbEFd0d8aFB4D1f7CBB751e0119 -bridge02Logic = 0x7C46146F50757Dc5A244C68C35FA2341351D5410 -bridge02Proxy = 0x17bAfDDB48b2bD2424da843df67ACfe9183E087E -*/ - diff --git a/helix-contract/deploy/deploy_linea2eth_ln.js b/helix-contract/deploy/deploy_linea2eth_ln.js deleted file mode 100644 index 54a95c54..00000000 --- a/helix-contract/deploy/deploy_linea2eth_ln.js +++ /dev/null @@ -1,367 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY - -// goerli test <> linea goerli test -const ethereumUrl = "https://rpc.ankr.com/eth_goerli"; -//const lineaUrl = "https://linea-goerli.infura.io/v3/cab8c3ad4f19458c873819725b65a185"; -const lineaUrl = "https://rpc.goerli.linea.build"; -const usdcLineaAddress = "0xB4257F31750961C8e536f5cfCBb3079437700416"; -const usdcEthereumAddress = "0x07865c6E87B9F70255377e024ace6630C1Eaa37F"; -const ethereumProxyAdmin = "0x3F3eDBda6124462a09E071c5D90e072E0d5d4ed4"; -const lineaProxyAdmin = "0x9bc1C7567DDBcaF2212185b6665D755d842d01E4"; -const messageServiceEthereumAddress = "0x70BaD09280FD342D02fe64119779BC1f0791BAC2"; -const messageServiceLineaAddress = "0xC499a572640B64eA1C8c194c43Bc3E19940719dC"; -const daoOnLinea = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -const daoOnEthereum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; - -const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -async function getLnBridgeTargetInitData(wallet, dao, messageService) { - const bridgeContract = await ethers.getContractFactory("Arb2EthTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, messageService], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function getLnBridgeSourceInitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Arb2EthSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function transferAndLockMargin( - wallet, - bridgeAddress, - provider, - tokenAddress, - amount, - receiver) { - const bridge = await ethers.getContractAt("LnOppositeBridgeSource", bridgeAddress, wallet); - const expectedFee = await bridge.totalFee( - provider, - tokenAddress, - amount); - console.log("expect fee is", expectedFee); - const providerInfo = await bridge.lnProviders(await bridge.getProviderKey(provider, tokenAddress)); - const expectedMargin = providerInfo.config.margin; - console.log("expect margin is", expectedMargin); - //const tx = await bridge.callStatic.transferAndLockMargin( - const tx = await bridge.transferAndLockMargin( - [ - provider, - tokenAddress, - providerInfo.lastTransferId, - expectedMargin, - expectedFee, - ], - amount, - wallet.address, - ); - console.log(tx); -} - -async function relay( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnOppositeBridgeTarget", bridgeAddress, wallet); - //const tx = await bridge.callStatic.relay( - await bridge.transferAndReleaseMargin( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - ); - //console.log(tx); -} - -async function slash( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("Linea2EthTarget", bridgeAddress, wallet); - const cost = ethers.utils.parseEther("0.0003"); - //return; - - //const tx = await bridge.callStatic.slashAndRemoteRefund( - await bridge.slashAndRemoteRefund( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - {value: cost }, - ); - //console.log(tx); -} - -async function requestWithdrawMargin( - wallet, - bridgeAddress, - lastTransferId, - sourceToken, - amount, -) { - const bridge = await ethers.getContractAt("Linea2EthTarget", bridgeAddress, wallet); - const cost = 0; - //const tx = await bridge.callStatic.requestWithdrawMargin( - await bridge.requestWithdrawMargin( - lastTransferId, - sourceToken, - amount, - {value: cost }, - ); - //console.log(tx); -} - -function wallet() { - const ethereumProvider = new ethers.providers.JsonRpcProvider(ethereumUrl); - const ethereumWallet = new ethers.Wallet(privateKey, ethereumProvider); - const lineaProvider = new ethers.providers.JsonRpcProvider(lineaUrl); - const lineaWallet = new ethers.Wallet(privateKey, lineaProvider); - return [lineaWallet, ethereumWallet]; -} - -async function getLnBridgeOnL1InitData(wallet, dao, messageService) { - const bridgeContract = await ethers.getContractFactory("Arb2EthTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, messageService], - "initialize", - ); - console.log("ln bridge on l1 init data:", initdata); -} - -async function getLnBridgeOnL2InitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("Arb2EthSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("ln bridge on l2 init data:", initdata); -} - -async function deployLnLineaBridgeOnL2(wallet, dao, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Linea2EthSource", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln bridge logic on L2, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, messageServiceLineaAddress], - wallet); - console.log("finish to deploy ln bridge proxy on L2, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deployLnLineaBridgeOnL1(wallet, dao, messageService, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("Linea2EthTarget", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln bridge logic on L1, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, messageServiceEthereumAddress], - wallet); - console.log("finish to deploy ln bridge proxy on L1, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deploy(lineaWallet, ethereumWallet) { - const lineaLnBridgeAddress = await deployLnLineaBridgeOnL2( - lineaWallet, - daoOnLinea, - lineaProxyAdmin - ); - const ethereumLnBridgeAddress = await deployLnLineaBridgeOnL1( - ethereumWallet, - daoOnEthereum, - messageServiceEthereumAddress, - ethereumProxyAdmin - ); - - const lineaLnBridge = await ethers.getContractAt("Linea2EthSource", lineaLnBridgeAddress, lineaWallet); - const ethereumLnBridge = await ethers.getContractAt("Linea2EthTarget", ethereumLnBridgeAddress, ethereumWallet); - await lineaLnBridge.updateFeeReceiver(daoOnLinea); - await lineaLnBridge.setRemoteBridge(ethereumLnBridgeAddress); - await ethereumLnBridge.setRemoteBridge(lineaLnBridgeAddress); - - // register special erc20 token - // native token weth - // we need replace this wusdc address by exist one - const usdcOnLinea = await ethers.getContractAt("Erc20", usdcLineaAddress, lineaWallet); - const usdcOnEthereum = await ethers.getContractAt("Erc20", usdcEthereumAddress, ethereumWallet); - - // register token - await lineaLnBridge.registerToken( - usdcLineaAddress, - usdcEthereumAddress, - // helix fee - 1500000, - // penaltyLnCollateral - 5000000, - 6, // local decimals - 6, // remote decimals - ); - - // register provider - await usdcOnLinea.approve(lineaLnBridge.address, ethers.utils.parseEther("10000000")); - await lineaLnBridge.updateProviderFeeAndMargin( - usdcLineaAddress, - 100000000, - 10000000, - 100 // liquidityFee - ); - return { - "LnBridgeOnLinea": lineaLnBridgeAddress, - "LnBridgeOnEthereum": ethereumLnBridgeAddress, - }; -} - -// 2. deploy mapping token factory -async function main() { - const wallets = wallet(); - const lineaWallet = wallets[0]; - const ethereumWallet = wallets[1]; - - /* - const deployed = await deploy(lineaWallet, ethereumWallet); - console.log(deployed); - return; - */ - - const lineaLnBridgeAddress = "0x9C80EdD342b5D179c3a87946fC1F0963BfcaAa09"; - const ethereumLnBridgeAddress = "0x91bdd735Dc214876605C18A57C7841CFF7eE959a"; - - const usdcOnLinea = await ethers.getContractAt("Erc20", usdcLineaAddress, lineaWallet); - //await usdcOnLinea.approve(lineaLnBridgeAddress, ethers.utils.parseEther("10000000")); - const usdcOnEthereum = await ethers.getContractAt("Erc20", usdcEthereumAddress, ethereumWallet); - //await usdcOnEthereum.approve(ethereumLnBridgeAddress, ethers.utils.parseEther("10000000")); - - //const amount1 = ethers.utils.parseEther("23"); - const amount1 = 4000000; - - // lock - /* - await transferAndLockMargin( - lineaWallet, - lineaLnBridgeAddress, - lineaWallet.address, - usdcLineaAddress, - amount1, - lineaWallet.address - ); - console.log("transfer and lock margin 1 successed"); - return; - */ - - // relay - // query: lastTransferId on linea - const lastTransferId = "0xF85022FE768C667060C58E78F436B0D35ACFBCAF7D8537D5A03AF3E55D32BD06"; - const timestamp = 1692674272; - const expectedTransferId = "0x30F50A395BFF7C6F503B152B03719A3B234E7D2C7B23C09CA0A7F9E216963E4F"; - - await relay( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - usdcLineaAddress, - usdcEthereumAddress, - lastTransferId, - timestamp, - lineaWallet.address, - amount1, - expectedTransferId, - ) - console.log("relay 1 successed"); - return; - - // slasher - await slash( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - usdcLineaAddress, - usdcEthereumAddress, - lastTransferId, - timestamp, - lineaWallet.address, - amount1, - expectedTransferId, - ); - console.log("slash successed"); - return; - - // withdraw - - await requestWithdrawMargin( - ethereumWallet, - ethereumLnBridgeAddress, - "0x5FF441A69D0C96D28F7A61497B762AF35DE9B20653F4F20ADE6C4077AE5E1220", //lastTransferId - usdcLineaAddress, - 3200000, // amount - ); - - console.log("withdraw successed"); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - -/* -lineaLnBridgeAddressLogic = 0x191121eC17587C3cE0BF689AFA36386F8D9C538F -lineaLnBridgeAddressProxy = 0x9C80EdD342b5D179c3a87946fC1F0963BfcaAa09 -ethereumLnBridgeAddressLogic = 0xD28086bE6cD7Ea8c2B2E2a99E8a8CaDff2F06427 -ethereumLnBridgeAddressProxy = 0x91bdd735Dc214876605C18A57C7841CFF7eE959a -*/ - diff --git a/helix-contract/deploy/deploy_ln_configure.js b/helix-contract/deploy/deploy_ln_configure.js new file mode 100644 index 00000000..33d1b023 --- /dev/null +++ b/helix-contract/deploy/deploy_ln_configure.js @@ -0,0 +1,558 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); +const fs = require("fs"); + +var ProxyDeployer = require("./proxy.js"); + +const privateKey = process.env.PRIKEY + +const kNativeTokenAddress = "0x0000000000000000000000000000000000000000"; +const relayer = "0xB2a0654C6b2D0975846968D5a3e729F5006c2894"; + +const lineaGoerliNetwork = { + name: "linea-goerli", + url: "https://rpc.goerli.linea.build", + chainId: 59140, + eth: "0x0000000000000000000000000000000000000000", +} + +const arbitrumGoerliNetwork = { + name: "arbitrum-goerli", + url: "https://goerli-rollup.arbitrum.io/rpc", + chainId: 421613, + eth: "0x0000000000000000000000000000000000000000", +}; + +const goerliNetwork = { + name: "goerli", + url: "https://rpc.ankr.com/eth_goerli", + chainId: 5, + eth: "0x0000000000000000000000000000000000000000", + mnt: "0xc1dC2d65A2243c22344E725677A3E3BEBD26E604", +}; + +const mantleGoerliNetwork = { + name: "mantle-goerli", + url: "https://rpc.testnet.mantle.xyz", + chainId: 5001, + mnt: "0x0000000000000000000000000000000000000000", +} + +const zkSyncGoerliNetwork = { + name: "zksync-goerli", + url: "https://zksync2-testnet.zksync.dev", + chainId: 280, + eth: "0x0000000000000000000000000000000000000000", +} + +const messagers = { + goerli: { + Eth2ArbSendService: "0xa4eE139bE76d277D997aCf9D58053D8DaF7E050a", + Eth2LineaSendService: "0x9878e74634544d92a043f1826a94465035FA51f4", + layerzeroMessager: "0xca4490875739BEb1c4ec9ee5d6814774114e1973", + axelarMessager: "0x037c7b64c80251Cf5C64Ed8f731c799Dc1856701" + }, + arbitrumGoerli: { + Eth2ArbReceiveService: "0x102F8D7Cfe692AA79c17E3958aB00D060Df0B88f", + layerzeroMessager: "0x953bE65E685099277F1f09Ebe10746810dC0593D", + axelarMessager: "0xBc30913CC01A2eC70483681841bbb43D2f77caEd" + }, + lineaGoerli: { + Eth2LineaReceiveService: "0x8200b3130416F633A696FB9bb0e689a356625075", + layerzeroMessager: "0xfB09042050868594a54a59EdEAEa96e2765dAd0B", + axelarMessager: "0x14DB1d462ED061b037C7920857Fc66522ed5bf85" + }, + mantleGoerli: { + layerzeroMessager: "0xBE4a32f37d11e8227444837DFb3c634d189ccEDc", + axelarMessager: "0xbb593913a4f3E4eE77861f743c697A4cb95837eF" + }, + zkSyncGoerli: { + layerzeroMessager: "0x7e303b0A3F08F9fa5F5629Abb998B8Deba89049B" + } +}; + +function wait(ms) { + return new Promise(resolve => setTimeout(() => resolve(), ms)); +}; + +function wallet(url) { + const provider = new ethers.providers.JsonRpcProvider(url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +async function connectArbAndEth(configure, arbWallet, goerliWallet) { + const eth2arbReceiveService = configure.messagers[arbitrumGoerliNetwork.name].Eth2ArbReceiveService; + const eth2arbSendService = configure.messagers[goerliNetwork.name].Eth2ArbSendService; + const arbOppositeBridgeProxy = configure.LnOppositeBridgeProxy; + const goerliOppositeBridgeProxy = configure.LnOppositeBridgeProxy; + const arbDefaultBridgeProxy = configure.LnDefaultBridgeProxy.others; + const goerliDefaultBridgeProxy = configure.LnDefaultBridgeProxy.others; + + const arbitrumReceiveService = await ethers.getContractAt("Eth2ArbReceiveService", eth2arbReceiveService, arbWallet); + const ethereumSendService = await ethers.getContractAt("Eth2ArbSendService", eth2arbSendService, goerliWallet); + // arb<>eth + // arb->eth opposite bridge using l1->l2 messager + console.log("start to connect arb->eth using l1->l2 messager"); + const arb2ethSource = await ethers.getContractAt("LnOppositeBridge", arbOppositeBridgeProxy, arbWallet); + const arb2ethTarget = await ethers.getContractAt("LnOppositeBridge", goerliOppositeBridgeProxy, goerliWallet); + await arbitrumReceiveService.authoriseAppCaller(arb2ethSource.address, true); + await ethereumSendService.authoriseAppCaller(arb2ethTarget.address, true); + await arb2ethSource.setReceiveService(goerliNetwork.chainId, arb2ethTarget.address, eth2arbReceiveService); + await arb2ethTarget.setSendService(arbitrumGoerliNetwork.chainId, arb2ethSource.address, eth2arbSendService); + // eth->arb default bridge using l1->l2 messager + console.log("start to connect eth->arb using l1->l2 messager"); + const eth2arbSource = await ethers.getContractAt("LnDefaultBridge", arbDefaultBridgeProxy, goerliWallet); + const eth2arbTarget = await ethers.getContractAt("LnDefaultBridge", goerliDefaultBridgeProxy, arbWallet); + await ethereumSendService.authoriseAppCaller(eth2arbSource.address, true); + await arbitrumReceiveService.authoriseAppCaller(eth2arbTarget.address, true); + await eth2arbSource.setSendService(arbitrumGoerliNetwork.chainId, eth2arbTarget.address, eth2arbSendService); + await eth2arbTarget.setReceiveService(goerliNetwork.chainId, eth2arbSource.address, eth2arbReceiveService); + console.log("finish connect arb<>eth token bridge"); +} + +async function connectLineaAndEth(configure, lineaWallet, goerliWallet) { + const eth2lineaReceiveService = configure.messagers[lineaGoerliNetwork.name].Eth2LineaReceiveService; + const eth2lineaSendService = configure.messagers[goerliNetwork.name].Eth2LineaSendService; + const lineaOppositeBridgeProxy = configure.LnOppositeBridgeProxy; + const goerliOppositeBridgeProxy = configure.LnOppositeBridgeProxy; + const lineaDefaultBridgeProxy = configure.LnDefaultBridgeProxy.others; + const goerliDefaultBridgeProxy = configure.LnDefaultBridgeProxy.others; + + const lineaReceiveService = await ethers.getContractAt("Eth2LineaReceiveService", eth2lineaReceiveService, lineaWallet); + const ethereumSendService = await ethers.getContractAt("Eth2LineaSendService", eth2lineaSendService, goerliWallet); + // linea<>eth + // linea->eth opposite bridge using l1->l2 messager + console.log("start to connect linea->eth using l1->l2 messager"); + const linea2ethSource = await ethers.getContractAt("LnOppositeBridge", lineaOppositeBridgeProxy, lineaWallet); + const linea2ethTarget = await ethers.getContractAt("LnOppositeBridge", goerliOppositeBridgeProxy, goerliWallet); + await lineaReceiveService.authoriseAppCaller(linea2ethSource.address, true); + await ethereumSendService.authoriseAppCaller(linea2ethTarget.address, true); + await linea2ethSource.setReceiveService(goerliNetwork.chainId, linea2ethTarget.address, eth2lineaReceiveService); + await linea2ethTarget.setSendService(lineaGoerliNetwork.chainId, linea2ethSource.address, eth2lineaSendService); + // eth->linea default bridge using l1->l2 messager + console.log("start to connect eth->linea using l1->l2 messager"); + const eth2lineaSource = await ethers.getContractAt("LnDefaultBridge", goerliDefaultBridgeProxy, goerliWallet); + const eth2lineaTarget = await ethers.getContractAt("LnDefaultBridge", lineaDefaultBridgeProxy, lineaWallet); + await lineaReceiveService.authoriseAppCaller(eth2lineaTarget.address, true); + await ethereumSendService.authoriseAppCaller(eth2lineaSource.address, true); + await eth2lineaSource.setSendService(lineaGoerliNetwork.chainId, eth2lineaTarget.address, eth2lineaSendService); + await eth2lineaTarget.setReceiveService(goerliNetwork.chainId, eth2lineaSource.address, eth2lineaReceiveService); + console.log("finish connect linea<>eth token bridge"); +} + +async function connectUsingLayerzero(configure, leftWallet, rightWallet, leftNetwork, rightNetwork) { + const leftMessagerAddess = configure.messagers[leftNetwork.name].layerzeroMessager; + const rightMessagerAddress = configure.messagers[rightNetwork.name].layerzeroMessager; + const leftBridgeProxy = leftNetwork.chainId === 280 ? configure.LnDefaultBridgeProxy.zkSync : configure.LnDefaultBridgeProxy.others; + const rightBridgeProxy = rightNetwork.chainId === 280 ? configure.LnDefaultBridgeProxy.zkSync : configure.LnDefaultBridgeProxy.others; + const leftMessager = await ethers.getContractAt("LayerZeroMessager", leftMessagerAddess, leftWallet); + const rightMessager = await ethers.getContractAt("LayerZeroMessager", rightMessagerAddress, rightWallet); + console.log("start to connect network by using layerzero"); + const left = await ethers.getContractAt("LnDefaultBridge", leftBridgeProxy, leftWallet); + const right = await ethers.getContractAt("LnDefaultBridge", rightBridgeProxy, rightWallet); + await leftMessager.authoriseAppCaller(left.address, true); + await rightMessager.authoriseAppCaller(right.address, true); + await left.setSendService(rightNetwork.chainId, right.address, leftMessagerAddess); + await right.setReceiveService(leftNetwork.chainId, left.address, rightMessagerAddress); + await left.setReceiveService(rightNetwork.chainId, right.address, leftMessagerAddess); + await right.setSendService(leftNetwork.chainId, left.address, rightMessagerAddress); +} + +async function connectUsingAxelar(configure, leftWallet, rightWallet, leftNetwork, rightNetwork) { + const leftMessagerAddress = configure.messagers[leftNetwork.name].axelarMessager; + const rightMessagerAddress = configure.messagers[rightNetwork.name].axelarMessager; + const leftBridgeProxy = leftNetwork.chainId === 280 ? configure.LnDefaultBridgeProxy.zkSync : configure.LnDefaultBridgeProxy.others; + const rightBridgeProxy = rightNetwork.chainId === 280 ? configure.LnDefaultBridgeProxy.zkSync : configure.LnDefaultBridgeProxy.others; + + const leftMessager = await ethers.getContractAt("AxelarMessager", leftMessagerAddress, leftWallet); + const rightMessager = await ethers.getContractAt("AxelarMessager", rightMessagerAddress, rightWallet); + console.log("start to connect network by using axelar"); + const left = await ethers.getContractAt("LnDefaultBridge", leftBridgeProxy, leftWallet); + const right = await ethers.getContractAt("LnDefaultBridge", rightBridgeProxy, rightWallet); + await leftMessager.authoriseAppCaller(left.address, true); + await rightMessager.authoriseAppCaller(right.address, true); + await left.setSendService(rightNetwork.chainId, right.address, leftMessagerAddress); + await right.setReceiveService(leftNetwork.chainId, left.address, rightMessagerAddress); + await left.setReceiveService(rightNetwork.chainId, right.address, leftMessagerAddress); + await right.setSendService(leftNetwork.chainId, left.address, rightMessagerAddress); +} + +async function connectAll(configure, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet) { + // arbitrum L2 message + await connectArbAndEth(configure, arbWallet, goerliWallet); + // linea L2 message + await connectLineaAndEth(configure, lineaWallet, goerliWallet); + await connectUsingLayerzero(configure, arbWallet, lineaWallet, arbitrumGoerliNetwork, lineaGoerliNetwork); + await connectUsingLayerzero(configure, arbWallet, mantleWallet, arbitrumGoerliNetwork, mantleGoerliNetwork); + await connectUsingLayerzero(configure, arbWallet, zkSyncWallet, arbitrumGoerliNetwork, zkSyncGoerliNetwork); + await connectUsingLayerzero(configure, lineaWallet, mantleWallet, lineaGoerliNetwork, mantleGoerliNetwork); + await connectUsingLayerzero(configure, lineaWallet, zkSyncWallet, lineaGoerliNetwork, zkSyncGoerliNetwork); + await connectUsingLayerzero(configure, zkSyncWallet, mantleWallet, zkSyncGoerliNetwork, mantleGoerliNetwork); + await connectUsingLayerzero(configure, zkSyncWallet, goerliWallet, zkSyncGoerliNetwork, goerliNetwork); + await connectUsingAxelar(configure, mantleWallet, goerliWallet, mantleGoerliNetwork, goerliNetwork); +} + +async function registerToken(configure, contractName, srcWallet, dstWallet, srcNetwork, dstNetwork, srcToken, dstToken) { + + let srcDecimals = 18; + let dstDecimals = 18; + let srcTokenAddress = srcNetwork[srcToken]; + let dstTokenAddress = dstNetwork[dstToken]; + if (srcToken !== 'eth' && srcToken !== 'mnt') { + srcTokenAddress = configure[srcToken][srcNetwork.name]; + } + if (dstToken !== 'eth' && dstToken !== 'mnt') { + dstTokenAddress = configure[dstToken][dstNetwork.name]; + } + if (srcTokenAddress != kNativeTokenAddress) { + const sourceToken = await ethers.getContractAt("Erc20", srcTokenAddress, srcWallet); + srcDecimals = await sourceToken.decimals(); + } + if (dstTokenAddress != kNativeTokenAddress) { + const targetToken = await ethers.getContractAt("Erc20", dstTokenAddress, dstWallet); + dstDecimals = await targetToken.decimals(); + } + let fee = ethers.utils.parseUnits("100", srcDecimals); + const penaltyDecimals = contractName == "LnOppositeBridge" ? srcDecimals : dstDecimals; + let penalty = ethers.utils.parseUnits("1000", penaltyDecimals); + if (srcTokenAddress == kNativeTokenAddress || dstTokenAddress == kNativeTokenAddress) { + fee = ethers.utils.parseUnits("0.001", srcDecimals); + penalty = ethers.utils.parseUnits("0.01", penaltyDecimals); + } + + const defaultAddress = srcNetwork.chainId === 280 ? configure.LnDefaultBridgeProxy.zkSync : configure.LnDefaultBridgeProxy.others; + const proxyAddress = contractName == "LnOppositeBridge" ? configure.LnOppositeBridgeProxy : defaultAddress; + + const source = await ethers.getContractAt(contractName, proxyAddress, srcWallet); + await source.setTokenInfo( + dstNetwork.chainId, + srcTokenAddress, + dstTokenAddress, + fee, + penalty, + srcDecimals, + dstDecimals); + console.log(`finished register token bridge: ${contractName}, ${srcNetwork.chainId}->${dstNetwork.chainId}, ${srcToken}->${dstToken}`); +} + +async function registerAllToken(configure, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet) { + //arb<>eth + await registerToken(configure, "LnOppositeBridge", arbWallet, goerliWallet, arbitrumGoerliNetwork, goerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, arbWallet, goerliNetwork, arbitrumGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnOppositeBridge", arbWallet, goerliWallet, arbitrumGoerliNetwork, goerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, arbWallet, goerliNetwork, arbitrumGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnOppositeBridge", arbWallet, goerliWallet, arbitrumGoerliNetwork, goerliNetwork, "eth", "eth"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, arbWallet, goerliNetwork, arbitrumGoerliNetwork, "eth", "eth"); + + // linea<>eth + await registerToken(configure, "LnOppositeBridge", lineaWallet, goerliWallet, lineaGoerliNetwork, goerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, lineaWallet, goerliNetwork, lineaGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnOppositeBridge", lineaWallet, goerliWallet, lineaGoerliNetwork, goerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, lineaWallet, goerliNetwork, lineaGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnOppositeBridge", lineaWallet, goerliWallet, lineaGoerliNetwork, goerliNetwork, "eth", "eth"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, lineaWallet, goerliNetwork, lineaGoerliNetwork, "eth", "eth"); + + //arb<>linea + await registerToken(configure, "LnDefaultBridge", arbWallet, lineaWallet, arbitrumGoerliNetwork, lineaGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", lineaWallet, arbWallet, lineaGoerliNetwork, arbitrumGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", arbWallet, lineaWallet, arbitrumGoerliNetwork, lineaGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", lineaWallet, arbWallet, lineaGoerliNetwork, arbitrumGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", arbWallet, lineaWallet, arbitrumGoerliNetwork, lineaGoerliNetwork, "eth", "eth"); + await registerToken(configure, "LnDefaultBridge", lineaWallet, arbWallet, lineaGoerliNetwork, arbitrumGoerliNetwork, "eth", "eth"); + + //arb<>mantle + await registerToken(configure, "LnDefaultBridge", arbWallet, mantleWallet, arbitrumGoerliNetwork, mantleGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", mantleWallet, arbWallet, mantleGoerliNetwork, arbitrumGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", arbWallet, mantleWallet, arbitrumGoerliNetwork, mantleGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", mantleWallet, arbWallet, mantleGoerliNetwork, arbitrumGoerliNetwork, "usdt", "usdt"); + + // mantle<>linea + await registerToken(configure, "LnDefaultBridge", mantleWallet, lineaWallet, mantleGoerliNetwork, lineaGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", lineaWallet, mantleWallet, lineaGoerliNetwork, mantleGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", mantleWallet, lineaWallet, mantleGoerliNetwork, lineaGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", lineaWallet, mantleWallet, lineaGoerliNetwork, mantleGoerliNetwork, "usdt", "usdt"); + + // mantle<>eth + await registerToken(configure, "LnDefaultBridge", goerliWallet, mantleWallet, goerliNetwork, mantleGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", mantleWallet, goerliWallet, mantleGoerliNetwork, goerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, mantleWallet, goerliNetwork, mantleGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", mantleWallet, goerliWallet, mantleGoerliNetwork, goerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, mantleWallet, goerliNetwork, mantleGoerliNetwork, "mnt", "mnt"); + await registerToken(configure, "LnDefaultBridge", mantleWallet, goerliWallet, mantleGoerliNetwork, goerliNetwork, "mnt", "mnt"); + + // zkSync<>eth + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, goerliWallet, zkSyncGoerliNetwork, goerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, zkSyncWallet, goerliNetwork, zkSyncGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, goerliWallet, zkSyncGoerliNetwork, goerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, zkSyncWallet, goerliNetwork, zkSyncGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, goerliWallet, zkSyncGoerliNetwork, goerliNetwork, "eth", "eth"); + await registerToken(configure, "LnDefaultBridge", goerliWallet, zkSyncWallet, goerliNetwork, zkSyncGoerliNetwork, "eth", "eth"); + + // zkSync<>arb + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, arbWallet, zkSyncGoerliNetwork, arbitrumGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", arbWallet, zkSyncWallet, arbitrumGoerliNetwork, zkSyncGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, arbWallet, zkSyncGoerliNetwork, arbitrumGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", arbWallet, zkSyncWallet, arbitrumGoerliNetwork, zkSyncGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, arbWallet, zkSyncGoerliNetwork, arbitrumGoerliNetwork, "eth", "eth"); + await registerToken(configure, "LnDefaultBridge", arbWallet, zkSyncWallet, arbitrumGoerliNetwork, zkSyncGoerliNetwork, "eth", "eth"); + + // zkSync<>linea + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, lineaWallet, zkSyncGoerliNetwork, lineaGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", lineaWallet, zkSyncWallet, lineaGoerliNetwork, zkSyncGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, lineaWallet, zkSyncGoerliNetwork, lineaGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", lineaWallet, zkSyncWallet, lineaGoerliNetwork, zkSyncGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, lineaWallet, zkSyncGoerliNetwork, lineaGoerliNetwork, "eth", "eth"); + await registerToken(configure, "LnDefaultBridge", lineaWallet, zkSyncWallet, lineaGoerliNetwork, zkSyncGoerliNetwork, "eth", "eth"); + + // zkSync<>mantle + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, mantleWallet, zkSyncGoerliNetwork, mantleGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", mantleWallet, zkSyncWallet, mantleGoerliNetwork, zkSyncGoerliNetwork, "usdc", "usdc"); + await registerToken(configure, "LnDefaultBridge", zkSyncWallet, mantleWallet, zkSyncGoerliNetwork, mantleGoerliNetwork, "usdt", "usdt"); + await registerToken(configure, "LnDefaultBridge", mantleWallet, zkSyncWallet, mantleGoerliNetwork, zkSyncGoerliNetwork, "usdt", "usdt"); +} + +async function registerRelayer(configure, contractName, srcWallet, dstWallet, srcNetwork, dstNetwork, srcToken, dstToken, increaseMargin) { + let srcTokenAddress = srcNetwork[srcToken]; + let dstTokenAddress = dstNetwork[dstToken]; + let srcDecimals = 18; + let dstDecimals = 18; + if (srcToken !== 'eth' && srcToken !== 'mnt') { + srcTokenAddress = configure[srcToken][srcNetwork.name]; + } + if (dstToken !== 'eth' && dstToken !== 'mnt') { + dstTokenAddress = configure[dstToken][dstNetwork.name]; + } + + let baseFeeAmount = "0.001"; + if (srcTokenAddress != kNativeTokenAddress) { + const sourceToken = await ethers.getContractAt("Erc20", srcTokenAddress, srcWallet); + srcDecimals = await sourceToken.decimals(); + baseFeeAmount = "20"; + } + if (dstTokenAddress != kNativeTokenAddress) { + const targetToken = await ethers.getContractAt("Erc20", dstTokenAddress, dstWallet); + dstDecimals = await targetToken.decimals(); + baseFeeAmount = "20"; + } + let baseFee = ethers.utils.parseUnits(baseFeeAmount, srcDecimals); + const liquidityFeeRate = 30; + + // default bridge + if (contractName == "LnDefaultBridge") { + const defaultAddress = srcNetwork.chainId === 280 ? configure.LnDefaultBridgeProxy.zkSync : configure.LnDefaultBridgeProxy.others; + // set source network + let margin = ethers.utils.parseUnits("1000000", dstDecimals); + let value = 0; + if (dstTokenAddress == kNativeTokenAddress) { + margin = ethers.utils.parseUnits("0.1", dstDecimals); + value = margin; + } + if (!increaseMargin) { + margin = 0; + value = 0; + } + const source = await ethers.getContractAt(contractName, defaultAddress, srcWallet); + await source.setProviderFee( + dstNetwork.chainId, + srcTokenAddress, + dstTokenAddress, + baseFee, + liquidityFeeRate + ); + // set target network + const target = await ethers.getContractAt(contractName, defaultAddress, dstWallet); + await target.depositProviderMargin( + srcNetwork.chainId, + srcTokenAddress, + dstTokenAddress, + margin, + {value: value}, + ); + } else { + let margin = ethers.utils.parseUnits("1000000", srcDecimals); + let value = 0; + if (srcTokenAddress == kNativeTokenAddress) { + margin = ethers.utils.parseUnits("0.1", dstDecimals); + value = margin; + } + if (!increaseMargin) { + margin = 0; + value = 0; + } + const source = await ethers.getContractAt(contractName, configure.LnOppositeBridgeProxy, srcWallet); + await source.updateProviderFeeAndMargin( + dstNetwork.chainId, + srcTokenAddress, + dstTokenAddress, + margin, + baseFee, + liquidityFeeRate, + {value: value} + ); + } + console.log(`finished register relayer: ${contractName}, ${srcNetwork.chainId}->${dstNetwork.chainId}, ${srcToken}->${dstToken}`); +} + +async function registerAllRelayer(configure, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet) { + //arb<>eth + const increaseMargin = false; + console.log("start to register arb<>eth relayer"); + await registerRelayer(configure, "LnOppositeBridge", arbWallet, goerliWallet, arbitrumGoerliNetwork, goerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, arbWallet, goerliNetwork, arbitrumGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnOppositeBridge", arbWallet, goerliWallet, arbitrumGoerliNetwork, goerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, arbWallet, goerliNetwork, arbitrumGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnOppositeBridge", arbWallet, goerliWallet, arbitrumGoerliNetwork, goerliNetwork, "eth", "eth", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, arbWallet, goerliNetwork, arbitrumGoerliNetwork, "eth", "eth", increaseMargin); + + // linea<>eth + console.log("start to register linea<>eth relayer"); + await registerRelayer(configure, "LnOppositeBridge", lineaWallet, goerliWallet, lineaGoerliNetwork, goerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, lineaWallet, goerliNetwork, lineaGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnOppositeBridge", lineaWallet, goerliWallet, lineaGoerliNetwork, goerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, lineaWallet, goerliNetwork, lineaGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnOppositeBridge", lineaWallet, goerliWallet, lineaGoerliNetwork, goerliNetwork, "eth", "eth", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, lineaWallet, goerliNetwork, lineaGoerliNetwork, "eth", "eth", increaseMargin); + + //arb<>linea + console.log("start to register linea<>arb relayer"); + await registerRelayer(configure, "LnDefaultBridge", arbWallet, lineaWallet, arbitrumGoerliNetwork, lineaGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", lineaWallet, arbWallet, lineaGoerliNetwork, arbitrumGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", arbWallet, lineaWallet, arbitrumGoerliNetwork, lineaGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", lineaWallet, arbWallet, lineaGoerliNetwork, arbitrumGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", arbWallet, lineaWallet, arbitrumGoerliNetwork, lineaGoerliNetwork, "eth", "eth", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", lineaWallet, arbWallet, lineaGoerliNetwork, arbitrumGoerliNetwork, "eth", "eth", increaseMargin); + + //arb<>mantle + console.log("start to register mantle<>arb relayer"); + await registerRelayer(configure, "LnDefaultBridge", arbWallet, mantleWallet, arbitrumGoerliNetwork, mantleGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, arbWallet, mantleGoerliNetwork, arbitrumGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", arbWallet, mantleWallet, arbitrumGoerliNetwork, mantleGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, arbWallet, mantleGoerliNetwork, arbitrumGoerliNetwork, "usdt", "usdt", increaseMargin); + + // mantle<>linea + console.log("start to register mantle<>linea relayer"); + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, lineaWallet, mantleGoerliNetwork, lineaGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", lineaWallet, mantleWallet, lineaGoerliNetwork, mantleGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, lineaWallet, mantleGoerliNetwork, lineaGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", lineaWallet, mantleWallet, lineaGoerliNetwork, mantleGoerliNetwork, "usdt", "usdt", increaseMargin); + + // mantle<>eth + console.log("start to register mantle<>eth relayer"); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, mantleWallet, goerliNetwork, mantleGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, goerliWallet, mantleGoerliNetwork, goerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, mantleWallet, goerliNetwork, mantleGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, goerliWallet, mantleGoerliNetwork, goerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, mantleWallet, goerliNetwork, mantleGoerliNetwork, "mnt", "mnt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, goerliWallet, mantleGoerliNetwork, goerliNetwork, "mnt", "mnt", increaseMargin); + + // arb<>zkSync + await registerRelayer(configure, "LnDefaultBridge", arbWallet, zkSyncWallet, arbitrumGoerliNetwork, zkSyncGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, arbWallet, zkSyncGoerliNetwork, arbitrumGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", arbWallet, zkSyncWallet, arbitrumGoerliNetwork, zkSyncGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, arbWallet, zkSyncGoerliNetwork, arbitrumGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", arbWallet, zkSyncWallet, arbitrumGoerliNetwork, zkSyncGoerliNetwork, "eth", "eth", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, arbWallet, zkSyncGoerliNetwork, arbitrumGoerliNetwork, "eth", "eth", increaseMargin); + // eth<>zkSync + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, zkSyncWallet, goerliNetwork, zkSyncGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, goerliWallet, zkSyncGoerliNetwork, goerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, zkSyncWallet, goerliNetwork, zkSyncGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, goerliWallet, zkSyncGoerliNetwork, goerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", goerliWallet, zkSyncWallet, goerliNetwork, zkSyncGoerliNetwork, "eth", "eth", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, goerliWallet, zkSyncGoerliNetwork, goerliNetwork, "eth", "eth", increaseMargin); + // mantle<>zkSync + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, zkSyncWallet, mantleGoerliNetwork, zkSyncGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, mantleWallet, zkSyncGoerliNetwork, mantleGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", mantleWallet, zkSyncWallet, mantleGoerliNetwork, zkSyncGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, mantleWallet, zkSyncGoerliNetwork, mantleGoerliNetwork, "usdt", "usdt", increaseMargin); + // linea<>zkSync + await registerRelayer(configure, "LnDefaultBridge", lineaWallet, zkSyncWallet, lineaGoerliNetwork, zkSyncGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, lineaWallet, zkSyncGoerliNetwork, lineaGoerliNetwork, "usdc", "usdc", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", lineaWallet, zkSyncWallet, lineaGoerliNetwork, zkSyncGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, lineaWallet, zkSyncGoerliNetwork, lineaGoerliNetwork, "usdt", "usdt", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", lineaWallet, zkSyncWallet, lineaGoerliNetwork, zkSyncGoerliNetwork, "eth", "eth", increaseMargin); + await registerRelayer(configure, "LnDefaultBridge", zkSyncWallet, lineaWallet, zkSyncGoerliNetwork, lineaGoerliNetwork, "eth", "eth", increaseMargin); +} + +async function mintToken(configure, tokenSymbol, network, wallet, to) { + const tokenAddress = configure[tokenSymbol][network.name]; + const token = await ethers.getContractAt("Erc20", tokenAddress, wallet); + const decimals = await token.decimals(); + const amount = ethers.utils.parseUnits("9000000", decimals); + console.log("start to mint token", tokenSymbol, amount); + await token.mint(to, amount); +} + +async function approveToken(configure, tokenSymbol, network, wallet) { + const tokenAddress = configure[tokenSymbol][network.name]; + const token = await ethers.getContractAt("Erc20", tokenAddress, wallet); + const decimals = await token.decimals(); + console.log("start to approve", tokenSymbol); + + const defaultAddress = network.chainId === 280 ? configure.LnDefaultBridgeProxy.zkSync : configure.LnDefaultBridgeProxy.others; + + await token.approve(defaultAddress, ethers.utils.parseUnits("10000000000000", decimals)); + await wait(5000); + if (network.chainId !== 280) { + await token.approve(configure.LnOppositeBridgeProxy, ethers.utils.parseUnits("10000000000000", decimals)); + await wait(5000); + } + console.log("finished to approve", tokenSymbol); +} + +async function mintAll(configure, relayer, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet) { + await mintToken(configure, "usdc", goerliNetwork, goerliWallet, relayer); + await mintToken(configure, "usdt", goerliNetwork, goerliWallet, relayer); + await mintToken(configure, "usdc", lineaGoerliNetwork, lineaWallet, relayer); + await mintToken(configure, "usdt", lineaGoerliNetwork, lineaWallet, relayer); + await mintToken(configure, "usdc", arbitrumGoerliNetwork, arbWallet, relayer); + await mintToken(configure, "usdt", arbitrumGoerliNetwork, arbWallet, relayer); + await mintToken(configure, "usdc", mantleGoerliNetwork, mantleWallet, relayer); + await mintToken(configure, "usdt", mantleGoerliNetwork, mantleWallet, relayer); + await mintToken(configure, "usdt", zkSyncGoerliNetwork, zkSyncWallet, relayer); + await mintToken(configure, "usdc", zkSyncGoerliNetwork, zkSyncWallet, relayer); +} + +async function approveAll(configure, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet) { + await approveToken(configure, "usdc", goerliNetwork, goerliWallet); + await approveToken(configure, "usdt", goerliNetwork, goerliWallet); + await approveToken(configure, "mnt", goerliNetwork, goerliWallet); + await approveToken(configure, "usdc", lineaGoerliNetwork, lineaWallet); + await approveToken(configure, "usdt", lineaGoerliNetwork, lineaWallet); + await approveToken(configure, "usdc", arbitrumGoerliNetwork, arbWallet); + await approveToken(configure, "usdt", arbitrumGoerliNetwork, arbWallet); + await approveToken(configure, "usdc", mantleGoerliNetwork, mantleWallet); + await approveToken(configure, "usdt", mantleGoerliNetwork, mantleWallet); + await approveToken(configure, "usdt", zkSyncGoerliNetwork, zkSyncWallet); + await approveToken(configure, "usdc", zkSyncGoerliNetwork, zkSyncWallet); +} + +// 2. deploy mapping token factory +async function main() { + const pathConfig = "./address/ln-dev.json"; + const configure = JSON.parse( + fs.readFileSync(pathConfig, "utf8") + ); + + const arbWallet = wallet(arbitrumGoerliNetwork.url); + const lineaWallet = wallet(lineaGoerliNetwork.url); + const goerliWallet = wallet(goerliNetwork.url); + const mantleWallet = wallet(mantleGoerliNetwork.url); + const zkSyncWallet = wallet(zkSyncGoerliNetwork.url); + + // set messager service + //await connectAll(configure, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet); + //await registerAllToken(configure, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet); + //await mintAll(configure, relayer, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet); + //await approveAll(configure, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet); + //await registerAllRelayer(configure, arbWallet, lineaWallet, goerliWallet, mantleWallet, zkSyncWallet); + console.log("finished!"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/helix-contract/deploy/deploy_ln_create2.js b/helix-contract/deploy/deploy_ln_create2.js new file mode 100644 index 00000000..e30ccbc9 --- /dev/null +++ b/helix-contract/deploy/deploy_ln_create2.js @@ -0,0 +1,44 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); + +var Create2 = require("./create2.js"); + +const privateKey = process.env.PRIKEY + +function wallet(url) { + const provider = new ethers.providers.JsonRpcProvider(url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +async function deployCreate2Deployer(networkUrl, version) { + const salt = ethers.utils.hexZeroPad(ethers.utils.hexlify(ethers.utils.toUtf8Bytes(version)), 32); + const create2Contract = await ethers.getContractFactory("Create2Deployer", wallet); + const bytecode = Create2.getDeployedBytecode(create2Contract, [], []); + const w = wallet(networkUrl); + const unsignedTransaction = { + from: w.address, + to: "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7", + data: `${salt}${bytecode.slice(2)}` + }; + const tx = await w.sendTransaction(unsignedTransaction); + console.log(`deploy create2 tx: ${tx.hash}, salt: ${salt}`); + return; +} + +// 2. deploy mapping token factory +async function main() { + //await deployCreate2Deployer('https://rpc.ankr.com/eth_goerli', 'v1.0.0'); + //await deployCreate2Deployer('https://goerli-rollup.arbitrum.io/rpc', 'v1.0.0'); + await deployCreate2Deployer('https://rpc.testnet.mantle.xyz', 'v1.0.0'); + await deployCreate2Deployer('https://rpc.goerli.linea.build', 'v1.0.0'); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); + diff --git a/helix-contract/deploy/deploy_ln_logic.js b/helix-contract/deploy/deploy_ln_logic.js new file mode 100644 index 00000000..96be6e5f --- /dev/null +++ b/helix-contract/deploy/deploy_ln_logic.js @@ -0,0 +1,89 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); + +var Create2 = require("./create2.js"); + +const privateKey = process.env.PRIKEY + +const arbitrumNetwork = { + url: "https://goerli-rollup.arbitrum.io/rpc", + deployer: "0xbe6b2860d3c17a719be0A4911EA0EE689e8357f3", +}; + +const goerliNetwork = { + url: "https://rpc.ankr.com/eth_goerli", + deployer: "0xbe6b2860d3c17a719be0A4911EA0EE689e8357f3", +}; + +const mantleNetwork = { + url: "https://rpc.testnet.mantle.xyz", + deployer: "0xbe6b2860d3c17a719be0A4911EA0EE689e8357f3", +}; + +const lineaNetwork = { + url: "https://rpc.goerli.linea.build", + deployer: "0xbe6b2860d3c17a719be0A4911EA0EE689e8357f3", +}; + +function wallet(url) { + const provider = new ethers.providers.JsonRpcProvider(url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +async function deployLnDefaultBridge(wallet, deployerAddress, salt) { + const bridgeContract = await ethers.getContractFactory("LnDefaultBridge", wallet); + const bytecode = Create2.getDeployedBytecode(bridgeContract, [], []); + const address = await Create2.deploy(deployerAddress, wallet, bytecode, salt); + console.log("finish to deploy ln default bridge logic, address: ", address); + return address; +} + +async function deployLnOppositeBridge(wallet, deployerAddress, salt) { + const bridgeContract = await ethers.getContractFactory("LnOppositeBridge", wallet); + const bytecode = Create2.getDeployedBytecode(bridgeContract, [], []); + const address = await Create2.deploy(deployerAddress, wallet, bytecode, salt); + console.log("finish to deploy ln opposite bridge logic, address: ", address); + return address; +} + +// 2. deploy mapping token factory +async function main() { + const networks = [goerliNetwork, mantleNetwork, arbitrumNetwork, lineaNetwork]; + for (const network of networks) { + const w = wallet(network.url); + //const logicAddress = await deployLnDefaultBridge(w, network.deployer, "ln-default-logic-v1.0.0"); + const logicAddress = await deployLnOppositeBridge(w, network.deployer, "ln-opposite-logic-v1.0.0"); + console.log("finish to deploy logic contract, network is: ", network.url); + } + return; + //const network = goerliNetwork; + //const network = mantleNetwork; + //const network = arbitrumNetwork; + //const network = lineaNetwork; + //const w = wallet(network.url); + //await deployLnDefaultBridge(w, network.deployer, "ln-default-logic-v1.0.3"); + //await deployLnOppositeBridge(w, network.deployer, "ln-opposite-logic-v1.0.2"); + for (const network of networks) { + // deploy logic bridge + const w = wallet(network.url); + const proxyAdmin = await ethers.getContractAt("ProxyAdmin", "0xE3979fFa68BBa1F53c6F502c8F5788B370d28730", w); + + // upgrade default bridge + const logicAddress = await deployLnDefaultBridge(w, network.deployer, "ln-default-logic-v1.0.7"); + await proxyAdmin.upgrade("0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", logicAddress); + // upgrade opposite bridge + //const logicAddress = await deployLnOppositeBridge(w, network.deployer, "ln-opposite-logic-v1.0.6"); + //await proxyAdmin.upgrade("0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", logicAddress); + console.log("finished"); + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); + diff --git a/helix-contract/deploy/deploy_ln_messager.js b/helix-contract/deploy/deploy_ln_messager.js new file mode 100644 index 00000000..daa475e7 --- /dev/null +++ b/helix-contract/deploy/deploy_ln_messager.js @@ -0,0 +1,179 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); + +var ProxyDeployer = require("./proxy.js"); + +const privateKey = process.env.PRIKEY + +const lineaNetwork = { + url: "https://rpc.goerli.linea.build", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 59140, + lzChainId: 10157, + // layerzero used + endpoint: "0x6aB5Ae6822647046626e83ee6dB8187151E1d5ab", + messageService: "0xC499a572640B64eA1C8c194c43Bc3E19940719dC", + axGateway: "0xe432150cce91c13a887f7D836923d5597adD8E31", + axGasService: "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", + axName: "linea", +}; + +// zkSync should be deployed first +const zkSyncNetwork = { + url: "https://zksync2-testnet.zksync.dev", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 280, + lzChainId: 10165, + //endpoint: "0x093D2CF57f764f09C3c2Ac58a42A2601B8C79281", + layerzeroMessager: "0x7e303b0A3F08F9fa5F5629Abb998B8Deba89049B", +}; + +const arbitrumNetwork = { + url: "https://goerli-rollup.arbitrum.io/rpc", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 421613, + lzChainId: 10143, + endpoint: "0x6aB5Ae6822647046626e83ee6dB8187151E1d5ab", + axGateway: "0xe432150cce91c13a887f7D836923d5597adD8E31", + axGasService: "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", + axName: "arbitrum", +}; + +const goerliNetwork = { + url: "https://rpc.ankr.com/eth_goerli", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 5, + lzChainId: 10121, + endpoint: "0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23", + messageService: "0x70BaD09280FD342D02fe64119779BC1f0791BAC2", + inbox: "0x6BEbC4925716945D46F0Ec336D5C2564F419682C", + axGateway: "0xe432150cce91c13a887f7D836923d5597adD8E31", + axGasService: "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", + axName: "ethereum-2", +}; + +const mantleNetwork = { + url: "https://rpc.testnet.mantle.xyz", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 5001, + lzChainId: 10181, + endpoint: "0x2cA20802fd1Fd9649bA8Aa7E50F0C82b479f35fe", + axGateway: "0xe432150cce91c13a887f7D836923d5597adD8E31", + axGasService: "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", + axName: "mantle", +} + +function wait(ms) { + return new Promise(resolve => setTimeout(() => resolve(), ms)); +}; + +function wallet(url) { + const provider = new ethers.providers.JsonRpcProvider(url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +async function deployContract(wallet, name, ...args) { + const messagerContract = await ethers.getContractFactory(name, wallet); + const messager = await messagerContract.deploy(...args); + await messager.deployed(); + console.log("finish to deploy service", name, messager.address); + return messager; +} + +// 2. deploy mapping token factory +async function main() { + const arbWallet = wallet(arbitrumNetwork.url); + const lineaWallet = wallet(lineaNetwork.url); + const goerliWallet = wallet(goerliNetwork.url); + const mantleWallet = wallet(mantleNetwork.url); + const zkSyncWallet = wallet(zkSyncNetwork.url); + + // deploy arb<>eth + console.log("deploy arb <> eth messager"); + const Eth2ArbReceiveService = await deployContract(arbWallet, "Eth2ArbReceiveService", arbitrumNetwork.dao, goerliNetwork.chainId); + const Eth2ArbSendService = await deployContract(goerliWallet, "Eth2ArbSendService", goerliNetwork.dao, goerliNetwork.inbox, arbitrumNetwork.chainId); + await wait(10000); + await Eth2ArbReceiveService.setRemoteMessager(Eth2ArbSendService.address); + await Eth2ArbSendService.setRemoteMessager(Eth2ArbReceiveService.address); + await wait(10000); + // deploy linea<>eth + console.log("deploy linea <> eth messager"); + const Eth2LineaReceiveService = await deployContract(lineaWallet, "Eth2LineaReceiveService", lineaNetwork.dao, lineaNetwork.messageService, goerliNetwork.chainId); + const Eth2LineaSendService = await deployContract(goerliWallet, "Eth2LineaSendService", goerliNetwork.dao, goerliNetwork.messageService, lineaNetwork.chainId); + await wait(10000); + await Eth2LineaReceiveService.setRemoteMessager(Eth2LineaSendService.address); + await Eth2LineaSendService.setRemoteMessager(Eth2LineaReceiveService.address); + await wait(10000); + // deploy layerZero + console.log("deploy layerzero messager"); + const lzGoerli = await deployContract(goerliWallet, "LayerZeroMessager", goerliNetwork.dao, goerliNetwork.endpoint); + const lzArbitrum = await deployContract(arbWallet, "LayerZeroMessager", arbitrumNetwork.dao, arbitrumNetwork.endpoint); + const lzLinea = await deployContract(lineaWallet, "LayerZeroMessager", lineaNetwork.dao, lineaNetwork.endpoint); + const lzMantle = await deployContract(mantleWallet, "LayerZeroMessager", mantleNetwork.dao, mantleNetwork.endpoint); + // zkSync has been deployed + const lzZkSync = await ethers.getContractAt("LayerZeroMessager", zkSyncNetwork.layerzeroMessager, zkSyncWallet); + console.log("confgure layerzero messager"); + await lzGoerli.setRemoteMessager(arbitrumNetwork.chainId, arbitrumNetwork.lzChainId, lzArbitrum.address); + await lzLinea.setRemoteMessager(arbitrumNetwork.chainId, arbitrumNetwork.lzChainId, lzArbitrum.address); + await lzMantle.setRemoteMessager(arbitrumNetwork.chainId, arbitrumNetwork.lzChainId, lzArbitrum.address); + await lzZkSync.setRemoteMessager(arbitrumNetwork.chainId, arbitrumNetwork.lzChainId, lzArbitrum.address); + + await lzGoerli.setRemoteMessager(lineaNetwork.chainId, lineaNetwork.lzChainId, lzLinea.address); + await lzArbitrum.setRemoteMessager(lineaNetwork.chainId, lineaNetwork.lzChainId, lzLinea.address); + await lzMantle.setRemoteMessager(lineaNetwork.chainId, lineaNetwork.lzChainId, lzLinea.address); + await lzZkSync.setRemoteMessager(lineaNetwork.chainId, lineaNetwork.lzChainId, lzLinea.address); + + await lzGoerli.setRemoteMessager(mantleNetwork.chainId, mantleNetwork.lzChainId, lzMantle.address); + await lzArbitrum.setRemoteMessager(mantleNetwork.chainId, mantleNetwork.lzChainId, lzMantle.address); + await lzLinea.setRemoteMessager(mantleNetwork.chainId, mantleNetwork.lzChainId, lzMantle.address); + await lzZkSync.setRemoteMessager(mantleNetwork.chainId, mantleNetwork.lzChainId, lzMantle.address); + + await lzGoerli.setRemoteMessager(zkSyncNetwork.chainId, zkSyncNetwork.lzChainId, lzZkSync.address); + await lzArbitrum.setRemoteMessager(zkSyncNetwork.chainId, zkSyncNetwork.lzChainId, lzZkSync.address); + await lzLinea.setRemoteMessager(zkSyncNetwork.chainId, zkSyncNetwork.lzChainId, lzZkSync.address); + await lzMantle.setRemoteMessager(zkSyncNetwork.chainId, zkSyncNetwork.lzChainId, lzZkSync.address); + + await lzArbitrum.setRemoteMessager(goerliNetwork.chainId, goerliNetwork.lzChainId, lzGoerli.address); + await lzLinea.setRemoteMessager(goerliNetwork.chainId, goerliNetwork.lzChainId, lzGoerli.address); + await lzMantle.setRemoteMessager(goerliNetwork.chainId, goerliNetwork.lzChainId, lzGoerli.address); + await lzZkSync.setRemoteMessager(goerliNetwork.chainId, goerliNetwork.lzChainId, lzGoerli.address); + // deploy axelar + console.log("deploy axelar messager"); + const axGoerli = await deployContract(goerliWallet, "AxelarMessager", goerliNetwork.dao, goerliNetwork.axGateway, goerliNetwork.axGasService); + const axArbitrum = await deployContract(arbWallet, "AxelarMessager", arbitrumNetwork.dao, arbitrumNetwork.axGateway, arbitrumNetwork.axGasService); + const axLinea = await deployContract(lineaWallet, "AxelarMessager", lineaNetwork.dao, lineaNetwork.axGateway, lineaNetwork.axGasService); + const axMantle = await deployContract(mantleWallet, "AxelarMessager", mantleNetwork.dao, mantleNetwork.axGateway, mantleNetwork.axGasService); + console.log("configure axelar messager"); + await axGoerli.setRemoteMessager(arbitrumNetwork.chainId, arbitrumNetwork.axName, axArbitrum.address); + await axArbitrum.setRemoteMessager(goerliNetwork.chainId, goerliNetwork.axName, axGoerli.address); + await axLinea.setRemoteMessager(goerliNetwork.chainId, goerliNetwork.axName, axGoerli.address); + await axMantle.setRemoteMessager(goerliNetwork.chainId, goerliNetwork.axName, axGoerli.address); + + await axGoerli.setRemoteMessager(lineaNetwork.chainId, lineaNetwork.axName, axLinea.address); + await axArbitrum.setRemoteMessager(lineaNetwork.chainId, lineaNetwork.axName, axLinea.address); + await axLinea.setRemoteMessager(arbitrumNetwork.chainId, arbitrumNetwork.axName, axArbitrum.address); + await axMantle.setRemoteMessager(arbitrumNetwork.chainId, arbitrumNetwork.axName, axArbitrum.address); + + await axGoerli.setRemoteMessager(mantleNetwork.chainId, mantleNetwork.axName, axMantle.address); + await axArbitrum.setRemoteMessager(mantleNetwork.chainId, mantleNetwork.axName, axMantle.address); + await axLinea.setRemoteMessager(mantleNetwork.chainId, mantleNetwork.axName, axMantle.address); + await axMantle.setRemoteMessager(lineaNetwork.chainId, lineaNetwork.axName, axLinea.address); + + /* + // deploy debug messager + await deployContract(goerliWallet, "DebugMessager"); + await deployContract(arbWallet, "DebugMessager"); + await deployContract(lineaWallet, "DebugMessager"); + await deployContract(mantleWallet, "DebugMessager"); + */ + console.log("finished!"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/helix-contract/deploy/deploy_ln_pause.js b/helix-contract/deploy/deploy_ln_pause.js new file mode 100644 index 00000000..ce2a2dfb --- /dev/null +++ b/helix-contract/deploy/deploy_ln_pause.js @@ -0,0 +1,97 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); + +var ProxyDeployer = require("./proxy.js"); + +const privateKey = process.env.PRIKEY + +const networks = { + lineaGoerli: { + url: "https://rpc.goerli.linea.build", + chainId: 59140, + defaultBridgeProxy: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridgeProxy: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + }, + arbitrumGoerli: { + url: "https://goerli-rollup.arbitrum.io/rpc", + chainId: 421613, + defaultBridgeProxy: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridgeProxy: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + }, + goerli: { + url: "https://rpc.ankr.com/eth_goerli", + chainId: 5, + defaultBridgeProxy: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridgeProxy: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + }, + mantleGoerli: { + url: "https://rpc.testnet.mantle.xyz", + chainId: 5001, + defaultBridgeProxy: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridgeProxy: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + }, +}; + +function wait(ms) { + return new Promise(resolve => setTimeout(() => resolve(), ms)); +}; + +function wallet(url) { + const provider = new ethers.providers.JsonRpcProvider(url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +async function pause(isDefault, network) { + const w = wallet(network.url); + if (isDefault) { + const bridge = await ethers.getContractAt("LnDefaultBridge", network.defaultBridgeProxy, w); + await bridge.pause(); + } else { + const bridge = await ethers.getContractAt("LnOppositeBridge", network.oppositeBridgeProxy, w); + await bridge.pause(); + } +} + +async function unpause(isDefault, network) { + const w = wallet(network.url); + if (isDefault) { + const bridge = await ethers.getContractAt("LnDefaultBridge", network.defaultBridgeProxy, w); + await bridge.unpause(); + } else { + const bridge = await ethers.getContractAt("LnOppositeBridge", network.oppositeBridgeProxy, w); + await bridge.unpause(); + } +} + +async function pauseAll() { + for (let network in networks) { + await pause(true, networks[network]); + } + for (let network in networks) { + await pause(false, networks[network]); + } + } +} + +async function unpauseAll() { + for (let network in networks) { + await unpause(true, networks[network]); + } + for (let network in networks) { + await unpause(false, networks[network]); + } +} + +async function main() { + await pauseAll(); + console.log("finished!"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/helix-contract/deploy/deploy_ln_proxy.js b/helix-contract/deploy/deploy_ln_proxy.js new file mode 100644 index 00000000..82354bad --- /dev/null +++ b/helix-contract/deploy/deploy_ln_proxy.js @@ -0,0 +1,113 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); +const fs = require("fs"); + +var ProxyDeployer = require("./proxy.js"); + +const privateKey = process.env.PRIKEY + +const lineaGoerliNetwork = { + name: "linea-goerli", + url: "https://rpc.goerli.linea.build", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", +}; + +const arbitrumGoerliNetwork = { + name: "arbitrum-goerli", + url: "https://goerli-rollup.arbitrum.io/rpc", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", +}; + +const goerliNetwork = { + name: "goerli", + url: "https://rpc.ankr.com/eth_goerli", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", +}; + +const mantleGoerliNetwork = { + name: "mantle-goerli", + url: "https://rpc.testnet.mantle.xyz", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", +}; + +function wallet(url) { + const provider = new ethers.providers.JsonRpcProvider(url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +async function deployLnDefaultBridgeProxy(wallet, salt, dao, proxyAdminAddress, logicAddress, deployer) { + const bridgeContract = await ethers.getContractFactory("LnDefaultBridge", wallet); + const lnBridgeProxy = await ProxyDeployer.deployProxyContract2( + deployer, + salt, + proxyAdminAddress, + bridgeContract, + logicAddress, + [dao], + wallet); + console.log("finish to deploy ln default bridge proxy, address:", lnBridgeProxy); + return lnBridgeProxy; +} + +async function deployLnOppositeBridgeProxy(wallet, salt, dao, proxyAdminAddress, logicAddress, deployer) { + const bridgeContract = await ethers.getContractFactory("LnOppositeBridge", wallet); + const lnBridgeProxy = await ProxyDeployer.deployProxyContract2( + deployer, + salt, + proxyAdminAddress, + bridgeContract, + logicAddress, + [dao], + wallet); + console.log("finish to deploy ln opposite bridge proxy, address:", lnBridgeProxy); + return lnBridgeProxy; +} + +async function deploy() { + // address path + const pathConfig = "./address/ln-dev.json"; + const configure = JSON.parse( + fs.readFileSync(pathConfig, "utf8") + ); + + const chains = [goerliNetwork, lineaGoerliNetwork, arbitrumGoerliNetwork, mantleGoerliNetwork]; + for (const chain of chains) { + const w = wallet(chain.url); + const proxyAdmin = configure.ProxyAdmin.others; + const defaultLogicAddress = configure.LnDefaultBridgeLogic.others; + const oppositeLogicAddress = configure.LnOppositeBridgeLogic; + const deployer = configure.deployer; + let proxyAddress = await deployLnDefaultBridgeProxy( + w, + "ln-default-v1.1.2", + chain.dao, + proxyAdmin, + defaultLogicAddress, + deployer, + ); + console.log("deploy proxy success", proxyAddress); + proxyAddress = await deployLnOppositeBridgeProxy( + w, + "ln-opposite-v1.1.2", + chain.dao, + proxyAdmin, + oppositeLogicAddress, + deployer, + ); + console.log("deploy proxy success", proxyAddress); + } +} + +async function main() { + await deploy(); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); + diff --git a/helix-contract/deploy/deploy_ln_switch_messager.js b/helix-contract/deploy/deploy_ln_switch_messager.js new file mode 100644 index 00000000..60e7444c --- /dev/null +++ b/helix-contract/deploy/deploy_ln_switch_messager.js @@ -0,0 +1,103 @@ +var ProxyDeployer = require("./proxy.js"); + +const privateKey = process.env.PRIKEY + +const lineaNetwork = { + url: "https://rpc.goerli.linea.build", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 59140, + defaultBridgeProxy: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridgeProxy: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + axelarMessager: "0xC39491aBf9EA6a81b1cd7e13869fd8b1204fe8A0", + debugMessager: "0x25Ce9C92526D002a11aBA105563a713357429A99", +}; + +const arbitrumNetwork = { + url: "https://goerli-rollup.arbitrum.io/rpc", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 421613, + defaultBridgeProxy: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridgeProxy: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + axelarMessager: "0x54269881797A501017Fa161f326469947D138877", + debugMessager: "0x7f431D5ba484Eb96811C469BE3DcbB23c67ae4a8", +}; + +const goerliNetwork = { + url: "https://rpc.ankr.com/eth_goerli", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 5, + defaultBridgeProxy: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridgeProxy: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + axelarMessager: "0x6B2F3bc0A01cf07F69e906465c2a1c7ea83fd49d", + debugMessager: "0x2e8D237226041FAFe3F66b6cfc54b064923D454E", +}; + +const mantleNetwork = { + url: "https://rpc.testnet.mantle.xyz", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + chainId: 5001, + defaultBridgeProxy: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridgeProxy: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + axelarMessager: "0xA50Ad777C803206F041622101f261168A0763066", + debugMessager: "0x84f7a56483C100ECb12CbB4A31b7873dAE0d8E9B", +}; + +function wallet(url) { + const provider = new ethers.providers.JsonRpcProvider(url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +async function switchMessager(bridgeName, sourceNetwork, sourceMessager, sourceWallet, targetNetwork, targetMessager, targetWallet) { + if (bridgeName == 'LnDefaultBridge') { + const sourceBridge = await ethers.getContractAt(bridgeName, sourceNetwork.defaultBridgeProxy, sourceWallet); + await sourceBridge.setSendService(targetNetwork.chainId, targetNetwork.defaultBridgeProxy, sourceMessager); + console.log("source switch finished"); + const targetBridge = await ethers.getContractAt(bridgeName, targetNetwork.defaultBridgeProxy, targetWallet); + await targetBridge.setReceiveService(sourceNetwork.chainId, sourceNetwork.defaultBridgeProxy, targetMessager); + console.log("target switch finished"); + } else { + const sourceBridge = await ethers.getContractAt(bridgeName, sourceNetwork.oppositeBridgeProxy, sourceWallet); + await sourceBridge.setReceiveService(targetNetwork.chainId, targetNetwork.oppositeBridgeProxy, sourceMessager); + console.log("source switch finished"); + const targetBridge = await ethers.getContractAt(bridgeName, targetNetwork.oppositeBridgeProxy, targetWallet); + await targetBridge.setSendService(sourceNetwork.chainId, sourceNetwork.oppositeBridgeProxy, targetMessager); + console.log("target switch finished"); + } +} + +async function switchToDebug(bridgeName, network, remoteNetwork) { + const w = wallet(network.url); + if (bridgeName == 'LnDefaultBridge') { + const bridge = await ethers.getContractAt(bridgeName, network.defaultBridgeProxy, w); + await bridge.setSendService(remoteNetwork.chainId, remoteNetwork.defaultBridgeProxy, network.debugMessager); + await bridge.setReceiveService(remoteNetwork.chainId, remoteNetwork.defaultBridgeProxy, network.debugMessager); + } else { + const bridge = await ethers.getContractAt(bridgeName, network.oppositeBridgeProxy, w); + await bridge.setReceiveService(remoteNetwork.chainId, remoteNetwork.oppositeBridgeProxy, network.debugMessager); + } + console.log("switch to debug messager finished"); +} + +async function deploy() { + const goerliWallet = wallet(goerliNetwork.url); + const mantleWallet = wallet(mantleNetwork.url); + //await switchToDebug("LnDefaultBridge", mantleNetwork, goerliNetwork); + //return; + await switchMessager( + "LnDefaultBridge", + goerliNetwork, goerliNetwork.axelarMessager, goerliWallet, + mantleNetwork, mantleNetwork.axelarMessager, mantleWallet); +} + +async function main() { + await deploy(); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); + diff --git a/helix-contract/deploy/deploy_ln_test.js b/helix-contract/deploy/deploy_ln_test.js new file mode 100644 index 00000000..190e20e8 --- /dev/null +++ b/helix-contract/deploy/deploy_ln_test.js @@ -0,0 +1,482 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); + +const privateKey = process.env.PRIKEY + +const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; + +const networks = { + "goerli": { + url: "https://rpc.ankr.com/eth_goerli", + defaultBridge: "0x258F81A0B59e0fD84604E3e9fc1b70718927c239", + oppositeBridge: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + chainId: 5, + usdc: "0x1a70127284B774fF4A4dbfe0115114642f0eca65", + usdt: "0x2303e4d55BF16a897Cb5Ab71c6225399509d9314", + }, + "arbitrum": { + url: "https://goerli-rollup.arbitrum.io/rpc", + defaultBridge: "0x258F81A0B59e0fD84604E3e9fc1b70718927c239", + oppositeBridge: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + chainId: 421613, + usdc: "0x39dE82E1d9B8F62E11022FC3FC127a82F93fE47E", + usdt: "0x6d828718c1097A4C573bc25c638Cc05bF10dFeAF", + }, + "linea": { + url: "https://rpc.goerli.linea.build", + defaultBridge: "0x258F81A0B59e0fD84604E3e9fc1b70718927c239", + oppositeBridge: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + chainId: 59140, + usdc: "0xb5E028f980dF5533cB0e8F04530B76637383d993", + usdt: "0xBC1A2f123Dc9CD2ec8d3cE42eF16c28F3C9bA686", + }, + "mantle": { + url: "https://rpc.testnet.mantle.xyz", + defaultBridge: "0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", + oppositeBridge: "0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", + chainId: 5001, + usdc: "0x0258Eb547bFEd540ed17843658C018569fe1E328", + usdt: "0x5F8D4232367759bCe5d9488D3ade77FCFF6B9b6B", + }, +}; + +function wait(ms) { + return new Promise(resolve => setTimeout(() => resolve(), ms)); +}; + +function getProviderKey( + remoteChainId, + provider, + sourceToken, + remoteToken +) { + const encode = ethers.utils.solidityPack([ + "uint256", + "address", + "address", + "address", + ], [remoteChainId, provider, sourceToken, remoteToken]); + return ethUtil.keccak256(encode); +} + +function getLockKey( + remoteChainId, + sourceToken, + remoteToken +) { + const encode = ethers.utils.solidityPack([ + "uint256", + "address", + "address", + ], [remoteChainId, sourceToken, remoteToken]); + return ethUtil.keccak256(encode); +} + +async function defaultTransferAndLockMargin( + wallet, + bridgeAddress, + remoteChainId, + sourceToken, + targetToken, + amount) { + const bridge = await ethers.getContractAt("LnDefaultBridge", bridgeAddress, wallet); + const expectedFee = await bridge.totalFee( + remoteChainId, + wallet.address, + sourceToken, + targetToken, + amount); + console.log("expect fee is", expectedFee); + const providerInfo = await bridge.srcProviders(getProviderKey(remoteChainId, wallet.address, sourceToken, targetToken)); + const expectedWithdrawNonce = providerInfo.config.withdrawNonce; + console.log("expect withdraw nonce is", expectedWithdrawNonce); + //const tx = await bridge.callStatic.transferAndLockMargin( + const tx = await bridge.transferAndLockMargin( + [ + remoteChainId, + wallet.address, + sourceToken, + targetToken, + providerInfo.lastTransferId, + expectedFee, + expectedWithdrawNonce, + ], + amount, + wallet.address, + ); + console.log(tx); +} + +async function oppositeTransferAndLockMargin( + wallet, + bridgeAddress, + remoteChainId, + sourceToken, + targetToken, + amount) { + const bridge = await ethers.getContractAt("LnOppositeBridge", bridgeAddress, wallet); + const expectedFee = await bridge.totalFee( + remoteChainId, + wallet.address, + sourceToken, + targetToken, + amount); + console.log("expect fee is", expectedFee); + const providerInfo = await bridge.srcProviders(getProviderKey(remoteChainId, wallet.address, sourceToken, targetToken)); + const expectedMargin = providerInfo.config.margin; + console.log("expect margin is", expectedMargin); + //const tx = await bridge.callStatic.transferAndLockMargin( + const tx = await bridge.transferAndLockMargin( + [ + remoteChainId, + wallet.address, + sourceToken, + targetToken, + providerInfo.lastTransferId, + expectedFee, + expectedMargin, + ], + amount, + wallet.address, + ); + console.log(tx); +} + +async function defaultRelay( + remoteChainId, + fromWallet, + toWallet, + fromBridgeAddress, + toBridgeAddress, + sourceToken, + targetToken, + amount, + previousTransferId, + expectedTransferId, +) { + const fromBridge = await ethers.getContractAt("LnDefaultBridge", fromBridgeAddress, fromWallet); + const lockInfo = await fromBridge.lockInfos(expectedTransferId); + const toBridge = await ethers.getContractAt("LnDefaultBridge", toBridgeAddress, toWallet); + //const tx = await bridge.callStatic.relay( + await toBridge.transferAndReleaseMargin( + [ + previousTransferId, + toWallet.address, + sourceToken, + targetToken, + amount, + lockInfo.timestamp, + toWallet.address, + ], + remoteChainId, + expectedTransferId, + ); + //console.log(tx); +} + +async function oppositeRelay( + remoteChainId, + fromWallet, + toWallet, + fromBridgeAddress, + toBridgeAddress, + sourceToken, + targetToken, + amount, + previousTransferId, + expectedTransferId, +) { + const fromBridge = await ethers.getContractAt("LnOppositeBridge", fromBridgeAddress, fromWallet); + const lockInfo = await fromBridge.lockInfos(expectedTransferId); + const toBridge = await ethers.getContractAt("LnOppositeBridge", toBridgeAddress, toWallet); + //const tx = await bridge.callStatic.relay( + await toBridge.transferAndReleaseMargin( + [ + previousTransferId, + toWallet.address, + sourceToken, + targetToken, + amount, + lockInfo.timestamp, + toWallet.address, + ], + remoteChainId, + expectedTransferId, + ); + //console.log(tx); +} + +async function slash( + wallet, + bridgeAddress, + provider, + sourceToken, + targetToken, + previousTransferId, + timestamp, + receiver, + amount, + expectedTransferId, +) { + const bridge = await ethers.getContractAt("Linea2EthTarget", bridgeAddress, wallet); + const cost = ethers.utils.parseEther("0.0003"); + //return; + + //const tx = await bridge.callStatic.slashAndRemoteRefund( + await bridge.slashAndRemoteRefund( + [ + previousTransferId, + provider, + sourceToken, + targetToken, + amount, + timestamp, + receiver, + ], + expectedTransferId, + {value: cost }, + ); + //console.log(tx); +} + +async function requestWithdrawMargin( + wallet, + bridgeAddress, + lastTransferId, + sourceToken, + amount, +) { + const bridge = await ethers.getContractAt("Linea2EthTarget", bridgeAddress, wallet); + const cost = 0; + //const tx = await bridge.callStatic.requestWithdrawMargin( + await bridge.requestWithdrawMargin( + lastTransferId, + sourceToken, + amount, + {value: cost }, + ); + //console.log(tx); +} + +function wallet(network) { + const provider = new ethers.providers.JsonRpcProvider(network.url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +async function withdraw(bridgeType, from, to, sourceToken, targetToken, amount, fee, extParams) { + const sourceTokenContract = await ethers.getContractAt("Erc20", sourceToken, from.wallet); + if (bridgeType == "default") { + const srcDecimals = await sourceTokenContract.decimals(); + const formatedAmount = ethers.utils.parseUnits(amount, srcDecimals); + // withdraw from source bridge + const bridge = await ethers.getContractAt("LnDefaultBridge", from.defaultBridge, from.wallet); + await bridge.requestWithdrawMargin( + to.chainId, + sourceToken, + targetToken, + formatedAmount, + extParams, + { value: fee }, + ); + } else { + const dstDecimals = await sourceTokenContract.decimals(); + const formatedAmount = ethers.utils.parseUnits(amount, dstDecimals); + // withdraw from target bridge + const srcBridge = await ethers.getContractAt("LnOppositeBridge", from.oppositeBridge, from.wallet); + const providerInfo = await srcBridge.srcProviders(getProviderKey(to.chainId, from.wallet.address, sourceToken, targetToken)); + const bridge = await ethers.getContractAt("LnOppositeBridge", to.oppositeBridge, to.wallet); + await bridge.requestWithdrawMargin( + from.chainId, + providerInfo.lastTransferId, + sourceToken, + targetToken, + formatedAmount, + extParams, + { value: fee }, + ); + } +} + +async function transfer(bridgeType, from, to, sourceToken, targetToken, amount) { + const sourceTokenContract = await ethers.getContractAt("Erc20", sourceToken, from.wallet); + const decimals = await sourceTokenContract.decimals(); + const formatedAmount = ethers.utils.parseUnits(amount, decimals); + if (bridgeType == "default") { + const bridge = await ethers.getContractAt("LnDefaultBridge", from.defaultBridge, from.wallet); + const previousInfo = await bridge.srcProviders(getProviderKey(to.chainId, from.wallet.address, sourceToken, targetToken)); + await defaultTransferAndLockMargin( + from.wallet, + from.defaultBridge, + to.chainId, + sourceToken, + targetToken, + formatedAmount + ); + console.log("[default] transfer and lock margin successed"); + await wait(10000); + // query and relay + /* + const providerInfo = await bridge.srcProviders(getProviderKey(to.chainId, from.wallet.address, sourceToken, targetToken)); + const expectedTransferId = providerInfo.lastTransferId; + await defaultRelay( + from.chainId, + from.wallet, + to.wallet, + from.defaultBridge, + to.defaultBridge, + sourceToken, + targetToken, + formatedAmount, + previousInfo.lastTransferId, + expectedTransferId, + ); + console.log("[default] relay and release margin successed"); + */ + } else { + const bridge = await ethers.getContractAt("LnOppositeBridge", from.oppositeBridge, from.wallet); + const previousInfo = await bridge.srcProviders(getProviderKey(to.chainId, from.wallet.address, sourceToken, targetToken)); + await oppositeTransferAndLockMargin( + from.wallet, + from.oppositeBridge, + to.chainId, + sourceToken, + targetToken, + formatedAmount + ); + console.log("[opposite] transfer and lock margin successed"); + // query and relay + /* + const providerInfo = await bridge.srcProviders(getProviderKey(to.chainId, from.wallet.address, sourceToken, targetToken)); + const expectedTransferId = providerInfo.lastTransferId; + await oppositeRelay( + from.chainId, + from.wallet, + to.wallet, + to.oppositeBridge, + to.oppositeBridge, + sourceToken, + targetToken, + formatedAmount, + previousInfo.lastTransferId, + expectedTransferId, + ); + console.log("[opposite] relay and release margin successed"); + */ + } +} + +async function lzFee(from, to) { + const fillAddress = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + const bridge = await ethers.getContractAt("LnDefaultBridge", from.defaultBridge, from.wallet); + const serviceAddress = (await bridge.messagers(to.chainId)).sendService; + const messager = await ethers.getContractAt("LayerZeroMessager", serviceAddress, from.wallet); + const message = await bridge.encodeWithdrawCall("0x1000000000000000000000000000000000000000000000000000000000000001", 1, fillAddress, fillAddress, fillAddress, 1000000000); + const lzPayload = ethers.utils.defaultAbiCoder.encode([ + "address", + "address", + "bytes", + ], [fillAddress, fillAddress, message]); + return await messager.fee(to.chainId, lzPayload); +} + +async function eth2arbFee(arb, eth, sourceToken, targetToken) { + const l1GasPrice = 2000000000; + const l2GasPrice = 100000000; + const l2GasLimit = 1000000; + const bridge = await ethers.getContractAt("LnOppositeBridge", eth.oppositeBridge, eth.wallet); + const srcBridge = await ethers.getContractAt("LnOppositeBridge", arb.oppositeBridge, arb.wallet); + console.log(eth.chainId, arb.wallet.address, sourceToken, targetToken); + + const providerInfo = await srcBridge.srcProviders(getProviderKey(eth.chainId, arb.wallet.address, sourceToken, targetToken)) + const serviceAddress = (await bridge.messagers(arb.chainId)).sendService; + const messager = await ethers.getContractAt("Eth2ArbSendService", serviceAddress, eth.wallet); + const message = await bridge.encodeWithdrawMargin(providerInfo.lastTransferId, sourceToken, targetToken, 1000000000); + const fee = await messager.fee(message.length, l1GasPrice, l2GasPrice, l2GasLimit, 10); + const extParams = await messager.encodeParams( + fee[0], + l2GasPrice, + l2GasLimit, + eth.wallet.address + ); + return [fee[1], extParams]; +} + +// 2. deploy mapping token factory +async function main() { + for (let network in networks) { + networks[network]['wallet'] = wallet(networks[network]); + } + + //await transfer("default", networks.goerli, networks.linea, networks.goerli.usdt, networks.linea.usdt, "320"); + //await transfer("opposite", networks.linea, networks.goerli, networks.linea.usdt, networks.goerli.usdt, "500"); + await transfer("default", networks.goerli, networks.mantle, networks.goerli.usdc, networks.mantle.usdc, "500"); + return; + //await transfer("default",networks.mantle, networks.goerli, networks.mantle.usdc, networks.goerli.usdc, "132"); + //console.log("transfer and relay successed"); + //return; + + /* + const fee = await lzFee(networks.arbitrum, networks.linea); + console.log(fee); + await withdraw("default", networks.arbitrum, networks.linea, networks.arbitrum.usdc, networks.linea.usdc, "20", fee.nativeFee, networks.arbitrum.wallet.address); + */ + /* + const sendInfo = await eth2arbFee(networks.arbitrum, networks.goerli, networks.arbitrum.usdc, networks.goerli.usdc); + console.log(sendInfo); + await withdraw("opposite", networks.arbitrum, networks.goerli, networks.arbitrum.usdc, networks.goerli.usdc, "20", sendInfo[0], sendInfo[1]); + return; + + // slasher + await slash( + ethereumWallet, + ethereumLnBridgeAddress, + ethereumWallet.address, + usdcLineaAddress, + usdcEthereumAddress, + lastTransferId, + timestamp, + lineaWallet.address, + amount1, + expectedTransferId, + ); + console.log("slash successed"); + return; + */ + + const testers = [ + "0x5861e3c9148D5DeB59b854805d8eCf3D5443fbEF", + "0x0e86Bf507Fd6025A5110dfb35df32ee2B2cf8A05", + "0x8481f3D3Be89c7a5D26176772c45ee3bDb307E79" + ]; + for (let networkName in networks) { + const network = networks[networkName]; + console.log(network.url); + const w = wallet(network); + const token = await ethers.getContractAt("Erc20", network.usdc, w); + const decimals = await token.decimals(); + for (const tester of testers) { + //for (const key of privateKeys) { + // mint token + await token.mint(tester, ethers.utils.parseUnits("500000", decimals)); + console.log(`mint for ${tester} successed`); + // approve + /* + const provider = new ethers.providers.JsonRpcProvider(network.url); + const testerWallet = new ethers.Wallet(key, provider); + const testerToken = await ethers.getContractAt("Erc20", network.usdt, testerWallet); + await testerToken.approve("0x54cc9716905ba8ebdD01E6364125cA338Cd0054E", ethers.utils.parseEther("1000000000000")); + await testerToken.approve("0x79e6f452f1e491a7aF0382FA0a6EF9368691960D", ethers.utils.parseEther("1000000000000")); + console.log(`${testerWallet.address} approve successed`); + */ + } + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/helix-contract/deploy/deploy_ln_test_token.js b/helix-contract/deploy/deploy_ln_test_token.js new file mode 100644 index 00000000..b073dc5b --- /dev/null +++ b/helix-contract/deploy/deploy_ln_test_token.js @@ -0,0 +1,98 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); + +const privateKey = process.env.PRIKEY + +const arbitrumNetwork = { + url: "https://goerli-rollup.arbitrum.io/rpc", + tokens: [ + { + name: "Helix Test Token USDT", + symbol: "USDT", + decimals: 18 + }, + { + name: "Helix Test Token USDC", + symbol: "USDC", + decimals: 18 + }, + ], +}; + +const goerliNetwork = { + url: "https://rpc.ankr.com/eth_goerli", + tokens: [ + { + name: "Helix Test Token USDT", + symbol: "USDT", + decimals: 6 + }, + { + name: "Helix Test Token USDC", + symbol: "USDC", + decimals: 6 + }, + ], +}; + +const mantleNetwork = { + url: "https://rpc.testnet.mantle.xyz", + tokens: [ + { + name: "Helix Test Token USDT", + symbol: "USDT", + decimals: 18 + }, + { + name: "Helix Test Token USDC", + symbol: "USDC", + decimals: 18 + }, + ], +}; + +const lineaNetwork = { + url: "https://rpc.goerli.linea.build", + tokens: [ + { + name: "Helix Test Token USDT", + symbol: "USDT", + decimals: 18 + }, + { + name: "Helix Test Token USDC", + symbol: "USDC", + decimals: 18 + }, + ], +}; + +function wallet(url) { + const provider = new ethers.providers.JsonRpcProvider(url); + const wallet = new ethers.Wallet(privateKey, provider); + return wallet; +} + +// 2. deploy mapping token factory +async function main() { + const networks = [goerliNetwork, mantleNetwork, arbitrumNetwork, lineaNetwork]; + for (const network of networks) { + const w = wallet(network.url); + + for (const tokenInfo of network.tokens) { + const tokenContract = await ethers.getContractFactory("HelixTestErc20", w); + const token = await tokenContract.deploy(tokenInfo.name, tokenInfo.symbol, tokenInfo.decimals); + await token.deployed(); + console.log(`finish to deploy test token contract, network is: ${network.url}, address is: ${token.address}`); + } + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); + diff --git a/helix-contract/deploy/deploy_pangolin2goerli.js b/helix-contract/deploy/deploy_pangolin2goerli.js deleted file mode 100644 index 70c9d800..00000000 --- a/helix-contract/deploy/deploy_pangolin2goerli.js +++ /dev/null @@ -1,421 +0,0 @@ -var ProxyDeployer = require("./proxy.js"); - -const backingFeeMarket = "0x4DBdC9767F03dd078B5a1FC05053Dd0C071Cc005"; -const backingOutboundLane = "0xAbd165DE531d26c229F9E43747a8d683eAD54C6c"; -const backingInboundLane = "0xB59a893f5115c1Ca737E36365302550074C32023"; - -const mtfFeeMarket = "0x6c73B30a48Bb633DC353ed406384F73dcACcA5C3"; -const mtfOutboundLane = "0x9B5010d562dDF969fbb85bC72222919B699b5F54"; -const mtfInboundLane = "0x0F6e081B1054c59559Cf162e82503F3f560cA4AF"; - -async function deployMessageEndpoint(wallet, inboundLane, ouboundLane, feeMarket, existedAddress) { - return await deployContract(wallet, "DarwiniaSub2EthMessageEndpoint", existedAddress, 2, inboundLane, ouboundLane, feeMarket, {gasLimit: 2000000}); -} - -async function deployContract(wallet, name, existedAddress, ...params) { - if (existedAddress) { - return await ethers.getContractAt(name, existedAddress, wallet); - } - const contract = await ethers.getContractFactory(name, wallet); - const contractDeployed = await contract.deploy(...params); - await contractDeployed.deployed(); - return contractDeployed; -} - -async function deployProxy(wallet, name, existedAddress, adminAddress, logicFactory, logicAddress, args) { - if (existedAddress) { - return await ethers.getContractAt(name, existedAddress, wallet); - } - const proxyContract = await ProxyDeployer.deployProxyContract( - adminAddress, - logicFactory, - logicAddress, - args, - wallet); - return await ethers.getContractAt(name, proxyContract.address, wallet); -} - -async function lockAndRemoteIssue(tokenAddress, backingAddress, amount, wallet, fee) { - const originToken = await ethers.getContractAt("WToken", tokenAddress, wallet); - await originToken.deposit({value: amount}); - await originToken.approve(backingAddress, amount); - const backing = await ethers.getContractAt("Erc20Sub2EthBacking", backingAddress, wallet); - //const tx = await backing.callStatic.lockAndRemoteIssuing( - const tx = await backing.lockAndRemoteIssuing( - tokenAddress, - wallet.address, - amount, - { - value: ethers.utils.parseEther(fee), - gasLimit: 200000, - }); -} - -async function lockAndRemoteIssueNative(backingAddress, amount, wallet, fee) { - const backing = await ethers.getContractAt("Erc20Sub2EthBacking", backingAddress, wallet); - const tx = await backing.lockAndRemoteIssuingNative( - wallet.address, - amount, - { - value: ethers.utils.parseEther(fee).add(amount), - gasLimit: 200000, - }); -} - -async function burnAndRemoteUnlock(mappingTokenAddress, mtfAddress, amount, mtfWallet, fee) { - const mappingToken = await ethers.getContractAt("Erc20", mappingTokenAddress, mtfWallet); - await mappingToken.approve(mtfAddress, amount); - const mtf = await ethers.getContractAt("Erc20Sub2EthMappingTokenFactory", mtfAddress, mtfWallet); - return await mtf.burnAndRemoteUnlock( - mappingTokenAddress, - mtfWallet.address, - amount, - { value: ethers.utils.parseEther(fee) }); -} - -async function burnAndRemoteUnlockNative(mappingTokenAddress, mtfAddress, amount, mtfWallet, fee) { - const mappingToken = await ethers.getContractAt("Erc20", mappingTokenAddress, mtfWallet); - await mappingToken.approve(mtfAddress, amount); - const mtf = await ethers.getContractAt("Erc20Sub2EthMappingTokenFactory", mtfAddress, mtfWallet); - return await mtf.burnAndRemoteUnlockNative( - mtfWallet.address, - amount, - { value: ethers.utils.parseEther(fee) }); -} - -async function remoteUnlockFailure(transferId, originAddress, mtfAddress, amount, mtfWallet, fee) { - const mtf = await ethers.getContractAt("Erc20Sub2EthMappingTokenFactory", mtfAddress, mtfWallet); - return await mtf.remoteUnlockFailure( - transferId, - originAddress, - mtfWallet.address, - amount, - { value: ethers.utils.parseEther(fee) }); -} - -function wait(ms) { - return new Promise(resolve => setTimeout(() => resolve(), ms)); -}; - -function wallet() { - const privateKey = process.env.PRIKEY - //const backingUrl = "https://pangoro-rpc.darwinia.network"; - const backingUrl = "https://pangolin-rpc.darwinia.network"; - //const backingUrl = "g2.pangoro-p2p.darwinia.network:9933"; - //const mtfUrl = "https://eth-goerli.g.alchemy.com/v2/WerPq7On62-wy_ARssv291ZPg1TGR5vi"; - const mtfUrl = "https://rpc.ankr.com/eth_goerli"; - const backingProvider = new ethers.providers.JsonRpcProvider(backingUrl); - const backingWallet = new ethers.Wallet(privateKey, backingProvider); - const mtfProvider = new ethers.providers.JsonRpcProvider(mtfUrl); - const mtfWallet = new ethers.Wallet(privateKey, mtfProvider); - return [backingWallet, mtfWallet]; -} - -async function deploy(backingWallet, mtfWallet) { - // deploy - console.log("start to deploy"); - const backingMessageEndpoint = await deployMessageEndpoint(backingWallet, backingInboundLane, backingOutboundLane, backingFeeMarket, null); - console.log("deploy backing message handle finished, address: ", backingMessageEndpoint.address); - const mtfMessageEndpoint = await deployMessageEndpoint(mtfWallet, mtfInboundLane, mtfOutboundLane, mtfFeeMarket, null); - console.log("deploy mtf message handle finished, address: ", mtfMessageEndpoint.address); - - // configure message handle - await backingMessageEndpoint.setRemoteEndpoint(mtfMessageEndpoint.address); - await mtfMessageEndpoint.setRemoteEndpoint(backingMessageEndpoint.address); - console.log("finish configure message handle"); - - const backingContractLogic = await ethers.getContractFactory("Erc20Sub2EthBacking", backingWallet); - const backingLogic = await backingContractLogic.deploy(); - await backingLogic.deployed(); - console.log("finish to deploy backing logic, address: ", backingLogic.address); - - const backingAdmin = await ProxyDeployer.deployProxyAdmin(backingWallet); - console.log("finish to deploy backing admin, address: ", backingAdmin.address); - const backingProxy = await ProxyDeployer.deployProxyContract( - backingAdmin.address, - backingContractLogic, - backingLogic.address, - [backingMessageEndpoint.address], - backingWallet); - console.log("finish to deploy backing proxy, address: ", backingProxy.address); - - const mtfContractLogic = await ethers.getContractFactory("Erc20Sub2EthMappingTokenFactory", mtfWallet); - const mtfLogic = await mtfContractLogic.deploy(); - await mtfLogic.deployed(); - console.log("finish to deploy mapping token factory logic, address: ", mtfLogic.address); - - const mtfAdmin = await ProxyDeployer.deployProxyAdmin(mtfWallet); - console.log("finish to deploy mapping token factory admin, address: ", mtfAdmin.address); - const mtfProxy = await ProxyDeployer.deployProxyContract( - mtfAdmin.address, - mtfContractLogic, - mtfLogic.address, - [mtfMessageEndpoint.address], - mtfWallet); - console.log("finish to deploy mapping token factory proxy, address: ", mtfProxy.address); - - const backing = await ethers.getContractAt("Erc20Sub2EthBacking", backingProxy.address, backingWallet); - await backing.setRemoteMappingTokenFactory(mtfProxy.address); - console.log("finish to configure backing"); - - const mtf = await ethers.getContractAt("Erc20Sub2EthMappingTokenFactory", mtfProxy.address, mtfWallet); - await mtf.setRemoteBacking(backingProxy.address); - console.log("finish to configure mapping token factory"); - - await backingMessageEndpoint.grantRole(await backingMessageEndpoint.CALLER_ROLE(), backing.address); - await mtfMessageEndpoint.grantRole(await mtfMessageEndpoint.CALLER_ROLE(), mtf.address); - console.log("grant role 01"); - await backingMessageEndpoint.grantRole(await backingMessageEndpoint.CALLEE_ROLE(), backing.address); - await mtfMessageEndpoint.grantRole(await mtfMessageEndpoint.CALLEE_ROLE(), mtf.address); - console.log("grant role 02"); - await backing.grantRole(await backing.OPERATOR_ROLE(), backingWallet.address); - await mtf.grantRole(await mtf.OPERATOR_ROLE(), mtfWallet.address); - console.log("grant role permission finished"); - - // register special erc20 token - // native token weth - // we need replace this wring address by exist one - const wethContract = await ethers.getContractFactory("WToken", backingWallet); - const weth = await wethContract.deploy("Darwinia Native Wrapped Token", "WORING", 18); - await weth.deployed(); - console.log("wring address is ", weth.address); - - // we need replace this exist ring address on ethereum - const ringErc20 = await ethers.getContractFactory("Erc20", mtfWallet); - const ring = await ringErc20.deploy("Goerli Native ORING", "ORING", 18); - await ring.deployed(); - console.log("ring address is ", ring.address); - await ring.transferOwnership(mtf.address); - - // register - const dailyLimit = ethers.utils.parseEther("3000000"); - await backing.changeDailyLimit(weth.address, dailyLimit); - await backing.setNativeWrappedToken(weth.address); - //const tx = await backing.callStatic.register( - //const tx = await mtf.register( - //weth.address, - //"Darwinia Smart", - //"Wrapped Ring", - //"WRING", - //18, - //dailyLimit - //); - const tx = await mtf.setMappingToken( - weth.address, - ring.address, - dailyLimit - ); - console.log("transaction is ", tx); - await mtf.setMappingNativeWrappedToken(ring.address); - - // deploy guard - const guardContract = await ethers.getContractFactory("Guard", mtfWallet); - const guard = await guardContract.deploy(["0x2cC60930C14FE8bD6fEd1602B75339B2b7CDc515"], 1, 600, mtf.address); - await guard.deployed(); - await mtf.updateGuard(guard.address); - return { - pangoro2goerli_sub2eth_goerli: { - messageEndpoint: mtfMessageEndpoint.address, - mappingTokenFactoryLogic: mtfLogic.address, - mappingTokenFactoryAdmin: mtfAdmin.address, - mappingTokenFactoryProxy: mtf.address, - ring: ring.address, - guard: guard.address - }, - pangoro2goerli_sub2eth_pangoro: { - messageEndpoint: backingMessageEndpoint.address, - backingLogic: backingLogic.address, - backingAdmin: backingAdmin.address, - backingProxy: backing.address, - WRING: weth.address, - }, - } -} - -async function redeployGuard(mtfWallet, mtf) { - const guardContract = await ethers.getContractFactory("Guard", mtfWallet); - const guard = await guardContract.deploy([mtfWallet.address], 1, 600, mtf.address); - await guard.deployed(); - await mtf.updateGuard(guard.address); -} - -async function deployWithExistContract(backingWallet, mtfWallet) { - const wringAddress = "0x69e392E057B5994da2b0E9661039970Ac4c26b8c"; - const ringErc20 = "0x69e392E057B5994da2b0E9661039970Ac4c26b8c"; - - // deploy - const backingMessageEndpoint = await deployMessageEndpoint(backingWallet, backingInboundLane, backingOutboundLane, backingFeeMarket, null); - console.log("deploy backing message handle finished, address: ", backingMessageEndpoint.address); - // MTF 01 - const mtfMessageEndpoint = await deployMessageEndpoint(mtfWallet, mtfInboundLane, mtfOutboundLane, mtfFeeMarket, null); - console.log("deploy mtf message handle finished, address: ", mtfMessageEndpoint.address); - - // configure message handle - await backingMessageEndpoint.setRemoteEndpoint(mtfMessageEndpoint.address); - console.log("backing set remote endpoint finished"); - // MTF 02 - await mtfMessageEndpoint.setRemoteEndpoint(backingMessageEndpoint.address); - console.log("mtf set remote endpoint finished"); - - const backingLogicFactory = await ethers.getContractFactory("Erc20Sub2EthBacking", wallet); - const backingLogic = await deployContract(backingWallet, "Erc20Sub2EthBacking", null); - console.log("finish to deploy backing logic, address: ", backingLogic.address); - - const backingAdmin = await deployContract(backingWallet, "ProxyAdmin", null); - console.log("finish to deploy backing admin, address: ", backingAdmin.address); - const backing = await deployProxy( - backingWallet, - "Erc20Sub2EthBacking", - null, - backingAdmin.address, - backingLogicFactory, - backingLogic.address, - [backingMessageEndpoint.address] - ) - console.log("finish to deploy backing proxy, address: ", backing.address); - - // MTF 03 - const mtfContractLogic = await ethers.getContractFactory("Erc20Sub2EthMappingTokenFactory", mtfWallet); - const mtfLogic = await deployContract(mtfWallet, "Erc20Sub2EthMappingTokenFactory", null); - console.log("finish to deploy mapping token factory logic, address: ", mtfLogic.address); - - // MTF 04 - const mtfAdmin = await deployContract(mtfWallet, "ProxyAdmin", null); - console.log("finish to deploy mapping token factory admin, address: ", mtfAdmin.address); - // MTF 05 - const mtf = await deployProxy( - mtfWallet, - "Erc20Sub2EthMappingTokenFactory", - null, - mtfAdmin.address, - mtfContractLogic, - mtfLogic.address, - [mtfMessageEndpoint.address]); - console.log("finish to deploy mapping token factory proxy, address: ", mtf.address); - - await backing.setRemoteMappingTokenFactory(mtf.address); - console.log("finish to configure backing"); - - // MTF 06 - await mtf.setRemoteBacking(backing.address); - console.log("finish to configure mapping token factory"); - - await backingMessageEndpoint.grantRole(await backingMessageEndpoint.CALLER_ROLE(), backing.address); - console.log("backing endpoint grant backing caller finished"); - // MTF 07 - await mtfMessageEndpoint.grantRole(await mtfMessageEndpoint.CALLER_ROLE(), mtf.address); - console.log("mtf endpoint grant mtf finished"); - await backingMessageEndpoint.grantRole(await backingMessageEndpoint.CALLEE_ROLE(), backing.address); - console.log("backing endpoint grant backing callee finished"); - // MTF 08 - await mtfMessageEndpoint.grantRole(await mtfMessageEndpoint.CALLEE_ROLE(), mtf.address); - console.log("mtf endpoint grant mtf callee finished"); - await backing.grantRole(await backing.OPERATOR_ROLE(), backingWallet.address); - console.log("backing grant operator finished"); - // MTF 09 - await mtf.grantRole(await mtf.OPERATOR_ROLE(), mtfWallet.address); - console.log("mtf grant operator finished"); - - // register - const dailyLimit = ethers.utils.parseEther("3000000"); - await backing.changeDailyLimit(wringAddress, dailyLimit); - await backing.setNativeWrappedToken(wringAddress); - // MTF 10 - const tx = await mtf.setMappingToken( - wringAddress, - ringErc20, - dailyLimit - ); - console.log("transaction is ", tx); - // MTF 11 - await mtf.setMappingNativeWrappedToken(ringErc20); - - // deploy guard - // MTF 12 - const guard = await deployContract(mtfWallet, "Guard", null, [mtfWallet.address], 1, 600, mtf.address); - // MTF 13 - await mtf.updateGuard(guard.address); - return { - pangoro2goerli_sub2eth_goerli: { - messageEndpoint: mtfMessageEndpoint.address, - mappingTokenFactoryLogic: mtfLogic.address, - mappingTokenFactoryAdmin: mtfAdmin.address, - mappingTokenFactoryProxy: mtf.address, - ring: ringErc20, - guard: guard.address - }, - pangoro2goerli_sub2eth_pangoro: { - messageEndpoint: backingMessageEndpoint.address, - backingLogic: backingLogic.address, - backingAdmin: backingAdmin.address, - backingProxy: backing.address, - WRING: wringAddress - }, - } -} - - -// 2. deploy mapping token factory -async function main() { - const wallets = wallet(); - const backingWallet = wallets[0]; - const mtfWallet = wallets[1]; - - const deployed = await deploy(backingWallet, mtfWallet); - ////const deployed = await deployWithExistContract(backingWallet, mtfWallet); - console.log(deployed); - //const backingInfo = deployed.pangoro2goerli_sub2eth_pangoro; - //const mtfInfo = deployed.pangoro2goerli_sub2eth_goerli; - //await lockAndRemoteIssue(backingInfo.WRING, backingInfo.backingProxy, ethers.utils.parseEther("1.1"), backingWallet, "30"); - //await lockAndRemoteIssueNative(backingInfo.backingProxy, ethers.utils.parseEther("1.2"), backingWallet, "30"); - - //const wethAddress = "0x46f01081e800BF47e43e7bAa6D98d45F6a0251E4"; - //const mtfAddress = "0xfcAcf3d08275031e5Bd453Cf2509301290858984"; - //const backingAddress = "0xaafFbF487e9dA8429E2E9502d0907e5fD6b0C320"; - //const ringAddress = "0x046D07d53926318d1F06c2c2A0F26a4de83E26c4"; - //const mtf = await ethers.getContractAt("Erc20Sub2EthMappingTokenFactory", mtfAddress, mtfWallet); - //const backing = await ethers.getContractAt("Erc20Sub2EthBacking", backingAddress, backingWallet); - - //const oldmtf = await ethers.getContractAt("Erc20Sub2EthMappingTokenFactory", "0x38d8af6834bc10856a161977534d0bca7419eacd", mtfWallet); - //await oldmtf.transferMappingTokenOwnership("0x69e392E057B5994da2b0E9661039970Ac4c26b8c", mtfAddress); - - - //const dailyLimit = ethers.utils.parseEther("200"); - //await mtf.changeDailyLimit(ringAddress, dailyLimit); - //await backing.changeDailyLimit(wethAddress, dailyLimit); - - //console.log(await mtf.calcMaxWithdraw(ringAddress)); - //await lockAndRemoteIssue(wethAddress, backingAddress, ethers.utils.parseEther("1.7"), backingWallet, "100"); - //await lockAndRemoteIssueNative(backingAddress, ethers.utils.parseEther("300"), backingWallet, "100"); - //await burnAndRemoteUnlock(ringAddress, mtfAddress, ethers.utils.parseEther("0.11"), mtfWallet, "0.01"); - //await burnAndRemoteUnlockNative(ringAddress, mtfAddress, ethers.utils.parseEther("0.12"), mtfWallet, "0.01"); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - - -/* - *{ - pangoro2goerli_sub2eth_goerli: { - messageEndpoint: '0xA4A380B592ceC969bD43BA54F8833d88b8b24811', - mappingTokenFactoryLogic: '0x0446bc7C1F034E6f502c21c7222632cC8Ddf74d4', - mappingTokenFactoryAdmin: '0xF206aC3dbbc9ee2cddd07718Fa1785BEd4D0b375', - mappingTokenFactoryProxy: '0x2a5fE3Cd11c6eEf7e3CeA26553e2694f0B0A9f9e', - ring: '0xeb93165E3CDb354c977A182AbF4fad3238E04319', - guard: '0x8C986EC362A38cA4A6a3fd4188C5318c689A187d' - }, - pangoro2goerli_sub2eth_pangoro: { - messageEndpoint: '0x83B4e8287693Ef159D2231C5ACa485D5d2AdEb38', - backingLogic: '0x492bAda46302BdC30950018f7d8fDE53701e6AFF', - backingAdmin: '0xd12917F42E09e216623010EB5f15c39d4978d322', - backingProxy: '0xeAb1F01a8f4A2687023B159c2063639Adad5304E', - WRING: '0x3F3eDBda6124462a09E071c5D90e072E0d5d4ed4' - } - wring address is 0x3F3eDBda6124462a09E071c5D90e072E0d5d4ed4 - ring address is 0xeb93165E3CDb354c977A182AbF4fad3238E04319 -}*/ diff --git a/helix-contract/deploy/deploy_pangoro2pangolin.js b/helix-contract/deploy/deploy_pangoro2pangolin.js deleted file mode 100644 index a8e9c0a9..00000000 --- a/helix-contract/deploy/deploy_pangoro2pangolin.js +++ /dev/null @@ -1,266 +0,0 @@ -var ProxyDeployer = require("./proxy.js"); - -const VERSION = 2; -/* -const backingUrl = "https://pangoro-rpc.darwinia.network"; -const backingNetworkId = "0x0000002d"; //45 -const backingBridgeNetworkId = "0x70616772"; -const backingTransactCallIndex = 9728;//1a01 -> 2600 -const backingSendmsgIndex = 10499;//0x1103 -> 2903 -const backingOutboundLaneId = "0x726f6c69"; -const backingStorageKeyForMarketFee = "0x30d35416864cf657db51d3bc8505602f2edb70953213f33a6ef6b8a5e3ffcab2"; -const backingStorageKeyForLatestNonce = "0xd86d7f611f4d004e041fda08f633f10196c246acb9b55077390e3ca723a0ca1f"; -const backingStorageKeyForLastDeliveredNonce = "0xd86d7f611f4d004e041fda08f633f101e5f83cf83f2127eb47afdc35d6e43fab"; -const backingSpecVersion = 6009; - -const mtfUrl = "https://pangolin-rpc.darwinia.network"; -const mtfNetworkId = "0x0000002b"; //43 -const mtfBridgeNetworkId = "0x7061676c"; -const mtfTransactCallIndex = 9728;//0x2901 -> 2600 -const mtfSendmsgIndex = 10499;//0x2b03 -> 2903 -const mtfOutboundLaneId = "0x726f6c69";//726f6c69 -const mtfStorageKeyForMarketFee = "0x7621b367d09b75f6876b13089ee0ded52edb70953213f33a6ef6b8a5e3ffcab2"; -const mtfStorageKeyForLatestNonce = "0xc9b76e645ba80b6ca47619d64cb5e58d96c246acb9b55077390e3ca723a0ca1f"; -const mtfStorageKeyForLastDeliveredNonce = "0xc9b76e645ba80b6ca47619d64cb5e58de5f83cf83f2127eb47afdc35d6e43fab"; -const mtfSpecVersion = 6010; -*/ -const mtfUrl = "https://pangoro-rpc.darwinia.network"; -const mtfNetworkId = "0x0000002d"; //45 -const mtfBridgeNetworkId = "0x70616772"; -const mtfTransactCallIndex = 9728;//1a01 -> 2600 -const mtfSendmsgIndex = 10499;//0x1103 -> 2903 -const mtfOutboundLaneId = "0x726f6c69"; -const mtfStorageKeyForMarketFee = "0x30d35416864cf657db51d3bc8505602f2edb70953213f33a6ef6b8a5e3ffcab2"; -const mtfStorageKeyForLatestNonce = "0xd86d7f611f4d004e041fda08f633f10196c246acb9b55077390e3ca723a0ca1f"; -const mtfStorageKeyForLastDeliveredNonce = "0xd86d7f611f4d004e041fda08f633f101e5f83cf83f2127eb47afdc35d6e43fab"; -const mtfSpecVersion = 6009; - -const backingUrl = "https://pangolin-rpc.darwinia.network"; -const backingNetworkId = "0x0000002b"; //43 -const backingBridgeNetworkId = "0x7061676c"; -const backingTransactCallIndex = 9728;//0x2901 -> 2600 -const backingSendmsgIndex = 10499;//0x2b03 -> 2903 -const backingOutboundLaneId = "0x726f6c69";//726f6c69 -const backingStorageKeyForMarketFee = "0x7621b367d09b75f6876b13089ee0ded52edb70953213f33a6ef6b8a5e3ffcab2"; -const backingStorageKeyForLatestNonce = "0xc9b76e645ba80b6ca47619d64cb5e58d96c246acb9b55077390e3ca723a0ca1f"; -const backingStorageKeyForLastDeliveredNonce = "0xc9b76e645ba80b6ca47619d64cb5e58de5f83cf83f2127eb47afdc35d6e43fab"; -const backingSpecVersion = 6010; - -async function deployMessageEndpoint(wallet, outboundLaneId, inboundLaneId) { - const handleContract = await ethers.getContractFactory("DarwiniaSub2SubMessageEndpoint", wallet); - const handle = await handleContract.deploy(VERSION, outboundLaneId, inboundLaneId); - await handle.deployed(); - return handle -} - -async function lockAndRemoteIssueNative(wethAddress, backingAddress, amount, wallet) { - const weth = await ethers.getContractAt("WToken", wethAddress, wallet); - await weth.deposit({value: amount}); - await weth.approve(backingAddress, amount); - const backing = await ethers.getContractAt("Erc20Sub2SubBacking", backingAddress, wallet); - const tx = await backing.callStatic.lockAndRemoteIssuing( - //await backing.lockAndRemoteIssuing( - mtfSpecVersion, - 1000000, - wethAddress, - wallet.address, - amount, - { value: ethers.utils.parseEther("150.0"), gasLimit: 2000000 }); - console.log("=========", tx); -} - -async function burnAndRemoteUnlockNative(xwethAddress, mtfAddress, amount, mtfWallet) { - const xweth = await ethers.getContractAt("MappingERC20", xwethAddress, mtfWallet); - await xweth.approve(mtfAddress, amount); - const mtf = await ethers.getContractAt("Erc20Sub2SubMappingTokenFactory", mtfAddress, mtfWallet); - return await mtf.burnAndRemoteUnlock( - backingSpecVersion, - 1000000, - xwethAddress, - wallet.address, - amount, - { value: ethers.utils.parseEther("100.0") }); -} - -async function remoteUnlockFailure(transferId, wethAddress, mtfAddress, amount, mtfWallet) { - const mtf = await ethers.getContractAt("Erc20Sub2SubMappingTokenFactory", mtfAddress, mtfWallet); - return await mtf.remoteUnlockFailure( - backingSpecVersion, - 1000000, - transferId, - wethAddress, - wallet.address, - amount, - { value: ethers.utils.parseEther("100.0") }); -} - -function wait(ms) { - return new Promise(resolve => setTimeout(() => resolve(), ms)); -}; - -// 2. deploy mapping token factory -async function main() { - const privateKey = process.env.PRIKEY - //const precompileStorageAddress = "0x0000000000000000000000000000000000000400"; - //const precompileDispatchAddress = "0x0000000000000000000000000000000000000401"; - - - // backing - const backingProvider = new ethers.providers.JsonRpcProvider(backingUrl); - const backingWallet = new ethers.Wallet(privateKey, backingProvider); - - // mapping token factory - const mtfProvider = new ethers.providers.JsonRpcProvider(mtfUrl); - const mtfWallet = new ethers.Wallet(privateKey, mtfProvider); - - // deploy - const backingMessageEndpoint = await deployMessageEndpoint(backingWallet, backingOutboundLaneId, mtfOutboundLaneId); - console.log("deploy backing message handle finished, address: ", backingMessageEndpoint.address); - const mtfMessageEndpoint = await deployMessageEndpoint(mtfWallet, mtfOutboundLaneId, backingOutboundLaneId); - console.log("deploy mtf message handle finished, address: ", mtfMessageEndpoint.address); - - // configure message handle - await backingMessageEndpoint.setRemoteHelix(mtfBridgeNetworkId, mtfNetworkId, mtfMessageEndpoint.address, {gasLimit: 1000000}); - console.log("backing set remote endpoint finished"); - await backingMessageEndpoint.setRemoteCallIndex(mtfTransactCallIndex, {gasLimit: 1000000}); - console.log("backing set remote callindex finished"); - //await backingMessageEndpoint.setLocalAddress(precompileStorageAddress, precompileDispatchAddress); - await backingMessageEndpoint.setLocalCallInfo(backingSendmsgIndex, {gasLimit: 1000000}); - console.log("backing set local info finished"); - await backingMessageEndpoint.setLocalStorageKey(backingStorageKeyForMarketFee, backingStorageKeyForLatestNonce, backingStorageKeyForLastDeliveredNonce, {gasLimit: 1000000}); - console.log("finish configure backing message handle"); - await mtfMessageEndpoint.setRemoteHelix(backingBridgeNetworkId, backingNetworkId, backingMessageEndpoint.address, {gasLimit: 1000000}); - await mtfMessageEndpoint.setRemoteCallIndex(backingTransactCallIndex, {gasLimit: 1000000}); - //await mtfMessageEndpoint.setLocalAddress(precompileStorageAddress, precompileDispatchAddress); - await mtfMessageEndpoint.setLocalCallInfo(mtfSendmsgIndex, {gasLimit: 1000000}); - await mtfMessageEndpoint.setLocalStorageKey(mtfStorageKeyForMarketFee, mtfStorageKeyForLatestNonce, mtfStorageKeyForLastDeliveredNonce, {gasLimit: 1000000}); - console.log("finish configure mapping token factory message handle"); - - // deploy backing & mapping token factory - // deploy erc20 logic - const erc20Contract = await ethers.getContractFactory("Erc20", mtfWallet); - const mappingTokenLogic = await erc20Contract.deploy("Darwinia Test ORing", "ORING", 18); - await mappingTokenLogic.deployed(); - console.log("finish to deploy mapping token logic, address: ", mappingTokenLogic.address); - - const backingContractLogic = await ethers.getContractFactory("Erc20Sub2SubBacking", backingWallet); - const backingLogic = await backingContractLogic.deploy(); - await backingLogic.deployed(); - console.log("finish to deploy backing logic, address: ", backingLogic.address); - - const backingAdmin = await ProxyDeployer.deployProxyAdmin(backingWallet); - console.log("finish to deploy backing admin, address: ", backingAdmin.address); - const backingProxy = await ProxyDeployer.deployProxyContract( - backingAdmin.address, - backingContractLogic, - backingLogic.address, - [backingMessageEndpoint.address], - backingWallet); - console.log("finish to deploy backing proxy, address: ", backingProxy.address); - - const mtfContractLogic = await ethers.getContractFactory("Erc20Sub2SubMappingTokenFactory", mtfWallet); - const mtfLogic = await mtfContractLogic.deploy(); - await mtfLogic.deployed(); - console.log("finish to deploy mapping token factory logic, address: ", mtfLogic.address); - - const mtfAdmin = await ProxyDeployer.deployProxyAdmin(mtfWallet); - console.log("finish to deploy mapping token factory admin, address: ", mtfAdmin.address); - const mtfProxy = await ProxyDeployer.deployProxyContract( - mtfAdmin.address, - mtfContractLogic, - mtfLogic.address, - [mtfMessageEndpoint.address], - mtfWallet); - console.log("finish to deploy mapping token factory proxy, address: ", mtfProxy.address); - - const backing = await ethers.getContractAt("Erc20Sub2SubBacking", backingProxy.address, backingWallet); - await backing.setChainName("Pangolin", {gasLimit: 1000000}); - await backing.setRemoteMappingTokenFactory(mtfProxy.address, {gasLimit: 1000000}); - console.log("finish to configure backing"); - - const mtf = await ethers.getContractAt("Erc20Sub2SubMappingTokenFactory", mtfProxy.address, mtfWallet); - await mtf.setRemoteBacking(backingProxy.address, {gasLimit: 1000000}); - //await mtf.setTokenContractLogic(0, mappingTokenLogic.address, {gasLimit: 1000000}); - //await mtf.setTokenContractLogic(1, mappingTokenLogic.address, {gasLimit: 1000000}); - console.log("finish to configure mapping token factory"); - - await backingMessageEndpoint.grantRole(await backingMessageEndpoint.CALLER_ROLE(), backing.address, {gasLimit: 1000000}); - await backingMessageEndpoint.grantRole(await backingMessageEndpoint.CALLEE_ROLE(), backing.address, {gasLimit: 1000000}); - await mtfMessageEndpoint.grantRole(await mtfMessageEndpoint.CALLER_ROLE(), mtf.address, {gasLimit: 1000000}); - await mtfMessageEndpoint.grantRole(await mtfMessageEndpoint.CALLEE_ROLE(), mtf.address, {gasLimit: 1000000}); - await backing.grantRole(await backing.OPERATOR_ROLE(), backingWallet.address, {gasLimit: 1000000}); - console.log("grant role permission finished"); - - // register special erc20 token - //const backing = await ethers.getContractAt("Erc20Sub2SubBacking", "0x63359a0BB8eF1f6cD141761375D583eCefD5Ecfc", backingWallet); - // native token weth - const wethContract = await ethers.getContractFactory("WToken", backingWallet); - const weth = await wethContract.deploy("wrapped oring", "WORING", 18); - await weth.deployed(); - console.log("weth address is ", weth.address); - - // register - const gasLimit = 5000000; - const specVersion = mtfSpecVersion; - const dailyLimit = ethers.utils.parseEther("10000"); - //const tx = await backing.callStatic.register( - const tx = await backing.register( - specVersion, - gasLimit, - weth.address, - "wrapped eth", - "weth", - 18, - dailyLimit, - { value: ethers.utils.parseEther("150.0"), gasLimit: 1000000 } - ); - console.log("transaction is ", tx); - - // waiting for bridger to relay message - while (true) { - const tokenLength = await mtf.tokenLength(); - if (tokenLength > 0) { - break; - } - await wait(3000); - } - console.log("waiting bridger finished ..."); - await lockAndRemoteIssueNative(weth.address, backing.address, ethers.utils.parseEther("200"), backingWallet); - - /* - await lockAndRemoteIssueNative( - "0x13378f170c69D924d96A34B9171157b4C10a18aA", - "0xb1ec308c293171B04e47841ae6869ae1e8895577", - ethers.utils.parseEther("1.5"), - backingWallet); - return; - */ - /* - // the deployed addresses - const mtfAddress = "0x0793e2726360224dA8cf781c048dF7acCa3Bb049"; - const backingAddress = "0x91Cdd894aD5cC203A026115B33e30670E5166504"; - const wethAddress = "0x78f3B1ae818c304Bbec76e244B67dEdC70506006"; - - // 1. lock and remote issue - const mtf = await ethers.getContractAt("Erc20Sub2SubMappingTokenFactory", mtfAddress, mtfWallet); - await lockAndRemoteIssueNative(wethAddress, backingAddress, ethers.utils.parseEther("1.3"), backingWallet); - - // 2. burn and remote unlock - const tx = await burnAndRemoteUnlockNative(await mtf.allMappingTokens(0), mtfAddress, ethers.utils.parseEther("1.3"), mtfWallet); - await burnAndRemoteUnlockNative(await mtf.allMappingTokens(0), mtfAddress, ethers.utils.parseEther("1.3"), mtfWallet); - await burnAndRemoteUnlockNative(await mtf.allMappingTokens(0), mtfAddress, ethers.utils.parseEther("1.3"), mtfWallet); - await burnAndRemoteUnlockNative(await mtf.allMappingTokens(0), mtfAddress, ethers.utils.parseEther("1.3"), mtfWallet); - console.log(tx); - - const transferId = "0x726f6c69000000000000009e"; - await remoteUnlockFailure(transferId, wethAddress, mtfAddress, ethers.utils.parseEther("1.3"), mtfWallet); - */ -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - diff --git a/helix-contract/deploy/deploy_sub2eth_lp.js b/helix-contract/deploy/deploy_sub2eth_lp.js deleted file mode 100644 index 6be69bc5..00000000 --- a/helix-contract/deploy/deploy_sub2eth_lp.js +++ /dev/null @@ -1,278 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY -const relayPrivateKey = process.env.RELAYPRIKEY; -//const darwiniaUrl = "https://pangoro-rpc.darwinia.network"; -const darwiniaUrl = "https://crab-rpc.darwinia.network"; -//const backingUrl = "g2.pangoro-p2p.darwinia.network:9933"; -//const mtfUrl = "https://eth-goerli.g.alchemy.com/v2/WerPq7On62-wy_ARssv291ZPg1TGR5vi"; -const ethereumUrl = "https://rpc.ankr.com/eth_goerli"; -const darwiniaSub2EthEndpointAddress = "0x528985686C6EC07B8D9f7BfB0dCFed9f74520b83"; -const ethereumSub2EthEndpointAddress = "0x190304Fd18c7185637b055Cd91077374b49E2659"; -const daoOnDarwinia = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -const daoOnEthereum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -//const wringAddress = "0x46f01081e800BF47e43e7bAa6D98d45F6a0251E4"; -const wringAddress = "0x2D2b97EA380b0185e9fDF8271d1AFB5d2Bf18329"; // wcrab -//const ringErc20Address = "0x046D07d53926318d1F06c2c2A0F26a4de83E26c4"; -const ringErc20Address = "0xBfEa6c80Ef15165a906E843eAfa2cC0708ED1767"; -//const darwiniaProxyAdmin = "0xF4aEF264D0e112D0bD1371278F39B3d80d1B4f8D"; -const darwiniaProxyAdmin = "0xdA84D53Bb0e11016A0462a72Bd577368153a722F"; -const ethereumProxyAdmin = "0x81E8220a2Ed7275982F59D316F02C2D301151F7C"; -const darwiniaChainId = 44; -const ethereumChainId = 5; - -function hash( - nonce, - issuingNative, - remoteToken, - sender, - receiver, - remoteAmount, - localChainId, - remoteChainId -) { - return ethUtil.keccak256( - abi.rawEncode( - ['uint256','bool','address','address','address','uint112','uint64','uint64'], - [ nonce, - issuingNative, - remoteToken, - sender, - receiver, - ethers.utils.formatUnits(remoteAmount, 0), - localChainId, - remoteChainId - ] - ) - ); - -} - -async function lockAndRemoteIssuing(nonce, tokenIndex, bridgeAddress, wallet, amount, fee, remoteIssuingNative) { - const bridge = await ethers.getContractAt("LpSub2EthBridge", bridgeAddress, wallet); - //const tx = await bridge.callStatic.lockAndRemoteIssuing( - const tx = await bridge.lockAndRemoteIssuing( - nonce, - wallet.address, - amount, - fee, - tokenIndex, - remoteIssuingNative, - { - gasLimit: 120000, - } - ); - //console.log(tx); -} - -async function lockAndRemoteIssueNative(nonce, bridgeAddress, wallet, amount, fee) { - const bridge = await ethers.getContractAt("LpSub2EthBridge", bridgeAddress, wallet); - //const tx = await bridge.callStatic.lockNativeAndRemoteIssuing( - const tx = await bridge.lockNativeAndRemoteIssuing( - amount, - fee, - wallet.address, - nonce, - false, - { - value: amount.add(fee), - gasLimit: 120000 - }, - ); - //console.log(tx); -} - -async function relay(nonce, token, sender, receiver, amount, sourceChainId, issuingNative, wallet, bridgeAddress) { - const bridge = await ethers.getContractAt("LpSub2EthBridge", bridgeAddress, wallet); - //const tx = await bridge.callStatic.relay( - await bridge.relay( - nonce, - token, - sender, - receiver, - amount, - sourceChainId, - issuingNative, - { - value: issuingNative ? amount : 0, - gasLimit: 120000 - } - ); - //console.log(tx); -} - -function wallet() { - const darwiniaProvider = new ethers.providers.JsonRpcProvider(darwiniaUrl); - const darwiniaWallet = new ethers.Wallet(privateKey, darwiniaProvider); - const darwiniaRelayWallet = new ethers.Wallet(relayPrivateKey, darwiniaProvider); - const ethereumProvider = new ethers.providers.JsonRpcProvider(ethereumUrl); - const ethereumWallet = new ethers.Wallet(privateKey, ethereumProvider); - const ethereumRelayWallet = new ethers.Wallet(relayPrivateKey, ethereumProvider); - return [darwiniaWallet, ethereumWallet, darwiniaRelayWallet, ethereumRelayWallet]; -} - -async function getLpBridgeInitData(wallet, localEndpoint, remoteEndpoint, dao) { - const bridgeContract = await ethers.getContractFactory("LpSub2EthBridge", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [localEndpoint, remoteEndpoint, dao], - "initialize", - ); - console.log("LpSub2EthBridge init data:", initdata); -} - -async function deployLpBridge(wallet, localEndpoint, remoteEndpoint, dao, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("LpSub2EthBridge", wallet); - const lpBridgeLogic = await bridgeContract.deploy(); - await lpBridgeLogic.deployed(); - console.log("finish to deploy lp bridge logic, address: ", lpBridgeLogic.address); - - const lpBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lpBridgeLogic.address, - [localEndpoint, remoteEndpoint, dao], - wallet); - console.log("finish to deploy lp bridge proxy, address:", lpBridgeProxy.address); - return lpBridgeProxy.address; -} - -async function deploy(darwiniaWallet, ethereumWallet) { - // deploy - const darwiniaMessageEndpoint = await ethers.getContractAt("DarwiniaSub2EthMessageEndpoint", darwiniaSub2EthEndpointAddress, darwiniaWallet); - const ethereumMessageEndpoint = await ethers.getContractAt("DarwiniaSub2EthMessageEndpoint", ethereumSub2EthEndpointAddress, ethereumWallet); - - const darwiniaLpBridgeAddress = await deployLpBridge( - darwiniaWallet, - darwiniaSub2EthEndpointAddress, - ethereumSub2EthEndpointAddress, - daoOnDarwinia, - darwiniaProxyAdmin - ); - const ethereumLpBridgeAddress = await deployLpBridge( - ethereumWallet, - ethereumSub2EthEndpointAddress, - darwiniaSub2EthEndpointAddress, - daoOnEthereum, - ethereumProxyAdmin - ); - - const darwiniaLpBridge = await ethers.getContractAt("LpSub2EthBridge", darwiniaLpBridgeAddress, darwiniaWallet); - const ethereumLpBridge = await ethers.getContractAt("LpSub2EthBridge", ethereumLpBridgeAddress, ethereumWallet); - await darwiniaLpBridge.updateFeeReceiver(daoOnDarwinia); - await ethereumLpBridge.updateFeeReceiver(daoOnEthereum); - await darwiniaLpBridge.setRemoteBridge(ethereumLpBridgeAddress); - await ethereumLpBridge.setRemoteBridge(darwiniaLpBridgeAddress); - - /*********** - await darwiniaMessageEndpoint.grantRole(await darwiniaMessageEndpoint.CALLER_ROLE(), darwiniaLpBridgeAddress); - await darwiniaMessageEndpoint.grantRole(await darwiniaMessageEndpoint.CALLEE_ROLE(), darwiniaLpBridgeAddress); - await ethereumMessageEndpoint.grantRole(await ethereumMessageEndpoint.CALLER_ROLE(), ethereumLpBridgeAddress); - await ethereumMessageEndpoint.grantRole(await ethereumMessageEndpoint.CALLEE_ROLE(), ethereumLpBridgeAddress); - ***********/ - - // register special erc20 token - // native token weth - // we need replace this wring address by exist one - const wring = await ethers.getContractAt("WToken", wringAddress, darwiniaWallet); - const ringErc20 = await ethers.getContractAt("Erc20", ringErc20Address, ethereumWallet); - - // register - await darwiniaLpBridge.registerToken( - wringAddress, - ringErc20Address, - // helix fee - 200, - // remote chain id - ethereumChainId, - 18, // local decimals - 18, // remote decimals - false - ); - await darwiniaLpBridge.setwTokenIndex(0); - await ethereumLpBridge.registerToken( - ringErc20Address, - wringAddress, - // helix fee - 100, - // remote chain id - darwiniaChainId, - 18, // local decimals - 18, // remote decimals - true - ); -} - -// 2. deploy mapping token factory -async function main() { - const wallets = wallet(); - const darwiniaWallet = wallets[0]; - const ethereumWallet = wallets[1]; - const darwiniaRelayWallet = wallets[2]; - const ethereumRelayWallet = wallets[3]; - - //const deployed = await deploy(darwiniaWallet, ethereumWallet); - //console.log(deployed); - - const bridgeOnDarwinia = "0x882Bd7aC70C4A4B1d6cE60a6366bC7cB87E0aA95"; - const bridgeOnEthereum = "0xDFF5f2360f88e6bbA7E90e79c57E07f1A9906F69"; - - - await getLpBridgeInitData(ethereumWallet, "0x9C80EdD342b5D179c3a87946fC1F0963BfcaAa09", "0x9bc1C7567DDBcaF2212185b6665D755d842d01E4", "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"); - //await getLpBridgeInitData(ethereumWallet, "0x9bc1C7567DDBcaF2212185b6665D755d842d01E4", "0x9C80EdD342b5D179c3a87946fC1F0963BfcaAa09", "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"); - return; - - //const wring = await ethers.getContractAt("WToken", wringAddress, darwiniaWallet); - //await wring.deposit({value: ethers.utils.parseEther("10")}); - //await wring.approve(bridgeOnDarwinia, ethers.utils.parseEther("1000")); - - //const ringErc20 = await ethers.getContractAt("Erc20", ringErc20Address, ethereumWallet); - //await ringErc20.approve(bridgeOnEthereum, ethers.utils.parseEther("10")); - //const ringErc20Relayer = await ethers.getContractAt("Erc20", ringErc20Address, ethereumRelayWallet); - //await ringErc20Relayer.approve(bridgeOnEthereum, ethers.utils.parseEther("10")); - - - // lock on darwina, issuing on ethereum - const amount1 = ethers.utils.parseEther("0.00110"); - const fee1 = ethers.utils.parseEther("0.00082"); - await lockAndRemoteIssuing(1, 0, bridgeOnDarwinia, darwiniaWallet, amount1, fee1, false); - const h1 = hash(1, false, ringErc20Address, darwiniaWallet.address, darwiniaWallet.address, amount1, darwiniaChainId, ethereumChainId); - console.log("h1", h1); - console.log("lock and remote issuing 1 successed"); - - // relay - await relay(1, ringErc20Address, darwiniaWallet.address, darwiniaWallet.address, amount1, darwiniaChainId, false, ethereumRelayWallet, bridgeOnEthereum); - console.log("relay 1 successed"); - - const amount2 = ethers.utils.parseEther("0.00112"); - const fee2 = ethers.utils.parseEther("0.00083"); - await lockAndRemoteIssueNative(2, bridgeOnDarwinia, darwiniaWallet, amount2, fee2); - console.log("lock and remote issuing 2 successed"); - const h2 = hash(2, false, ringErc20Address, darwiniaWallet.address, darwiniaWallet.address, amount2, darwiniaChainId, ethereumChainId); - console.log("h2", h2); - - await relay(2, ringErc20Address, darwiniaWallet.address, darwiniaWallet.address, amount1, darwiniaChainId, false, ethereumRelayWallet, bridgeOnEthereum); - console.log("relay 2 successed"); - - // lock on etheruem issuing on darwinia - const amounte1 = ethers.utils.parseEther("0.000017"); - const feee1 = ethers.utils.parseEther("0.00000082"); - await lockAndRemoteIssuing(1, 0, bridgeOnEthereum, ethereumWallet, amounte1, feee1, true); - console.log("lock and remote issuing 1 successed"); - const he1 = hash(1, true, wringAddress, ethereumWallet.address, ethereumWallet.address, amounte1, ethereumChainId, darwiniaChainId); - console.log("he1", he1); - await relay(1, wringAddress, darwiniaWallet.address, darwiniaWallet.address, amounte1, ethereumChainId, true, darwiniaRelayWallet, bridgeOnDarwinia); - console.log("relay 1 successed"); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - diff --git a/helix-contract/deploy/deploy_zksync2eth_target.js b/helix-contract/deploy/deploy_zksync2eth_target.js deleted file mode 100644 index b5bed783..00000000 --- a/helix-contract/deploy/deploy_zksync2eth_target.js +++ /dev/null @@ -1,487 +0,0 @@ -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -var ProxyDeployer = require("./proxy.js"); - -const privateKey = process.env.PRIKEY - -// goerli test <> zkSync goerli test -const ethereumUrl = "https://rpc.ankr.com/eth_goerli"; -const zkSyncUrl = "https://zksync2-testnet.zksync.dev"; -const ethereumProxyAdmin = "0x3F3eDBda6124462a09E071c5D90e072E0d5d4ed4"; -const zkSyncProxyAdmin = "0x66d86a686e50c98bac236105efafb99ee7605dc5"; -const mailboxEthereumAddress = "0x1908e2BF4a88F91E4eF0DC72f02b8Ea36BEa2319"; -const daoOnEthereum = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; -const daoOnZkSync = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; - -const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -const zkSyncLnBridgeAddress = "0x9422E7883d1F9Dd2E0f5926D585115542D6C71dA"; - -const tokenInfos = { - RING: { - sourceAddress: "0x61C31A1fA4a8D765e63D4285f368aA2f4d912DbB", - targetAddress: "0x1836BAFa3016Dd5Ce543D0F7199cB858ec69F41E", - protocolFee: ethers.utils.parseEther("1.5"), - penalty: ethers.utils.parseEther("20"), - providerFee: ethers.utils.parseEther("2.5"), - providerLiquidityRate: 10, - sourceDecimals: 18, - targetDecimals: 18, - margin: ethers.utils.parseEther("10000"), - slashFund: ethers.utils.parseEther("10"), - }, - USDC: { - sourceAddress: "0x0faF6df7054946141266420b43783387A78d82A9", - targetAddress: "0xd35CCeEAD182dcee0F148EbaC9447DA2c4D449c4", - protocolFee: 1500000, - penalty: 10000000, - providerFee: 2500000, - providerLiquidityRate: 10, - sourceDecimals: 6, - targetDecimals: 6, - margin: 10000000000, - slashFund: 10000000, - }, - ETH: { - sourceAddress: "0x0000000000000000000000000000000000000000", - targetAddress: "0x0000000000000000000000000000000000000000", - protocolFee: ethers.utils.parseEther("0.0001"), - penalty: ethers.utils.parseEther("0.001"), - providerFee: ethers.utils.parseEther("0.00015"), - providerLiquidityRate: 10, - sourceDecimals: 18, - targetDecimals: 18, - margin: ethers.utils.parseEther("0.1"), - slashFund: ethers.utils.parseEther("0.0001"), - }, -}; - -async function getLnBridgeTargetInitData(wallet, dao, inbox) { - const bridgeContract = await ethers.getContractFactory("ZkSync2EthTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, inbox], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function getLnBridgeSourceInitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("ZkSync2EthSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("LnBridgeInitData init data:", initdata); -} - -async function transferAndLockMargin( - wallet, - bridgeAddress, - provider, - tokenAddress, - amount, - receiver) { - const bridge = await ethers.getContractAt("LnOppositeBridgeSource", bridgeAddress, wallet); - const expectedFee = await bridge.totalFee( - provider, - tokenAddress, - amount); - console.log("expect fee is", expectedFee); - const providerInfo = await bridge.lnProviders(await bridge.getProviderKey(provider, tokenAddress)); - const expectedMargin = providerInfo.config.margin; - console.log("expect margin is", expectedMargin); - let value = expectedFee.add(amount); - if (tokenAddress !== "0x0000000000000000000000000000000000000000") { - value = 0; - } - //const tx = await bridge.callStatic.transferAndLockMargin( - const tx = await bridge.transferAndLockMargin( - [ - provider, - tokenAddress, - providerInfo.lastTransferId, - expectedMargin, - expectedFee, - ], - amount, - wallet.address, - { value: value }, - ); - console.log(tx); -} - -async function relay( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("LnOppositeBridgeTarget", bridgeAddress, wallet); - //const tx = await bridge.callStatic.relay( - await bridge.transferAndReleaseMargin( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - ); - //console.log(tx); -} - -async function slash( - wallet, - bridgeAddress, - provider, - sourceToken, - targetToken, - previousTransferId, - timestamp, - receiver, - amount, - expectedTransferId, -) { - const bridge = await ethers.getContractAt("Arb2EthTarget", bridgeAddress, wallet); - const maxSubmissionCost = await bridge.submissionRefundFee( - 30000000000, - previousTransferId, - previousTransferId, - provider, - sourceToken, - wallet.address, - 10, - ); - const maxGas = 1000000; - const gasPriceBid = 20000000000; - const cost = maxSubmissionCost.add("0x470de4df820000"); - //return; - - //const tx = await bridge.callStatic.slashAndRemoteRefund( - await bridge.slashAndRemoteRefund( - [ - previousTransferId, - provider, - sourceToken, - targetToken, - amount, - timestamp, - receiver, - ], - expectedTransferId, - maxSubmissionCost, - maxGas, - gasPriceBid, - {value: cost }, - ); - //console.log(tx); -} - -async function requestWithdrawMargin( - wallet, - bridgeAddress, - lastTransferId, - sourceToken, - amount, -) { - const bridge = await ethers.getContractAt("Arb2EthTarget", bridgeAddress, wallet); - const maxSubmissionCost = await bridge.submissionWithdrawFee( - 30000000000, - lastTransferId, - sourceToken, - amount, - 10, - ); - const maxGas = 1000000; - const gasPriceBid = 20000000000; - const cost = maxSubmissionCost.add("0x470de4df820000"); - //return; - - //const tx = await bridge.callStatic.requestWithdrawMargin( - await bridge.requestWithdrawMargin( - lastTransferId, - sourceToken, - amount, - maxSubmissionCost, - maxGas, - gasPriceBid, - {value: cost }, - ); - //console.log(tx); -} - -function wallet() { - const ethereumProvider = new ethers.providers.JsonRpcProvider(ethereumUrl); - const ethereumWallet = new ethers.Wallet(privateKey, ethereumProvider); - const zkSyncProvider = new ethers.providers.JsonRpcProvider(zkSyncUrl); - const zkSyncWallet = new ethers.Wallet(privateKey, zkSyncProvider); - return [zkSyncWallet, ethereumWallet]; -} - -async function getLnBridgeOnL1InitData(wallet, dao, inbox) { - const bridgeContract = await ethers.getContractFactory("ZkSync2EthTarget", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao, inbox], - "initialize", - ); - console.log("ln bridge on l1 init data:", initdata); -} - -async function getLnBridgeOnL2InitData(wallet, dao) { - const bridgeContract = await ethers.getContractFactory("ZkSync2EthSource", wallet); - const initdata = await ProxyDeployer.getInitializerData( - bridgeContract.interface, - [dao], - "initialize", - ); - console.log("ln bridge on l2 init data:", initdata); -} - -async function deployLnZkSyncBridgeOnL2(wallet, dao, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("ZkSync2EthSource", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln bridge logic on L2, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao], - wallet); - console.log("finish to deploy ln bridge proxy on L2, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deployLnZkSyncBridgeOnL1(wallet, dao, inbox, proxyAdminAddress) { - const bridgeContract = await ethers.getContractFactory("ZkSync2EthTarget", wallet); - const lnBridgeLogic = await bridgeContract.deploy(); - await lnBridgeLogic.deployed(); - console.log("finish to deploy ln bridge logic on L1, address: ", lnBridgeLogic.address); - - const lnBridgeProxy = await ProxyDeployer.deployProxyContract( - proxyAdminAddress, - bridgeContract, - lnBridgeLogic.address, - [dao, inbox], - wallet); - console.log("finish to deploy ln bridge proxy on L1, address:", lnBridgeProxy.address); - return lnBridgeProxy.address; -} - -async function deploy(zkSyncWallet, ethereumWallet) { - const ethereumLnBridgeAddress = await deployLnZkSyncBridgeOnL1( - ethereumWallet, - daoOnEthereum, - mailboxEthereumAddress, - ethereumProxyAdmin - ); - - const zkSyncLnBridge = await ethers.getContractAt("ZkSync2EthSource", zkSyncLnBridgeAddress, zkSyncWallet); - const ethereumLnBridge = await ethers.getContractAt("ZkSync2EthTarget", ethereumLnBridgeAddress, ethereumWallet); - await zkSyncLnBridge.updateFeeReceiver(daoOnZkSync); - await zkSyncLnBridge.setRemoteBridge(ethereumLnBridgeAddress); - await ethereumLnBridge.setRemoteBridge(zkSyncLnBridgeAddress); - - return { - "LnBridgeOnZkSync": zkSyncLnBridgeAddress, - "LnBridgeOnEthereum": ethereumLnBridgeAddress, - }; -} - -async function registerToken(token, sourceLnBridgeAddress, wallet) { - const tokenInfo = tokenInfos[token]; - const zkSyncLnBridge = await ethers.getContractAt("ZkSync2EthSource", sourceLnBridgeAddress, wallet); - - // register token - await zkSyncLnBridge.registerToken( - tokenInfo.sourceAddress, - tokenInfo.targetAddress, - tokenInfo.protocolFee, - tokenInfo.penalty, - tokenInfo.sourceDecimals, - tokenInfo.targetDecimals, - ); - console.log("register token finished", token); - - -} - -async function registerProvider(token, sourceLnBridgeAddress, targetLnBridgeAddress, sourceWallet, targetWallet) { - // register provider - const tokenInfo = tokenInfos[token]; - const sourceLnBridge = await ethers.getContractAt("ZkSync2EthSource", sourceLnBridgeAddress, sourceWallet); - let value = tokenInfo.margin; - if (tokenInfo.sourceAddress !== "0x0000000000000000000000000000000000000000") { - const sourceToken = await ethers.getContractAt("Erc20", tokenInfo.sourceAddress, sourceWallet); - const targetToken = await ethers.getContractAt("Erc20", tokenInfo.targetAddress, targetWallet); - await sourceToken.approve(sourceLnBridgeAddress, ethers.utils.parseEther("10000000")); - await targetToken.approve(targetLnBridgeAddress, ethers.utils.parseEther("10000000")); - value = 0; - } - await sourceLnBridge.updateProviderFeeAndMargin( - tokenInfo.sourceAddress, - tokenInfo.margin, - tokenInfo.providerFee, - tokenInfo.providerLiquidityRate, - { value: value }, - ); - console.log("register provider finished", token); -} - -async function lockToken( - token, - bridgeAddress, - amount, - sourceWallet, - targetWallet, -) { - const tokenInfo = tokenInfos[token]; - // lock - await transferAndLockMargin( - sourceWallet, - bridgeAddress, - sourceWallet.address, - tokenInfo.sourceAddress, - amount, - sourceWallet.address, - ); - console.log("transfer and lock margin 1 successed"); -} - -async function updateProviderInfo( - zkSyncLnBridgeAddress, - zkSyncWallet, - margin, - baseFee, - liquidityFeeRate, -) { - const zkSyncLnBridge = await ethers.getContractAt("ZkSync2EthSource", zkSyncLnBridgeAddress, zkSyncWallet); - await zkSyncLnBridge.updateProviderFeeAndMargin( - ringZkSyncAddress, - margin, - baseFee, - liquidityFeeRate, - ); -} - -// 2. deploy mapping token factory -async function main() { - const wallets = wallet(); - const zkSyncWallet = wallets[0]; - const ethereumWallet = wallets[1]; - - //await getLnBridgeTargetInitData(zkSyncWallet, "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", "0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f"); - //await getLnBridgeSourceInitData(zkSyncWallet, "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"); - //return; - - /* - const deployed = await deploy(zkSyncWallet, ethereumWallet); - console.log(deployed); - return; - */ - - const ethereumLnBridgeAddress = "0x6E7b0Af10aB840a47c47AeC97107487D2a17Eb2F"; - - /* - await registerToken('ETH', zkSyncLnBridgeAddress, zkSyncWallet); - await registerProvider('ETH', zkSyncLnBridgeAddress, ethereumLnBridgeAddress, zkSyncWallet, ethereumWallet); - return; - */ - - const amount = ethers.utils.parseEther("0.001"); - //const amount = 20000000; - await lockToken( - 'ETH', - zkSyncLnBridgeAddress, - amount, - zkSyncWallet, - ethereumWallet, - ); - return; - - - // relay - // query: lastTransferId on zkSync - const lastTransferId = "0x356AB27AE7C69D5BDB71C97B96EF45362E71EADE1A5EFE1ADF5706FC0DFC8625"; - const timestamp = 1688961375; - const expectedTransferId = "0xD1207442C3AC4BABC7500E06C2C08E3E5A46A452D92A7936A9B90ECE22C55E5E"; - - /* - await relay( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - ringZkSyncAddress, - ringEthereumAddress, - lastTransferId, - timestamp, - zkSyncWallet.address, - amount1, - expectedTransferId, - ) - console.log("relay 1 successed"); - return; - */ - - - // slasher - /* - await slash( - ethereumWallet, - ethereumLnBridgeAddress, - ethereumWallet.address, - ringZkSyncAddress, - ringEthereumAddress, - lastTransferId, - timestamp, - zkSyncWallet.address, - amount1, - expectedTransferId, - ); - console.log("slash successed"); - return; - */ - - // withdraw - - await requestWithdrawMargin( - ethereumWallet, - ethereumLnBridgeAddress, - "0xDD5703D47E4494FFC87660F3CBF2AFBA7A137755A91C81DC7ED120BB18E33A83", //lastTransferId - ringZkSyncAddress, - ethers.utils.parseEther("3"), // amount - ); - - console.log("withdraw successed"); - -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - -/* -zkSyncLnBridgeAddressLogic = 0xBFA90e358a9B2218ceb900afD9ac78691C92ABa6 -zkSyncLnBridgeAddressProxy = 0x7B8413FA1c1033844ac813A2E6475E15FB0fb3BA -ethereumLnBridgeAddressLogic = 0x0BA214a9Ab958C1A19D913f2Ac00119d27f196bB -ethereumLnBridgeAddressProxy = 0x3B1A953bFa72Af4ae3494b08e453BFF30a06A550 -*/ - diff --git a/helix-contract/deploy/erc721_backing_with_cbridge.js b/helix-contract/deploy/erc721_backing_with_cbridge.js deleted file mode 100644 index 3466d59b..00000000 --- a/helix-contract/deploy/erc721_backing_with_cbridge.js +++ /dev/null @@ -1,21 +0,0 @@ -var MappingTokenDeployer = require("./deploy.js") -var ProxyDeployer = require("./proxy.js"); - -// 1. deploy backing -async function main() { - // goerli testnet - const helixHandler = await MappingTokenDeployer.deploycBridgeHelixHandler("0xF25170F86E4291a99a9A560032Fe9948b8BcFBB2"); - console.log("helix handler is", helixHandler); - const admin = await ProxyDeployer.deployProxyAdmin(); - console.log("admin address is", admin.address); - await admin.deployed(); - const backing = await MappingTokenDeployer.deployErc721Backing(admin.address, helixHandler); - console.log("erc721 backing", backing.proxy.address); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/helix-contract/deploy/erc721_mtf_with_cbridge.js b/helix-contract/deploy/erc721_mtf_with_cbridge.js deleted file mode 100644 index 36687d7e..00000000 --- a/helix-contract/deploy/erc721_mtf_with_cbridge.js +++ /dev/null @@ -1,21 +0,0 @@ -var MappingTokenDeployer = require("./deploy.js") -var ProxyDeployer = require("./proxy.js"); - -// 2. deploy mapping token factory -async function main() { - // bsc testnet - const helixHandler = await MappingTokenDeployer.deploycBridgeHelixHandler("0xAd204986D6cB67A5Bc76a3CB8974823F43Cb9AAA"); - console.log("helix handler is", helixHandler.address); - const admin = await ProxyDeployer.deployProxyAdmin(); - console.log("admin address is", admin.address); - await admin.deployed(); - const mtf = await MappingTokenDeployer.deployErc721MappingTokenFactory(admin.address, helixHandler.address); - console.log("erc721 mtf", mtf.proxy.address); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/helix-contract/deploy/flatten-ln-arb2eth.sh b/helix-contract/deploy/flatten-ln-arb2eth.sh deleted file mode 100644 index b8290b75..00000000 --- a/helix-contract/deploy/flatten-ln-arb2eth.sh +++ /dev/null @@ -1,4 +0,0 @@ -path=flatten/lnv2 -mkdir -p $path -yarn flat contracts/ln/Arb2EthTarget.sol --output $path/Arb2EthTarget.sol -yarn flat contracts/ln/Arb2EthSource.sol --output $path/Arb2EthSource.sol diff --git a/helix-contract/deploy/flatten-ln-eth2arb.sh b/helix-contract/deploy/flatten-ln-eth2arb.sh deleted file mode 100644 index 391a6b02..00000000 --- a/helix-contract/deploy/flatten-ln-eth2arb.sh +++ /dev/null @@ -1,5 +0,0 @@ -path=flatten/lnv2 -mkdir -p $path -yarn flat contracts/ln/Eth2ArbSource.sol --output $path/Eth2ArbSource.sol -yarn flat contracts/ln/Eth2ArbTarget.sol --output $path/Eth2ArbTarget.sol - diff --git a/helix-contract/deploy/flatten-ln-eth2linea.sh b/helix-contract/deploy/flatten-ln-eth2linea.sh deleted file mode 100644 index c6d315a6..00000000 --- a/helix-contract/deploy/flatten-ln-eth2linea.sh +++ /dev/null @@ -1,4 +0,0 @@ -path=flatten/lnv2 -mkdir -p $path -yarn flat contracts/ln/Eth2LineaSource.sol --output $path/Eth2LineaSource.sol -yarn flat contracts/ln/Eth2LineaTarget.sol --output $path/Eth2LineaTarget.sol diff --git a/helix-contract/deploy/flatten-ln-eth2zksync.sh b/helix-contract/deploy/flatten-ln-eth2zksync.sh deleted file mode 100644 index 68ec498d..00000000 --- a/helix-contract/deploy/flatten-ln-eth2zksync.sh +++ /dev/null @@ -1,4 +0,0 @@ -path=flatten/lnv2 -mkdir -p $path -yarn flat contracts/ln/Eth2ZkSyncSource.sol --output $path/Eth2ZkSyncSource.sol -yarn flat contracts/ln/Eth2ZkSyncTarget.sol --output $path/Eth2ZkSyncTarget.sol diff --git a/helix-contract/deploy/flatten-ln-layerzero.sh b/helix-contract/deploy/flatten-ln-layerzero.sh deleted file mode 100644 index 5834f274..00000000 --- a/helix-contract/deploy/flatten-ln-layerzero.sh +++ /dev/null @@ -1,3 +0,0 @@ -path=flatten/lnv2 -mkdir -p $path -yarn flat contracts/ln/LnBridgeBaseLZ.sol --output $path/LnBridgeBaseLZ.sol diff --git a/helix-contract/deploy/flatten-ln-linea2eth.sh b/helix-contract/deploy/flatten-ln-linea2eth.sh deleted file mode 100644 index 3849a53f..00000000 --- a/helix-contract/deploy/flatten-ln-linea2eth.sh +++ /dev/null @@ -1,4 +0,0 @@ -path=flatten/lnv2 -mkdir -p $path -yarn flat contracts/ln/Linea2EthSource.sol --output $path/Linea2EthSource.sol -yarn flat contracts/ln/Linea2EthTarget.sol --output $path/Linea2EthTarget.sol diff --git a/helix-contract/deploy/flatten-ln-zksync2eth.sh b/helix-contract/deploy/flatten-ln-zksync2eth.sh deleted file mode 100644 index b3b77fb9..00000000 --- a/helix-contract/deploy/flatten-ln-zksync2eth.sh +++ /dev/null @@ -1,4 +0,0 @@ -path=flatten/lnv2 -mkdir -p $path -yarn flat contracts/ln/ZkSync2EthSource.sol --output $path/ZkSync2EthSource.sol -yarn flat contracts/ln/ZkSync2EthTarget.sol --output $path/ZkSync2EthTarget.sol diff --git a/helix-contract/deploy/flatten-ln.sh b/helix-contract/deploy/flatten-ln.sh new file mode 100644 index 00000000..47550d25 --- /dev/null +++ b/helix-contract/deploy/flatten-ln.sh @@ -0,0 +1,11 @@ +path=flatten/lnv2 +mkdir -p $path +yarn flat contracts/ln/LnDefaultBridge.sol --output $path/LnDefaultBridge.sol +yarn flat contracts/ln/LnOppositeBridge.sol --output $path/LnOppositeBridge.sol +yarn flat contracts/ln/messager/Eth2LineaSendService.sol --output $path/Eth2LineaSendService.sol +yarn flat contracts/ln/messager/Eth2LineaReceiveService.sol --output $path/Eth2LineaReceiveService.sol +yarn flat contracts/ln/messager/Eth2ArbSendService.sol --output $path/Eth2ArbSendService.sol +yarn flat contracts/ln/messager/Eth2ArbReceiveService.sol --output $path/Eth2ArbReceiveService.sol +yarn flat contracts/ln/messager/LayerZeroMessager.sol --output $path/LayerZeroMessager.sol +yarn flat contracts/ln/messager/debugMessager.sol --output $path/debugMessager.sol +yarn flat contracts/ln/test/TestToken.sol --output $path/TestToken.sol diff --git a/helix-contract/deploy/flatten-lp.sh b/helix-contract/deploy/flatten-lp.sh deleted file mode 100644 index 01a0d59b..00000000 --- a/helix-contract/deploy/flatten-lp.sh +++ /dev/null @@ -1,4 +0,0 @@ -path=flatten/lp -mkdir -p $path -yarn flat contracts/mapping-token/v2/lp/LpSub2EthBridge.sol --output $path/LpSub2EthBridge.sol -yarn flat contracts/mapping-token/v2/lp/LpSub2SubBridge.sol --output $path/LpSub2SubBridge.sol diff --git a/helix-contract/deploy/flatten-parachain.sh b/helix-contract/deploy/flatten-parachain.sh deleted file mode 100644 index 0ab81de2..00000000 --- a/helix-contract/deploy/flatten-parachain.sh +++ /dev/null @@ -1,4 +0,0 @@ -path=flatten/toparachain -mkdir -p $path -yarn flat contracts/mapping-token/v2/native-parachain-protocol/NativeParachainBacking.sol --output $path/NativeParachainBacking.sol -yarn flat contracts/mapping-token/v2/message-endpoints/Darwinia2ParaMessageEndpoint.sol --output $path/Darwinia2ParaMessageEndpoint.sol diff --git a/helix-contract/deploy/proxy.js b/helix-contract/deploy/proxy.js index 5ae933e6..e3ba413f 100644 --- a/helix-contract/deploy/proxy.js +++ b/helix-contract/deploy/proxy.js @@ -1,3 +1,6 @@ +const { deployContract } = require("solidity-create2-deployer"); +var Create2 = require("./create2.js"); + var ProxyDeployer = { getInitializerData: function( contractInterface, @@ -23,6 +26,16 @@ var ProxyDeployer = { const proxy = await proxyContract.deploy(logicAddress, proxyAdminAddr, calldata) await proxy.deployed(); return proxy; + }, + deployProxyContract2: async function(deployerAddress, salt, proxyAdminAdder, logicFactory, logicAddress, args, wallet) { + const calldata = ProxyDeployer.getInitializerData(logicFactory.interface, args, "initialize"); + const proxyContract = await ethers.getContractFactory("TransparentUpgradeableProxy", wallet); + const deployedBytecode = Create2.getDeployedBytecode( + proxyContract, + ["address", "address", "bytes"], + [logicAddress, proxyAdminAdder, calldata] + ); + return await Create2.deploy(deployerAddress, wallet, deployedBytecode, salt); } } diff --git a/helix-contract/flatten/arbi2eth/LnArbitrumL1Issuing.sol b/helix-contract/flatten/arbi2eth/LnArbitrumL1Issuing.sol deleted file mode 100644 index c3ccc319..00000000 --- a/helix-contract/flatten/arbi2eth/LnArbitrumL1Issuing.sol +++ /dev/null @@ -1,2293 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 3/14/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/lp/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File contracts/mapping-token/v2/lp/interface/ILnBridgeBacking.sol -// License-Identifier: MIT - - -interface ILnBridgeBacking { - function withdrawLiquidity(bytes32[] memory hashes, bool withdrawNative, address liquidityProvider) external; -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/mapping-token/v2/lp/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lpBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lpBridgeHelper:transferFrom token failed"); - } -} - -// File contracts/mapping-token/v2/lp/base/LnBridgeIssuing.sol -// License-Identifier: MIT - - -contract LnBridgeIssuing is LnBridgeHelper { - mapping(bytes32 => address) public issuedMessages; - - event TransferRelayed(bytes32 transferId, address relayer); - - function relay( - uint256 nonce, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId, - bool issuingNative - ) payable external { - bytes32 transferId = keccak256(abi.encodePacked(nonce, issuingNative, token, sender, receiver, amount, sourceChainId, uint64(block.chainid))); - require(issuedMessages[transferId] == address(0), "lpBridgeIssuing:message exist"); - issuedMessages[transferId] = msg.sender; - if (issuingNative) { - require(msg.value == amount, "lpBridgeIssuing:invalid amount"); - payable(receiver).transfer(amount); - } else { - _safeTransferFrom(token, msg.sender, receiver, uint256(amount)); - } - emit TransferRelayed(transferId, msg.sender); - } - - // only lpProvider can request withdraw liquidity - function _encodeWithdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver) public view returns(bytes memory) { - for (uint idx = 0; idx < transferIds.length; idx++) { - address lpProvider = issuedMessages[transferIds[idx]]; - require(lpProvider == msg.sender, "invalid lpProvider"); - } - return abi.encodeWithSelector(ILnBridgeBacking.withdrawLiquidity.selector, transferIds, withdrawNative, receiver); - } - - // we only allowed token sender or receiver cancel the transaction - function _cancelIssuing( - uint256 nonce, - bool issuingNative, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId - ) internal returns(bytes32 transferId) { - require(sender == msg.sender || receiver == msg.sender, "lpBridgeIssuing:only sender or receiver allowed"); - transferId = keccak256(abi.encodePacked(nonce, issuingNative, token, sender, receiver, amount, sourceChainId, uint64(block.chainid))); - if (issuedMessages[transferId] == msg.sender) { - return transferId; - } - require(issuedMessages[transferId] == address(0), "lpBridgeIssuing:message exist"); - issuedMessages[transferId] = msg.sender; - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File @arbitrum/nitro-contracts/src/bridge/IOwnable.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.4.21 <0.9.0; - -interface IOwnable { - function owner() external view returns (address); -} - -// File @arbitrum/nitro-contracts/src/bridge/IBridge.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IBridge { - event MessageDelivered( - uint256 indexed messageIndex, - bytes32 indexed beforeInboxAcc, - address inbox, - uint8 kind, - address sender, - bytes32 messageDataHash, - uint256 baseFeeL1, - uint64 timestamp - ); - - event BridgeCallTriggered( - address indexed outbox, - address indexed to, - uint256 value, - bytes data - ); - - event InboxToggle(address indexed inbox, bool enabled); - - event OutboxToggle(address indexed outbox, bool enabled); - - event SequencerInboxUpdated(address newSequencerInbox); - - function allowedDelayedInboxList(uint256) external returns (address); - - function allowedOutboxList(uint256) external returns (address); - - /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - function delayedInboxAccs(uint256) external view returns (bytes32); - - /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - function sequencerInboxAccs(uint256) external view returns (bytes32); - - function rollup() external view returns (IOwnable); - - function sequencerInbox() external view returns (address); - - function activeOutbox() external view returns (address); - - function allowedDelayedInboxes(address inbox) external view returns (bool); - - function allowedOutboxes(address outbox) external view returns (bool); - - function sequencerReportedSubMessageCount() external view returns (uint256); - - /** - * @dev Enqueue a message in the delayed inbox accumulator. - * These messages are later sequenced in the SequencerInbox, either - * by the sequencer as part of a normal batch, or by force inclusion. - */ - function enqueueDelayedMessage( - uint8 kind, - address sender, - bytes32 messageDataHash - ) external payable returns (uint256); - - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external returns (bool success, bytes memory returnData); - - function delayedMessageCount() external view returns (uint256); - - function sequencerMessageCount() external view returns (uint256); - - // ---------- onlySequencerInbox functions ---------- - - function enqueueSequencerMessage( - bytes32 dataHash, - uint256 afterDelayedMessagesRead, - uint256 prevMessageCount, - uint256 newMessageCount - ) - external - returns ( - uint256 seqMessageIndex, - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 acc - ); - - /** - * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type - * This is done through a separate function entrypoint instead of allowing the sequencer inbox - * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either - * every delayed inbox or every sequencer inbox call. - */ - function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) - external - returns (uint256 msgNum); - - // ---------- onlyRollupOrOwner functions ---------- - - function setSequencerInbox(address _sequencerInbox) external; - - function setDelayedInbox(address inbox, bool enabled) external; - - function setOutbox(address inbox, bool enabled) external; - - // ---------- initializer ---------- - - function initialize(IOwnable rollup_) external; -} - -// File @arbitrum/nitro-contracts/src/bridge/IDelayedMessageProvider.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IDelayedMessageProvider { - /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator - event InboxMessageDelivered(uint256 indexed messageNum, bytes data); - - /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator - /// same as InboxMessageDelivered but the batch data is available in tx.input - event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); -} - -// File @arbitrum/nitro-contracts/src/libraries/IGasRefunder.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IGasRefunder { - function onGasSpent( - address payable spender, - uint256 gasUsed, - uint256 calldataSize - ) external returns (bool success); -} - -abstract contract GasRefundEnabled { - /// @dev this refunds the sender for execution costs of the tx - /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging - /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded - modifier refundsGas(IGasRefunder gasRefunder) { - uint256 startGasLeft = gasleft(); - _; - if (address(gasRefunder) != address(0)) { - uint256 calldataSize; - assembly { - calldataSize := calldatasize() - } - uint256 calldataWords = (calldataSize + 31) / 32; - // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost - startGasLeft += calldataWords * 6 + (calldataWords**2) / 512; - // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call - // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input - // solhint-disable-next-line avoid-tx-origin - if (msg.sender != tx.origin) { - // We can't be sure if this calldata came from the top level tx, - // so to be safe we tell the gas refunder there was no calldata. - calldataSize = 0; - } - gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize); - } - } -} - -// File @arbitrum/nitro-contracts/src/bridge/ISequencerInbox.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; -pragma experimental ABIEncoderV2; - - - -interface ISequencerInbox is IDelayedMessageProvider { - struct MaxTimeVariation { - uint256 delayBlocks; - uint256 futureBlocks; - uint256 delaySeconds; - uint256 futureSeconds; - } - - struct TimeBounds { - uint64 minTimestamp; - uint64 maxTimestamp; - uint64 minBlockNumber; - uint64 maxBlockNumber; - } - - enum BatchDataLocation { - TxInput, - SeparateBatchEvent, - NoData - } - - event SequencerBatchDelivered( - uint256 indexed batchSequenceNumber, - bytes32 indexed beforeAcc, - bytes32 indexed afterAcc, - bytes32 delayedAcc, - uint256 afterDelayedMessagesRead, - TimeBounds timeBounds, - BatchDataLocation dataLocation - ); - - event OwnerFunctionCalled(uint256 indexed id); - - /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input - event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data); - - /// @dev a valid keyset was added - event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes); - - /// @dev a keyset was invalidated - event InvalidateKeyset(bytes32 indexed keysetHash); - - function totalDelayedMessagesRead() external view returns (uint256); - - function bridge() external view returns (IBridge); - - /// @dev The size of the batch header - // solhint-disable-next-line func-name-mixedcase - function HEADER_LENGTH() external view returns (uint256); - - /// @dev If the first batch data byte after the header has this bit set, - /// the sequencer inbox has authenticated the data. Currently not used. - // solhint-disable-next-line func-name-mixedcase - function DATA_AUTHENTICATED_FLAG() external view returns (bytes1); - - function rollup() external view returns (IOwnable); - - function isBatchPoster(address) external view returns (bool); - - struct DasKeySetInfo { - bool isValidKeyset; - uint64 creationBlock; - } - - // https://github.com/ethereum/solidity/issues/11826 - // function maxTimeVariation() external view returns (MaxTimeVariation calldata); - // function dasKeySetInfo(bytes32) external view returns (DasKeySetInfo calldata); - - /// @notice Remove force inclusion delay after a L1 chainId fork - function removeDelayAfterFork() external; - - /// @notice Force messages from the delayed inbox to be included in the chain - /// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and - /// maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these - /// messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages. - /// @param _totalDelayedMessagesRead The total number of messages to read up to - /// @param kind The kind of the last message to be included - /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included - /// @param baseFeeL1 The l1 gas price of the last message to be included - /// @param sender The sender of the last message to be included - /// @param messageDataHash The messageDataHash of the last message to be included - function forceInclusion( - uint256 _totalDelayedMessagesRead, - uint8 kind, - uint64[2] calldata l1BlockAndTime, - uint256 baseFeeL1, - address sender, - bytes32 messageDataHash - ) external; - - function inboxAccs(uint256 index) external view returns (bytes32); - - function batchCount() external view returns (uint256); - - function isValidKeysetHash(bytes32 ksHash) external view returns (bool); - - /// @notice the creation block is intended to still be available after a keyset is deleted - function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256); - - // ---------- BatchPoster functions ---------- - - function addSequencerL2BatchFromOrigin( - uint256 sequenceNumber, - bytes calldata data, - uint256 afterDelayedMessagesRead, - IGasRefunder gasRefunder - ) external; - - function addSequencerL2Batch( - uint256 sequenceNumber, - bytes calldata data, - uint256 afterDelayedMessagesRead, - IGasRefunder gasRefunder, - uint256 prevMessageCount, - uint256 newMessageCount - ) external; - - // ---------- onlyRollupOrOwner functions ---------- - - /** - * @notice Set max delay for sequencer inbox - * @param maxTimeVariation_ the maximum time variation parameters - */ - function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external; - - /** - * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox - * @param addr the address - * @param isBatchPoster_ if the specified address should be authorized as a batch poster - */ - function setIsBatchPoster(address addr, bool isBatchPoster_) external; - - /** - * @notice Makes Data Availability Service keyset valid - * @param keysetBytes bytes of the serialized keyset - */ - function setValidKeyset(bytes calldata keysetBytes) external; - - /** - * @notice Invalidates a Data Availability Service keyset - * @param ksHash hash of the keyset - */ - function invalidateKeysetHash(bytes32 ksHash) external; - - // ---------- initializer ---------- - - function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external; -} - -// File @arbitrum/nitro-contracts/src/bridge/IInbox.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - - - -interface IInbox is IDelayedMessageProvider { - function bridge() external view returns (IBridge); - - function sequencerInbox() external view returns (ISequencerInbox); - - /** - * @notice Send a generic L2 message to the chain - * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input - * This method will be disabled upon L1 fork to prevent replay attacks on L2 - * @param messageData Data of the message being sent - */ - function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256); - - /** - * @notice Send a generic L2 message to the chain - * @dev This method can be used to send any type of message that doesn't require L1 validation - * This method will be disabled upon L1 fork to prevent replay attacks on L2 - * @param messageData Data of the message being sent - */ - function sendL2Message(bytes calldata messageData) external returns (uint256); - - function sendL1FundedUnsignedTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - bytes calldata data - ) external payable returns (uint256); - - function sendL1FundedContractTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - address to, - bytes calldata data - ) external payable returns (uint256); - - function sendUnsignedTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - function sendContractTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - /** - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendL1FundedUnsignedTransactionToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - bytes calldata data - ) external payable returns (uint256); - - /** - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendUnsignedTransactionToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - /** - * @notice Send a message to initiate L2 withdrawal - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendWithdrawEthToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - uint256 value, - address withdrawTo - ) external returns (uint256); - - /** - * @notice Get the L1 fee for submitting a retryable - * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value - * @dev This formula may change in the future, to future proof your code query this method instead of inlining!! - * @param dataLength The length of the retryable's calldata, in bytes - * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used - */ - function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) - external - view - returns (uint256); - - /** - * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract - * @dev This does not trigger the fallback function when receiving in the L2 side. - * Look into retryable tickets if you are interested in this functionality. - * @dev This function should not be called inside contract constructors - */ - function depositEth() external payable returns (uint256); - - /** - * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts - * @dev all msg.value will deposited to callValueRefundAddress on L2 - * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error - * @param to destination L2 contract address - * @param l2CallValue call value for retryable L2 message - * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee - * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance - * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled - * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param data ABI encoded data of L2 message - * @return unique message number of the retryable transaction - */ - function createRetryableTicket( - address to, - uint256 l2CallValue, - uint256 maxSubmissionCost, - address excessFeeRefundAddress, - address callValueRefundAddress, - uint256 gasLimit, - uint256 maxFeePerGas, - bytes calldata data - ) external payable returns (uint256); - - /** - * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts - * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds - * come from the deposit alone, rather than falling back on the user's L2 balance - * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress). - * createRetryableTicket method is the recommended standard. - * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error - * @param to destination L2 contract address - * @param l2CallValue call value for retryable L2 message - * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee - * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance - * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled - * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param data ABI encoded data of L2 message - * @return unique message number of the retryable transaction - */ - function unsafeCreateRetryableTicket( - address to, - uint256 l2CallValue, - uint256 maxSubmissionCost, - address excessFeeRefundAddress, - address callValueRefundAddress, - uint256 gasLimit, - uint256 maxFeePerGas, - bytes calldata data - ) external payable returns (uint256); - - // ---------- onlyRollupOrOwner functions ---------- - - /// @notice pauses all inbox functionality - function pause() external; - - /// @notice unpauses all inbox functionality - function unpause() external; - - // ---------- initializer ---------- - - /** - * @dev function to be called one time during the inbox upgrade process - * this is used to fix the storage slots - */ - function postUpgradeInit(IBridge _bridge) external; - - function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external; -} - -// File contracts/mapping-token/v2/lp/LnArbitrumL1Issuing.sol -// License-Identifier: MIT - - - - -contract LnArbitrumL1Issuing is Initializable, LnAccessController, LnBridgeIssuing { - IInbox public inbox; - address public remoteBacking; - - event TransferCanceled(bytes32 transferId, address sender); - - receive() external payable {} - - function initialize(address _dao, address _inbox) public initializer { - inbox = IInbox(_inbox); - _initialize(_dao); - } - - function setRemoteBacking(address _remoteBacking) external onlyDao { - remoteBacking = _remoteBacking; - } - - function submissionFee( - uint256 baseFee, - bytes32[] memory transferIds, - bool withdrawNative, - address receiver, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory withdrawCall = _encodeWithdrawLiquidity(transferIds, withdrawNative, receiver); - uint256 fee = IInbox(inbox).calculateRetryableSubmissionFee(withdrawCall.length, baseFee); - return fee + fee * percentIncrease / 100; - } - - function requestWithdrawLiquidity( - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid, - bytes32[] memory transferIds, - bool withdrawNative, - address receiver - ) payable external whenNotPaused { - bytes memory withdrawCall = _encodeWithdrawLiquidity(transferIds, withdrawNative, receiver); - _sendMessage( - maxSubmissionCost, - maxGas, - gasPriceBid, - withdrawCall, - msg.value); - } - - function _sendMessage( - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid, - bytes memory message, - uint256 prepaid - ) internal returns(uint256) { - return inbox.createRetryableTicket{ value: prepaid }( - remoteBacking, - 0, - maxSubmissionCost, - msg.sender, - msg.sender, - maxGas, - gasPriceBid, - message - ); - } - - function requestCancelIssuing( - uint256 nonce, - bool issuingNative, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId, - bool withdrawNative, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes32 transferId = _cancelIssuing(nonce, issuingNative, token, sender, receiver, amount, sourceChainId); - bytes32[] memory transferIds = new bytes32[](1); - transferIds[0] = transferId; - // return token to the source sender - bytes memory withdrawCall = _encodeWithdrawLiquidity(transferIds, withdrawNative, sender); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, withdrawCall, msg.value); - emit TransferCanceled(transferId, msg.sender); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/arbi2eth/LnArbitrumL2Backing.sol b/helix-contract/flatten/arbi2eth/LnArbitrumL2Backing.sol deleted file mode 100644 index bdc9fe31..00000000 --- a/helix-contract/flatten/arbi2eth/LnArbitrumL2Backing.sol +++ /dev/null @@ -1,1882 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 3/14/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/lp/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File contracts/mapping-token/interfaces/IWToken.sol -// License-Identifier: MIT - - -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/mapping-token/v2/lp/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lpBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lpBridgeHelper:transferFrom token failed"); - } -} - -// File contracts/mapping-token/v2/lp/base/LnBridgeBacking.sol -// License-Identifier: MIT - - - -/// @title LnBridgeBacking -/// @notice LnBridgeBacking is a contract to help user lock token and then trigger remote chain issuing -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnBridgeBacking is LnBridgeHelper { - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - uint32 constant public INVALID_TOKEN_INDEX = type(uint32).max; - // the registered token info - // localToken and remoteToken is the pair of erc20 token addresses - // helixFee is charged by system, if it's bigger then half of user's fee, descrease it to the half - // remoteChainId is the remote block.chainid - // remoteIsNative is true when the remoteToken is the remote wrapped native token - struct TokenInfo { - address localToken; - address remoteToken; - uint112 helixFee; - uint64 remoteChainId; - uint8 localDecimals; - uint8 remoteDecimals; - bool remoteIsNative; - } - // registered token info - TokenInfo[] public tokens; - // each time cross chain transfer, amount and fee can't be larger than type(uint112).max - struct LockInfo { - uint32 tokenIndex; - uint112 amount; - uint112 fee; - } - mapping(bytes32 => LockInfo) public lockInfos; - address public feeReceiver; - uint32 public wTokenIndex = INVALID_TOKEN_INDEX; - - event TokenLocked( - uint64 toChainId, - bool isNative, - bool issuingNative, - uint256 nonce, - bytes32 transferId, - address token, - uint112 amount, - uint112 fee, - address receiver); - event FeeUpdated(bytes32 transferId, uint256 fee); - event LiquidityWithdrawn(bytes32 transferId, address receiver); - - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "lpBridgeBacking:invalid helix fee receiver"); - feeReceiver = _feeReceiver; - } - - function _updateHelixFee(uint32 _tokenIndex, uint112 _helixFee) internal { - require(_tokenIndex < tokens.length, "lpBridgeBacking:invalid token index"); - tokens[_tokenIndex].helixFee = _helixFee; - } - - function _setwTokenIndex(uint32 _wTokenIndex) internal { - wTokenIndex = _wTokenIndex; - } - - function _registerToken( - address localToken, - address remoteToken, - uint112 helixFee, - uint64 remoteChainId, - uint8 localDecimals, - uint8 remoteDecimals, - bool remoteIsNative - ) internal { - tokens.push(TokenInfo(localToken, remoteToken, helixFee, remoteChainId, localDecimals, remoteDecimals, remoteIsNative)); - } - - function _lockAndRemoteIssuing( - bool lockNative, - uint256 nonce, - uint32 tokenIndex, - uint112 amount, - uint112 fee, - address receiver, - bool issuingNative - ) internal returns(TokenInfo memory tokenInfo) { - tokenInfo = tokens[tokenIndex]; - require(fee > tokenInfo.helixFee && amount > 0, "lpBridgeBacking:fee or amount is not enough"); - require(!issuingNative || tokenInfo.remoteIsNative, "lpBridgeBacking:remote not native"); - uint256 remoteAmount = uint256(amount) * 10**tokenInfo.remoteDecimals / 10**tokenInfo.localDecimals; - require(remoteAmount < MAX_TRANSFER_AMOUNT, "lpBridgeBacking:overflow amount"); - bytes32 transferId = keccak256(abi.encodePacked( - nonce, - issuingNative, - tokenInfo.remoteToken, - msg.sender, - receiver, - uint112(remoteAmount), - uint64(block.chainid), - tokenInfo.remoteChainId)); - require(lockInfos[transferId].amount == 0, "lpBridgeBacking:transferId exist"); - lockInfos[transferId] = LockInfo(tokenIndex, amount, fee); - emit TokenLocked(tokenInfo.remoteChainId, lockNative, issuingNative, nonce, transferId, tokenInfo.localToken, amount, fee, receiver); - } - - function lockAndRemoteIssuing( - uint256 nonce, - address receiver, - uint112 amount, - uint112 fee, - uint32 tokenIndex, - bool issuingNative - ) external { - require(tokens.length > tokenIndex, "lpBridgeBacking:token not registered"); - TokenInfo memory info = _lockAndRemoteIssuing(false, nonce, tokenIndex, amount, fee, receiver, issuingNative); - _safeTransferFrom(info.localToken, msg.sender, address(this), amount + fee); - } - - function lockNativeAndRemoteIssuing( - uint112 amount, - uint112 fee, - address receiver, - uint256 nonce, - bool issuingNative - ) external payable { - require(amount + fee == msg.value, "lpBridgeBacking:amount unmatched"); - require(wTokenIndex != INVALID_TOKEN_INDEX, "lpBridgeBacking:not support"); - TokenInfo memory info = _lockAndRemoteIssuing(true, nonce, wTokenIndex, amount, fee, receiver, issuingNative); - IWToken(info.localToken).deposit{value: amount + fee}(); - } - - function _increaseFee(bytes32 transferId, uint256 fee) internal returns(uint32 tokenIndex) { - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.amount > 0 && lockInfo.tokenIndex < tokens.length, "lpBridgeBacking:invalid transferId"); - uint256 newFee = lockInfo.fee + fee; - require(newFee < MAX_TRANSFER_AMOUNT, "lpBridgeBacking:fee too large"); - lockInfos[transferId].fee = uint112(newFee); - tokenIndex = lockInfo.tokenIndex; - emit FeeUpdated(transferId, newFee); - } - - function increaseFee(bytes32 transferId, uint256 fee) external { - uint32 tokenIndex = _increaseFee(transferId, fee); - TokenInfo memory tokenInfo = tokens[tokenIndex]; - _safeTransferFrom(tokenInfo.localToken, msg.sender, address(this), fee); - } - - function increaseNativeFee(bytes32 transferId) external payable { - uint32 tokenIndex = _increaseFee(transferId, msg.value); - require(tokenIndex == wTokenIndex && wTokenIndex != INVALID_TOKEN_INDEX, "lpBridgeBacking:invalid token index"); - TokenInfo memory tokenInfo = tokens[tokenIndex]; - IWToken(tokenInfo.localToken).deposit{value: msg.value}(); - } - - // we require the same token to withdrawn - function _withdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver - ) internal { - require(transferIds.length > 0, "lpBridgeBacking:invalid transferIds size"); - uint32 tokenIndex = lockInfos[transferIds[0]].tokenIndex; - require(tokenIndex < tokens.length, "lpBridgeBacking:out of token size"); - uint256 amount = 0; - uint256 fee = 0; - for (uint i = 0; i < transferIds.length; i++) { - bytes32 transferId = transferIds[i]; - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.amount > 0 && lockInfo.tokenIndex < tokens.length, "lpBridgeBacking:invalid transferId"); - require(lockInfo.tokenIndex == tokenIndex, "lpBridgeBacking:invalid tokenindex"); - //can't delete lockInfos directly - lockInfos[transferId].tokenIndex = INVALID_TOKEN_INDEX; - amount += lockInfo.amount; - fee += lockInfo.fee; - emit LiquidityWithdrawn(transferId, receiver); - } - TokenInfo memory tokenInfo = tokens[tokenIndex]; - uint256 helixFee = transferIds.length * tokenInfo.helixFee; - if (helixFee > fee / 2) { - helixFee = fee / 2; - } - uint256 lpAmount = amount + fee - helixFee; - if (withdrawNative && tokenIndex == wTokenIndex) { - IWToken(tokenInfo.localToken).withdraw(lpAmount); - payable(receiver).transfer(lpAmount); - } else { - _safeTransfer(tokenInfo.localToken, receiver, lpAmount); - } - _safeTransfer(tokenInfo.localToken, feeReceiver, helixFee); - } - - function tokenLength() external view returns (uint) { - return tokens.length; - } -} - -// File @arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - - -library AddressAliasHelper { - uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); - - /// @notice Utility function that converts the address in the L1 that submitted a tx to - /// the inbox to the msg.sender viewed in the L2 - /// @param l1Address the address in the L1 that triggered the tx to L2 - /// @return l2Address L2 address as viewed in msg.sender - function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { - unchecked { - l2Address = address(uint160(l1Address) + OFFSET); - } - } - - /// @notice Utility function that converts the msg.sender viewed in the L2 to the - /// address in the L1 that submitted a tx to the inbox - /// @param l2Address L2 address as viewed in msg.sender - /// @return l1Address the address in the L1 that triggered the tx to L2 - function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { - unchecked { - l1Address = address(uint160(l2Address) - OFFSET); - } - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/mapping-token/v2/lp/LnArbitrumL2Backing.sol -// License-Identifier: MIT - - - - -contract LnArbitrumL2Backing is Initializable, LnAccessController, LnBridgeBacking { - address public remoteIssuing; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == AddressAliasHelper.applyL1ToL2Alias(remoteIssuing), "LnArbitrumL2Backing:invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - _setFeeReceiver(dao); - _setwTokenIndex(INVALID_TOKEN_INDEX); - } - - function setwTokenIndex(uint32 _wTokenIndex) external onlyDao { - _setwTokenIndex(_wTokenIndex); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function updateHelixFee(uint32 _tokenIndex, uint112 _helixFee) external onlyDao { - _updateHelixFee(_tokenIndex, _helixFee); - } - - function setRemoteIssuing(address _remoteIssuing) external onlyDao { - remoteIssuing = _remoteIssuing; - } - - // backing mode called - function registerToken( - address local, - address remote, - uint112 helixFee, - uint32 remoteChainId, - uint8 localDecimals, - uint8 remoteDecimals, - bool remoteIsNative - ) external onlyOperator { - _registerToken(local, remote, helixFee, remoteChainId, localDecimals, remoteDecimals, remoteIsNative); - } - - function withdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver - ) external onlyRemoteBridge whenNotPaused { - _withdrawLiquidity(transferIds, withdrawNative, receiver); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Arb2EthSource.sol b/helix-contract/flatten/lnv2/Arb2EthSource.sol deleted file mode 100644 index 8cec442c..00000000 --- a/helix-contract/flatten/lnv2/Arb2EthSource.sol +++ /dev/null @@ -1,2056 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/12/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnOppositeBridgeSource.sol -// License-Identifier: MIT - - -/// @title LnBridgeSource -/// @notice LnBridgeSource is a contract to help user transfer token to liquidity node and generate proof, -/// then the liquidity node must transfer the same amount of the token to the user on target chain. -/// Otherwise if timeout the slasher can paid for relayer and slash the transfer, then request slash from lnProvider's margin. -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnOppositeBridgeSource is LnBridgeHelper { - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - // the Liquidity Node provider info - // Liquidity Node need register first - struct LnProviderConfigure { - uint112 margin; - uint112 baseFee; - // liquidityFeeRate / 100,000 * amount = liquidityFee - // the max liquidity fee rate is 0.255% - uint8 liquidityFeeRate; - } - struct LnProviderInfo { - LnProviderConfigure config; - bool pause; - bytes32 lastTransferId; - } - - // the Snapshot is the state of the token bridge when user prepare to transfer across chains. - // If the snapshot updated when the across chain transfer confirmed, it will - // 1. if lastTransferId updated, revert - // 2. if margin decrease or totalFee increase, revert - // 3. if margin increase or totalFee decrease, success - struct Snapshot { - address provider; - address sourceToken; - bytes32 transferId; - uint112 depositedMargin; - uint112 totalFee; - } - // registered token info - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // registered lnProviders - mapping(bytes32=>LnProviderInfo) public lnProviders; - // each time cross chain transfer, amount and fee can't be larger than type(uint112).max - struct LockInfo { - // amount + providerFee + penaltyLnCollateral - // the Indexer should be care about this value, it will frozen lnProvider's margin when the transfer not finished. - // and when the slasher slash success, this amount of token will be transfer from lnProvider's margin to slasher. - uint112 amountWithFeeAndPenalty; - bool hasSlashed; - } - // key: transferId = hash(proviousTransferId, timestamp, targetToken, receiver, targetAmount) - // * `proviousTransferId` is used to ensure the continuous of the transfer - // * `timestamp` is the block.timestmap to judge timeout on target chain(here we support source and target chain has the same world clock) - // * `targetToken`, `receiver` and `targetAmount` are used on target chain to transfer target token. - mapping(bytes32 => LockInfo) public lockInfos; - address public feeReceiver; - - event TokenLocked( - bytes32 transferId, - address provider, - address sourceToken, - uint112 amount, - uint112 fee, - uint64 timestamp, - address receiver); - event LiquidityWithdrawn(address provider, address token, uint112 amount); - event Slash(bytes32 transferId, address provider, address token, uint112 margin, address slasher); - // relayer - event LnProviderUpdated(address provider, address token, uint112 margin, uint112 baseFee, uint8 liquidityfeeRate); - - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - feeReceiver = _feeReceiver; - } - - function _updateProtocolFee(address _token, uint112 _protocolFee) internal { - require(tokenInfos[_token].isRegistered, "token not registered"); - tokenInfos[_token].protocolFee = _protocolFee; - } - - function _updatePenaltyLnCollateral(address _token, uint112 _penaltyLnCollateral) internal { - require(tokenInfos[_token].isRegistered, "token not registered"); - tokenInfos[_token].penaltyLnCollateral = _penaltyLnCollateral; - } - - function providerPause(address sourceToken) external { - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - lnProviders[providerKey].pause = true; - } - - function providerUnpause(address sourceToken) external { - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - lnProviders[providerKey].pause = false; - } - - // lnProvider can register or update its configure by using this function - // * `margin` is the increased value of the deposited margin - function updateProviderFeeAndMargin( - address sourceToken, - uint112 margin, - uint112 baseFee, - uint8 liquidityFeeRate - ) external payable { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token is not registered"); - - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - - LnProviderConfigure memory config = LnProviderConfigure( - // the margin can be only increased here - margin + providerInfo.config.margin, - baseFee, - liquidityFeeRate - ); - - lnProviders[providerKey].config = config; - - if (sourceToken == address(0)) { - require(msg.value == margin, "invalid margin value"); - } else { - if (margin > 0) { - _safeTransferFrom(sourceToken, msg.sender, address(this), margin); - } - } - emit LnProviderUpdated(msg.sender, sourceToken, config.margin, baseFee, liquidityFeeRate); - } - - function _registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) internal { - tokenInfos[sourceToken] = TokenInfo( - targetToken, - protocolFee, - penaltyLnCollateral, - sourceDecimals, - targetDecimals, - true - ); - } - - function calculateProviderFee(LnProviderConfigure memory config, uint112 amount) internal pure returns(uint256) { - return uint256(config.baseFee) + uint256(config.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; - } - - // the fee user should paid when transfer. - // totalFee = providerFee + protocolFee - // providerFee = provider.baseFee + provider.liquidityFeeRate * amount - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - bytes32 providerKey = getProviderKey(provider, sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.config, amount); - return providerFee + tokenInfos[sourceToken].protocolFee; - } - - // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. - // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. - // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. - // 1. the state(lastTransferId, fee, margin) must match snapshot - // 2. transferId not exist - function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - bytes32 providerKey = getProviderKey(snapshot.provider, snapshot.sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - - require(!providerInfo.pause, "provider paused"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; - - uint256 providerFee = calculateProviderFee(providerInfo.config, amount); - - // Note: this requirement is not enough to ensure that the lnProvider's margin is enough because there maybe some frozen margins in other transfers - require(providerInfo.config.margin >= amount + tokenInfo.penaltyLnCollateral + uint112(providerFee), "amount not valid"); - - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired"); - require(snapshot.totalFee >= tokenInfo.protocolFee + providerFee, "fee is invalid"); - require(snapshot.depositedMargin <= providerInfo.config.margin, "margin updated"); - - uint256 targetAmount = uint256(amount) * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - uint64 timestamp = uint64(block.timestamp); - bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - uint112(targetAmount))); - require(lockInfos[transferId].amountWithFeeAndPenalty == 0, "transferId exist"); - lockInfos[transferId] = LockInfo(amount + tokenInfo.penaltyLnCollateral + uint112(providerFee), false); - - // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; - - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); - if (tokenInfo.protocolFee > 0) { - _safeTransferNative(feeReceiver, tokenInfo.protocolFee); - } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; - if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); - } - } else { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - snapshot.provider, - amount + providerFee - ); - if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - feeReceiver, - tokenInfo.protocolFee - ); - } - } - emit TokenLocked( - transferId, - snapshot.provider, - snapshot.sourceToken, - amount, - uint112(providerFee), - timestamp, - receiver); - } - - // this slash is called by remote message - // the token should be sent to the slasher who slash and finish the transfer on target chain. - // latestSlashTransferId is the latest slashed transfer trusted from the target chain, and the current slash transfer cannot be executed before the latestSlash transfer. - // after slash, the margin of lnProvider need to be updated - function _slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address sourceToken, - address provider, - address slasher - ) internal { - // check lastTransfer - // ensure last slash transfer(checked on target chain) has been slashed - LockInfo memory lastLockInfo = lockInfos[latestSlashTransferId]; - require(lastLockInfo.hasSlashed || latestSlashTransferId == INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); - LockInfo memory lockInfo = lockInfos[transferId]; - - // ensure transfer exist and not slashed yet - require(!lockInfo.hasSlashed, "transfer has been slashed"); - require(lockInfo.amountWithFeeAndPenalty > 0, "lnBridgeSource:invalid transferId"); - - bytes32 providerKey = getProviderKey(provider, sourceToken); - - LnProviderInfo memory lnProvider = lnProviders[providerKey]; - lockInfos[transferId].hasSlashed = true; - // transfer token to the slasher - uint256 slashAmount = lockInfo.amountWithFeeAndPenalty; - require(lnProvider.config.margin >= slashAmount, "margin not enough"); - uint112 updatedMargin = lnProvider.config.margin - uint112(slashAmount); - lnProviders[providerKey].config.margin = updatedMargin; - - if (sourceToken == address(0)) { - _safeTransferNative(slasher, slashAmount); - } else { - _safeTransfer(sourceToken, slasher, slashAmount); - } - - emit Slash(transferId, provider, sourceToken, updatedMargin, slasher); - } - - // lastTransfer is the latest slash transfer, all transfer must be relayed or slashed - // if user use the snapshot before this transaction to send cross-chain transfer, it should be reverted because this `_withdrawMargin` will decrease margin. - function _withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) internal { - // check the latest slash transfer - // ensure latest slash tranfer(verified on target chain) has been slashed on source chain - LockInfo memory lastRefundLockInfo = lockInfos[latestSlashTransferId]; - require(lastRefundLockInfo.hasSlashed || latestSlashTransferId == INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); - - // use this condition to ensure that the withdraw message is sent by the provider - // the parameter provider is the message sender of this remote withdraw call - bytes32 providerKey = getProviderKey(provider, sourceToken); - LnProviderInfo memory lnProvider = lnProviders[providerKey]; - - // ensure all transfer has finished - require(lnProvider.lastTransferId == lastTransferId, "invalid last transferid"); - require(lnProvider.config.margin >= amount, "margin not enough"); - uint112 updatedMargin = lnProvider.config.margin - amount; - lnProviders[providerKey].config.margin = updatedMargin; - if (sourceToken == address(0)) { - _safeTransferNative(provider, amount); - } else { - _safeTransfer(sourceToken, provider, amount); - } - emit LiquidityWithdrawn(provider, sourceToken, updatedMargin); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File @arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - - -library AddressAliasHelper { - uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); - - /// @notice Utility function that converts the address in the L1 that submitted a tx to - /// the inbox to the msg.sender viewed in the L2 - /// @param l1Address the address in the L1 that triggered the tx to L2 - /// @return l2Address L2 address as viewed in msg.sender - function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { - unchecked { - l2Address = address(uint160(l1Address) + OFFSET); - } - } - - /// @notice Utility function that converts the msg.sender viewed in the L2 to the - /// address in the L1 that submitted a tx to the inbox - /// @param l2Address L2 address as viewed in msg.sender - /// @return l1Address the address in the L1 that triggered the tx to L2 - function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { - unchecked { - l1Address = address(uint160(l2Address) - OFFSET); - } - } -} - -// File contracts/ln/Arb2EthSource.sol -// License-Identifier: MIT - - - - -contract Arb2EthSource is Initializable, LnAccessController, LnOppositeBridgeSource { - address public remoteBridge; - address public remoteBridgeAlias; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == remoteBridgeAlias, "invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - _setFeeReceiver(dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function updateProtocolFee(address token, uint112 _protocolFee) external onlyDao { - _updateProtocolFee(token, _protocolFee); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - remoteBridgeAlias = AddressAliasHelper.applyL1ToL2Alias(remoteBridge); - } - - function setRemoteBridgeAlias(address _remoteBridgeAlias) external onlyDao { - remoteBridgeAlias = _remoteBridgeAlias; - } - - function registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) external onlyOperator { - _registerToken(sourceToken, targetToken, protocolFee, penaltyLnCollateral, sourceDecimals, targetDecimals); - } - - function slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external onlyRemoteBridge whenNotPaused { - _slash(latestSlashTransferId, transferId, sourceToken, provider, slasher); - } - - function withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdrawMargin(latestSlashTransferId, lastTransferId, provider, sourceToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Arb2EthTarget.sol b/helix-contract/flatten/lnv2/Arb2EthTarget.sol deleted file mode 100644 index 04de0e04..00000000 --- a/helix-contract/flatten/lnv2/Arb2EthTarget.sol +++ /dev/null @@ -1,2483 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/12/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/ln/interface/ILnOppositeBridgeSource.sol -// License-Identifier: MIT - - -interface ILnOppositeBridgeSource { - function slash( - bytes32 lastRefundTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external; - - function withdrawMargin( - bytes32 lastRefundTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external; -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnOppositeBridgeTarget.sol -// License-Identifier: MIT - - -contract LnOppositeBridgeTarget is LnBridgeHelper { - uint256 constant public MIN_REFUND_TIMESTAMP = 30 * 60; - - // if slasher == address(0), this FillTransfer is relayed by lnProvider - // otherwise, this FillTransfer is slashed by slasher - // if there is no slash transfer before, then it's latestSlashTransferId is assigned by INIT_SLASH_TRANSFER_ID, a special flag - struct SlashInfo { - address provider; - address sourceToken; - address slasher; - } - - // transferId => latest slash transfer Id - mapping(bytes32 => bytes32) public fillTransfers; - // transferId => Slash info - mapping(bytes32 => SlashInfo) public slashInfos; - - event TransferFilled(bytes32 transferId, address slasher); - - // if slasher is nonzero, then it's a slash fill transfer - function _checkPreviousAndFillTransfer( - bytes32 transferId, - bytes32 previousTransferId - ) internal { - // the first fill transfer, we fill the INIT_SLASH_TRANSFER_ID as the latest slash transferId - if (previousTransferId == bytes32(0)) { - fillTransfers[transferId] = INIT_SLASH_TRANSFER_ID; - } else { - // Find the previous slash fill, it is a slash fill if the slasher is not zero address. - bytes32 previousLatestSlashTransferId = fillTransfers[previousTransferId]; - require(previousLatestSlashTransferId != bytes32(0), "previous fill not exist"); - - SlashInfo memory previousSlashInfo = slashInfos[previousTransferId]; - // we use latestSlashTransferId to store the latest slash transferId - // if previous.slasher != 0, then previous is slashed - // if previous.slasher == 0, then previous is not slashed - bytes32 latestSlashTransferId = previousSlashInfo.slasher != address(0) ? previousTransferId : previousLatestSlashTransferId; - - fillTransfers[transferId] = latestSlashTransferId; - } - } - - // fill transfer - // 1. if transfer is not slashed or relayed, LnProvider relay message to fill the transfer, and the transfer finished on target chain - // 2. if transfer is timeout and not processed, slasher(any account) can fill the transfer and request slash - // if it's filled by slasher, we store the address of the slasher - // expectedTransferId used to ensure the parameter is the same as on source chain - // some cases - // 1) If transferId is not exist on source chain, it'll be rejected by source chain when shashed. - // 2) If transferId exist on source chain. We have the same hash process on source and target chain, so the previousTransferId is trusted. - // 2.1) If transferId is the first transfer Id of this provider, then previousTransferId is zero and the latestSlashTransferId is INIT_SLASH_TRANSFER_ID - // 2.2) If transferId is not the first transfer, then it's latestSlashTransferId has the next two scenarios - // * the previousTransfer is a slash transfer, then latestSlashTransferId is previousTransferId - // * the previousTransfer is a normal relayed transfer, then latestSlashTransferId is previousTransfer's latestSlashTransferId - // I. transferId is trusted => previousTransferId is trusted => previousTransfer.previousTransferId is trusted => ... => firstTransfer is trusted - // II. transferId is trusted => previousTransferId is trusted => latestSlashTransferId is trusted if previousTransfer is a slash transfer - // III. Both I and II => latestSlashTransferId is trusted if previousTransfer is normal relayed tranfer - function _fillTransfer( - TransferParameter calldata params, - bytes32 expectedTransferId - ) internal { - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount)); - require(expectedTransferId == transferId, "check expected transferId failed"); - // Make sure this transfer was never filled before - require(fillTransfers[transferId] == bytes32(0), "fill exist"); - - _checkPreviousAndFillTransfer(transferId, params.previousTransferId); - - if (params.targetToken == address(0)) { - require(msg.value >= params.amount, "invalid amount"); - _safeTransferNative(params.receiver, params.amount); - } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); - } - } - - function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external { - // normal relay message, fill slasher as zero - require(params.provider == msg.sender, "invalid provider"); - _fillTransfer(params, expectedTransferId); - - emit TransferFilled(expectedTransferId, address(0)); - } - - // The condition for slash is that the transfer has timed out - // Meanwhile we need to request a slash transaction to the source chain to withdraw the LnProvider's margin - // On the source chain, we need to verify all the transfers before has been relayed or slashed. - // So we needs to carry the the previous shash transferId to ensure that the slash is continuous. - function _slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId - ) internal returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_REFUND_TIMESTAMP, "slash time not expired"); - _fillTransfer(params, expectedTransferId); - - // slasher = msg.sender - slashInfos[expectedTransferId] = SlashInfo(params.provider, params.sourceToken, msg.sender); - - // Do not slash `transferId` in source chain unless `latestSlashTransferId` has been slashed - message = _encodeSlashCall( - fillTransfers[expectedTransferId], - expectedTransferId, - params.provider, - params.sourceToken, - msg.sender - ); - emit TransferFilled(expectedTransferId, msg.sender); - } - - // we use this to verify that the transfer has been slashed by user and it can resend the slash request - function _retrySlashAndRemoteRefund(bytes32 transferId) internal view returns(bytes memory message) { - bytes32 latestSlashTransferId = fillTransfers[transferId]; - // transfer must be filled - require(latestSlashTransferId != bytes32(0), "invalid transfer id"); - // transfer must be slashed - SlashInfo memory slashInfo = slashInfos[transferId]; - require(slashInfo.slasher != address(0), "slasher not exist"); - message = _encodeSlashCall( - latestSlashTransferId, - transferId, - slashInfo.provider, - slashInfo.sourceToken, - slashInfo.slasher - ); - } - - function _encodeSlashCall( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) internal pure returns(bytes memory) { - return abi.encodeWithSelector( - ILnOppositeBridgeSource.slash.selector, - latestSlashTransferId, - transferId, - provider, - sourceToken, - slasher - ); - } - - function _requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount - ) internal view returns(bytes memory message) { - bytes32 latestSlashTransferId = fillTransfers[lastTransferId]; - require(latestSlashTransferId != bytes32(0), "invalid last transfer"); - - return abi.encodeWithSelector( - ILnOppositeBridgeSource.withdrawMargin.selector, - latestSlashTransferId, - lastTransferId, - msg.sender, - sourceToken, - amount - ); - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @arbitrum/nitro-contracts/src/bridge/IOwnable.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.4.21 <0.9.0; - -interface IOwnable { - function owner() external view returns (address); -} - -// File @arbitrum/nitro-contracts/src/bridge/IBridge.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IBridge { - event MessageDelivered( - uint256 indexed messageIndex, - bytes32 indexed beforeInboxAcc, - address inbox, - uint8 kind, - address sender, - bytes32 messageDataHash, - uint256 baseFeeL1, - uint64 timestamp - ); - - event BridgeCallTriggered( - address indexed outbox, - address indexed to, - uint256 value, - bytes data - ); - - event InboxToggle(address indexed inbox, bool enabled); - - event OutboxToggle(address indexed outbox, bool enabled); - - event SequencerInboxUpdated(address newSequencerInbox); - - function allowedDelayedInboxList(uint256) external returns (address); - - function allowedOutboxList(uint256) external returns (address); - - /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - function delayedInboxAccs(uint256) external view returns (bytes32); - - /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - function sequencerInboxAccs(uint256) external view returns (bytes32); - - function rollup() external view returns (IOwnable); - - function sequencerInbox() external view returns (address); - - function activeOutbox() external view returns (address); - - function allowedDelayedInboxes(address inbox) external view returns (bool); - - function allowedOutboxes(address outbox) external view returns (bool); - - function sequencerReportedSubMessageCount() external view returns (uint256); - - /** - * @dev Enqueue a message in the delayed inbox accumulator. - * These messages are later sequenced in the SequencerInbox, either - * by the sequencer as part of a normal batch, or by force inclusion. - */ - function enqueueDelayedMessage( - uint8 kind, - address sender, - bytes32 messageDataHash - ) external payable returns (uint256); - - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external returns (bool success, bytes memory returnData); - - function delayedMessageCount() external view returns (uint256); - - function sequencerMessageCount() external view returns (uint256); - - // ---------- onlySequencerInbox functions ---------- - - function enqueueSequencerMessage( - bytes32 dataHash, - uint256 afterDelayedMessagesRead, - uint256 prevMessageCount, - uint256 newMessageCount - ) - external - returns ( - uint256 seqMessageIndex, - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 acc - ); - - /** - * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type - * This is done through a separate function entrypoint instead of allowing the sequencer inbox - * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either - * every delayed inbox or every sequencer inbox call. - */ - function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) - external - returns (uint256 msgNum); - - // ---------- onlyRollupOrOwner functions ---------- - - function setSequencerInbox(address _sequencerInbox) external; - - function setDelayedInbox(address inbox, bool enabled) external; - - function setOutbox(address inbox, bool enabled) external; - - // ---------- initializer ---------- - - function initialize(IOwnable rollup_) external; -} - -// File @arbitrum/nitro-contracts/src/bridge/IDelayedMessageProvider.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IDelayedMessageProvider { - /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator - event InboxMessageDelivered(uint256 indexed messageNum, bytes data); - - /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator - /// same as InboxMessageDelivered but the batch data is available in tx.input - event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); -} - -// File @arbitrum/nitro-contracts/src/libraries/IGasRefunder.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IGasRefunder { - function onGasSpent( - address payable spender, - uint256 gasUsed, - uint256 calldataSize - ) external returns (bool success); -} - -abstract contract GasRefundEnabled { - /// @dev this refunds the sender for execution costs of the tx - /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging - /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded - modifier refundsGas(IGasRefunder gasRefunder) { - uint256 startGasLeft = gasleft(); - _; - if (address(gasRefunder) != address(0)) { - uint256 calldataSize; - assembly { - calldataSize := calldatasize() - } - uint256 calldataWords = (calldataSize + 31) / 32; - // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost - startGasLeft += calldataWords * 6 + (calldataWords**2) / 512; - // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call - // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input - // solhint-disable-next-line avoid-tx-origin - if (msg.sender != tx.origin) { - // We can't be sure if this calldata came from the top level tx, - // so to be safe we tell the gas refunder there was no calldata. - calldataSize = 0; - } - gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize); - } - } -} - -// File @arbitrum/nitro-contracts/src/bridge/ISequencerInbox.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; -pragma experimental ABIEncoderV2; - - - -interface ISequencerInbox is IDelayedMessageProvider { - struct MaxTimeVariation { - uint256 delayBlocks; - uint256 futureBlocks; - uint256 delaySeconds; - uint256 futureSeconds; - } - - struct TimeBounds { - uint64 minTimestamp; - uint64 maxTimestamp; - uint64 minBlockNumber; - uint64 maxBlockNumber; - } - - enum BatchDataLocation { - TxInput, - SeparateBatchEvent, - NoData - } - - event SequencerBatchDelivered( - uint256 indexed batchSequenceNumber, - bytes32 indexed beforeAcc, - bytes32 indexed afterAcc, - bytes32 delayedAcc, - uint256 afterDelayedMessagesRead, - TimeBounds timeBounds, - BatchDataLocation dataLocation - ); - - event OwnerFunctionCalled(uint256 indexed id); - - /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input - event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data); - - /// @dev a valid keyset was added - event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes); - - /// @dev a keyset was invalidated - event InvalidateKeyset(bytes32 indexed keysetHash); - - function totalDelayedMessagesRead() external view returns (uint256); - - function bridge() external view returns (IBridge); - - /// @dev The size of the batch header - // solhint-disable-next-line func-name-mixedcase - function HEADER_LENGTH() external view returns (uint256); - - /// @dev If the first batch data byte after the header has this bit set, - /// the sequencer inbox has authenticated the data. Currently not used. - // solhint-disable-next-line func-name-mixedcase - function DATA_AUTHENTICATED_FLAG() external view returns (bytes1); - - function rollup() external view returns (IOwnable); - - function isBatchPoster(address) external view returns (bool); - - struct DasKeySetInfo { - bool isValidKeyset; - uint64 creationBlock; - } - - // https://github.com/ethereum/solidity/issues/11826 - // function maxTimeVariation() external view returns (MaxTimeVariation calldata); - // function dasKeySetInfo(bytes32) external view returns (DasKeySetInfo calldata); - - /// @notice Remove force inclusion delay after a L1 chainId fork - function removeDelayAfterFork() external; - - /// @notice Force messages from the delayed inbox to be included in the chain - /// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and - /// maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these - /// messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages. - /// @param _totalDelayedMessagesRead The total number of messages to read up to - /// @param kind The kind of the last message to be included - /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included - /// @param baseFeeL1 The l1 gas price of the last message to be included - /// @param sender The sender of the last message to be included - /// @param messageDataHash The messageDataHash of the last message to be included - function forceInclusion( - uint256 _totalDelayedMessagesRead, - uint8 kind, - uint64[2] calldata l1BlockAndTime, - uint256 baseFeeL1, - address sender, - bytes32 messageDataHash - ) external; - - function inboxAccs(uint256 index) external view returns (bytes32); - - function batchCount() external view returns (uint256); - - function isValidKeysetHash(bytes32 ksHash) external view returns (bool); - - /// @notice the creation block is intended to still be available after a keyset is deleted - function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256); - - // ---------- BatchPoster functions ---------- - - function addSequencerL2BatchFromOrigin( - uint256 sequenceNumber, - bytes calldata data, - uint256 afterDelayedMessagesRead, - IGasRefunder gasRefunder - ) external; - - function addSequencerL2Batch( - uint256 sequenceNumber, - bytes calldata data, - uint256 afterDelayedMessagesRead, - IGasRefunder gasRefunder, - uint256 prevMessageCount, - uint256 newMessageCount - ) external; - - // ---------- onlyRollupOrOwner functions ---------- - - /** - * @notice Set max delay for sequencer inbox - * @param maxTimeVariation_ the maximum time variation parameters - */ - function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external; - - /** - * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox - * @param addr the address - * @param isBatchPoster_ if the specified address should be authorized as a batch poster - */ - function setIsBatchPoster(address addr, bool isBatchPoster_) external; - - /** - * @notice Makes Data Availability Service keyset valid - * @param keysetBytes bytes of the serialized keyset - */ - function setValidKeyset(bytes calldata keysetBytes) external; - - /** - * @notice Invalidates a Data Availability Service keyset - * @param ksHash hash of the keyset - */ - function invalidateKeysetHash(bytes32 ksHash) external; - - // ---------- initializer ---------- - - function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external; -} - -// File @arbitrum/nitro-contracts/src/bridge/IInbox.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - - - -interface IInbox is IDelayedMessageProvider { - function bridge() external view returns (IBridge); - - function sequencerInbox() external view returns (ISequencerInbox); - - /** - * @notice Send a generic L2 message to the chain - * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input - * This method will be disabled upon L1 fork to prevent replay attacks on L2 - * @param messageData Data of the message being sent - */ - function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256); - - /** - * @notice Send a generic L2 message to the chain - * @dev This method can be used to send any type of message that doesn't require L1 validation - * This method will be disabled upon L1 fork to prevent replay attacks on L2 - * @param messageData Data of the message being sent - */ - function sendL2Message(bytes calldata messageData) external returns (uint256); - - function sendL1FundedUnsignedTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - bytes calldata data - ) external payable returns (uint256); - - function sendL1FundedContractTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - address to, - bytes calldata data - ) external payable returns (uint256); - - function sendUnsignedTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - function sendContractTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - /** - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendL1FundedUnsignedTransactionToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - bytes calldata data - ) external payable returns (uint256); - - /** - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendUnsignedTransactionToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - /** - * @notice Send a message to initiate L2 withdrawal - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendWithdrawEthToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - uint256 value, - address withdrawTo - ) external returns (uint256); - - /** - * @notice Get the L1 fee for submitting a retryable - * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value - * @dev This formula may change in the future, to future proof your code query this method instead of inlining!! - * @param dataLength The length of the retryable's calldata, in bytes - * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used - */ - function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) - external - view - returns (uint256); - - /** - * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract - * @dev This does not trigger the fallback function when receiving in the L2 side. - * Look into retryable tickets if you are interested in this functionality. - * @dev This function should not be called inside contract constructors - */ - function depositEth() external payable returns (uint256); - - /** - * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts - * @dev all msg.value will deposited to callValueRefundAddress on L2 - * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error - * @param to destination L2 contract address - * @param l2CallValue call value for retryable L2 message - * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee - * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance - * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled - * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param data ABI encoded data of L2 message - * @return unique message number of the retryable transaction - */ - function createRetryableTicket( - address to, - uint256 l2CallValue, - uint256 maxSubmissionCost, - address excessFeeRefundAddress, - address callValueRefundAddress, - uint256 gasLimit, - uint256 maxFeePerGas, - bytes calldata data - ) external payable returns (uint256); - - /** - * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts - * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds - * come from the deposit alone, rather than falling back on the user's L2 balance - * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress). - * createRetryableTicket method is the recommended standard. - * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error - * @param to destination L2 contract address - * @param l2CallValue call value for retryable L2 message - * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee - * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance - * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled - * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param data ABI encoded data of L2 message - * @return unique message number of the retryable transaction - */ - function unsafeCreateRetryableTicket( - address to, - uint256 l2CallValue, - uint256 maxSubmissionCost, - address excessFeeRefundAddress, - address callValueRefundAddress, - uint256 gasLimit, - uint256 maxFeePerGas, - bytes calldata data - ) external payable returns (uint256); - - // ---------- onlyRollupOrOwner functions ---------- - - /// @notice pauses all inbox functionality - function pause() external; - - /// @notice unpauses all inbox functionality - function unpause() external; - - // ---------- initializer ---------- - - /** - * @dev function to be called one time during the inbox upgrade process - * this is used to fix the storage slots - */ - function postUpgradeInit(IBridge _bridge) external; - - function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external; -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Arb2EthTarget.sol -// License-Identifier: MIT - - - - -contract Arb2EthTarget is Initializable, LnAccessController, LnOppositeBridgeTarget { - IInbox public inbox; - address public remoteBridge; - - event WithdrawMargin(bytes32 lastTransferId, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _inbox) public initializer { - inbox = IInbox(_inbox); - _initialize(_dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function submissionRefundFee( - uint256 baseFee, - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory slashCall = _encodeSlashCall( - latestSlashTransferId, - transferId, - provider, - sourceToken, - slasher - ); - uint256 fee = inbox.calculateRetryableSubmissionFee(slashCall.length, baseFee); - return fee + fee * percentIncrease / 100; - } - - function submissionWithdrawFee( - uint256 baseFee, - bytes32 lastTransferId, - address sourceToken, - uint112 amount, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory withdrawCall = _requestWithdrawMargin( - lastTransferId, - sourceToken, - amount - ); - uint256 fee = inbox.calculateRetryableSubmissionFee(withdrawCall.length, baseFee); - return fee + fee * percentIncrease / 100; - } - - function _sendMessage( - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid, - bytes memory message, - uint256 prepaid - ) internal returns(uint256) { - return inbox.createRetryableTicket{ value: prepaid }( - remoteBridge, - 0, - maxSubmissionCost, - msg.sender, - msg.sender, - maxGas, - gasPriceBid, - message - ); - } - - function slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory refundCallMessage = _slashAndRemoteRefund( - params, - expectedTransferId - ); - uint256 valueUsed = address(0) == params.targetToken ? params.amount : 0; - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, refundCallMessage, msg.value - valueUsed); - } - - function retryRemoteRefund( - bytes32 transferId, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory refundCallMessage = _retrySlashAndRemoteRefund(transferId); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, refundCallMessage, msg.value); - } - - function requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory cancelWithdrawMarginCall = _requestWithdrawMargin( - lastTransferId, - sourceToken, - amount - ); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, cancelWithdrawMarginCall, msg.value); - emit WithdrawMargin(lastTransferId, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2ArbReceiveService.sol b/helix-contract/flatten/lnv2/Eth2ArbReceiveService.sol new file mode 100644 index 00000000..28c7c395 --- /dev/null +++ b/helix-contract/flatten/lnv2/Eth2ArbReceiveService.sol @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * + * 10/17/2023 + **/ + +pragma solidity ^0.8.17; + +// File contracts/ln/base/LnAccessController.sol +// License-Identifier: MIT + +/// @title LnAccessController +/// @notice LnAccessController is a contract to control the access permission +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnAccessController { + address public dao; + address public operator; + + mapping(address=>bool) public callerWhiteList; + + modifier onlyDao() { + require(msg.sender == dao, "!dao"); + _; + } + + modifier onlyOperator() { + require(msg.sender == operator, "!operator"); + _; + } + + modifier onlyWhiteListCaller() { + require(callerWhiteList[msg.sender], "caller not in white list"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; + } + + function authoriseAppCaller(address appAddress, bool enable) onlyDao external { + callerWhiteList[appAddress] = enable; + } + + function transferOwnership(address _dao) onlyDao external { + dao = _dao; + } +} + +// File contracts/ln/interface/ILowLevelMessager.sol +// License-Identifier: MIT + +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} + +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; +} + +// File @arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol@v1.0.1 +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// License-Identifier: BUSL-1.1 + + +library AddressAliasHelper { + uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); + + /// @notice Utility function that converts the address in the L1 that submitted a tx to + /// the inbox to the msg.sender viewed in the L2 + /// @param l1Address the address in the L1 that triggered the tx to L2 + /// @return l2Address L2 address as viewed in msg.sender + function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { + unchecked { + l2Address = address(uint160(l1Address) + OFFSET); + } + } + + /// @notice Utility function that converts the msg.sender viewed in the L2 to the + /// address in the L1 that submitted a tx to the inbox + /// @param l2Address L2 address as viewed in msg.sender + /// @return l1Address the address in the L1 that triggered the tx to L2 + function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { + unchecked { + l1Address = address(uint160(l2Address) - OFFSET); + } + } +} + +// File contracts/ln/messager/Eth2ArbReceiveService.sol +// License-Identifier: MIT + + + +// from ethereum to arbitrum messager +contract Eth2ArbReceiveService is ILowLevelMessageReceiver, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + address public remoteMessagerAlias; + + mapping(address=>address) public appPairs; + + modifier onlyRemoteBridge() { + require(msg.sender == remoteMessagerAlias, "invalid remote caller"); + _; + } + + constructor(address _dao, uint256 _remoteChainId) { + _initialize(_dao); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessagerAlias = AddressAliasHelper.applyL1ToL2Alias(_remoteMessager); + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function recvMessage(address _remoteApp, address _localApp, bytes memory _message) onlyRemoteBridge external { + address remoteAppAddress = appPairs[_localApp]; + require(remoteAppAddress == _remoteApp, "invalid remote app"); + (bool result,) = _localApp.call(_message); + require(result == true, "local call failed"); + } +} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2ArbSendService.sol b/helix-contract/flatten/lnv2/Eth2ArbSendService.sol new file mode 100644 index 00000000..ec6ed992 --- /dev/null +++ b/helix-contract/flatten/lnv2/Eth2ArbSendService.sol @@ -0,0 +1,693 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * + * 10/17/2023 + **/ + +pragma solidity ^0.8.17; + +// File contracts/ln/base/LnAccessController.sol +// License-Identifier: MIT + +/// @title LnAccessController +/// @notice LnAccessController is a contract to control the access permission +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnAccessController { + address public dao; + address public operator; + + mapping(address=>bool) public callerWhiteList; + + modifier onlyDao() { + require(msg.sender == dao, "!dao"); + _; + } + + modifier onlyOperator() { + require(msg.sender == operator, "!operator"); + _; + } + + modifier onlyWhiteListCaller() { + require(callerWhiteList[msg.sender], "caller not in white list"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; + } + + function authoriseAppCaller(address appAddress, bool enable) onlyDao external { + callerWhiteList[appAddress] = enable; + } + + function transferOwnership(address _dao) onlyDao external { + dao = _dao; + } +} + +// File contracts/ln/interface/ILowLevelMessager.sol +// License-Identifier: MIT + +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} + +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; +} + +// File @arbitrum/nitro-contracts/src/bridge/IOwnable.sol@v1.0.1 +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// License-Identifier: BUSL-1.1 + +// solhint-disable-next-line compiler-version +pragma solidity >=0.4.21 <0.9.0; + +interface IOwnable { + function owner() external view returns (address); +} + +// File @arbitrum/nitro-contracts/src/bridge/IBridge.sol@v1.0.1 +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// License-Identifier: BUSL-1.1 + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; + +interface IBridge { + event MessageDelivered( + uint256 indexed messageIndex, + bytes32 indexed beforeInboxAcc, + address inbox, + uint8 kind, + address sender, + bytes32 messageDataHash, + uint256 baseFeeL1, + uint64 timestamp + ); + + event BridgeCallTriggered( + address indexed outbox, + address indexed to, + uint256 value, + bytes data + ); + + event InboxToggle(address indexed inbox, bool enabled); + + event OutboxToggle(address indexed outbox, bool enabled); + + event SequencerInboxUpdated(address newSequencerInbox); + + function allowedDelayedInboxList(uint256) external returns (address); + + function allowedOutboxList(uint256) external returns (address); + + /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. + function delayedInboxAccs(uint256) external view returns (bytes32); + + /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. + function sequencerInboxAccs(uint256) external view returns (bytes32); + + function rollup() external view returns (IOwnable); + + function sequencerInbox() external view returns (address); + + function activeOutbox() external view returns (address); + + function allowedDelayedInboxes(address inbox) external view returns (bool); + + function allowedOutboxes(address outbox) external view returns (bool); + + function sequencerReportedSubMessageCount() external view returns (uint256); + + /** + * @dev Enqueue a message in the delayed inbox accumulator. + * These messages are later sequenced in the SequencerInbox, either + * by the sequencer as part of a normal batch, or by force inclusion. + */ + function enqueueDelayedMessage( + uint8 kind, + address sender, + bytes32 messageDataHash + ) external payable returns (uint256); + + function executeCall( + address to, + uint256 value, + bytes calldata data + ) external returns (bool success, bytes memory returnData); + + function delayedMessageCount() external view returns (uint256); + + function sequencerMessageCount() external view returns (uint256); + + // ---------- onlySequencerInbox functions ---------- + + function enqueueSequencerMessage( + bytes32 dataHash, + uint256 afterDelayedMessagesRead, + uint256 prevMessageCount, + uint256 newMessageCount + ) + external + returns ( + uint256 seqMessageIndex, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 acc + ); + + /** + * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type + * This is done through a separate function entrypoint instead of allowing the sequencer inbox + * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either + * every delayed inbox or every sequencer inbox call. + */ + function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) + external + returns (uint256 msgNum); + + // ---------- onlyRollupOrOwner functions ---------- + + function setSequencerInbox(address _sequencerInbox) external; + + function setDelayedInbox(address inbox, bool enabled) external; + + function setOutbox(address inbox, bool enabled) external; + + // ---------- initializer ---------- + + function initialize(IOwnable rollup_) external; +} + +// File @arbitrum/nitro-contracts/src/bridge/IDelayedMessageProvider.sol@v1.0.1 +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// License-Identifier: BUSL-1.1 + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; + +interface IDelayedMessageProvider { + /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator + event InboxMessageDelivered(uint256 indexed messageNum, bytes data); + + /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator + /// same as InboxMessageDelivered but the batch data is available in tx.input + event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); +} + +// File @arbitrum/nitro-contracts/src/libraries/IGasRefunder.sol@v1.0.1 +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// License-Identifier: BUSL-1.1 + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; + +interface IGasRefunder { + function onGasSpent( + address payable spender, + uint256 gasUsed, + uint256 calldataSize + ) external returns (bool success); +} + +abstract contract GasRefundEnabled { + /// @dev this refunds the sender for execution costs of the tx + /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging + /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded + modifier refundsGas(IGasRefunder gasRefunder) { + uint256 startGasLeft = gasleft(); + _; + if (address(gasRefunder) != address(0)) { + uint256 calldataSize; + assembly { + calldataSize := calldatasize() + } + uint256 calldataWords = (calldataSize + 31) / 32; + // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost + startGasLeft += calldataWords * 6 + (calldataWords**2) / 512; + // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call + // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input + // solhint-disable-next-line avoid-tx-origin + if (msg.sender != tx.origin) { + // We can't be sure if this calldata came from the top level tx, + // so to be safe we tell the gas refunder there was no calldata. + calldataSize = 0; + } + gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize); + } + } +} + +// File @arbitrum/nitro-contracts/src/bridge/ISequencerInbox.sol@v1.0.1 +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// License-Identifier: BUSL-1.1 + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; +pragma experimental ABIEncoderV2; + + + +interface ISequencerInbox is IDelayedMessageProvider { + struct MaxTimeVariation { + uint256 delayBlocks; + uint256 futureBlocks; + uint256 delaySeconds; + uint256 futureSeconds; + } + + struct TimeBounds { + uint64 minTimestamp; + uint64 maxTimestamp; + uint64 minBlockNumber; + uint64 maxBlockNumber; + } + + enum BatchDataLocation { + TxInput, + SeparateBatchEvent, + NoData + } + + event SequencerBatchDelivered( + uint256 indexed batchSequenceNumber, + bytes32 indexed beforeAcc, + bytes32 indexed afterAcc, + bytes32 delayedAcc, + uint256 afterDelayedMessagesRead, + TimeBounds timeBounds, + BatchDataLocation dataLocation + ); + + event OwnerFunctionCalled(uint256 indexed id); + + /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input + event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data); + + /// @dev a valid keyset was added + event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes); + + /// @dev a keyset was invalidated + event InvalidateKeyset(bytes32 indexed keysetHash); + + function totalDelayedMessagesRead() external view returns (uint256); + + function bridge() external view returns (IBridge); + + /// @dev The size of the batch header + // solhint-disable-next-line func-name-mixedcase + function HEADER_LENGTH() external view returns (uint256); + + /// @dev If the first batch data byte after the header has this bit set, + /// the sequencer inbox has authenticated the data. Currently not used. + // solhint-disable-next-line func-name-mixedcase + function DATA_AUTHENTICATED_FLAG() external view returns (bytes1); + + function rollup() external view returns (IOwnable); + + function isBatchPoster(address) external view returns (bool); + + struct DasKeySetInfo { + bool isValidKeyset; + uint64 creationBlock; + } + + // https://github.com/ethereum/solidity/issues/11826 + // function maxTimeVariation() external view returns (MaxTimeVariation calldata); + // function dasKeySetInfo(bytes32) external view returns (DasKeySetInfo calldata); + + /// @notice Remove force inclusion delay after a L1 chainId fork + function removeDelayAfterFork() external; + + /// @notice Force messages from the delayed inbox to be included in the chain + /// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and + /// maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these + /// messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages. + /// @param _totalDelayedMessagesRead The total number of messages to read up to + /// @param kind The kind of the last message to be included + /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included + /// @param baseFeeL1 The l1 gas price of the last message to be included + /// @param sender The sender of the last message to be included + /// @param messageDataHash The messageDataHash of the last message to be included + function forceInclusion( + uint256 _totalDelayedMessagesRead, + uint8 kind, + uint64[2] calldata l1BlockAndTime, + uint256 baseFeeL1, + address sender, + bytes32 messageDataHash + ) external; + + function inboxAccs(uint256 index) external view returns (bytes32); + + function batchCount() external view returns (uint256); + + function isValidKeysetHash(bytes32 ksHash) external view returns (bool); + + /// @notice the creation block is intended to still be available after a keyset is deleted + function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256); + + // ---------- BatchPoster functions ---------- + + function addSequencerL2BatchFromOrigin( + uint256 sequenceNumber, + bytes calldata data, + uint256 afterDelayedMessagesRead, + IGasRefunder gasRefunder + ) external; + + function addSequencerL2Batch( + uint256 sequenceNumber, + bytes calldata data, + uint256 afterDelayedMessagesRead, + IGasRefunder gasRefunder, + uint256 prevMessageCount, + uint256 newMessageCount + ) external; + + // ---------- onlyRollupOrOwner functions ---------- + + /** + * @notice Set max delay for sequencer inbox + * @param maxTimeVariation_ the maximum time variation parameters + */ + function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external; + + /** + * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox + * @param addr the address + * @param isBatchPoster_ if the specified address should be authorized as a batch poster + */ + function setIsBatchPoster(address addr, bool isBatchPoster_) external; + + /** + * @notice Makes Data Availability Service keyset valid + * @param keysetBytes bytes of the serialized keyset + */ + function setValidKeyset(bytes calldata keysetBytes) external; + + /** + * @notice Invalidates a Data Availability Service keyset + * @param ksHash hash of the keyset + */ + function invalidateKeysetHash(bytes32 ksHash) external; + + // ---------- initializer ---------- + + function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external; +} + +// File @arbitrum/nitro-contracts/src/bridge/IInbox.sol@v1.0.1 +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// License-Identifier: BUSL-1.1 + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; + + + +interface IInbox is IDelayedMessageProvider { + function bridge() external view returns (IBridge); + + function sequencerInbox() external view returns (ISequencerInbox); + + /** + * @notice Send a generic L2 message to the chain + * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input + * This method will be disabled upon L1 fork to prevent replay attacks on L2 + * @param messageData Data of the message being sent + */ + function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256); + + /** + * @notice Send a generic L2 message to the chain + * @dev This method can be used to send any type of message that doesn't require L1 validation + * This method will be disabled upon L1 fork to prevent replay attacks on L2 + * @param messageData Data of the message being sent + */ + function sendL2Message(bytes calldata messageData) external returns (uint256); + + function sendL1FundedUnsignedTransaction( + uint256 gasLimit, + uint256 maxFeePerGas, + uint256 nonce, + address to, + bytes calldata data + ) external payable returns (uint256); + + function sendL1FundedContractTransaction( + uint256 gasLimit, + uint256 maxFeePerGas, + address to, + bytes calldata data + ) external payable returns (uint256); + + function sendUnsignedTransaction( + uint256 gasLimit, + uint256 maxFeePerGas, + uint256 nonce, + address to, + uint256 value, + bytes calldata data + ) external returns (uint256); + + function sendContractTransaction( + uint256 gasLimit, + uint256 maxFeePerGas, + address to, + uint256 value, + bytes calldata data + ) external returns (uint256); + + /** + * @dev This method can only be called upon L1 fork and will not alias the caller + * This method will revert if not called from origin + */ + function sendL1FundedUnsignedTransactionToFork( + uint256 gasLimit, + uint256 maxFeePerGas, + uint256 nonce, + address to, + bytes calldata data + ) external payable returns (uint256); + + /** + * @dev This method can only be called upon L1 fork and will not alias the caller + * This method will revert if not called from origin + */ + function sendUnsignedTransactionToFork( + uint256 gasLimit, + uint256 maxFeePerGas, + uint256 nonce, + address to, + uint256 value, + bytes calldata data + ) external returns (uint256); + + /** + * @notice Send a message to initiate L2 withdrawal + * @dev This method can only be called upon L1 fork and will not alias the caller + * This method will revert if not called from origin + */ + function sendWithdrawEthToFork( + uint256 gasLimit, + uint256 maxFeePerGas, + uint256 nonce, + uint256 value, + address withdrawTo + ) external returns (uint256); + + /** + * @notice Get the L1 fee for submitting a retryable + * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value + * @dev This formula may change in the future, to future proof your code query this method instead of inlining!! + * @param dataLength The length of the retryable's calldata, in bytes + * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used + */ + function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) + external + view + returns (uint256); + + /** + * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract + * @dev This does not trigger the fallback function when receiving in the L2 side. + * Look into retryable tickets if you are interested in this functionality. + * @dev This function should not be called inside contract constructors + */ + function depositEth() external payable returns (uint256); + + /** + * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts + * @dev all msg.value will deposited to callValueRefundAddress on L2 + * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error + * @param to destination L2 contract address + * @param l2CallValue call value for retryable L2 message + * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee + * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance + * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled + * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) + * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) + * @param data ABI encoded data of L2 message + * @return unique message number of the retryable transaction + */ + function createRetryableTicket( + address to, + uint256 l2CallValue, + uint256 maxSubmissionCost, + address excessFeeRefundAddress, + address callValueRefundAddress, + uint256 gasLimit, + uint256 maxFeePerGas, + bytes calldata data + ) external payable returns (uint256); + + /** + * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts + * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds + * come from the deposit alone, rather than falling back on the user's L2 balance + * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress). + * createRetryableTicket method is the recommended standard. + * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error + * @param to destination L2 contract address + * @param l2CallValue call value for retryable L2 message + * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee + * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance + * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled + * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) + * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) + * @param data ABI encoded data of L2 message + * @return unique message number of the retryable transaction + */ + function unsafeCreateRetryableTicket( + address to, + uint256 l2CallValue, + uint256 maxSubmissionCost, + address excessFeeRefundAddress, + address callValueRefundAddress, + uint256 gasLimit, + uint256 maxFeePerGas, + bytes calldata data + ) external payable returns (uint256); + + // ---------- onlyRollupOrOwner functions ---------- + + /// @notice pauses all inbox functionality + function pause() external; + + /// @notice unpauses all inbox functionality + function unpause() external; + + // ---------- initializer ---------- + + /** + * @dev function to be called one time during the inbox upgrade process + * this is used to fix the storage slots + */ + function postUpgradeInit(IBridge _bridge) external; + + function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external; +} + +// File contracts/ln/messager/Eth2ArbSendService.sol +// License-Identifier: MIT + + + +// from ethereum to arbitrum messager +contract Eth2ArbSendService is ILowLevelMessageSender, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + IInbox public inbox; + address public remoteMessager; + mapping(address=>address) public appPairs; + + constructor(address _dao, address _inbox, uint256 _remoteChainId) { + _initialize(_dao); + inbox = IInbox(_inbox); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessager = _remoteMessager; + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function sendMessage(uint256 _remoteChainId, bytes memory _message, bytes memory _params) onlyWhiteListCaller external payable { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + address remoteAppAddress = appPairs[msg.sender]; + require(remoteAppAddress != address(0), "app not registered"); + + (uint256 maxSubmissionCost, uint256 l2GasPrice, uint256 l2GasLimit, address refunder) = abi.decode(_params, (uint256, uint256, uint256, address)); + + bytes memory remoteReceiveCall = abi.encodeWithSelector( + ILowLevelMessageReceiver.recvMessage.selector, + msg.sender, + remoteAppAddress, + _message + ); + inbox.createRetryableTicket{value: msg.value}( + remoteMessager, + 0, + maxSubmissionCost, + refunder, + refunder, + l2GasLimit, + l2GasPrice, + remoteReceiveCall + ); + } + + function fee( + uint256 _callSize, + uint256 _l1GasPrice, + uint256 _l2GasPrice, + uint256 _l2GasLimit, + uint256 _percentIncrease + ) external view returns(uint256, uint256) { + uint256 submissionFee = inbox.calculateRetryableSubmissionFee(_callSize, _l1GasPrice); + uint256 scaleSubmissionFee = submissionFee + submissionFee * _percentIncrease / 100; + return (scaleSubmissionFee, scaleSubmissionFee + _l2GasPrice * _l2GasLimit); + } + + function encodeParams( + uint256 _maxSubmissionCost, + uint256 _l2GasPrice, + uint256 _l2GasLimit, + address _refunder + ) external pure returns(bytes memory) { + return abi.encode(_maxSubmissionCost, _l2GasPrice, _l2GasLimit, _refunder); + } +} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2ArbSource.sol b/helix-contract/flatten/lnv2/Eth2ArbSource.sol deleted file mode 100644 index 037e7d4d..00000000 --- a/helix-contract/flatten/lnv2/Eth2ArbSource.sol +++ /dev/null @@ -1,2646 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/11/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/interface/ILnDefaultBridgeTarget.sol -// License-Identifier: MIT - - -interface ILnDefaultBridgeTarget { - function slash( - LnBridgeHelper.TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external; - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external; -} - -// File contracts/ln/base/LnDefaultBridgeSource.sol -// License-Identifier: MIT - - - -/// @title LnPositiveBridgeSource -/// @notice LnPositiveBridgeSource is a contract to help user transfer token to liquidity node and generate proof, -/// then the liquidity node must transfer the same amount of the token to the user on target chain. -/// Otherwise if timeout the slasher can send a slash request message to target chain, then force transfer from lnProvider's margin to the user. -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnDefaultBridgeSource is LnBridgeHelper { - // the time(seconds) for liquidity provider to delivery message - // if timeout, slasher can work. - uint256 constant public MIN_SLASH_TIMESTAMP = 30 * 60; - // liquidity fee base rate - // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - // max transfer amount one time - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - - // provider fee is paid to liquidity node's account - // the fee is charged by the same token that user transfered - // providerFee = baseFee + liquidityFeeRate/LIQUIDITY_FEE_RATE_BASE * sendAmount - struct LnProviderFee { - uint112 baseFee; - uint8 liquidityFeeRate; - } - - struct LnProviderInfo { - LnProviderFee fee; - // we use this nonce to generate the unique withdraw id - uint64 withdrawNonce; - bytes32 lastTransferId; - } - // the Snapshot is the state of the token bridge when user prepare to transfer across chains. - // If the snapshot updated when the across chain transfer confirmed, it will - // 1. if lastTransferId or withdrawNonce updated, revert - // 2. if totalFee increase, revert - // 3. if totalFee decrease, success - struct Snapshot { - address provider; - address sourceToken; - bytes32 transferId; - uint112 totalFee; - uint64 withdrawNonce; - } - - // lock info - // the fee and penalty is the state of the transfer confirmed - struct LockInfo { - uint112 fee; - uint112 penalty; - bool isLocked; - } - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // providerKey => provider info - mapping(bytes32=>LnProviderInfo) public lnProviders; - // transferId => lock info - mapping(bytes32=>LockInfo) public lockInfos; - - address public protocolFeeReceiver; - - event TokenLocked( - bytes32 transferId, - address provider, - address sourceToken, - uint112 amount, - uint112 fee, - uint64 timestamp, - address receiver); - event LnProviderUpdated(address provider, address sourceToken, uint112 baseFee, uint8 liquidityfeeRate); - - // protocolFeeReceiver is the protocol fee reciever, we don't use the contract itself as the receiver - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - protocolFeeReceiver = _feeReceiver; - } - - // register or update token info, it can be only called by contract owner - // source token can only map a unique target token on target chain - function _setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) internal { - tokenInfos[_sourceToken] = TokenInfo( - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals, - true - ); - } - - // lnProvider register - // 1. set fee on source chain - // 2. deposit margin on target chain - function setProviderFee( - address sourceToken, - uint112 baseFee, - uint8 liquidityFeeRate - ) external { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderFee memory providerFee = LnProviderFee(baseFee, liquidityFeeRate); - - // we only update the field fee of the provider info - // if the provider has not been registered, then this line will register, otherwise update fee - lnProviders[providerKey].fee = providerFee; - - emit LnProviderUpdated(msg.sender, sourceToken, baseFee, liquidityFeeRate); - } - - function calculateProviderFee(LnProviderFee memory fee, uint112 amount) internal pure returns(uint256) { - return uint256(fee.baseFee) + uint256(fee.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; - } - - // the fee user should paid when transfer. - // totalFee = providerFee + protocolFee - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - return providerFee + tokenInfo.protocolFee; - } - - // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. - // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. - // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. - // 1. the state(lastTransferId, fee, withdrawNonce) must match snapshot - // 2. transferId not exist - function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(snapshot.provider, snapshot.sourceToken, tokenInfo.targetToken); - - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired:transfer"); - require(snapshot.withdrawNonce == providerInfo.withdrawNonce, "snapshot expired:withdraw"); - require(snapshot.totalFee >= providerFee + tokenInfo.protocolFee && providerFee > 0, "fee is invalid"); - - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(amount)); - uint64 timestamp = uint64(block.timestamp); - bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - targetAmount - )); - require(!lockInfos[transferId].isLocked, "transferId exist"); - // if the transfer refund, then the fee and penalty should be given to slasher, but the protocol fee is ignored - // and we use the penalty value configure at the moment transfer confirmed - lockInfos[transferId] = LockInfo(snapshot.totalFee, tokenInfo.penaltyLnCollateral, true); - - // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; - - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); - if (tokenInfo.protocolFee > 0) { - _safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); - } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; - if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); - } - } else { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - snapshot.provider, - amount + providerFee - ); - if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - protocolFeeReceiver, - tokenInfo.protocolFee - ); - } - } - emit TokenLocked( - transferId, - snapshot.provider, - snapshot.sourceToken, - targetAmount, - uint112(providerFee), - timestamp, - receiver); - } - - function _sourceAmountToTargetAmount( - TokenInfo memory tokenInfo, - uint256 amount - ) internal pure returns(uint112) { - uint256 targetAmount = amount * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - return uint112(targetAmount); - } - - function _slashAndRemoteRelease( - TransferParameter memory params, - bytes32 expectedTransferId - ) internal view returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "invalid timestamp"); - TokenInfo memory tokenInfo = tokenInfos[params.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(params.amount)); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - targetAmount - )); - require(expectedTransferId == transferId, "expected transfer id not match"); - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.isLocked, "lock info not match"); - uint112 targetFee = _sourceAmountToTargetAmount(tokenInfo, lockInfo.fee); - uint112 targetPenalty = _sourceAmountToTargetAmount(tokenInfo, lockInfo.penalty); - - message = _encodeSlashCall( - params, - msg.sender, - targetFee, - targetPenalty - ); - } - - function _withdrawMargin( - address sourceToken, - uint112 amount - ) internal returns(bytes memory message) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - lnProviders[providerKey].withdrawNonce = providerInfo.withdrawNonce + 1; - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, amount); - message = _encodeWithdrawCall( - providerInfo.lastTransferId, - providerInfo.withdrawNonce + 1, - msg.sender, - sourceToken, - tokenInfo.targetToken, - targetAmount - ); - } - - function _encodeSlashCall( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.slash.selector, - params, - slasher, - fee, - penalty - ); - } - - function _encodeWithdrawCall( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.withdraw.selector, - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @arbitrum/nitro-contracts/src/bridge/IOwnable.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.4.21 <0.9.0; - -interface IOwnable { - function owner() external view returns (address); -} - -// File @arbitrum/nitro-contracts/src/bridge/IBridge.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IBridge { - event MessageDelivered( - uint256 indexed messageIndex, - bytes32 indexed beforeInboxAcc, - address inbox, - uint8 kind, - address sender, - bytes32 messageDataHash, - uint256 baseFeeL1, - uint64 timestamp - ); - - event BridgeCallTriggered( - address indexed outbox, - address indexed to, - uint256 value, - bytes data - ); - - event InboxToggle(address indexed inbox, bool enabled); - - event OutboxToggle(address indexed outbox, bool enabled); - - event SequencerInboxUpdated(address newSequencerInbox); - - function allowedDelayedInboxList(uint256) external returns (address); - - function allowedOutboxList(uint256) external returns (address); - - /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - function delayedInboxAccs(uint256) external view returns (bytes32); - - /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - function sequencerInboxAccs(uint256) external view returns (bytes32); - - function rollup() external view returns (IOwnable); - - function sequencerInbox() external view returns (address); - - function activeOutbox() external view returns (address); - - function allowedDelayedInboxes(address inbox) external view returns (bool); - - function allowedOutboxes(address outbox) external view returns (bool); - - function sequencerReportedSubMessageCount() external view returns (uint256); - - /** - * @dev Enqueue a message in the delayed inbox accumulator. - * These messages are later sequenced in the SequencerInbox, either - * by the sequencer as part of a normal batch, or by force inclusion. - */ - function enqueueDelayedMessage( - uint8 kind, - address sender, - bytes32 messageDataHash - ) external payable returns (uint256); - - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external returns (bool success, bytes memory returnData); - - function delayedMessageCount() external view returns (uint256); - - function sequencerMessageCount() external view returns (uint256); - - // ---------- onlySequencerInbox functions ---------- - - function enqueueSequencerMessage( - bytes32 dataHash, - uint256 afterDelayedMessagesRead, - uint256 prevMessageCount, - uint256 newMessageCount - ) - external - returns ( - uint256 seqMessageIndex, - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 acc - ); - - /** - * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type - * This is done through a separate function entrypoint instead of allowing the sequencer inbox - * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either - * every delayed inbox or every sequencer inbox call. - */ - function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) - external - returns (uint256 msgNum); - - // ---------- onlyRollupOrOwner functions ---------- - - function setSequencerInbox(address _sequencerInbox) external; - - function setDelayedInbox(address inbox, bool enabled) external; - - function setOutbox(address inbox, bool enabled) external; - - // ---------- initializer ---------- - - function initialize(IOwnable rollup_) external; -} - -// File @arbitrum/nitro-contracts/src/bridge/IDelayedMessageProvider.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IDelayedMessageProvider { - /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator - event InboxMessageDelivered(uint256 indexed messageNum, bytes data); - - /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator - /// same as InboxMessageDelivered but the batch data is available in tx.input - event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); -} - -// File @arbitrum/nitro-contracts/src/libraries/IGasRefunder.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IGasRefunder { - function onGasSpent( - address payable spender, - uint256 gasUsed, - uint256 calldataSize - ) external returns (bool success); -} - -abstract contract GasRefundEnabled { - /// @dev this refunds the sender for execution costs of the tx - /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging - /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded - modifier refundsGas(IGasRefunder gasRefunder) { - uint256 startGasLeft = gasleft(); - _; - if (address(gasRefunder) != address(0)) { - uint256 calldataSize; - assembly { - calldataSize := calldatasize() - } - uint256 calldataWords = (calldataSize + 31) / 32; - // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost - startGasLeft += calldataWords * 6 + (calldataWords**2) / 512; - // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call - // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input - // solhint-disable-next-line avoid-tx-origin - if (msg.sender != tx.origin) { - // We can't be sure if this calldata came from the top level tx, - // so to be safe we tell the gas refunder there was no calldata. - calldataSize = 0; - } - gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize); - } - } -} - -// File @arbitrum/nitro-contracts/src/bridge/ISequencerInbox.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; -pragma experimental ABIEncoderV2; - - - -interface ISequencerInbox is IDelayedMessageProvider { - struct MaxTimeVariation { - uint256 delayBlocks; - uint256 futureBlocks; - uint256 delaySeconds; - uint256 futureSeconds; - } - - struct TimeBounds { - uint64 minTimestamp; - uint64 maxTimestamp; - uint64 minBlockNumber; - uint64 maxBlockNumber; - } - - enum BatchDataLocation { - TxInput, - SeparateBatchEvent, - NoData - } - - event SequencerBatchDelivered( - uint256 indexed batchSequenceNumber, - bytes32 indexed beforeAcc, - bytes32 indexed afterAcc, - bytes32 delayedAcc, - uint256 afterDelayedMessagesRead, - TimeBounds timeBounds, - BatchDataLocation dataLocation - ); - - event OwnerFunctionCalled(uint256 indexed id); - - /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input - event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data); - - /// @dev a valid keyset was added - event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes); - - /// @dev a keyset was invalidated - event InvalidateKeyset(bytes32 indexed keysetHash); - - function totalDelayedMessagesRead() external view returns (uint256); - - function bridge() external view returns (IBridge); - - /// @dev The size of the batch header - // solhint-disable-next-line func-name-mixedcase - function HEADER_LENGTH() external view returns (uint256); - - /// @dev If the first batch data byte after the header has this bit set, - /// the sequencer inbox has authenticated the data. Currently not used. - // solhint-disable-next-line func-name-mixedcase - function DATA_AUTHENTICATED_FLAG() external view returns (bytes1); - - function rollup() external view returns (IOwnable); - - function isBatchPoster(address) external view returns (bool); - - struct DasKeySetInfo { - bool isValidKeyset; - uint64 creationBlock; - } - - // https://github.com/ethereum/solidity/issues/11826 - // function maxTimeVariation() external view returns (MaxTimeVariation calldata); - // function dasKeySetInfo(bytes32) external view returns (DasKeySetInfo calldata); - - /// @notice Remove force inclusion delay after a L1 chainId fork - function removeDelayAfterFork() external; - - /// @notice Force messages from the delayed inbox to be included in the chain - /// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and - /// maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these - /// messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages. - /// @param _totalDelayedMessagesRead The total number of messages to read up to - /// @param kind The kind of the last message to be included - /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included - /// @param baseFeeL1 The l1 gas price of the last message to be included - /// @param sender The sender of the last message to be included - /// @param messageDataHash The messageDataHash of the last message to be included - function forceInclusion( - uint256 _totalDelayedMessagesRead, - uint8 kind, - uint64[2] calldata l1BlockAndTime, - uint256 baseFeeL1, - address sender, - bytes32 messageDataHash - ) external; - - function inboxAccs(uint256 index) external view returns (bytes32); - - function batchCount() external view returns (uint256); - - function isValidKeysetHash(bytes32 ksHash) external view returns (bool); - - /// @notice the creation block is intended to still be available after a keyset is deleted - function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256); - - // ---------- BatchPoster functions ---------- - - function addSequencerL2BatchFromOrigin( - uint256 sequenceNumber, - bytes calldata data, - uint256 afterDelayedMessagesRead, - IGasRefunder gasRefunder - ) external; - - function addSequencerL2Batch( - uint256 sequenceNumber, - bytes calldata data, - uint256 afterDelayedMessagesRead, - IGasRefunder gasRefunder, - uint256 prevMessageCount, - uint256 newMessageCount - ) external; - - // ---------- onlyRollupOrOwner functions ---------- - - /** - * @notice Set max delay for sequencer inbox - * @param maxTimeVariation_ the maximum time variation parameters - */ - function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external; - - /** - * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox - * @param addr the address - * @param isBatchPoster_ if the specified address should be authorized as a batch poster - */ - function setIsBatchPoster(address addr, bool isBatchPoster_) external; - - /** - * @notice Makes Data Availability Service keyset valid - * @param keysetBytes bytes of the serialized keyset - */ - function setValidKeyset(bytes calldata keysetBytes) external; - - /** - * @notice Invalidates a Data Availability Service keyset - * @param ksHash hash of the keyset - */ - function invalidateKeysetHash(bytes32 ksHash) external; - - // ---------- initializer ---------- - - function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external; -} - -// File @arbitrum/nitro-contracts/src/bridge/IInbox.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - - - -interface IInbox is IDelayedMessageProvider { - function bridge() external view returns (IBridge); - - function sequencerInbox() external view returns (ISequencerInbox); - - /** - * @notice Send a generic L2 message to the chain - * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input - * This method will be disabled upon L1 fork to prevent replay attacks on L2 - * @param messageData Data of the message being sent - */ - function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256); - - /** - * @notice Send a generic L2 message to the chain - * @dev This method can be used to send any type of message that doesn't require L1 validation - * This method will be disabled upon L1 fork to prevent replay attacks on L2 - * @param messageData Data of the message being sent - */ - function sendL2Message(bytes calldata messageData) external returns (uint256); - - function sendL1FundedUnsignedTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - bytes calldata data - ) external payable returns (uint256); - - function sendL1FundedContractTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - address to, - bytes calldata data - ) external payable returns (uint256); - - function sendUnsignedTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - function sendContractTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - /** - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendL1FundedUnsignedTransactionToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - bytes calldata data - ) external payable returns (uint256); - - /** - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendUnsignedTransactionToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - /** - * @notice Send a message to initiate L2 withdrawal - * @dev This method can only be called upon L1 fork and will not alias the caller - * This method will revert if not called from origin - */ - function sendWithdrawEthToFork( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - uint256 value, - address withdrawTo - ) external returns (uint256); - - /** - * @notice Get the L1 fee for submitting a retryable - * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value - * @dev This formula may change in the future, to future proof your code query this method instead of inlining!! - * @param dataLength The length of the retryable's calldata, in bytes - * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used - */ - function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) - external - view - returns (uint256); - - /** - * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract - * @dev This does not trigger the fallback function when receiving in the L2 side. - * Look into retryable tickets if you are interested in this functionality. - * @dev This function should not be called inside contract constructors - */ - function depositEth() external payable returns (uint256); - - /** - * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts - * @dev all msg.value will deposited to callValueRefundAddress on L2 - * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error - * @param to destination L2 contract address - * @param l2CallValue call value for retryable L2 message - * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee - * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance - * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled - * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param data ABI encoded data of L2 message - * @return unique message number of the retryable transaction - */ - function createRetryableTicket( - address to, - uint256 l2CallValue, - uint256 maxSubmissionCost, - address excessFeeRefundAddress, - address callValueRefundAddress, - uint256 gasLimit, - uint256 maxFeePerGas, - bytes calldata data - ) external payable returns (uint256); - - /** - * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts - * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds - * come from the deposit alone, rather than falling back on the user's L2 balance - * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress). - * createRetryableTicket method is the recommended standard. - * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error - * @param to destination L2 contract address - * @param l2CallValue call value for retryable L2 message - * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee - * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance - * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled - * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param data ABI encoded data of L2 message - * @return unique message number of the retryable transaction - */ - function unsafeCreateRetryableTicket( - address to, - uint256 l2CallValue, - uint256 maxSubmissionCost, - address excessFeeRefundAddress, - address callValueRefundAddress, - uint256 gasLimit, - uint256 maxFeePerGas, - bytes calldata data - ) external payable returns (uint256); - - // ---------- onlyRollupOrOwner functions ---------- - - /// @notice pauses all inbox functionality - function pause() external; - - /// @notice unpauses all inbox functionality - function unpause() external; - - // ---------- initializer ---------- - - /** - * @dev function to be called one time during the inbox upgrade process - * this is used to fix the storage slots - */ - function postUpgradeInit(IBridge _bridge) external; - - function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external; -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Eth2ArbSource.sol -// License-Identifier: MIT - - - - -contract Eth2ArbSource is Initializable, LnAccessController, LnDefaultBridgeSource { - IInbox public inbox; - address public remoteBridge; - - event WithdrawMargin(address sourceToken, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _inbox) public initializer { - inbox = IInbox(_inbox); - _initialize(_dao); - _setFeeReceiver(_dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function submissionSlashFee( - uint256 baseFee, - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory slashCall = _encodeSlashCall( - params, - slasher, - fee, - penalty - ); - uint256 submissionFee = inbox.calculateRetryableSubmissionFee(slashCall.length, baseFee); - return submissionFee + submissionFee * percentIncrease / 100; - } - - function submissionWithdrawFee( - uint256 baseFee, - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount, - uint256 percentIncrease - ) external view returns(uint256) { - bytes memory withdrawCall = _encodeWithdrawCall( - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - uint256 fee = inbox.calculateRetryableSubmissionFee(withdrawCall.length, baseFee); - return fee + fee * percentIncrease / 100; - } - - function _sendMessage( - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid, - bytes memory message, - uint256 prepaid - ) internal returns(uint256) { - return inbox.createRetryableTicket{ value: prepaid }( - remoteBridge, - 0, - maxSubmissionCost, - msg.sender, - msg.sender, - maxGas, - gasPriceBid, - message - ); - } - - // this function can retry - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 gasPriceBid - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(maxSubmissionCost, maxGas, gasPriceBid, withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2ArbTarget.sol b/helix-contract/flatten/lnv2/Eth2ArbTarget.sol deleted file mode 100644 index 3527147c..00000000 --- a/helix-contract/flatten/lnv2/Eth2ArbTarget.sol +++ /dev/null @@ -1,1923 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/11/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnDefaultBridgeTarget.sol -// License-Identifier: MIT - -contract LnDefaultBridgeTarget is LnBridgeHelper { - uint256 constant public MIN_SLASH_TIMESTAMP = 30 * 60; - - struct ProviderInfo { - uint256 margin; - // use this slash gas reserve to pay the slash fee if transfer filled but timeout - uint256 slashReserveFund; - uint64 lastExpireFillTime; - uint64 withdrawNonce; - } - - // providerKey => margin - // providerKey = hash(provider, sourceToken, targetToken) - mapping(bytes32=>ProviderInfo) public lnProviderInfos; - - // if timestamp > 0, the Transfer has been relayed or slashed - // if slasher == address(0), this FillTransfer is relayed by lnProvider - // otherwise, this FillTransfer is slashed by slasher - struct FillTransfer { - uint64 timestamp; - address slasher; - } - - // transferId => FillTransfer - mapping(bytes32 => FillTransfer) public fillTransfers; - - event TransferFilled(address provider, bytes32 transferId); - event Slash(bytes32 transferId, address provider, address token, uint256 margin, address slasher); - event MarginUpdated(address provider, address token, uint256 amount, uint64 withdrawNonce); - event SlashReserveUpdated(address provider, address token, uint256 amount); - - function depositProviderMargin( - address sourceToken, - address targetToken, - uint256 margin - ) external payable { - require(margin > 0, "invalid margin"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin + margin; - lnProviderInfos[providerKey].margin = updatedMargin; - if (targetToken == address(0)) { - require(msg.value == margin, "invalid margin value"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), margin); - } - emit MarginUpdated(msg.sender, sourceToken, updatedMargin, providerInfo.withdrawNonce); - } - - function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId - ) external payable { - require(params.provider == msg.sender, "invalid provider"); - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - require(expectedTransferId == transferId, "check expected transferId failed"); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - // Make sure this transfer was never filled before - require(fillTransfer.timestamp == 0, "transfer has been filled"); - - fillTransfers[transferId].timestamp = uint64(block.timestamp); - if (block.timestamp - MIN_SLASH_TIMESTAMP > params.timestamp) { - bytes32 providerKey = getDefaultProviderKey(msg.sender, params.sourceToken, params.targetToken); - lnProviderInfos[providerKey].lastExpireFillTime = uint64(block.timestamp); - } - - if (params.targetToken == address(0)) { - require(msg.value == params.amount, "lnBridgeTarget:invalid amount"); - _safeTransferNative(params.receiver, params.amount); - } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); - } - emit TransferFilled(params.provider, transferId); - } - - function depositSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external payable { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedAmount = providerInfo.slashReserveFund + amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - require(msg.value == amount, "amount invalid"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - // withdraw slash fund - // provider can't withdraw until the block.timestamp overtime lastExpireFillTime for a period of time - function withdrawSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - require(amount <= providerInfo.slashReserveFund, "reserve not enough"); - require(block.timestamp - MIN_SLASH_TIMESTAMP >= providerInfo.lastExpireFillTime, "time not expired"); - uint256 updatedAmount = providerInfo.slashReserveFund - amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - _safeTransferNative(msg.sender, amount); - } else { - _safeTransfer(targetToken, msg.sender, amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - function _withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal { - // ensure all transfer has finished - require(lastTransferId == bytes32(0) || fillTransfers[lastTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - // all the early withdraw info ignored - require(providerInfo.withdrawNonce < withdrawNonce, "withdraw nonce expired"); - - // transfer token - require(providerInfo.margin >= amount, "margin not enough"); - uint256 updatedMargin = providerInfo.margin - amount; - lnProviderInfos[providerKey].margin = updatedMargin; - lnProviderInfos[providerKey].withdrawNonce = withdrawNonce; - - if (targetToken == address(0)) { - _safeTransferNative(provider, amount); - } else { - _safeTransfer(targetToken, provider, amount); - } - emit MarginUpdated(provider, sourceToken, updatedMargin, withdrawNonce); - } - - function _slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal { - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - require(fillTransfer.slasher == address(0), "transfer has been slashed"); - bytes32 providerKey = getDefaultProviderKey(params.provider, params.sourceToken, params.targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin; - // transfer is not filled - if (fillTransfer.timestamp == 0) { - require(params.timestamp < block.timestamp - MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId] = FillTransfer(uint64(block.timestamp), slasher); - - // 1. transfer token to receiver - // 2. trnasfer fee and penalty to slasher - // update margin - uint256 marginCost = params.amount + fee + penalty; - require(providerInfo.margin >= marginCost, "margin not enough"); - updatedMargin = providerInfo.margin - marginCost; - lnProviderInfos[providerKey].margin = updatedMargin; - - if (params.targetToken == address(0)) { - _safeTransferNative(params.receiver, params.amount); - _safeTransferNative(slasher, fee + penalty); - } else { - _safeTransfer(params.targetToken, params.receiver, uint256(params.amount)); - _safeTransfer(params.targetToken, slasher, fee + penalty); - } - } else { - require(fillTransfer.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId].slasher = slasher; - uint112 slashRefund = penalty / 5; - // transfer slashRefund to slasher - require(providerInfo.slashReserveFund >= slashRefund, "slashReserveFund not enough"); - lnProviderInfos[providerKey].slashReserveFund = providerInfo.slashReserveFund - slashRefund; - if (params.targetToken == address(0)) { - _safeTransferNative(slasher, slashRefund); - } else { - _safeTransfer(params.targetToken, slasher, slashRefund); - } - } - emit Slash(transferId, params.provider, params.sourceToken, updatedMargin, slasher); - } -} - -// File @arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol@v1.0.1 -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// License-Identifier: BUSL-1.1 - - -library AddressAliasHelper { - uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); - - /// @notice Utility function that converts the address in the L1 that submitted a tx to - /// the inbox to the msg.sender viewed in the L2 - /// @param l1Address the address in the L1 that triggered the tx to L2 - /// @return l2Address L2 address as viewed in msg.sender - function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { - unchecked { - l2Address = address(uint160(l1Address) + OFFSET); - } - } - - /// @notice Utility function that converts the msg.sender viewed in the L2 to the - /// address in the L1 that submitted a tx to the inbox - /// @param l2Address L2 address as viewed in msg.sender - /// @return l1Address the address in the L1 that triggered the tx to L2 - function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { - unchecked { - l1Address = address(uint160(l2Address) - OFFSET); - } - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Eth2ArbTarget.sol -// License-Identifier: MIT - - - - -contract Eth2ArbTarget is Initializable, LnAccessController, LnDefaultBridgeTarget { - address public remoteBridge; - address public remoteBridgeAlias; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == remoteBridgeAlias, "invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - remoteBridgeAlias = AddressAliasHelper.applyL1ToL2Alias(remoteBridge); - } - - function setRemoteBridgeAlias(address _remoteBridgeAlias) external onlyDao { - remoteBridgeAlias = _remoteBridgeAlias; - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external onlyRemoteBridge whenNotPaused { - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2LineaReceiveService.sol b/helix-contract/flatten/lnv2/Eth2LineaReceiveService.sol new file mode 100644 index 00000000..53b643c0 --- /dev/null +++ b/helix-contract/flatten/lnv2/Eth2LineaReceiveService.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * + * 10/17/2023 + **/ + +pragma solidity ^0.8.17; + +// File contracts/ln/base/LnAccessController.sol +// License-Identifier: MIT + +/// @title LnAccessController +/// @notice LnAccessController is a contract to control the access permission +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnAccessController { + address public dao; + address public operator; + + mapping(address=>bool) public callerWhiteList; + + modifier onlyDao() { + require(msg.sender == dao, "!dao"); + _; + } + + modifier onlyOperator() { + require(msg.sender == operator, "!operator"); + _; + } + + modifier onlyWhiteListCaller() { + require(callerWhiteList[msg.sender], "caller not in white list"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; + } + + function authoriseAppCaller(address appAddress, bool enable) onlyDao external { + callerWhiteList[appAddress] = enable; + } + + function transferOwnership(address _dao) onlyDao external { + dao = _dao; + } +} + +// File contracts/ln/messager/interface/ILineaMessageService.sol +// License-Identifier: MIT + +interface ILineaMessageService { + function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable; + function sender() external view returns (address); +} + +// File contracts/ln/interface/ILowLevelMessager.sol +// License-Identifier: MIT + +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} + +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; +} + +// File contracts/ln/messager/Eth2LineaReceiveService.sol +// License-Identifier: MIT + + + +// from ethereum to linea messager +contract Eth2LineaReceiveService is ILowLevelMessageReceiver, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + ILineaMessageService public messageService; + address public remoteMessager; + + mapping(address=>address) public appPairs; + + modifier onlyRemoteBridge() { + require(msg.sender == address(messageService), "invalid msg.sender"); + require(messageService.sender() == remoteMessager, "invalid remote caller"); + _; + } + + constructor(address _dao, address _messageService, uint256 _remoteChainId) { + _initialize(_dao); + messageService = ILineaMessageService(_messageService); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessager = _remoteMessager; + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function recvMessage(address _remoteApp, address _localApp, bytes memory _message) onlyRemoteBridge external { + address remoteAppAddress = appPairs[_localApp]; + require(remoteAppAddress == _remoteApp, "invalid remote app"); + (bool result,) = _localApp.call(_message); + require(result == true, "local call failed"); + } +} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2LineaSendService.sol b/helix-contract/flatten/lnv2/Eth2LineaSendService.sol new file mode 100644 index 00000000..9aa5e6b2 --- /dev/null +++ b/helix-contract/flatten/lnv2/Eth2LineaSendService.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * + * 10/17/2023 + **/ + +pragma solidity ^0.8.17; + +// File contracts/ln/base/LnAccessController.sol +// License-Identifier: MIT + +/// @title LnAccessController +/// @notice LnAccessController is a contract to control the access permission +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnAccessController { + address public dao; + address public operator; + + mapping(address=>bool) public callerWhiteList; + + modifier onlyDao() { + require(msg.sender == dao, "!dao"); + _; + } + + modifier onlyOperator() { + require(msg.sender == operator, "!operator"); + _; + } + + modifier onlyWhiteListCaller() { + require(callerWhiteList[msg.sender], "caller not in white list"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; + } + + function authoriseAppCaller(address appAddress, bool enable) onlyDao external { + callerWhiteList[appAddress] = enable; + } + + function transferOwnership(address _dao) onlyDao external { + dao = _dao; + } +} + +// File contracts/ln/interface/ILowLevelMessager.sol +// License-Identifier: MIT + +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} + +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; +} + +// File contracts/ln/messager/interface/ILineaMessageService.sol +// License-Identifier: MIT + +interface ILineaMessageService { + function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable; + function sender() external view returns (address); +} + +// File contracts/ln/messager/Eth2LineaSendService.sol +// License-Identifier: MIT + + + +// from ethereum to linea messager +contract Eth2LineaSendService is ILowLevelMessageSender, LnAccessController { + uint256 immutable public REMOTE_CHAINID; + ILineaMessageService public messageService; + address public remoteMessager; + + mapping(address=>address) public appPairs; + + constructor(address _dao, address _messageService, uint256 _remoteChainId) { + _initialize(_dao); + messageService = ILineaMessageService(_messageService); + REMOTE_CHAINID = _remoteChainId; + } + + function setRemoteMessager(address _remoteMessager) onlyDao external { + remoteMessager = _remoteMessager; + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + appPairs[msg.sender] = _remoteBridge; + } + + function sendMessage(uint256 _remoteChainId, bytes memory _message, bytes memory) onlyWhiteListCaller external payable { + require(_remoteChainId == REMOTE_CHAINID, "invalid remote chainId"); + address remoteAppAddress = appPairs[msg.sender]; + require(remoteAppAddress != address(0), "app not registered"); + + bytes memory remoteReceiveCall = abi.encodeWithSelector( + ILowLevelMessageReceiver.recvMessage.selector, + msg.sender, + remoteAppAddress, + _message + ); + messageService.sendMessage{value: msg.value}( + remoteMessager, + msg.value, + remoteReceiveCall + ); + } +} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2LineaSource.sol b/helix-contract/flatten/lnv2/Eth2LineaSource.sol deleted file mode 100644 index d6095e64..00000000 --- a/helix-contract/flatten/lnv2/Eth2LineaSource.sol +++ /dev/null @@ -1,2059 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/22/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File contracts/ln/interface/ILineaMessageService.sol -// License-Identifier: MIT - -interface ILineaMessageService { - function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable; - function sender() external view returns (address); -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/interface/ILnDefaultBridgeTarget.sol -// License-Identifier: MIT - - -interface ILnDefaultBridgeTarget { - function slash( - LnBridgeHelper.TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external; - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external; -} - -// File contracts/ln/base/LnDefaultBridgeSource.sol -// License-Identifier: MIT - - - -/// @title LnPositiveBridgeSource -/// @notice LnPositiveBridgeSource is a contract to help user transfer token to liquidity node and generate proof, -/// then the liquidity node must transfer the same amount of the token to the user on target chain. -/// Otherwise if timeout the slasher can send a slash request message to target chain, then force transfer from lnProvider's margin to the user. -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnDefaultBridgeSource is LnBridgeHelper { - // the time(seconds) for liquidity provider to delivery message - // if timeout, slasher can work. - uint256 constant public MIN_SLASH_TIMESTAMP = 30 * 60; - // liquidity fee base rate - // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - // max transfer amount one time - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - - // provider fee is paid to liquidity node's account - // the fee is charged by the same token that user transfered - // providerFee = baseFee + liquidityFeeRate/LIQUIDITY_FEE_RATE_BASE * sendAmount - struct LnProviderFee { - uint112 baseFee; - uint8 liquidityFeeRate; - } - - struct LnProviderInfo { - LnProviderFee fee; - // we use this nonce to generate the unique withdraw id - uint64 withdrawNonce; - bytes32 lastTransferId; - } - // the Snapshot is the state of the token bridge when user prepare to transfer across chains. - // If the snapshot updated when the across chain transfer confirmed, it will - // 1. if lastTransferId or withdrawNonce updated, revert - // 2. if totalFee increase, revert - // 3. if totalFee decrease, success - struct Snapshot { - address provider; - address sourceToken; - bytes32 transferId; - uint112 totalFee; - uint64 withdrawNonce; - } - - // lock info - // the fee and penalty is the state of the transfer confirmed - struct LockInfo { - uint112 fee; - uint112 penalty; - bool isLocked; - } - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // providerKey => provider info - mapping(bytes32=>LnProviderInfo) public lnProviders; - // transferId => lock info - mapping(bytes32=>LockInfo) public lockInfos; - - address public protocolFeeReceiver; - - event TokenLocked( - bytes32 transferId, - address provider, - address sourceToken, - uint112 amount, - uint112 fee, - uint64 timestamp, - address receiver); - event LnProviderUpdated(address provider, address sourceToken, uint112 baseFee, uint8 liquidityfeeRate); - - // protocolFeeReceiver is the protocol fee reciever, we don't use the contract itself as the receiver - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - protocolFeeReceiver = _feeReceiver; - } - - // register or update token info, it can be only called by contract owner - // source token can only map a unique target token on target chain - function _setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) internal { - tokenInfos[_sourceToken] = TokenInfo( - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals, - true - ); - } - - // lnProvider register - // 1. set fee on source chain - // 2. deposit margin on target chain - function setProviderFee( - address sourceToken, - uint112 baseFee, - uint8 liquidityFeeRate - ) external { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderFee memory providerFee = LnProviderFee(baseFee, liquidityFeeRate); - - // we only update the field fee of the provider info - // if the provider has not been registered, then this line will register, otherwise update fee - lnProviders[providerKey].fee = providerFee; - - emit LnProviderUpdated(msg.sender, sourceToken, baseFee, liquidityFeeRate); - } - - function calculateProviderFee(LnProviderFee memory fee, uint112 amount) internal pure returns(uint256) { - return uint256(fee.baseFee) + uint256(fee.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; - } - - // the fee user should paid when transfer. - // totalFee = providerFee + protocolFee - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - return providerFee + tokenInfo.protocolFee; - } - - // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. - // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. - // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. - // 1. the state(lastTransferId, fee, withdrawNonce) must match snapshot - // 2. transferId not exist - function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(snapshot.provider, snapshot.sourceToken, tokenInfo.targetToken); - - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired:transfer"); - require(snapshot.withdrawNonce == providerInfo.withdrawNonce, "snapshot expired:withdraw"); - require(snapshot.totalFee >= providerFee + tokenInfo.protocolFee && providerFee > 0, "fee is invalid"); - - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(amount)); - uint64 timestamp = uint64(block.timestamp); - bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - targetAmount - )); - require(!lockInfos[transferId].isLocked, "transferId exist"); - // if the transfer refund, then the fee and penalty should be given to slasher, but the protocol fee is ignored - // and we use the penalty value configure at the moment transfer confirmed - lockInfos[transferId] = LockInfo(snapshot.totalFee, tokenInfo.penaltyLnCollateral, true); - - // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; - - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); - if (tokenInfo.protocolFee > 0) { - _safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); - } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; - if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); - } - } else { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - snapshot.provider, - amount + providerFee - ); - if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - protocolFeeReceiver, - tokenInfo.protocolFee - ); - } - } - emit TokenLocked( - transferId, - snapshot.provider, - snapshot.sourceToken, - targetAmount, - uint112(providerFee), - timestamp, - receiver); - } - - function _sourceAmountToTargetAmount( - TokenInfo memory tokenInfo, - uint256 amount - ) internal pure returns(uint112) { - uint256 targetAmount = amount * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - return uint112(targetAmount); - } - - function _slashAndRemoteRelease( - TransferParameter memory params, - bytes32 expectedTransferId - ) internal view returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "invalid timestamp"); - TokenInfo memory tokenInfo = tokenInfos[params.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(params.amount)); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - targetAmount - )); - require(expectedTransferId == transferId, "expected transfer id not match"); - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.isLocked, "lock info not match"); - uint112 targetFee = _sourceAmountToTargetAmount(tokenInfo, lockInfo.fee); - uint112 targetPenalty = _sourceAmountToTargetAmount(tokenInfo, lockInfo.penalty); - - message = _encodeSlashCall( - params, - msg.sender, - targetFee, - targetPenalty - ); - } - - function _withdrawMargin( - address sourceToken, - uint112 amount - ) internal returns(bytes memory message) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - lnProviders[providerKey].withdrawNonce = providerInfo.withdrawNonce + 1; - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, amount); - message = _encodeWithdrawCall( - providerInfo.lastTransferId, - providerInfo.withdrawNonce + 1, - msg.sender, - sourceToken, - tokenInfo.targetToken, - targetAmount - ); - } - - function _encodeSlashCall( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.slash.selector, - params, - slasher, - fee, - penalty - ); - } - - function _encodeWithdrawCall( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.withdraw.selector, - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Eth2LineaSource.sol -// License-Identifier: MIT - - - - -contract Eth2LineaSource is Initializable, LnAccessController, LnDefaultBridgeSource { - ILineaMessageService public messageService; - address public remoteBridge; - - event WithdrawMargin(address sourceToken, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _messageService) public initializer { - messageService = ILineaMessageService(_messageService); - _initialize(_dao); - _setFeeReceiver(_dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function _sendMessage( - bytes memory message, - uint256 fee - ) internal { - messageService.sendMessage{ value: fee }( - remoteBridge, - fee, - message - ); - } - - // this function can retry - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2LineaTarget.sol b/helix-contract/flatten/lnv2/Eth2LineaTarget.sol deleted file mode 100644 index d2fbe310..00000000 --- a/helix-contract/flatten/lnv2/Eth2LineaTarget.sol +++ /dev/null @@ -1,1897 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/22/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnDefaultBridgeTarget.sol -// License-Identifier: MIT - -contract LnDefaultBridgeTarget is LnBridgeHelper { - uint256 constant public MIN_SLASH_TIMESTAMP = 30 * 60; - - struct ProviderInfo { - uint256 margin; - // use this slash gas reserve to pay the slash fee if transfer filled but timeout - uint256 slashReserveFund; - uint64 lastExpireFillTime; - uint64 withdrawNonce; - } - - // providerKey => margin - // providerKey = hash(provider, sourceToken, targetToken) - mapping(bytes32=>ProviderInfo) public lnProviderInfos; - - // if timestamp > 0, the Transfer has been relayed or slashed - // if slasher == address(0), this FillTransfer is relayed by lnProvider - // otherwise, this FillTransfer is slashed by slasher - struct FillTransfer { - uint64 timestamp; - address slasher; - } - - // transferId => FillTransfer - mapping(bytes32 => FillTransfer) public fillTransfers; - - event TransferFilled(address provider, bytes32 transferId); - event Slash(bytes32 transferId, address provider, address token, uint256 margin, address slasher); - event MarginUpdated(address provider, address token, uint256 amount, uint64 withdrawNonce); - event SlashReserveUpdated(address provider, address token, uint256 amount); - - function depositProviderMargin( - address sourceToken, - address targetToken, - uint256 margin - ) external payable { - require(margin > 0, "invalid margin"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin + margin; - lnProviderInfos[providerKey].margin = updatedMargin; - if (targetToken == address(0)) { - require(msg.value == margin, "invalid margin value"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), margin); - } - emit MarginUpdated(msg.sender, sourceToken, updatedMargin, providerInfo.withdrawNonce); - } - - function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId - ) external payable { - require(params.provider == msg.sender, "invalid provider"); - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - require(expectedTransferId == transferId, "check expected transferId failed"); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - // Make sure this transfer was never filled before - require(fillTransfer.timestamp == 0, "transfer has been filled"); - - fillTransfers[transferId].timestamp = uint64(block.timestamp); - if (block.timestamp - MIN_SLASH_TIMESTAMP > params.timestamp) { - bytes32 providerKey = getDefaultProviderKey(msg.sender, params.sourceToken, params.targetToken); - lnProviderInfos[providerKey].lastExpireFillTime = uint64(block.timestamp); - } - - if (params.targetToken == address(0)) { - require(msg.value == params.amount, "lnBridgeTarget:invalid amount"); - _safeTransferNative(params.receiver, params.amount); - } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); - } - emit TransferFilled(params.provider, transferId); - } - - function depositSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external payable { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedAmount = providerInfo.slashReserveFund + amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - require(msg.value == amount, "amount invalid"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - // withdraw slash fund - // provider can't withdraw until the block.timestamp overtime lastExpireFillTime for a period of time - function withdrawSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - require(amount <= providerInfo.slashReserveFund, "reserve not enough"); - require(block.timestamp - MIN_SLASH_TIMESTAMP >= providerInfo.lastExpireFillTime, "time not expired"); - uint256 updatedAmount = providerInfo.slashReserveFund - amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - _safeTransferNative(msg.sender, amount); - } else { - _safeTransfer(targetToken, msg.sender, amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - function _withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal { - // ensure all transfer has finished - require(lastTransferId == bytes32(0) || fillTransfers[lastTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - // all the early withdraw info ignored - require(providerInfo.withdrawNonce < withdrawNonce, "withdraw nonce expired"); - - // transfer token - require(providerInfo.margin >= amount, "margin not enough"); - uint256 updatedMargin = providerInfo.margin - amount; - lnProviderInfos[providerKey].margin = updatedMargin; - lnProviderInfos[providerKey].withdrawNonce = withdrawNonce; - - if (targetToken == address(0)) { - _safeTransferNative(provider, amount); - } else { - _safeTransfer(targetToken, provider, amount); - } - emit MarginUpdated(provider, sourceToken, updatedMargin, withdrawNonce); - } - - function _slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal { - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - require(fillTransfer.slasher == address(0), "transfer has been slashed"); - bytes32 providerKey = getDefaultProviderKey(params.provider, params.sourceToken, params.targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin; - // transfer is not filled - if (fillTransfer.timestamp == 0) { - require(params.timestamp < block.timestamp - MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId] = FillTransfer(uint64(block.timestamp), slasher); - - // 1. transfer token to receiver - // 2. trnasfer fee and penalty to slasher - // update margin - uint256 marginCost = params.amount + fee + penalty; - require(providerInfo.margin >= marginCost, "margin not enough"); - updatedMargin = providerInfo.margin - marginCost; - lnProviderInfos[providerKey].margin = updatedMargin; - - if (params.targetToken == address(0)) { - _safeTransferNative(params.receiver, params.amount); - _safeTransferNative(slasher, fee + penalty); - } else { - _safeTransfer(params.targetToken, params.receiver, uint256(params.amount)); - _safeTransfer(params.targetToken, slasher, fee + penalty); - } - } else { - require(fillTransfer.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId].slasher = slasher; - uint112 slashRefund = penalty / 5; - // transfer slashRefund to slasher - require(providerInfo.slashReserveFund >= slashRefund, "slashReserveFund not enough"); - lnProviderInfos[providerKey].slashReserveFund = providerInfo.slashReserveFund - slashRefund; - if (params.targetToken == address(0)) { - _safeTransferNative(slasher, slashRefund); - } else { - _safeTransfer(params.targetToken, slasher, slashRefund); - } - } - emit Slash(transferId, params.provider, params.sourceToken, updatedMargin, slasher); - } -} - -// File contracts/ln/interface/ILineaMessageService.sol -// License-Identifier: MIT - -interface ILineaMessageService { - function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable; - function sender() external view returns (address); -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Eth2LineaTarget.sol -// License-Identifier: MIT - - - - -contract Eth2LineaTarget is Initializable, LnAccessController, LnDefaultBridgeTarget { - address public remoteBridge; - address public messageService; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(ILineaMessageService(messageService).sender() == remoteBridge, "invalid remote caller"); - _; - } - - function initialize(address _dao, address _messageService) public initializer { - _initialize(_dao); - messageService = _messageService; - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external onlyRemoteBridge whenNotPaused { - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2ZkSyncSource.sol b/helix-contract/flatten/lnv2/Eth2ZkSyncSource.sol deleted file mode 100644 index 168252f2..00000000 --- a/helix-contract/flatten/lnv2/Eth2ZkSyncSource.sol +++ /dev/null @@ -1,2091 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/11/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/ln/interface/IZksyncMailbox.sol -// License-Identifier: MIT - -interface IMailbox { - function requestL2Transaction( - address _contractL2, - uint256 _l2Value, - bytes calldata _calldata, - uint256 _l2GasLimit, - uint256 _l2GasPerPubdataByteLimit, - bytes[] calldata _factoryDeps, - address _refundRecipient - ) external payable returns (bytes32 canonicalTxHash); - - // this function is used to calculate the fee on L2 - function l2TransactionBaseCost( - uint256 _gasPrice, - uint256 _l2GasLimit, - uint256 _l2GasPerPubdataByteLimit - ) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/interface/ILnDefaultBridgeTarget.sol -// License-Identifier: MIT - - -interface ILnDefaultBridgeTarget { - function slash( - LnBridgeHelper.TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external; - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external; -} - -// File contracts/ln/base/LnDefaultBridgeSource.sol -// License-Identifier: MIT - - - -/// @title LnPositiveBridgeSource -/// @notice LnPositiveBridgeSource is a contract to help user transfer token to liquidity node and generate proof, -/// then the liquidity node must transfer the same amount of the token to the user on target chain. -/// Otherwise if timeout the slasher can send a slash request message to target chain, then force transfer from lnProvider's margin to the user. -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnDefaultBridgeSource is LnBridgeHelper { - // the time(seconds) for liquidity provider to delivery message - // if timeout, slasher can work. - uint256 constant public MIN_SLASH_TIMESTAMP = 30 * 60; - // liquidity fee base rate - // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - // max transfer amount one time - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - - // provider fee is paid to liquidity node's account - // the fee is charged by the same token that user transfered - // providerFee = baseFee + liquidityFeeRate/LIQUIDITY_FEE_RATE_BASE * sendAmount - struct LnProviderFee { - uint112 baseFee; - uint8 liquidityFeeRate; - } - - struct LnProviderInfo { - LnProviderFee fee; - // we use this nonce to generate the unique withdraw id - uint64 withdrawNonce; - bytes32 lastTransferId; - } - // the Snapshot is the state of the token bridge when user prepare to transfer across chains. - // If the snapshot updated when the across chain transfer confirmed, it will - // 1. if lastTransferId or withdrawNonce updated, revert - // 2. if totalFee increase, revert - // 3. if totalFee decrease, success - struct Snapshot { - address provider; - address sourceToken; - bytes32 transferId; - uint112 totalFee; - uint64 withdrawNonce; - } - - // lock info - // the fee and penalty is the state of the transfer confirmed - struct LockInfo { - uint112 fee; - uint112 penalty; - bool isLocked; - } - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // providerKey => provider info - mapping(bytes32=>LnProviderInfo) public lnProviders; - // transferId => lock info - mapping(bytes32=>LockInfo) public lockInfos; - - address public protocolFeeReceiver; - - event TokenLocked( - bytes32 transferId, - address provider, - address sourceToken, - uint112 amount, - uint112 fee, - uint64 timestamp, - address receiver); - event LnProviderUpdated(address provider, address sourceToken, uint112 baseFee, uint8 liquidityfeeRate); - - // protocolFeeReceiver is the protocol fee reciever, we don't use the contract itself as the receiver - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - protocolFeeReceiver = _feeReceiver; - } - - // register or update token info, it can be only called by contract owner - // source token can only map a unique target token on target chain - function _setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) internal { - tokenInfos[_sourceToken] = TokenInfo( - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals, - true - ); - } - - // lnProvider register - // 1. set fee on source chain - // 2. deposit margin on target chain - function setProviderFee( - address sourceToken, - uint112 baseFee, - uint8 liquidityFeeRate - ) external { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderFee memory providerFee = LnProviderFee(baseFee, liquidityFeeRate); - - // we only update the field fee of the provider info - // if the provider has not been registered, then this line will register, otherwise update fee - lnProviders[providerKey].fee = providerFee; - - emit LnProviderUpdated(msg.sender, sourceToken, baseFee, liquidityFeeRate); - } - - function calculateProviderFee(LnProviderFee memory fee, uint112 amount) internal pure returns(uint256) { - return uint256(fee.baseFee) + uint256(fee.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; - } - - // the fee user should paid when transfer. - // totalFee = providerFee + protocolFee - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - return providerFee + tokenInfo.protocolFee; - } - - // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. - // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. - // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. - // 1. the state(lastTransferId, fee, withdrawNonce) must match snapshot - // 2. transferId not exist - function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(snapshot.provider, snapshot.sourceToken, tokenInfo.targetToken); - - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired:transfer"); - require(snapshot.withdrawNonce == providerInfo.withdrawNonce, "snapshot expired:withdraw"); - require(snapshot.totalFee >= providerFee + tokenInfo.protocolFee && providerFee > 0, "fee is invalid"); - - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(amount)); - uint64 timestamp = uint64(block.timestamp); - bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - targetAmount - )); - require(!lockInfos[transferId].isLocked, "transferId exist"); - // if the transfer refund, then the fee and penalty should be given to slasher, but the protocol fee is ignored - // and we use the penalty value configure at the moment transfer confirmed - lockInfos[transferId] = LockInfo(snapshot.totalFee, tokenInfo.penaltyLnCollateral, true); - - // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; - - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); - if (tokenInfo.protocolFee > 0) { - _safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); - } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; - if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); - } - } else { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - snapshot.provider, - amount + providerFee - ); - if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - protocolFeeReceiver, - tokenInfo.protocolFee - ); - } - } - emit TokenLocked( - transferId, - snapshot.provider, - snapshot.sourceToken, - targetAmount, - uint112(providerFee), - timestamp, - receiver); - } - - function _sourceAmountToTargetAmount( - TokenInfo memory tokenInfo, - uint256 amount - ) internal pure returns(uint112) { - uint256 targetAmount = amount * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - return uint112(targetAmount); - } - - function _slashAndRemoteRelease( - TransferParameter memory params, - bytes32 expectedTransferId - ) internal view returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "invalid timestamp"); - TokenInfo memory tokenInfo = tokenInfos[params.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(params.amount)); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - targetAmount - )); - require(expectedTransferId == transferId, "expected transfer id not match"); - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.isLocked, "lock info not match"); - uint112 targetFee = _sourceAmountToTargetAmount(tokenInfo, lockInfo.fee); - uint112 targetPenalty = _sourceAmountToTargetAmount(tokenInfo, lockInfo.penalty); - - message = _encodeSlashCall( - params, - msg.sender, - targetFee, - targetPenalty - ); - } - - function _withdrawMargin( - address sourceToken, - uint112 amount - ) internal returns(bytes memory message) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - lnProviders[providerKey].withdrawNonce = providerInfo.withdrawNonce + 1; - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, amount); - message = _encodeWithdrawCall( - providerInfo.lastTransferId, - providerInfo.withdrawNonce + 1, - msg.sender, - sourceToken, - tokenInfo.targetToken, - targetAmount - ); - } - - function _encodeSlashCall( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.slash.selector, - params, - slasher, - fee, - penalty - ); - } - - function _encodeWithdrawCall( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.withdraw.selector, - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Eth2ZkSyncSource.sol -// License-Identifier: MIT - - - - -contract Eth2ZkSyncSource is Initializable, LnAccessController, LnDefaultBridgeSource { - IMailbox public mailbox; - address public remoteBridge; - - event WithdrawMargin(address sourceToken, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _mailbox) public initializer { - mailbox = IMailbox(_mailbox); - _initialize(_dao); - _setFeeReceiver(_dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function l2Fee( - uint256 gasPrice, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) external view returns(uint256) { - return mailbox.l2TransactionBaseCost(gasPrice, l2GasLimit, l2GasPerPubdataByteLimit); - } - - function _sendMessage( - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit, - bytes memory message, - uint256 prepaid - ) internal returns(bytes32) { - return mailbox.requestL2Transaction{ value: prepaid }( - remoteBridge, - 0, - message, - l2GasLimit, - l2GasPerPubdataByteLimit, - new bytes[](0), - msg.sender - ); - } - - // this function can retry - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Eth2ZkSyncTarget.sol b/helix-contract/flatten/lnv2/Eth2ZkSyncTarget.sol deleted file mode 100644 index 8a74b49d..00000000 --- a/helix-contract/flatten/lnv2/Eth2ZkSyncTarget.sol +++ /dev/null @@ -1,1894 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/11/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnDefaultBridgeTarget.sol -// License-Identifier: MIT - -contract LnDefaultBridgeTarget is LnBridgeHelper { - uint256 constant public MIN_SLASH_TIMESTAMP = 30 * 60; - - struct ProviderInfo { - uint256 margin; - // use this slash gas reserve to pay the slash fee if transfer filled but timeout - uint256 slashReserveFund; - uint64 lastExpireFillTime; - uint64 withdrawNonce; - } - - // providerKey => margin - // providerKey = hash(provider, sourceToken, targetToken) - mapping(bytes32=>ProviderInfo) public lnProviderInfos; - - // if timestamp > 0, the Transfer has been relayed or slashed - // if slasher == address(0), this FillTransfer is relayed by lnProvider - // otherwise, this FillTransfer is slashed by slasher - struct FillTransfer { - uint64 timestamp; - address slasher; - } - - // transferId => FillTransfer - mapping(bytes32 => FillTransfer) public fillTransfers; - - event TransferFilled(address provider, bytes32 transferId); - event Slash(bytes32 transferId, address provider, address token, uint256 margin, address slasher); - event MarginUpdated(address provider, address token, uint256 amount, uint64 withdrawNonce); - event SlashReserveUpdated(address provider, address token, uint256 amount); - - function depositProviderMargin( - address sourceToken, - address targetToken, - uint256 margin - ) external payable { - require(margin > 0, "invalid margin"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin + margin; - lnProviderInfos[providerKey].margin = updatedMargin; - if (targetToken == address(0)) { - require(msg.value == margin, "invalid margin value"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), margin); - } - emit MarginUpdated(msg.sender, sourceToken, updatedMargin, providerInfo.withdrawNonce); - } - - function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId - ) external payable { - require(params.provider == msg.sender, "invalid provider"); - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - require(expectedTransferId == transferId, "check expected transferId failed"); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - // Make sure this transfer was never filled before - require(fillTransfer.timestamp == 0, "transfer has been filled"); - - fillTransfers[transferId].timestamp = uint64(block.timestamp); - if (block.timestamp - MIN_SLASH_TIMESTAMP > params.timestamp) { - bytes32 providerKey = getDefaultProviderKey(msg.sender, params.sourceToken, params.targetToken); - lnProviderInfos[providerKey].lastExpireFillTime = uint64(block.timestamp); - } - - if (params.targetToken == address(0)) { - require(msg.value == params.amount, "lnBridgeTarget:invalid amount"); - _safeTransferNative(params.receiver, params.amount); - } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); - } - emit TransferFilled(params.provider, transferId); - } - - function depositSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external payable { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedAmount = providerInfo.slashReserveFund + amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - require(msg.value == amount, "amount invalid"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - // withdraw slash fund - // provider can't withdraw until the block.timestamp overtime lastExpireFillTime for a period of time - function withdrawSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - require(amount <= providerInfo.slashReserveFund, "reserve not enough"); - require(block.timestamp - MIN_SLASH_TIMESTAMP >= providerInfo.lastExpireFillTime, "time not expired"); - uint256 updatedAmount = providerInfo.slashReserveFund - amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - _safeTransferNative(msg.sender, amount); - } else { - _safeTransfer(targetToken, msg.sender, amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - function _withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal { - // ensure all transfer has finished - require(lastTransferId == bytes32(0) || fillTransfers[lastTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - // all the early withdraw info ignored - require(providerInfo.withdrawNonce < withdrawNonce, "withdraw nonce expired"); - - // transfer token - require(providerInfo.margin >= amount, "margin not enough"); - uint256 updatedMargin = providerInfo.margin - amount; - lnProviderInfos[providerKey].margin = updatedMargin; - lnProviderInfos[providerKey].withdrawNonce = withdrawNonce; - - if (targetToken == address(0)) { - _safeTransferNative(provider, amount); - } else { - _safeTransfer(targetToken, provider, amount); - } - emit MarginUpdated(provider, sourceToken, updatedMargin, withdrawNonce); - } - - function _slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal { - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - require(fillTransfer.slasher == address(0), "transfer has been slashed"); - bytes32 providerKey = getDefaultProviderKey(params.provider, params.sourceToken, params.targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin; - // transfer is not filled - if (fillTransfer.timestamp == 0) { - require(params.timestamp < block.timestamp - MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId] = FillTransfer(uint64(block.timestamp), slasher); - - // 1. transfer token to receiver - // 2. trnasfer fee and penalty to slasher - // update margin - uint256 marginCost = params.amount + fee + penalty; - require(providerInfo.margin >= marginCost, "margin not enough"); - updatedMargin = providerInfo.margin - marginCost; - lnProviderInfos[providerKey].margin = updatedMargin; - - if (params.targetToken == address(0)) { - _safeTransferNative(params.receiver, params.amount); - _safeTransferNative(slasher, fee + penalty); - } else { - _safeTransfer(params.targetToken, params.receiver, uint256(params.amount)); - _safeTransfer(params.targetToken, slasher, fee + penalty); - } - } else { - require(fillTransfer.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId].slasher = slasher; - uint112 slashRefund = penalty / 5; - // transfer slashRefund to slasher - require(providerInfo.slashReserveFund >= slashRefund, "slashReserveFund not enough"); - lnProviderInfos[providerKey].slashReserveFund = providerInfo.slashReserveFund - slashRefund; - if (params.targetToken == address(0)) { - _safeTransferNative(slasher, slashRefund); - } else { - _safeTransfer(params.targetToken, slasher, slashRefund); - } - } - emit Slash(transferId, params.provider, params.sourceToken, updatedMargin, slasher); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Eth2ZkSyncTarget.sol -// License-Identifier: MIT - - - -contract Eth2ZkSyncTarget is Initializable, LnAccessController, LnDefaultBridgeTarget { - uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); - address public remoteBridge; - address public remoteBridgeAlias; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == remoteBridgeAlias, "invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - // l1 address to l2 address - remoteBridgeAlias = address(uint160(_remoteBridge) + offset); - } - - function setRemoteBridgeAlias(address _remoteBridgeAlias) external onlyDao { - remoteBridgeAlias = _remoteBridgeAlias; - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external onlyRemoteBridge whenNotPaused { - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/LayerZeroBridge.sol b/helix-contract/flatten/lnv2/LayerZeroBridge.sol deleted file mode 100644 index 834dfd74..00000000 --- a/helix-contract/flatten/lnv2/LayerZeroBridge.sol +++ /dev/null @@ -1,2388 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/25/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnDefaultBridgeTarget.sol -// License-Identifier: MIT - -contract LnDefaultBridgeTarget is LnBridgeHelper { - uint256 constant private MIN_SLASH_TIMESTAMP = 30 * 60; - - struct ProviderInfo { - uint256 margin; - // use this slash gas reserve to pay the slash fee if transfer filled but timeout - uint256 slashReserveFund; - uint64 lastExpireFillTime; - uint64 withdrawNonce; - } - - // providerKey => margin - // providerKey = hash(provider, sourceToken, targetToken) - mapping(bytes32=>ProviderInfo) public lnProviderInfos; - - // if timestamp > 0, the Transfer has been relayed or slashed - // if slasher == address(0), this FillTransfer is relayed by lnProvider - // otherwise, this FillTransfer is slashed by slasher - struct FillTransfer { - uint64 timestamp; - address slasher; - } - - // transferId => FillTransfer - mapping(bytes32 => FillTransfer) public fillTransfers; - - event TransferFilled(address provider, bytes32 transferId); - event Slash(bytes32 transferId, address provider, address token, uint256 margin, address slasher); - event MarginUpdated(address provider, address token, uint256 amount, uint64 withdrawNonce); - event SlashReserveUpdated(address provider, address token, uint256 amount); - - function depositProviderMargin( - address sourceToken, - address targetToken, - uint256 margin - ) external payable { - require(margin > 0, "invalid margin"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin + margin; - lnProviderInfos[providerKey].margin = updatedMargin; - if (targetToken == address(0)) { - require(msg.value == margin, "invalid margin value"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), margin); - } - emit MarginUpdated(msg.sender, sourceToken, updatedMargin, providerInfo.withdrawNonce); - } - - function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId - ) external payable { - require(params.provider == msg.sender, "invalid provider"); - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - require(expectedTransferId == transferId, "check expected transferId failed"); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - // Make sure this transfer was never filled before - require(fillTransfer.timestamp == 0, "transfer has been filled"); - - fillTransfers[transferId].timestamp = uint64(block.timestamp); - if (block.timestamp - MIN_SLASH_TIMESTAMP > params.timestamp) { - bytes32 providerKey = getDefaultProviderKey(msg.sender, params.sourceToken, params.targetToken); - lnProviderInfos[providerKey].lastExpireFillTime = uint64(block.timestamp); - } - - if (params.targetToken == address(0)) { - require(msg.value == params.amount, "lnBridgeTarget:invalid amount"); - _safeTransferNative(params.receiver, params.amount); - } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); - } - emit TransferFilled(params.provider, transferId); - } - - function depositSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external payable { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedAmount = providerInfo.slashReserveFund + amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - require(msg.value == amount, "amount invalid"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - // withdraw slash fund - // provider can't withdraw until the block.timestamp overtime lastExpireFillTime for a period of time - function withdrawSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - require(amount <= providerInfo.slashReserveFund, "reserve not enough"); - require(block.timestamp - MIN_SLASH_TIMESTAMP >= providerInfo.lastExpireFillTime, "time not expired"); - uint256 updatedAmount = providerInfo.slashReserveFund - amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - _safeTransferNative(msg.sender, amount); - } else { - _safeTransfer(targetToken, msg.sender, amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - function _withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal { - // ensure all transfer has finished - require(lastTransferId == bytes32(0) || fillTransfers[lastTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - // all the early withdraw info ignored - require(providerInfo.withdrawNonce < withdrawNonce, "withdraw nonce expired"); - - // transfer token - require(providerInfo.margin >= amount, "margin not enough"); - uint256 updatedMargin = providerInfo.margin - amount; - lnProviderInfos[providerKey].margin = updatedMargin; - lnProviderInfos[providerKey].withdrawNonce = withdrawNonce; - - if (targetToken == address(0)) { - _safeTransferNative(provider, amount); - } else { - _safeTransfer(targetToken, provider, amount); - } - emit MarginUpdated(provider, sourceToken, updatedMargin, withdrawNonce); - } - - function _slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal { - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - require(fillTransfer.slasher == address(0), "transfer has been slashed"); - bytes32 providerKey = getDefaultProviderKey(params.provider, params.sourceToken, params.targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin; - // transfer is not filled - if (fillTransfer.timestamp == 0) { - require(params.timestamp < block.timestamp - MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId] = FillTransfer(uint64(block.timestamp), slasher); - - // 1. transfer token to receiver - // 2. trnasfer fee and penalty to slasher - // update margin - uint256 marginCost = params.amount + fee + penalty; - require(providerInfo.margin >= marginCost, "margin not enough"); - updatedMargin = providerInfo.margin - marginCost; - lnProviderInfos[providerKey].margin = updatedMargin; - - if (params.targetToken == address(0)) { - _safeTransferNative(params.receiver, params.amount); - _safeTransferNative(slasher, fee + penalty); - } else { - _safeTransfer(params.targetToken, params.receiver, uint256(params.amount)); - _safeTransfer(params.targetToken, slasher, fee + penalty); - } - } else { - require(fillTransfer.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId].slasher = slasher; - uint112 slashRefund = penalty / 5; - // transfer slashRefund to slasher - require(providerInfo.slashReserveFund >= slashRefund, "slashReserveFund not enough"); - lnProviderInfos[providerKey].slashReserveFund = providerInfo.slashReserveFund - slashRefund; - if (params.targetToken == address(0)) { - _safeTransferNative(slasher, slashRefund); - } else { - _safeTransfer(params.targetToken, slasher, slashRefund); - } - } - emit Slash(transferId, params.provider, params.sourceToken, updatedMargin, slasher); - } -} - -// File contracts/ln/interface/ILnDefaultBridgeTarget.sol -// License-Identifier: MIT - - -interface ILnDefaultBridgeTarget { - function slash( - LnBridgeHelper.TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external; - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external; -} - -// File contracts/ln/base/LnDefaultBridgeSource.sol -// License-Identifier: MIT - - - -/// @title LnPositiveBridgeSource -/// @notice LnPositiveBridgeSource is a contract to help user transfer token to liquidity node and generate proof, -/// then the liquidity node must transfer the same amount of the token to the user on target chain. -/// Otherwise if timeout the slasher can send a slash request message to target chain, then force transfer from lnProvider's margin to the user. -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnDefaultBridgeSource is LnBridgeHelper { - // the time(seconds) for liquidity provider to delivery message - // if timeout, slasher can work. - uint256 constant private MIN_SLASH_TIMESTAMP = 30 * 60; - // liquidity fee base rate - // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - // max transfer amount one time - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - - // provider fee is paid to liquidity node's account - // the fee is charged by the same token that user transfered - // providerFee = baseFee + liquidityFeeRate/LIQUIDITY_FEE_RATE_BASE * sendAmount - struct LnProviderFee { - uint112 baseFee; - uint8 liquidityFeeRate; - } - - struct LnProviderInfo { - LnProviderFee fee; - // we use this nonce to generate the unique withdraw id - uint64 withdrawNonce; - bytes32 lastTransferId; - } - // the Snapshot is the state of the token bridge when user prepare to transfer across chains. - // If the snapshot updated when the across chain transfer confirmed, it will - // 1. if lastTransferId or withdrawNonce updated, revert - // 2. if totalFee increase, revert - // 3. if totalFee decrease, success - struct Snapshot { - address provider; - address sourceToken; - bytes32 transferId; - uint112 totalFee; - uint64 withdrawNonce; - } - - // lock info - // the fee and penalty is the state of the transfer confirmed - struct LockInfo { - uint112 fee; - uint112 penalty; - bool isLocked; - } - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // providerKey => provider info - mapping(bytes32=>LnProviderInfo) public lnProviders; - // transferId => lock info - mapping(bytes32=>LockInfo) public lockInfos; - - address public protocolFeeReceiver; - - event TokenLocked( - bytes32 transferId, - address provider, - address sourceToken, - uint112 amount, - uint112 fee, - uint64 timestamp, - address receiver); - event LnProviderUpdated(address provider, address sourceToken, uint112 baseFee, uint8 liquidityfeeRate); - - // protocolFeeReceiver is the protocol fee reciever, we don't use the contract itself as the receiver - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - protocolFeeReceiver = _feeReceiver; - } - - // register or update token info, it can be only called by contract owner - // source token can only map a unique target token on target chain - function _setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) internal { - tokenInfos[_sourceToken] = TokenInfo( - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals, - true - ); - } - - // lnProvider register - // 1. set fee on source chain - // 2. deposit margin on target chain - function setProviderFee( - address sourceToken, - uint112 baseFee, - uint8 liquidityFeeRate - ) external { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderFee memory providerFee = LnProviderFee(baseFee, liquidityFeeRate); - - // we only update the field fee of the provider info - // if the provider has not been registered, then this line will register, otherwise update fee - lnProviders[providerKey].fee = providerFee; - - emit LnProviderUpdated(msg.sender, sourceToken, baseFee, liquidityFeeRate); - } - - function calculateProviderFee(LnProviderFee memory fee, uint112 amount) internal pure returns(uint256) { - return uint256(fee.baseFee) + uint256(fee.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; - } - - // the fee user should paid when transfer. - // totalFee = providerFee + protocolFee - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - return providerFee + tokenInfo.protocolFee; - } - - // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. - // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. - // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. - // 1. the state(lastTransferId, fee, withdrawNonce) must match snapshot - // 2. transferId not exist - function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(snapshot.provider, snapshot.sourceToken, tokenInfo.targetToken); - - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired:transfer"); - require(snapshot.withdrawNonce == providerInfo.withdrawNonce, "snapshot expired:withdraw"); - require(snapshot.totalFee >= providerFee + tokenInfo.protocolFee && providerFee > 0, "fee is invalid"); - - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(amount)); - uint64 timestamp = uint64(block.timestamp); - bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - targetAmount - )); - require(!lockInfos[transferId].isLocked, "transferId exist"); - // if the transfer refund, then the fee and penalty should be given to slasher, but the protocol fee is ignored - // and we use the penalty value configure at the moment transfer confirmed - lockInfos[transferId] = LockInfo(snapshot.totalFee, tokenInfo.penaltyLnCollateral, true); - - // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; - - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); - if (tokenInfo.protocolFee > 0) { - _safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); - } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; - if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); - } - } else { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - snapshot.provider, - amount + providerFee - ); - if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - protocolFeeReceiver, - tokenInfo.protocolFee - ); - } - } - emit TokenLocked( - transferId, - snapshot.provider, - snapshot.sourceToken, - targetAmount, - uint112(providerFee), - timestamp, - receiver); - } - - function _sourceAmountToTargetAmount( - TokenInfo memory tokenInfo, - uint256 amount - ) internal pure returns(uint112) { - uint256 targetAmount = amount * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - return uint112(targetAmount); - } - - function _slashAndRemoteRelease( - TransferParameter memory params, - bytes32 expectedTransferId - ) internal view returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "invalid timestamp"); - TokenInfo memory tokenInfo = tokenInfos[params.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(params.amount)); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - targetAmount - )); - require(expectedTransferId == transferId, "expected transfer id not match"); - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.isLocked, "lock info not match"); - uint112 targetFee = _sourceAmountToTargetAmount(tokenInfo, lockInfo.fee); - uint112 targetPenalty = _sourceAmountToTargetAmount(tokenInfo, lockInfo.penalty); - - message = _encodeSlashCall( - params, - msg.sender, - targetFee, - targetPenalty - ); - } - - function _withdrawMargin( - address sourceToken, - uint112 amount - ) internal returns(bytes memory message) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - lnProviders[providerKey].withdrawNonce = providerInfo.withdrawNonce + 1; - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, amount); - message = _encodeWithdrawCall( - providerInfo.lastTransferId, - providerInfo.withdrawNonce + 1, - msg.sender, - sourceToken, - tokenInfo.targetToken, - targetAmount - ); - } - - function _encodeSlashCall( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) public pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.slash.selector, - params, - slasher, - fee, - penalty - ); - } - - function _encodeWithdrawCall( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) public pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.withdraw.selector, - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - } -} - -// File contracts/ln/interface/ILayerZeroEndpoint.sol -// License-Identifier: MIT - -interface ILayerZeroEndpoint { - function send( - uint16 _dstChainId, - bytes calldata _destination, - bytes calldata _payload, - address payable _refundAddress, - address _zroPaymentAddress, - bytes calldata _adapterParams - ) external payable; - - function estimateFees( - uint16 _dstChainId, - address _userApplication, - bytes calldata _payload, - bool _payInZRO, - bytes calldata _adapterParam - ) external view returns (uint nativeFee, uint zroFee); -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/LayerZeroBridge.sol -// License-Identifier: MIT - - - - - -contract LayerZeroBridge is Initializable, LnAccessController, LnDefaultBridgeSource, LnDefaultBridgeTarget { - ILayerZeroEndpoint public endpoint; - address public remoteBridge; - bytes32 public trustedRemote; - uint16 public remoteChainId; - - event WithdrawMargin(address sourceToken, uint112 amount); - event CallResult(bytes srcAddress, bool successed); - - receive() external payable {} - - modifier onlyRemoteBridge(bytes calldata srcAddress) { - require(msg.sender == address(endpoint), "invalid caller"); - require(trustedRemote == keccak256(srcAddress), "invalid remote caller"); - _; - } - - function initialize(address _dao, address _endpoint, uint16 _remoteChainId) public initializer { - _initialize(_dao); - endpoint = ILayerZeroEndpoint(_endpoint); - _setFeeReceiver(_dao); - remoteChainId = _remoteChainId; - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - trustedRemote = keccak256(abi.encodePacked(_remoteBridge, address(this))); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function estimateSlashFee( - TransferParameter calldata params - ) external view returns(uint256 nativeFee, uint256 zroFee) { - bytes memory slashCallMessage = _encodeSlashCall( - params, - msg.sender, - 0, - 0 - ); - return endpoint.estimateFees( - remoteChainId, - remoteBridge, - slashCallMessage, - false, - bytes("") - ); - } - - function estimateWithdrawFee( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external view returns(uint256 nativeFee, uint256 zroFee) { - bytes memory withdrawCallMessage = _encodeWithdrawCall( - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - return endpoint.estimateFees( - remoteChainId, - remoteBridge, - withdrawCallMessage, - false, - bytes("") - ); - } - - function _sendMessage( - bytes memory message, - uint256 prepaid - ) internal { - bytes memory destination = abi.encodePacked( - remoteBridge, - address(this) - ); - endpoint.send{ value: prepaid }( - remoteChainId, - destination, - message, - payable(msg.sender), - // zro payment, future parameter - address(0x0), - bytes("") - ); - } - - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } - - function lzReceive( - uint16 _srcChainId, - bytes calldata _srcAddress, - uint64, //nonce unused - bytes calldata _payload) onlyRemoteBridge(_srcAddress) whenNotPaused external { - require(_srcChainId == remoteChainId, "invalid src chainid"); - // call - (bool success,) = address(this).call(_payload); - // don't revert to prevent message block - emit CallResult(_srcAddress, success); - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external { - require(msg.sender == address(this), "only self"); - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external { - require(msg.sender == address(this), "only self"); - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/LayerZeroMessager.sol b/helix-contract/flatten/lnv2/LayerZeroMessager.sol new file mode 100644 index 00000000..577b0a99 --- /dev/null +++ b/helix-contract/flatten/lnv2/LayerZeroMessager.sol @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * + * 10/17/2023 + **/ + +pragma solidity ^0.8.17; + +// File contracts/ln/interface/ILowLevelMessager.sol +// License-Identifier: MIT + +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} + +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; +} + +// File contracts/ln/base/LnAccessController.sol +// License-Identifier: MIT + +/// @title LnAccessController +/// @notice LnAccessController is a contract to control the access permission +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnAccessController { + address public dao; + address public operator; + + mapping(address=>bool) public callerWhiteList; + + modifier onlyDao() { + require(msg.sender == dao, "!dao"); + _; + } + + modifier onlyOperator() { + require(msg.sender == operator, "!operator"); + _; + } + + modifier onlyWhiteListCaller() { + require(callerWhiteList[msg.sender], "caller not in white list"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; + } + + function authoriseAppCaller(address appAddress, bool enable) onlyDao external { + callerWhiteList[appAddress] = enable; + } + + function transferOwnership(address _dao) onlyDao external { + dao = _dao; + } +} + +// File contracts/ln/messager/interface/ILayerZeroEndpoint.sol +// License-Identifier: MIT + +interface ILayerZeroEndpoint { + function send( + uint16 _dstChainId, + bytes calldata _destination, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable; + + function estimateFees( + uint16 _dstChainId, + address _userApplication, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParam + ) external view returns (uint nativeFee, uint zroFee); +} + +// File contracts/ln/messager/LayerZeroMessager.sol +// License-Identifier: MIT + + + +contract LayerZeroMessager is LnAccessController { + ILayerZeroEndpoint public endpoint; + + struct RemoteMessager { + uint16 lzRemoteChainId; + address messager; + } + + // app remoteChainId => layerzero remote messager + mapping(uint256=>RemoteMessager) public remoteMessagers; + // lz remoteChainId => trustedRemotes + mapping(uint16=>bytes32) public trustedRemotes; + + // token bridge pair + // hash(lzRemoteChainId, localAppAddress) => remoteAppAddress + mapping(bytes32=>address) public remoteAppReceivers; + mapping(bytes32=>address) public remoteAppSenders; + + event CallResult(uint16 lzRemoteChainId, bytes srcAddress, bool successed); + event CallerUnMatched(uint16 lzRemoteChainId, bytes srcAddress, address remoteAppAddress); + + constructor(address _dao, address _endpoint) { + _initialize(_dao); + endpoint = ILayerZeroEndpoint(_endpoint); + } + + modifier onlyRemoteBridge(uint16 lzRemoteChainId, bytes calldata srcAddress) { + require(msg.sender == address(endpoint), "invalid caller"); + require(trustedRemotes[lzRemoteChainId] == keccak256(srcAddress), "invalid remote caller"); + _; + } + + function setRemoteMessager(uint256 _appRemoteChainId, uint16 _lzRemoteChainId, address _remoteMessager) onlyDao external { + remoteMessagers[_appRemoteChainId] = RemoteMessager(_lzRemoteChainId, _remoteMessager); + trustedRemotes[_lzRemoteChainId] = keccak256(abi.encodePacked(_remoteMessager, address(this))); + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.lzRemoteChainId, msg.sender)); + remoteAppReceivers[key] = _remoteBridge; + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) onlyWhiteListCaller external { + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.lzRemoteChainId, msg.sender)); + remoteAppSenders[key] = _remoteBridge; + } + + function sendMessage(uint256 _remoteChainId, bytes memory _message, bytes memory _params) onlyWhiteListCaller external payable { + address refunder = address(bytes20(_params)); + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "remote not configured"); + bytes memory destination = abi.encodePacked( + remoteMessager.messager, + address(this) + ); + bytes32 key = keccak256(abi.encodePacked(remoteMessager.lzRemoteChainId, msg.sender)); + address remoteAppAddress = remoteAppReceivers[key]; + require(remoteAppAddress != address(0), "app pair not registered"); + bytes memory lzPayload = abi.encode(msg.sender, remoteAppAddress, _message); + endpoint.send{ value: msg.value }( + remoteMessager.lzRemoteChainId, + destination, + lzPayload, + payable(refunder), + // zro payment, future parameter + address(0x0), + bytes("") + ); + } + + function lzReceive( + uint16 _srcChainId, + bytes calldata _srcAddress, + uint64, //nonce unused + bytes calldata _payload) onlyRemoteBridge(_srcChainId, _srcAddress) external { + // call + (address remoteAppAddress, address localAppAddress, bytes memory message) = abi.decode(_payload, (address, address, bytes)); + bytes32 key = keccak256(abi.encodePacked(_srcChainId, localAppAddress)); + if (remoteAppAddress != remoteAppSenders[key]) { + emit CallerUnMatched(_srcChainId, _srcAddress, remoteAppAddress); + return; + } + (bool success,) = localAppAddress.call(message); + // don't revert to prevent message block + emit CallResult(_srcChainId, _srcAddress, success); + } + + function fee( + uint256 _remoteChainId, + bytes memory _message + ) external view returns(uint256 nativeFee, uint256 zroFee) { + RemoteMessager memory remoteMessager = remoteMessagers[_remoteChainId]; + require(remoteMessager.messager != address(0), "messager not configured"); + return endpoint.estimateFees( + remoteMessager.lzRemoteChainId, + remoteMessager.messager, + _message, + false, + bytes("") + ); + } +} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Linea2EthSource.sol b/helix-contract/flatten/lnv2/Linea2EthSource.sol deleted file mode 100644 index 1113d1f3..00000000 --- a/helix-contract/flatten/lnv2/Linea2EthSource.sol +++ /dev/null @@ -1,2031 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/22/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnOppositeBridgeSource.sol -// License-Identifier: MIT - - -/// @title LnBridgeSource -/// @notice LnBridgeSource is a contract to help user transfer token to liquidity node and generate proof, -/// then the liquidity node must transfer the same amount of the token to the user on target chain. -/// Otherwise if timeout the slasher can paid for relayer and slash the transfer, then request slash from lnProvider's margin. -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnOppositeBridgeSource is LnBridgeHelper { - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - // the Liquidity Node provider info - // Liquidity Node need register first - struct LnProviderConfigure { - uint112 margin; - uint112 baseFee; - // liquidityFeeRate / 100,000 * amount = liquidityFee - // the max liquidity fee rate is 0.255% - uint8 liquidityFeeRate; - } - struct LnProviderInfo { - LnProviderConfigure config; - bool pause; - bytes32 lastTransferId; - } - - // the Snapshot is the state of the token bridge when user prepare to transfer across chains. - // If the snapshot updated when the across chain transfer confirmed, it will - // 1. if lastTransferId updated, revert - // 2. if margin decrease or totalFee increase, revert - // 3. if margin increase or totalFee decrease, success - struct Snapshot { - address provider; - address sourceToken; - bytes32 transferId; - uint112 depositedMargin; - uint112 totalFee; - } - // registered token info - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // registered lnProviders - mapping(bytes32=>LnProviderInfo) public lnProviders; - // each time cross chain transfer, amount and fee can't be larger than type(uint112).max - struct LockInfo { - // amount + providerFee + penaltyLnCollateral - // the Indexer should be care about this value, it will frozen lnProvider's margin when the transfer not finished. - // and when the slasher slash success, this amount of token will be transfer from lnProvider's margin to slasher. - uint112 amountWithFeeAndPenalty; - bool hasSlashed; - } - // key: transferId = hash(proviousTransferId, timestamp, targetToken, receiver, targetAmount) - // * `proviousTransferId` is used to ensure the continuous of the transfer - // * `timestamp` is the block.timestmap to judge timeout on target chain(here we support source and target chain has the same world clock) - // * `targetToken`, `receiver` and `targetAmount` are used on target chain to transfer target token. - mapping(bytes32 => LockInfo) public lockInfos; - address public feeReceiver; - - event TokenLocked( - bytes32 transferId, - address provider, - address sourceToken, - uint112 amount, - uint112 fee, - uint64 timestamp, - address receiver); - event LiquidityWithdrawn(address provider, address token, uint112 amount); - event Slash(bytes32 transferId, address provider, address token, uint112 margin, address slasher); - // relayer - event LnProviderUpdated(address provider, address token, uint112 margin, uint112 baseFee, uint8 liquidityfeeRate); - - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - feeReceiver = _feeReceiver; - } - - function _updateProtocolFee(address _token, uint112 _protocolFee) internal { - require(tokenInfos[_token].isRegistered, "token not registered"); - tokenInfos[_token].protocolFee = _protocolFee; - } - - function _updatePenaltyLnCollateral(address _token, uint112 _penaltyLnCollateral) internal { - require(tokenInfos[_token].isRegistered, "token not registered"); - tokenInfos[_token].penaltyLnCollateral = _penaltyLnCollateral; - } - - function providerPause(address sourceToken) external { - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - lnProviders[providerKey].pause = true; - } - - function providerUnpause(address sourceToken) external { - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - lnProviders[providerKey].pause = false; - } - - // lnProvider can register or update its configure by using this function - // * `margin` is the increased value of the deposited margin - function updateProviderFeeAndMargin( - address sourceToken, - uint112 margin, - uint112 baseFee, - uint8 liquidityFeeRate - ) external payable { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token is not registered"); - - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - - LnProviderConfigure memory config = LnProviderConfigure( - // the margin can be only increased here - margin + providerInfo.config.margin, - baseFee, - liquidityFeeRate - ); - - lnProviders[providerKey].config = config; - - if (sourceToken == address(0)) { - require(msg.value == margin, "invalid margin value"); - } else { - if (margin > 0) { - _safeTransferFrom(sourceToken, msg.sender, address(this), margin); - } - } - emit LnProviderUpdated(msg.sender, sourceToken, config.margin, baseFee, liquidityFeeRate); - } - - function _registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) internal { - tokenInfos[sourceToken] = TokenInfo( - targetToken, - protocolFee, - penaltyLnCollateral, - sourceDecimals, - targetDecimals, - true - ); - } - - function calculateProviderFee(LnProviderConfigure memory config, uint112 amount) internal pure returns(uint256) { - return uint256(config.baseFee) + uint256(config.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; - } - - // the fee user should paid when transfer. - // totalFee = providerFee + protocolFee - // providerFee = provider.baseFee + provider.liquidityFeeRate * amount - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - bytes32 providerKey = getProviderKey(provider, sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.config, amount); - return providerFee + tokenInfos[sourceToken].protocolFee; - } - - // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. - // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. - // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. - // 1. the state(lastTransferId, fee, margin) must match snapshot - // 2. transferId not exist - function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - bytes32 providerKey = getProviderKey(snapshot.provider, snapshot.sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - - require(!providerInfo.pause, "provider paused"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; - - uint256 providerFee = calculateProviderFee(providerInfo.config, amount); - - // Note: this requirement is not enough to ensure that the lnProvider's margin is enough because there maybe some frozen margins in other transfers - require(providerInfo.config.margin >= amount + tokenInfo.penaltyLnCollateral + uint112(providerFee), "amount not valid"); - - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired"); - require(snapshot.totalFee >= tokenInfo.protocolFee + providerFee, "fee is invalid"); - require(snapshot.depositedMargin <= providerInfo.config.margin, "margin updated"); - - uint256 targetAmount = uint256(amount) * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - uint64 timestamp = uint64(block.timestamp); - bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - uint112(targetAmount))); - require(lockInfos[transferId].amountWithFeeAndPenalty == 0, "transferId exist"); - lockInfos[transferId] = LockInfo(amount + tokenInfo.penaltyLnCollateral + uint112(providerFee), false); - - // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; - - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); - if (tokenInfo.protocolFee > 0) { - _safeTransferNative(feeReceiver, tokenInfo.protocolFee); - } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; - if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); - } - } else { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - snapshot.provider, - amount + providerFee - ); - if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - feeReceiver, - tokenInfo.protocolFee - ); - } - } - emit TokenLocked( - transferId, - snapshot.provider, - snapshot.sourceToken, - amount, - uint112(providerFee), - timestamp, - receiver); - } - - // this slash is called by remote message - // the token should be sent to the slasher who slash and finish the transfer on target chain. - // latestSlashTransferId is the latest slashed transfer trusted from the target chain, and the current slash transfer cannot be executed before the latestSlash transfer. - // after slash, the margin of lnProvider need to be updated - function _slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address sourceToken, - address provider, - address slasher - ) internal { - // check lastTransfer - // ensure last slash transfer(checked on target chain) has been slashed - LockInfo memory lastLockInfo = lockInfos[latestSlashTransferId]; - require(lastLockInfo.hasSlashed || latestSlashTransferId == INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); - LockInfo memory lockInfo = lockInfos[transferId]; - - // ensure transfer exist and not slashed yet - require(!lockInfo.hasSlashed, "transfer has been slashed"); - require(lockInfo.amountWithFeeAndPenalty > 0, "lnBridgeSource:invalid transferId"); - - bytes32 providerKey = getProviderKey(provider, sourceToken); - - LnProviderInfo memory lnProvider = lnProviders[providerKey]; - lockInfos[transferId].hasSlashed = true; - // transfer token to the slasher - uint256 slashAmount = lockInfo.amountWithFeeAndPenalty; - require(lnProvider.config.margin >= slashAmount, "margin not enough"); - uint112 updatedMargin = lnProvider.config.margin - uint112(slashAmount); - lnProviders[providerKey].config.margin = updatedMargin; - - if (sourceToken == address(0)) { - _safeTransferNative(slasher, slashAmount); - } else { - _safeTransfer(sourceToken, slasher, slashAmount); - } - - emit Slash(transferId, provider, sourceToken, updatedMargin, slasher); - } - - // lastTransfer is the latest slash transfer, all transfer must be relayed or slashed - // if user use the snapshot before this transaction to send cross-chain transfer, it should be reverted because this `_withdrawMargin` will decrease margin. - function _withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) internal { - // check the latest slash transfer - // ensure latest slash tranfer(verified on target chain) has been slashed on source chain - LockInfo memory lastRefundLockInfo = lockInfos[latestSlashTransferId]; - require(lastRefundLockInfo.hasSlashed || latestSlashTransferId == INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); - - // use this condition to ensure that the withdraw message is sent by the provider - // the parameter provider is the message sender of this remote withdraw call - bytes32 providerKey = getProviderKey(provider, sourceToken); - LnProviderInfo memory lnProvider = lnProviders[providerKey]; - - // ensure all transfer has finished - require(lnProvider.lastTransferId == lastTransferId, "invalid last transferid"); - require(lnProvider.config.margin >= amount, "margin not enough"); - uint112 updatedMargin = lnProvider.config.margin - amount; - lnProviders[providerKey].config.margin = updatedMargin; - if (sourceToken == address(0)) { - _safeTransferNative(provider, amount); - } else { - _safeTransfer(sourceToken, provider, amount); - } - emit LiquidityWithdrawn(provider, sourceToken, updatedMargin); - } -} - -// File contracts/ln/interface/ILineaMessageService.sol -// License-Identifier: MIT - -interface ILineaMessageService { - function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable; - function sender() external view returns (address); -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Linea2EthSource.sol -// License-Identifier: MIT - - - - -contract Linea2EthSource is Initializable, LnAccessController, LnOppositeBridgeSource { - address public remoteBridge; - // linea message service address - address public messageService; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(ILineaMessageService(messageService).sender() == remoteBridge, "invalid remote caller"); - _; - } - - function initialize(address _dao, address _messageService) public initializer { - _initialize(_dao); - _setFeeReceiver(_dao); - messageService = _messageService; - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function updateProtocolFee(address token, uint112 _protocolFee) external onlyDao { - _updateProtocolFee(token, _protocolFee); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) external onlyOperator { - _registerToken(sourceToken, targetToken, protocolFee, penaltyLnCollateral, sourceDecimals, targetDecimals); - } - - function slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external onlyRemoteBridge whenNotPaused { - _slash(latestSlashTransferId, transferId, sourceToken, provider, slasher); - } - - function withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdrawMargin(latestSlashTransferId, lastTransferId, provider, sourceToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/Linea2EthTarget.sol b/helix-contract/flatten/lnv2/Linea2EthTarget.sol deleted file mode 100644 index e9ab33b3..00000000 --- a/helix-contract/flatten/lnv2/Linea2EthTarget.sol +++ /dev/null @@ -1,1897 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/22/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File contracts/ln/interface/ILineaMessageService.sol -// License-Identifier: MIT - -interface ILineaMessageService { - function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable; - function sender() external view returns (address); -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/interface/ILnOppositeBridgeSource.sol -// License-Identifier: MIT - - -interface ILnOppositeBridgeSource { - function slash( - bytes32 lastRefundTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external; - - function withdrawMargin( - bytes32 lastRefundTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external; -} - -// File contracts/ln/base/LnOppositeBridgeTarget.sol -// License-Identifier: MIT - - -contract LnOppositeBridgeTarget is LnBridgeHelper { - uint256 constant public MIN_REFUND_TIMESTAMP = 30 * 60; - - // if slasher == address(0), this FillTransfer is relayed by lnProvider - // otherwise, this FillTransfer is slashed by slasher - // if there is no slash transfer before, then it's latestSlashTransferId is assigned by INIT_SLASH_TRANSFER_ID, a special flag - struct SlashInfo { - address provider; - address sourceToken; - address slasher; - } - - // transferId => latest slash transfer Id - mapping(bytes32 => bytes32) public fillTransfers; - // transferId => Slash info - mapping(bytes32 => SlashInfo) public slashInfos; - - event TransferFilled(bytes32 transferId, address slasher); - - // if slasher is nonzero, then it's a slash fill transfer - function _checkPreviousAndFillTransfer( - bytes32 transferId, - bytes32 previousTransferId - ) internal { - // the first fill transfer, we fill the INIT_SLASH_TRANSFER_ID as the latest slash transferId - if (previousTransferId == bytes32(0)) { - fillTransfers[transferId] = INIT_SLASH_TRANSFER_ID; - } else { - // Find the previous slash fill, it is a slash fill if the slasher is not zero address. - bytes32 previousLatestSlashTransferId = fillTransfers[previousTransferId]; - require(previousLatestSlashTransferId != bytes32(0), "previous fill not exist"); - - SlashInfo memory previousSlashInfo = slashInfos[previousTransferId]; - // we use latestSlashTransferId to store the latest slash transferId - // if previous.slasher != 0, then previous is slashed - // if previous.slasher == 0, then previous is not slashed - bytes32 latestSlashTransferId = previousSlashInfo.slasher != address(0) ? previousTransferId : previousLatestSlashTransferId; - - fillTransfers[transferId] = latestSlashTransferId; - } - } - - // fill transfer - // 1. if transfer is not slashed or relayed, LnProvider relay message to fill the transfer, and the transfer finished on target chain - // 2. if transfer is timeout and not processed, slasher(any account) can fill the transfer and request slash - // if it's filled by slasher, we store the address of the slasher - // expectedTransferId used to ensure the parameter is the same as on source chain - // some cases - // 1) If transferId is not exist on source chain, it'll be rejected by source chain when shashed. - // 2) If transferId exist on source chain. We have the same hash process on source and target chain, so the previousTransferId is trusted. - // 2.1) If transferId is the first transfer Id of this provider, then previousTransferId is zero and the latestSlashTransferId is INIT_SLASH_TRANSFER_ID - // 2.2) If transferId is not the first transfer, then it's latestSlashTransferId has the next two scenarios - // * the previousTransfer is a slash transfer, then latestSlashTransferId is previousTransferId - // * the previousTransfer is a normal relayed transfer, then latestSlashTransferId is previousTransfer's latestSlashTransferId - // I. transferId is trusted => previousTransferId is trusted => previousTransfer.previousTransferId is trusted => ... => firstTransfer is trusted - // II. transferId is trusted => previousTransferId is trusted => latestSlashTransferId is trusted if previousTransfer is a slash transfer - // III. Both I and II => latestSlashTransferId is trusted if previousTransfer is normal relayed tranfer - function _fillTransfer( - TransferParameter calldata params, - bytes32 expectedTransferId - ) internal { - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount)); - require(expectedTransferId == transferId, "check expected transferId failed"); - // Make sure this transfer was never filled before - require(fillTransfers[transferId] == bytes32(0), "fill exist"); - - _checkPreviousAndFillTransfer(transferId, params.previousTransferId); - - if (params.targetToken == address(0)) { - require(msg.value >= params.amount, "invalid amount"); - _safeTransferNative(params.receiver, params.amount); - } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); - } - } - - function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external { - // normal relay message, fill slasher as zero - require(params.provider == msg.sender, "invalid provider"); - _fillTransfer(params, expectedTransferId); - - emit TransferFilled(expectedTransferId, address(0)); - } - - // The condition for slash is that the transfer has timed out - // Meanwhile we need to request a slash transaction to the source chain to withdraw the LnProvider's margin - // On the source chain, we need to verify all the transfers before has been relayed or slashed. - // So we needs to carry the the previous shash transferId to ensure that the slash is continuous. - function _slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId - ) internal returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_REFUND_TIMESTAMP, "slash time not expired"); - _fillTransfer(params, expectedTransferId); - - // slasher = msg.sender - slashInfos[expectedTransferId] = SlashInfo(params.provider, params.sourceToken, msg.sender); - - // Do not slash `transferId` in source chain unless `latestSlashTransferId` has been slashed - message = _encodeSlashCall( - fillTransfers[expectedTransferId], - expectedTransferId, - params.provider, - params.sourceToken, - msg.sender - ); - emit TransferFilled(expectedTransferId, msg.sender); - } - - // we use this to verify that the transfer has been slashed by user and it can resend the slash request - function _retrySlashAndRemoteRefund(bytes32 transferId) internal view returns(bytes memory message) { - bytes32 latestSlashTransferId = fillTransfers[transferId]; - // transfer must be filled - require(latestSlashTransferId != bytes32(0), "invalid transfer id"); - // transfer must be slashed - SlashInfo memory slashInfo = slashInfos[transferId]; - require(slashInfo.slasher != address(0), "slasher not exist"); - message = _encodeSlashCall( - latestSlashTransferId, - transferId, - slashInfo.provider, - slashInfo.sourceToken, - slashInfo.slasher - ); - } - - function _encodeSlashCall( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) internal pure returns(bytes memory) { - return abi.encodeWithSelector( - ILnOppositeBridgeSource.slash.selector, - latestSlashTransferId, - transferId, - provider, - sourceToken, - slasher - ); - } - - function _requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount - ) internal view returns(bytes memory message) { - bytes32 latestSlashTransferId = fillTransfers[lastTransferId]; - require(latestSlashTransferId != bytes32(0), "invalid last transfer"); - - return abi.encodeWithSelector( - ILnOppositeBridgeSource.withdrawMargin.selector, - latestSlashTransferId, - lastTransferId, - msg.sender, - sourceToken, - amount - ); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/Linea2EthTarget.sol -// License-Identifier: MIT - - - - -contract Linea2EthTarget is Initializable, LnAccessController, LnOppositeBridgeTarget { - ILineaMessageService public messageService; - address public remoteBridge; - - event WithdrawMargin(bytes32 lastTransferId, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _messageService) public initializer { - messageService = ILineaMessageService(_messageService); - _initialize(_dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function _sendMessage( - bytes memory message, - uint256 fee - ) internal { - messageService.sendMessage{ value: fee }( - remoteBridge, - fee, - message - ); - } - - function slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external whenNotPaused { - bytes memory refundCallMessage = _slashAndRemoteRefund( - params, - expectedTransferId - ); - uint256 valueUsed = address(0) == params.targetToken ? params.amount : 0; - _sendMessage(refundCallMessage, msg.value - valueUsed); - } - - function retryRemoteRefund( - bytes32 transferId - ) payable external whenNotPaused { - bytes memory refundCallMessage = _retrySlashAndRemoteRefund(transferId); - _sendMessage(refundCallMessage, msg.value); - } - - function requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount - ) payable external whenNotPaused { - bytes memory cancelWithdrawMarginCall = _requestWithdrawMargin( - lastTransferId, - sourceToken, - amount - ); - _sendMessage(cancelWithdrawMarginCall, msg.value); - emit WithdrawMargin(lastTransferId, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/LnBridgeBaseLZ.sol b/helix-contract/flatten/lnv2/LnBridgeBaseLZ.sol deleted file mode 100644 index 70a57e36..00000000 --- a/helix-contract/flatten/lnv2/LnBridgeBaseLZ.sol +++ /dev/null @@ -1,2388 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/25/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnDefaultBridgeTarget.sol -// License-Identifier: MIT - -contract LnDefaultBridgeTarget is LnBridgeHelper { - uint256 constant private MIN_SLASH_TIMESTAMP = 30 * 60; - - struct ProviderInfo { - uint256 margin; - // use this slash gas reserve to pay the slash fee if transfer filled but timeout - uint256 slashReserveFund; - uint64 lastExpireFillTime; - uint64 withdrawNonce; - } - - // providerKey => margin - // providerKey = hash(provider, sourceToken, targetToken) - mapping(bytes32=>ProviderInfo) public lnProviderInfos; - - // if timestamp > 0, the Transfer has been relayed or slashed - // if slasher == address(0), this FillTransfer is relayed by lnProvider - // otherwise, this FillTransfer is slashed by slasher - struct FillTransfer { - uint64 timestamp; - address slasher; - } - - // transferId => FillTransfer - mapping(bytes32 => FillTransfer) public fillTransfers; - - event TransferFilled(address provider, bytes32 transferId); - event Slash(bytes32 transferId, address provider, address token, uint256 margin, address slasher); - event MarginUpdated(address provider, address token, uint256 amount, uint64 withdrawNonce); - event SlashReserveUpdated(address provider, address token, uint256 amount); - - function depositProviderMargin( - address sourceToken, - address targetToken, - uint256 margin - ) external payable { - require(margin > 0, "invalid margin"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin + margin; - lnProviderInfos[providerKey].margin = updatedMargin; - if (targetToken == address(0)) { - require(msg.value == margin, "invalid margin value"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), margin); - } - emit MarginUpdated(msg.sender, sourceToken, updatedMargin, providerInfo.withdrawNonce); - } - - function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId - ) external payable { - require(params.provider == msg.sender, "invalid provider"); - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - require(expectedTransferId == transferId, "check expected transferId failed"); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - // Make sure this transfer was never filled before - require(fillTransfer.timestamp == 0, "transfer has been filled"); - - fillTransfers[transferId].timestamp = uint64(block.timestamp); - if (block.timestamp - MIN_SLASH_TIMESTAMP > params.timestamp) { - bytes32 providerKey = getDefaultProviderKey(msg.sender, params.sourceToken, params.targetToken); - lnProviderInfos[providerKey].lastExpireFillTime = uint64(block.timestamp); - } - - if (params.targetToken == address(0)) { - require(msg.value == params.amount, "lnBridgeTarget:invalid amount"); - _safeTransferNative(params.receiver, params.amount); - } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); - } - emit TransferFilled(params.provider, transferId); - } - - function depositSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external payable { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedAmount = providerInfo.slashReserveFund + amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - require(msg.value == amount, "amount invalid"); - } else { - _safeTransferFrom(targetToken, msg.sender, address(this), amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - // withdraw slash fund - // provider can't withdraw until the block.timestamp overtime lastExpireFillTime for a period of time - function withdrawSlashFundReserve( - address sourceToken, - address targetToken, - uint256 amount - ) external { - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - require(amount <= providerInfo.slashReserveFund, "reserve not enough"); - require(block.timestamp - MIN_SLASH_TIMESTAMP >= providerInfo.lastExpireFillTime, "time not expired"); - uint256 updatedAmount = providerInfo.slashReserveFund - amount; - lnProviderInfos[providerKey].slashReserveFund = updatedAmount; - if (targetToken == address(0)) { - _safeTransferNative(msg.sender, amount); - } else { - _safeTransfer(targetToken, msg.sender, amount); - } - emit SlashReserveUpdated(msg.sender, sourceToken, updatedAmount); - } - - function _withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) internal { - // ensure all transfer has finished - require(lastTransferId == bytes32(0) || fillTransfers[lastTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - // all the early withdraw info ignored - require(providerInfo.withdrawNonce < withdrawNonce, "withdraw nonce expired"); - - // transfer token - require(providerInfo.margin >= amount, "margin not enough"); - uint256 updatedMargin = providerInfo.margin - amount; - lnProviderInfos[providerKey].margin = updatedMargin; - lnProviderInfos[providerKey].withdrawNonce = withdrawNonce; - - if (targetToken == address(0)) { - _safeTransferNative(provider, amount); - } else { - _safeTransfer(targetToken, provider, amount); - } - emit MarginUpdated(provider, sourceToken, updatedMargin, withdrawNonce); - } - - function _slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) internal { - require(params.previousTransferId == bytes32(0) || fillTransfers[params.previousTransferId].timestamp > 0, "last transfer not filled"); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount - )); - FillTransfer memory fillTransfer = fillTransfers[transferId]; - require(fillTransfer.slasher == address(0), "transfer has been slashed"); - bytes32 providerKey = getDefaultProviderKey(params.provider, params.sourceToken, params.targetToken); - ProviderInfo memory providerInfo = lnProviderInfos[providerKey]; - uint256 updatedMargin = providerInfo.margin; - // transfer is not filled - if (fillTransfer.timestamp == 0) { - require(params.timestamp < block.timestamp - MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId] = FillTransfer(uint64(block.timestamp), slasher); - - // 1. transfer token to receiver - // 2. trnasfer fee and penalty to slasher - // update margin - uint256 marginCost = params.amount + fee + penalty; - require(providerInfo.margin >= marginCost, "margin not enough"); - updatedMargin = providerInfo.margin - marginCost; - lnProviderInfos[providerKey].margin = updatedMargin; - - if (params.targetToken == address(0)) { - _safeTransferNative(params.receiver, params.amount); - _safeTransferNative(slasher, fee + penalty); - } else { - _safeTransfer(params.targetToken, params.receiver, uint256(params.amount)); - _safeTransfer(params.targetToken, slasher, fee + penalty); - } - } else { - require(fillTransfer.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "time not expired"); - fillTransfers[transferId].slasher = slasher; - uint112 slashRefund = penalty / 5; - // transfer slashRefund to slasher - require(providerInfo.slashReserveFund >= slashRefund, "slashReserveFund not enough"); - lnProviderInfos[providerKey].slashReserveFund = providerInfo.slashReserveFund - slashRefund; - if (params.targetToken == address(0)) { - _safeTransferNative(slasher, slashRefund); - } else { - _safeTransfer(params.targetToken, slasher, slashRefund); - } - } - emit Slash(transferId, params.provider, params.sourceToken, updatedMargin, slasher); - } -} - -// File contracts/ln/interface/ILnDefaultBridgeTarget.sol -// License-Identifier: MIT - - -interface ILnDefaultBridgeTarget { - function slash( - LnBridgeHelper.TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external; - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external; -} - -// File contracts/ln/base/LnDefaultBridgeSource.sol -// License-Identifier: MIT - - - -/// @title LnPositiveBridgeSource -/// @notice LnPositiveBridgeSource is a contract to help user transfer token to liquidity node and generate proof, -/// then the liquidity node must transfer the same amount of the token to the user on target chain. -/// Otherwise if timeout the slasher can send a slash request message to target chain, then force transfer from lnProvider's margin to the user. -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnDefaultBridgeSource is LnBridgeHelper { - // the time(seconds) for liquidity provider to delivery message - // if timeout, slasher can work. - uint256 constant private MIN_SLASH_TIMESTAMP = 30 * 60; - // liquidity fee base rate - // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - // max transfer amount one time - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - - // provider fee is paid to liquidity node's account - // the fee is charged by the same token that user transfered - // providerFee = baseFee + liquidityFeeRate/LIQUIDITY_FEE_RATE_BASE * sendAmount - struct LnProviderFee { - uint112 baseFee; - uint8 liquidityFeeRate; - } - - struct LnProviderInfo { - LnProviderFee fee; - // we use this nonce to generate the unique withdraw id - uint64 withdrawNonce; - bytes32 lastTransferId; - } - // the Snapshot is the state of the token bridge when user prepare to transfer across chains. - // If the snapshot updated when the across chain transfer confirmed, it will - // 1. if lastTransferId or withdrawNonce updated, revert - // 2. if totalFee increase, revert - // 3. if totalFee decrease, success - struct Snapshot { - address provider; - address sourceToken; - bytes32 transferId; - uint112 totalFee; - uint64 withdrawNonce; - } - - // lock info - // the fee and penalty is the state of the transfer confirmed - struct LockInfo { - uint112 fee; - uint112 penalty; - bool isLocked; - } - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // providerKey => provider info - mapping(bytes32=>LnProviderInfo) public lnProviders; - // transferId => lock info - mapping(bytes32=>LockInfo) public lockInfos; - - address public protocolFeeReceiver; - - event TokenLocked( - bytes32 transferId, - address provider, - address sourceToken, - uint112 amount, - uint112 fee, - uint64 timestamp, - address receiver); - event LnProviderUpdated(address provider, address sourceToken, uint112 baseFee, uint8 liquidityfeeRate); - - // protocolFeeReceiver is the protocol fee reciever, we don't use the contract itself as the receiver - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - protocolFeeReceiver = _feeReceiver; - } - - // register or update token info, it can be only called by contract owner - // source token can only map a unique target token on target chain - function _setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) internal { - tokenInfos[_sourceToken] = TokenInfo( - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals, - true - ); - } - - // lnProvider register - // 1. set fee on source chain - // 2. deposit margin on target chain - function setProviderFee( - address sourceToken, - uint112 baseFee, - uint8 liquidityFeeRate - ) external { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderFee memory providerFee = LnProviderFee(baseFee, liquidityFeeRate); - - // we only update the field fee of the provider info - // if the provider has not been registered, then this line will register, otherwise update fee - lnProviders[providerKey].fee = providerFee; - - emit LnProviderUpdated(msg.sender, sourceToken, baseFee, liquidityFeeRate); - } - - function calculateProviderFee(LnProviderFee memory fee, uint112 amount) internal pure returns(uint256) { - return uint256(fee.baseFee) + uint256(fee.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; - } - - // the fee user should paid when transfer. - // totalFee = providerFee + protocolFee - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - bytes32 providerKey = getDefaultProviderKey(provider, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - return providerFee + tokenInfo.protocolFee; - } - - // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. - // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. - // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. - // 1. the state(lastTransferId, fee, withdrawNonce) must match snapshot - // 2. transferId not exist - function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(snapshot.provider, snapshot.sourceToken, tokenInfo.targetToken); - - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.fee, amount); - - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired:transfer"); - require(snapshot.withdrawNonce == providerInfo.withdrawNonce, "snapshot expired:withdraw"); - require(snapshot.totalFee >= providerFee + tokenInfo.protocolFee && providerFee > 0, "fee is invalid"); - - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(amount)); - uint64 timestamp = uint64(block.timestamp); - bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - targetAmount - )); - require(!lockInfos[transferId].isLocked, "transferId exist"); - // if the transfer refund, then the fee and penalty should be given to slasher, but the protocol fee is ignored - // and we use the penalty value configure at the moment transfer confirmed - lockInfos[transferId] = LockInfo(snapshot.totalFee, tokenInfo.penaltyLnCollateral, true); - - // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; - - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); - if (tokenInfo.protocolFee > 0) { - _safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); - } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; - if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); - } - } else { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - snapshot.provider, - amount + providerFee - ); - if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - protocolFeeReceiver, - tokenInfo.protocolFee - ); - } - } - emit TokenLocked( - transferId, - snapshot.provider, - snapshot.sourceToken, - targetAmount, - uint112(providerFee), - timestamp, - receiver); - } - - function _sourceAmountToTargetAmount( - TokenInfo memory tokenInfo, - uint256 amount - ) internal pure returns(uint112) { - uint256 targetAmount = amount * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - return uint112(targetAmount); - } - - function _slashAndRemoteRelease( - TransferParameter memory params, - bytes32 expectedTransferId - ) internal view returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_SLASH_TIMESTAMP, "invalid timestamp"); - TokenInfo memory tokenInfo = tokenInfos[params.sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, uint256(params.amount)); - - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - targetAmount - )); - require(expectedTransferId == transferId, "expected transfer id not match"); - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.isLocked, "lock info not match"); - uint112 targetFee = _sourceAmountToTargetAmount(tokenInfo, lockInfo.fee); - uint112 targetPenalty = _sourceAmountToTargetAmount(tokenInfo, lockInfo.penalty); - - message = _encodeSlashCall( - params, - msg.sender, - targetFee, - targetPenalty - ); - } - - function _withdrawMargin( - address sourceToken, - uint112 amount - ) internal returns(bytes memory message) { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token not registered"); - - bytes32 providerKey = getDefaultProviderKey(msg.sender, sourceToken, tokenInfo.targetToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - lnProviders[providerKey].withdrawNonce = providerInfo.withdrawNonce + 1; - uint112 targetAmount = _sourceAmountToTargetAmount(tokenInfo, amount); - message = _encodeWithdrawCall( - providerInfo.lastTransferId, - providerInfo.withdrawNonce + 1, - msg.sender, - sourceToken, - tokenInfo.targetToken, - targetAmount - ); - } - - function _encodeSlashCall( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) public pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.slash.selector, - params, - slasher, - fee, - penalty - ); - } - - function _encodeWithdrawCall( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) public pure returns(bytes memory message) { - return abi.encodeWithSelector( - ILnDefaultBridgeTarget.withdraw.selector, - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - } -} - -// File contracts/ln/interface/ILayerZeroEndpoint.sol -// License-Identifier: MIT - -interface ILayerZeroEndpoint { - function send( - uint16 _dstChainId, - bytes calldata _destination, - bytes calldata _payload, - address payable _refundAddress, - address _zroPaymentAddress, - bytes calldata _adapterParams - ) external payable; - - function estimateFees( - uint16 _dstChainId, - address _userApplication, - bytes calldata _payload, - bool _payInZRO, - bytes calldata _adapterParam - ) external view returns (uint nativeFee, uint zroFee); -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/LnBridgeBaseLZ.sol -// License-Identifier: MIT - - - - - -contract LnBridgeBaseLZ is Initializable, LnAccessController, LnDefaultBridgeSource, LnDefaultBridgeTarget { - ILayerZeroEndpoint public endpoint; - address public remoteBridge; - bytes32 public trustedRemote; - uint16 public remoteChainId; - - event WithdrawMargin(address sourceToken, uint112 amount); - event CallResult(bytes srcAddress, bool successed); - - receive() external payable {} - - modifier onlyRemoteBridge(bytes calldata srcAddress) { - require(msg.sender == address(endpoint), "invalid caller"); - require(trustedRemote == keccak256(srcAddress), "invalid remote caller"); - _; - } - - function initialize(address _dao, address _endpoint, uint16 _remoteChainId) public initializer { - _initialize(_dao); - endpoint = ILayerZeroEndpoint(_endpoint); - _setFeeReceiver(_dao); - remoteChainId = _remoteChainId; - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - trustedRemote = keccak256(abi.encodePacked(_remoteBridge, address(this))); - } - - function setTokenInfo( - address _sourceToken, - address _targetToken, - uint112 _protocolFee, - uint112 _penaltyLnCollateral, - uint8 _sourceDecimals, - uint8 _targetDecimals - ) external onlyDao { - _setTokenInfo( - _sourceToken, - _targetToken, - _protocolFee, - _penaltyLnCollateral, - _sourceDecimals, - _targetDecimals - ); - } - - function estimateSlashFee( - TransferParameter calldata params - ) external view returns(uint256 nativeFee, uint256 zroFee) { - bytes memory slashCallMessage = _encodeSlashCall( - params, - msg.sender, - 0, - 0 - ); - return endpoint.estimateFees( - remoteChainId, - remoteBridge, - slashCallMessage, - false, - bytes("") - ); - } - - function estimateWithdrawFee( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external view returns(uint256 nativeFee, uint256 zroFee) { - bytes memory withdrawCallMessage = _encodeWithdrawCall( - lastTransferId, - withdrawNonce, - provider, - sourceToken, - targetToken, - amount - ); - return endpoint.estimateFees( - remoteChainId, - remoteBridge, - withdrawCallMessage, - false, - bytes("") - ); - } - - function _sendMessage( - bytes memory message, - uint256 prepaid - ) internal { - bytes memory destination = abi.encodePacked( - remoteBridge, - address(this) - ); - endpoint.send{ value: prepaid }( - remoteChainId, - destination, - message, - payable(msg.sender), - // zro payment, future parameter - address(0x0), - bytes("") - ); - } - - function slashAndRemoteRelease( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external whenNotPaused { - bytes memory slashCallMessage = _slashAndRemoteRelease( - params, - expectedTransferId - ); - _sendMessage(slashCallMessage, msg.value); - } - - function requestWithdrawMargin( - address sourceToken, - uint112 amount - ) payable external whenNotPaused { - bytes memory withdrawCallMessage = _withdrawMargin( - sourceToken, - amount - ); - _sendMessage(withdrawCallMessage, msg.value); - emit WithdrawMargin(sourceToken, amount); - } - - function lzReceive( - uint16 _srcChainId, - bytes calldata _srcAddress, - uint64, //nonce unused - bytes calldata _payload) onlyRemoteBridge(_srcAddress) whenNotPaused external { - require(_srcChainId == remoteChainId, "invalid src chainid"); - // call - (bool success,) = address(this).call(_payload); - // don't revert to prevent message block - emit CallResult(_srcAddress, success); - } - - function slash( - TransferParameter memory params, - address slasher, - uint112 fee, - uint112 penalty - ) external { - require(msg.sender == address(this), "only self"); - _slash( - params, - slasher, - fee, - penalty - ); - } - - function withdraw( - bytes32 lastTransferId, - uint64 withdrawNonce, - address provider, - address sourceToken, - address targetToken, - uint112 amount - ) external { - require(msg.sender == address(this), "only self"); - _withdraw(lastTransferId, withdrawNonce, provider, sourceToken, targetToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/LnDefaultBridge.sol b/helix-contract/flatten/lnv2/LnDefaultBridge.sol new file mode 100644 index 00000000..a54959f4 --- /dev/null +++ b/helix-contract/flatten/lnv2/LnDefaultBridge.sol @@ -0,0 +1,1461 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * + * 10/17/2023 + **/ + +pragma solidity ^0.8.17; + +// File contracts/ln/interface/ILowLevelMessager.sol +// License-Identifier: MIT + +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} + +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; +} + +// File contracts/ln/base/LnAccessController.sol +// License-Identifier: MIT + +/// @title LnAccessController +/// @notice LnAccessController is a contract to control the access permission +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnAccessController { + address public dao; + address public operator; + + mapping(address=>bool) public callerWhiteList; + + modifier onlyDao() { + require(msg.sender == dao, "!dao"); + _; + } + + modifier onlyOperator() { + require(msg.sender == operator, "!operator"); + _; + } + + modifier onlyWhiteListCaller() { + require(callerWhiteList[msg.sender], "caller not in white list"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; + } + + function authoriseAppCaller(address appAddress, bool enable) onlyDao external { + callerWhiteList[appAddress] = enable; + } + + function transferOwnership(address _dao) onlyDao external { + dao = _dao; + } +} + +// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} + +// File contracts/ln/base/LnBridgeHelper.sol +// License-Identifier: MIT + +library LnBridgeHelper { + // the time(seconds) for liquidity provider to delivery message + // if timeout, slasher can work. + uint256 constant public SLASH_EXPIRE_TIME = 30 * 60; + bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); + // liquidity fee base rate + // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount + uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; + + struct TransferParameter { + bytes32 previousTransferId; + address provider; + address sourceToken; + address targetToken; + uint112 amount; + uint256 timestamp; + address receiver; + } + + // sourceToken and targetToken is the pair of erc20 token(or native) addresses + // if sourceToken == address(0), then it's native token + // if targetToken == address(0), then remote is native token + // * `protocolFee` is the protocol fee charged by system + // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. + struct TokenInfo { + uint112 protocolFee; + uint112 penaltyLnCollateral; + uint8 sourceDecimals; + uint8 targetDecimals; + bool isRegistered; + } + + function sourceAmountToTargetAmount( + TokenInfo memory tokenInfo, + uint112 amount + ) internal pure returns(uint112) { + uint256 targetAmount = uint256(amount) * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; + require(targetAmount < type(uint112).max, "overflow amount"); + return uint112(targetAmount); + } + + function calculateProviderFee(uint112 baseFee, uint16 liquidityFeeRate, uint112 amount) internal pure returns(uint112) { + uint256 fee = uint256(baseFee) + uint256(liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; + require(fee < type(uint112).max, "overflow fee"); + return uint112(fee); + } + + function safeTransfer( + address token, + address receiver, + uint256 amount + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector( + IERC20.transfer.selector, + receiver, + amount + )); + require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); + } + + function safeTransferFrom( + address token, + address sender, + address receiver, + uint256 amount + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector( + IERC20.transferFrom.selector, + sender, + receiver, + amount + )); + require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); + } + + function safeTransferNative( + address receiver, + uint256 amount + ) internal { + (bool success,) = payable(receiver).call{value: amount}(""); + require(success, "lnBridgeHelper:transfer native token failed"); + } + + function getProviderKey(uint256 remoteChainId, address provider, address sourceToken, address targetToken) pure internal returns(bytes32) { + return keccak256(abi.encodePacked( + remoteChainId, + provider, + sourceToken, + targetToken + )); + } + + function getTokenKey(uint256 remoteChainId, address sourceToken, address targetToken) pure internal returns(bytes32) { + return keccak256(abi.encodePacked( + remoteChainId, + sourceToken, + targetToken + )); + } +} + +// File contracts/ln/interface/ILnDefaultBridgeTarget.sol +// License-Identifier: MIT + +interface ILnDefaultBridgeTarget { + function slash( + LnBridgeHelper.TransferParameter memory params, + uint256 remoteChainId, + address slasher, + uint112 fee, + uint112 penalty + ) external; + + function withdraw( + uint256 _sourceChainId, + bytes32 lastTransferId, + uint64 withdrawNonce, + address provider, + address sourceToken, + address targetToken, + uint112 amount + ) external; +} + +// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) + + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } +} + +// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) + + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract Pausable is Context { + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + bool private _paused; + + /** + * @dev Initializes the contract in unpaused state. + */ + constructor() { + _paused = false; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + _requireNotPaused(); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + _requirePaused(); + _; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Throws if the contract is paused. + */ + function _requireNotPaused() internal view virtual { + require(!paused(), "Pausable: paused"); + } + + /** + * @dev Throws if the contract is not paused. + */ + function _requirePaused() internal view virtual { + require(paused(), "Pausable: not paused"); + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pause() internal virtual whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpause() internal virtual whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} + +// File contracts/ln/base/LnDefaultBridgeSource.sol +// License-Identifier: MIT + + + +/// @title LnDefaultBridgeSource +/// @notice LnDefaultBridgeSource is a contract to help user transfer token to liquidity node and generate proof, +/// then the liquidity node must transfer the same amount of the token to the user on target chain. +/// Otherwise if timeout the slasher can send a slash request message to target chain, then force transfer from lnProvider's margin to the user. +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnDefaultBridgeSource is Pausable { + // provider fee is paid to liquidity node's account + // the fee is charged by the same token that user transfered + // providerFee = baseFee + liquidityFeeRate/LIQUIDITY_FEE_RATE_BASE * sendAmount + struct SourceProviderConfigure { + uint112 baseFee; + uint16 liquidityFeeRate; + uint64 withdrawNonce; + bool pause; + } + + struct SourceProviderInfo { + SourceProviderConfigure config; + // we use this nonce to generate the unique withdraw id + bytes32 lastTransferId; + } + // the Snapshot is the state of the token bridge when user prepare to transfer across chains. + // If the snapshot updated when the across chain transfer confirmed, it will + // 1. if lastTransferId or withdrawNonce updated, revert + // 2. if totalFee increase, revert + // 3. if totalFee decrease, success + struct Snapshot { + uint256 remoteChainId; + address provider; + address sourceToken; + address targetToken; + bytes32 transferId; + uint112 totalFee; + uint64 withdrawNonce; + } + + // lock info + // the fee and penalty is the state of the transfer confirmed + struct LockInfo { + uint112 fee; + uint112 penalty; + // the timestamp when token locked, if zero, the lockinfo not exist + uint32 timestamp; + } + + // hash(remoteChainId, sourceToken, targetToken) => token info + mapping(bytes32=>LnBridgeHelper.TokenInfo) public tokenInfos; + // hash(remoteChainId, provider, sourceToken, targetToken) => provider info + mapping(bytes32=>SourceProviderInfo) public srcProviders; + // transferId => lock info + mapping(bytes32=>LockInfo) public lockInfos; + + address public protocolFeeReceiver; + + event TokenLocked( + uint256 remoteChainId, + bytes32 transferId, + address provider, + address sourceToken, + address targetToken, + uint112 amount, + uint112 fee, + uint32 timestamp, + address receiver); + event LnProviderUpdated(uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint112 baseFee, uint8 liquidityfeeRate); + + event WithdrawMarginRequest(uint256 remoteChainId, address sourceToken, address targetToken, uint112 amount); + event SlashRequest(uint256 remoteChainId, address sourceToken, address targetToken, bytes32 expectedTransferId); + + // protocolFeeReceiver is the protocol fee reciever, we don't use the contract itself as the receiver + /// @notice should be called by special privileges + function _updateFeeReceiver(address _feeReceiver) internal { + require(_feeReceiver != address(this), "invalid system fee receiver"); + protocolFeeReceiver = _feeReceiver; + } + + // register or update token info, it can be only called by contract owner + // source token can only map a unique target token on target chain + /// @notice should be called by special privileges + function _setTokenInfo( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _protocolFee, + uint112 _penaltyLnCollateral, + uint8 _sourceDecimals, + uint8 _targetDecimals + ) internal { + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _sourceToken, _targetToken)); + tokenInfos[key] = LnBridgeHelper.TokenInfo( + _protocolFee, + _penaltyLnCollateral, + _sourceDecimals, + _targetDecimals, + true + ); + } + + function providerPause(uint256 _remoteChainId, address _sourceToken, address _targetToken) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + srcProviders[providerKey].config.pause = true; + } + + function providerUnpause(uint256 _remoteChainId, address _sourceToken, address _targetToken) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + srcProviders[providerKey].config.pause = false; + } + + // lnProvider register + // 1. set fee on source chain + // 2. deposit margin on target chain + function setProviderFee( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _baseFee, + uint8 _liquidityFeeRate + ) external { + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _sourceToken, _targetToken)); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[key]; + require(tokenInfo.isRegistered, "token not registered"); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + + require(_liquidityFeeRate < LnBridgeHelper.LIQUIDITY_FEE_RATE_BASE, "liquidity fee too large"); + SourceProviderConfigure memory providerConfigure = srcProviders[providerKey].config; + providerConfigure.baseFee = _baseFee; + providerConfigure.liquidityFeeRate = _liquidityFeeRate; + + // we only update the field fee of the provider info + // if the provider has not been registered, then this line will register, otherwise update fee + srcProviders[providerKey].config = providerConfigure; + + emit LnProviderUpdated(_remoteChainId, msg.sender, _sourceToken, _targetToken, _baseFee, _liquidityFeeRate); + } + + // the fee user should paid when transfer. + // totalFee = providerFee + protocolFee + function totalFee( + uint256 _remoteChainId, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external view returns(uint256) { + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _sourceToken, _targetToken)); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[key]; + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + uint112 providerFee = LnBridgeHelper.calculateProviderFee(providerInfo.config.baseFee, providerInfo.config.liquidityFeeRate, _amount); + return providerFee + tokenInfo.protocolFee; + } + + // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. + // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. + // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. + // 1. the state(lastTransferId, fee, withdrawNonce) must match snapshot + // 2. transferId not exist + function transferAndLockMargin( + Snapshot calldata _snapshot, + uint112 _amount, + address _receiver + ) whenNotPaused external payable { + require(_amount > 0, "invalid amount"); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[ + LnBridgeHelper.getTokenKey(_snapshot.remoteChainId, _snapshot.sourceToken, _snapshot.targetToken) + ]; + require(tokenInfo.isRegistered, "token not registered"); + + bytes32 providerKey = LnBridgeHelper.getProviderKey(_snapshot.remoteChainId, _snapshot.provider, _snapshot.sourceToken, _snapshot.targetToken); + + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + require(!providerInfo.config.pause, "provider paused"); + uint112 providerFee = LnBridgeHelper.calculateProviderFee(providerInfo.config.baseFee, providerInfo.config.liquidityFeeRate, _amount); + + // the chain state not match snapshot + require(providerInfo.lastTransferId == _snapshot.transferId, "snapshot expired:transfer"); + require(_snapshot.withdrawNonce == providerInfo.config.withdrawNonce, "snapshot expired:withdraw"); + require(_snapshot.totalFee >= providerFee + tokenInfo.protocolFee && providerFee > 0, "fee is invalid"); + + uint112 targetAmount = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, _amount); + require(targetAmount > 0, "invalid target amount"); + require(block.timestamp < type(uint32).max, "timestamp overflow"); + bytes32 transferId = keccak256(abi.encodePacked( + block.chainid, + _snapshot.remoteChainId, + _snapshot.transferId, + _snapshot.provider, + _snapshot.sourceToken, + _snapshot.targetToken, + _receiver, + targetAmount + )); + require(lockInfos[transferId].timestamp == 0, "transferId exist"); + // if the transfer refund, then the fee and penalty should be given to slasher, but the protocol fee is ignored + // and we use the penalty value configure at the moment transfer confirmed + lockInfos[transferId] = LockInfo(providerFee, tokenInfo.penaltyLnCollateral, uint32(block.timestamp)); + + // update the state to prevent other transfers using the same snapshot + srcProviders[providerKey].lastTransferId = transferId; + + if (_snapshot.sourceToken == address(0)) { + require(_amount + _snapshot.totalFee == msg.value, "amount unmatched"); + LnBridgeHelper.safeTransferNative(_snapshot.provider, _amount + providerFee); + if (tokenInfo.protocolFee > 0) { + LnBridgeHelper.safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); + } + uint256 refund = _snapshot.totalFee - tokenInfo.protocolFee - providerFee; + if ( refund > 0 ) { + LnBridgeHelper.safeTransferNative(msg.sender, refund); + } + } else { + LnBridgeHelper.safeTransferFrom( + _snapshot.sourceToken, + msg.sender, + _snapshot.provider, + _amount + providerFee + ); + if (tokenInfo.protocolFee > 0) { + LnBridgeHelper.safeTransferFrom( + _snapshot.sourceToken, + msg.sender, + protocolFeeReceiver, + tokenInfo.protocolFee + ); + } + } + emit TokenLocked( + _snapshot.remoteChainId, + transferId, + _snapshot.provider, + _snapshot.sourceToken, + _snapshot.targetToken, + targetAmount, + providerFee, + uint32(block.timestamp), + _receiver); + } + + function _slashAndRemoteReleaseCall( + LnBridgeHelper.TransferParameter memory _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId + ) internal view returns(bytes memory message) { + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _params.sourceToken, _params.targetToken)); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[key]; + require(tokenInfo.isRegistered, "token not registered"); + uint112 targetAmount = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, _params.amount); + + bytes32 transferId = keccak256(abi.encodePacked( + block.chainid, + _remoteChainId, + _params.previousTransferId, + _params.provider, + _params.sourceToken, + _params.targetToken, + _params.receiver, + targetAmount + )); + require(_expectedTransferId == transferId, "expected transfer id not match"); + LockInfo memory lockInfo = lockInfos[transferId]; + require(lockInfo.timestamp == _params.timestamp, "invalid timestamp"); + require(block.timestamp > lockInfo.timestamp + LnBridgeHelper.SLASH_EXPIRE_TIME, "invalid timestamp"); + uint112 targetFee = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, lockInfo.fee); + uint112 targetPenalty = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, lockInfo.penalty); + + message = abi.encodeWithSelector( + ILnDefaultBridgeTarget.slash.selector, + _params, + block.chainid, + msg.sender, // slasher + targetFee, + targetPenalty + ); + } + + function _withdrawMarginCall( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _amount + ) internal returns(bytes memory message) { + bytes32 key = keccak256(abi.encodePacked(_remoteChainId, _sourceToken, _targetToken)); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[key]; + require(tokenInfo.isRegistered, "token not registered"); + + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + srcProviders[providerKey].config.withdrawNonce = providerInfo.config.withdrawNonce + 1; + uint112 targetAmount = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, _amount); + message = abi.encodeWithSelector( + ILnDefaultBridgeTarget.withdraw.selector, + block.chainid, + providerInfo.lastTransferId, + providerInfo.config.withdrawNonce + 1, + msg.sender, //provider, + _sourceToken, + _targetToken, + targetAmount + ); + } + + function encodeSlashCall( + LnBridgeHelper.TransferParameter memory _params, + uint256 _localChainId, + address _slasher, + uint112 _fee, + uint112 _penalty + ) public pure returns(bytes memory message) { + return abi.encodeWithSelector( + ILnDefaultBridgeTarget.slash.selector, + _params, + _localChainId, + _slasher, + _fee, + _penalty + ); + } + + function encodeWithdrawCall( + bytes32 _lastTransferId, + uint256 _localChainId, + uint64 _withdrawNonce, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) public pure returns(bytes memory message) { + return abi.encodeWithSelector( + ILnDefaultBridgeTarget.withdraw.selector, + _localChainId, + _lastTransferId, + _withdrawNonce, + _provider, + _sourceToken, + _targetToken, + _amount + ); + } + + function _sendMessageToTarget(uint256 _remoteChainId, bytes memory _payload, bytes memory _extParams) internal virtual {} + + function requestSlashAndRemoteRelease( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId, + bytes memory _extParams + ) payable external { + bytes memory slashCallMessage = _slashAndRemoteReleaseCall( + _params, + _remoteChainId, + _expectedTransferId + ); + _sendMessageToTarget(_remoteChainId, slashCallMessage, _extParams); + emit SlashRequest(_remoteChainId, _params.sourceToken, _params.targetToken, _expectedTransferId); + } + + function requestWithdrawMargin( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _amount, + bytes memory _extParams + ) payable external { + bytes memory withdrawCallMessage = _withdrawMarginCall( + _remoteChainId, + _sourceToken, + _targetToken, + _amount + ); + _sendMessageToTarget(_remoteChainId, withdrawCallMessage, _extParams); + emit WithdrawMarginRequest(_remoteChainId, _sourceToken, _targetToken, _amount); + } +} + +// File contracts/ln/base/LnDefaultBridgeTarget.sol +// License-Identifier: MIT + +contract LnDefaultBridgeTarget { + struct TargetProviderInfo { + uint256 margin; + // use this slash gas reserve to pay the slash fee if transfer filled but timeout + uint256 slashReserveFund; + uint64 lastExpireFillTime; + uint64 withdrawNonce; + } + + // providerKey => margin + // providerKey = hash(remoteChainId, provider, sourceToken, targetToken) + mapping(bytes32=>TargetProviderInfo) public tgtProviders; + + // if timestamp > 0, the Transfer has been relayed or slashed + // if slasher == address(0), this FillTransfer is relayed by lnProvider + // otherwise, this FillTransfer is slashed by slasher + struct FillTransfer { + uint64 timestamp; + address slasher; + } + + // transferId => FillTransfer + mapping(bytes32 => FillTransfer) public fillTransfers; + + event TransferFilled(bytes32 transferId, address provider); + event Slash(bytes32 transferId, uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint256 margin, address slasher); + event MarginUpdated(uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint256 amount, uint64 withdrawNonce); + event SlashReserveUpdated(address provider, address sourceToken, address targetToken, uint256 amount); + + modifier allowRemoteCall(uint256 _remoteChainId) { + _verifyRemote(_remoteChainId); + _; + } + + function _verifyRemote(uint256 _remoteChainId) internal virtual {} + + function depositProviderMargin( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint256 _margin + ) external payable { + require(_margin > 0, "invalid margin"); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; + uint256 updatedMargin = providerInfo.margin + _margin; + tgtProviders[providerKey].margin = updatedMargin; + if (_targetToken == address(0)) { + require(msg.value == _margin, "invalid margin value"); + } else { + LnBridgeHelper.safeTransferFrom(_targetToken, msg.sender, address(this), _margin); + } + emit MarginUpdated(_remoteChainId, msg.sender, _sourceToken, _targetToken, updatedMargin, providerInfo.withdrawNonce); + } + + function transferAndReleaseMargin( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId + ) external payable { + require(_params.provider == msg.sender, "invalid provider"); + require(_params.previousTransferId == bytes32(0) || fillTransfers[_params.previousTransferId].timestamp > 0, "last transfer not filled"); + bytes32 transferId = keccak256(abi.encodePacked( + _remoteChainId, + block.chainid, + _params.previousTransferId, + _params.provider, + _params.sourceToken, + _params.targetToken, + _params.receiver, + _params.amount + )); + require(_expectedTransferId == transferId, "check expected transferId failed"); + FillTransfer memory fillTransfer = fillTransfers[transferId]; + // Make sure this transfer was never filled before + require(fillTransfer.timestamp == 0, "transfer has been filled"); + + fillTransfers[transferId].timestamp = uint64(block.timestamp); + if (block.timestamp - LnBridgeHelper.SLASH_EXPIRE_TIME > _params.timestamp) { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _params.sourceToken, _params.targetToken); + tgtProviders[providerKey].lastExpireFillTime = uint64(block.timestamp); + } + + if (_params.targetToken == address(0)) { + require(msg.value == _params.amount, "lnBridgeTarget:invalid amount"); + LnBridgeHelper.safeTransferNative(_params.receiver, _params.amount); + } else { + LnBridgeHelper.safeTransferFrom(_params.targetToken, msg.sender, _params.receiver, uint256(_params.amount)); + } + emit TransferFilled(transferId, _params.provider); + } + + function depositSlashFundReserve( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint256 _amount + ) external payable { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; + uint256 updatedAmount = providerInfo.slashReserveFund + _amount; + tgtProviders[providerKey].slashReserveFund = updatedAmount; + if (_targetToken == address(0)) { + require(msg.value == _amount, "amount invalid"); + } else { + LnBridgeHelper.safeTransferFrom(_targetToken, msg.sender, address(this), _amount); + } + emit SlashReserveUpdated(msg.sender, _sourceToken, _targetToken, updatedAmount); + } + + // withdraw slash fund + // provider can't withdraw until the block.timestamp overtime lastExpireFillTime for a period of time + function withdrawSlashFundReserve( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint256 _amount + ) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; + require(_amount <= providerInfo.slashReserveFund, "reserve not enough"); + require(block.timestamp - LnBridgeHelper.SLASH_EXPIRE_TIME >= providerInfo.lastExpireFillTime, "time not expired"); + uint256 updatedAmount = providerInfo.slashReserveFund - _amount; + tgtProviders[providerKey].slashReserveFund = updatedAmount; + if (_targetToken == address(0)) { + LnBridgeHelper.safeTransferNative(msg.sender, _amount); + } else { + LnBridgeHelper.safeTransfer(_targetToken, msg.sender, _amount); + } + emit SlashReserveUpdated(msg.sender, _sourceToken, _targetToken, updatedAmount); + } + + function withdraw( + uint256 _remoteChainId, + bytes32 _lastTransferId, + uint64 _withdrawNonce, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external allowRemoteCall(_remoteChainId) { + // ensure all transfer has finished + require(_lastTransferId == bytes32(0) || fillTransfers[_lastTransferId].timestamp > 0, "last transfer not filled"); + + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; + // all the early withdraw info ignored + require(providerInfo.withdrawNonce < _withdrawNonce, "withdraw nonce expired"); + + // transfer token + require(providerInfo.margin >= _amount, "margin not enough"); + uint256 updatedMargin = providerInfo.margin - _amount; + tgtProviders[providerKey].margin = updatedMargin; + tgtProviders[providerKey].withdrawNonce = _withdrawNonce; + + if (_targetToken == address(0)) { + LnBridgeHelper.safeTransferNative(_provider, _amount); + } else { + LnBridgeHelper.safeTransfer(_targetToken, _provider, _amount); + } + emit MarginUpdated(_remoteChainId, _provider, _sourceToken, _targetToken, updatedMargin, _withdrawNonce); + } + + function slash( + LnBridgeHelper.TransferParameter memory _params, + uint256 _remoteChainId, + address _slasher, + uint112 _fee, + uint112 _penalty + ) external allowRemoteCall(_remoteChainId) { + require(_params.previousTransferId == bytes32(0) || fillTransfers[_params.previousTransferId].timestamp > 0, "last transfer not filled"); + + bytes32 transferId = keccak256(abi.encodePacked( + _remoteChainId, + block.chainid, + _params.previousTransferId, + _params.provider, + _params.sourceToken, + _params.targetToken, + _params.receiver, + _params.amount + )); + FillTransfer memory fillTransfer = fillTransfers[transferId]; + require(fillTransfer.slasher == address(0), "transfer has been slashed"); + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _params.provider, _params.sourceToken, _params.targetToken); + TargetProviderInfo memory providerInfo = tgtProviders[providerKey]; + uint256 updatedMargin = providerInfo.margin; + // transfer is not filled + if (fillTransfer.timestamp == 0) { + require(_params.timestamp < block.timestamp - LnBridgeHelper.SLASH_EXPIRE_TIME, "time not expired"); + fillTransfers[transferId] = FillTransfer(uint64(block.timestamp), _slasher); + + // 1. transfer token to receiver + // 2. trnasfer fee and penalty to slasher + // update margin + uint256 marginCost = _params.amount + _fee + _penalty; + require(providerInfo.margin >= marginCost, "margin not enough"); + updatedMargin = providerInfo.margin - marginCost; + tgtProviders[providerKey].margin = updatedMargin; + + if (_params.targetToken == address(0)) { + LnBridgeHelper.safeTransferNative(_params.receiver, _params.amount); + LnBridgeHelper.safeTransferNative(_slasher, _fee + _penalty); + } else { + LnBridgeHelper.safeTransfer(_params.targetToken, _params.receiver, uint256(_params.amount)); + LnBridgeHelper.safeTransfer(_params.targetToken, _slasher, _fee + _penalty); + } + } else { + require(fillTransfer.timestamp > _params.timestamp + LnBridgeHelper.SLASH_EXPIRE_TIME, "time not expired"); + fillTransfers[transferId].slasher = _slasher; + uint112 slashRefund = _penalty / 5; + // transfer slashRefund to slasher + require(providerInfo.slashReserveFund >= slashRefund, "slashReserveFund not enough"); + tgtProviders[providerKey].slashReserveFund = providerInfo.slashReserveFund - slashRefund; + if (_params.targetToken == address(0)) { + LnBridgeHelper.safeTransferNative(_slasher, slashRefund); + } else { + LnBridgeHelper.safeTransfer(_params.targetToken, _slasher, slashRefund); + } + } + emit Slash(transferId, _remoteChainId, _params.provider, _params.sourceToken, _params.targetToken, updatedMargin, _slasher); + } +} + +// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) + + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + * + * [IMPORTANT] + * ==== + * You shouldn't rely on `isContract` to protect against flash loan attacks! + * + * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets + * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract + * constructor. + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize/address.code.length, which returns 0 + // for contracts in construction, since the code is only stored at the end + // of the constructor execution. + + return account.code.length > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + /// @solidity memory-safe-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) + + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be + * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in + * case an upgrade adds a module that needs to be initialized. + * + * For example: + * + * [.hljs-theme-light.nopadding] + * ``` + * contract MyToken is ERC20Upgradeable { + * function initialize() initializer public { + * __ERC20_init("MyToken", "MTK"); + * } + * } + * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { + * function initializeV2() reinitializer(2) public { + * __ERC20Permit_init("MyToken"); + * } + * } + * ``` + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * [CAUTION] + * ==== + * Avoid leaving a contract uninitialized. + * + * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation + * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke + * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() { + * _disableInitializers(); + * } + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + * @custom:oz-retyped-from bool + */ + uint8 private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Triggered when the contract has been initialized or reinitialized. + */ + event Initialized(uint8 version); + + /** + * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, + * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. + */ + modifier initializer() { + bool isTopLevelCall = !_initializing; + require( + (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), + "Initializable: contract is already initialized" + ); + _initialized = 1; + if (isTopLevelCall) { + _initializing = true; + } + _; + if (isTopLevelCall) { + _initializing = false; + emit Initialized(1); + } + } + + /** + * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the + * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be + * used to initialize parent contracts. + * + * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original + * initialization step. This is essential to configure modules that are added through upgrades and that require + * initialization. + * + * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in + * a contract, executing them in the right order is up to the developer or operator. + */ + modifier reinitializer(uint8 version) { + require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); + _initialized = version; + _initializing = true; + _; + _initializing = false; + emit Initialized(version); + } + + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} and {reinitializer} modifiers, directly or indirectly. + */ + modifier onlyInitializing() { + require(_initializing, "Initializable: contract is not initializing"); + _; + } + + /** + * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. + * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized + * to any version. It is recommended to use this to lock implementation contracts that are designed to be called + * through proxies. + */ + function _disableInitializers() internal virtual { + require(!_initializing, "Initializable: contract is initializing"); + if (_initialized < type(uint8).max) { + _initialized = type(uint8).max; + emit Initialized(type(uint8).max); + } + } +} + +// File contracts/ln/LnDefaultBridge.sol +// License-Identifier: MIT + + + + + +contract LnDefaultBridge is Initializable, LnAccessController, LnDefaultBridgeSource, LnDefaultBridgeTarget { + struct MessagerService { + address sendService; + address receiveService; + } + + // remoteChainId => messager + mapping(uint256=>MessagerService) public messagers; + + receive() external payable {} + + function initialize(address dao) public initializer { + _initialize(dao); + _updateFeeReceiver(dao); + } + + function unpause() external onlyOperator { + _unpause(); + } + + function pause() external onlyOperator { + _pause(); + } + + // the remote endpoint is unique, if we want multi-path to remote endpoint, then the messager should support multi-path + function setSendService(uint256 _remoteChainId, address _remoteBridge, address _service) external onlyDao { + messagers[_remoteChainId].sendService = _service; + ILowLevelMessageSender(_service).registerRemoteReceiver(_remoteChainId, _remoteBridge); + } + + function setReceiveService(uint256 _remoteChainId, address _remoteBridge, address _service) external onlyDao { + messagers[_remoteChainId].receiveService = _service; + ILowLevelMessageReceiver(_service).registerRemoteSender(_remoteChainId, _remoteBridge); + } + + function updateFeeReceiver(address _receiver) external onlyDao { + _updateFeeReceiver(_receiver); + } + + function setTokenInfo( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _protocolFee, + uint112 _penaltyLnCollateral, + uint8 _sourceDecimals, + uint8 _targetDecimals + ) external onlyDao { + _setTokenInfo( + _remoteChainId, + _sourceToken, + _targetToken, + _protocolFee, + _penaltyLnCollateral, + _sourceDecimals, + _targetDecimals + ); + } + + function _sendMessageToTarget(uint256 _remoteChainId, bytes memory _payload, bytes memory _extParams) whenNotPaused internal override { + address sendService = messagers[_remoteChainId].sendService; + require(sendService != address(0), "invalid messager"); + ILowLevelMessageSender(sendService).sendMessage{value: msg.value}(_remoteChainId, _payload, _extParams); + } + + function _verifyRemote(uint256 _remoteChainId) whenNotPaused internal view override { + address receiveService = messagers[_remoteChainId].receiveService; + require(receiveService == msg.sender, "invalid messager"); + } +} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/LnOppositeBridge.sol b/helix-contract/flatten/lnv2/LnOppositeBridge.sol new file mode 100644 index 00000000..547d3f49 --- /dev/null +++ b/helix-contract/flatten/lnv2/LnOppositeBridge.sol @@ -0,0 +1,1433 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * + * 10/17/2023 + **/ + +pragma solidity ^0.8.17; + +// File contracts/ln/base/LnAccessController.sol +// License-Identifier: MIT + +/// @title LnAccessController +/// @notice LnAccessController is a contract to control the access permission +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnAccessController { + address public dao; + address public operator; + + mapping(address=>bool) public callerWhiteList; + + modifier onlyDao() { + require(msg.sender == dao, "!dao"); + _; + } + + modifier onlyOperator() { + require(msg.sender == operator, "!operator"); + _; + } + + modifier onlyWhiteListCaller() { + require(callerWhiteList[msg.sender], "caller not in white list"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; + } + + function authoriseAppCaller(address appAddress, bool enable) onlyDao external { + callerWhiteList[appAddress] = enable; + } + + function transferOwnership(address _dao) onlyDao external { + dao = _dao; + } +} + +// File contracts/ln/interface/ILowLevelMessager.sol +// License-Identifier: MIT + +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} + +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; +} + +// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} + +// File contracts/ln/base/LnBridgeHelper.sol +// License-Identifier: MIT + +library LnBridgeHelper { + // the time(seconds) for liquidity provider to delivery message + // if timeout, slasher can work. + uint256 constant public SLASH_EXPIRE_TIME = 30 * 60; + bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); + // liquidity fee base rate + // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount + uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; + + struct TransferParameter { + bytes32 previousTransferId; + address provider; + address sourceToken; + address targetToken; + uint112 amount; + uint256 timestamp; + address receiver; + } + + // sourceToken and targetToken is the pair of erc20 token(or native) addresses + // if sourceToken == address(0), then it's native token + // if targetToken == address(0), then remote is native token + // * `protocolFee` is the protocol fee charged by system + // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. + struct TokenInfo { + uint112 protocolFee; + uint112 penaltyLnCollateral; + uint8 sourceDecimals; + uint8 targetDecimals; + bool isRegistered; + } + + function sourceAmountToTargetAmount( + TokenInfo memory tokenInfo, + uint112 amount + ) internal pure returns(uint112) { + uint256 targetAmount = uint256(amount) * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; + require(targetAmount < type(uint112).max, "overflow amount"); + return uint112(targetAmount); + } + + function calculateProviderFee(uint112 baseFee, uint16 liquidityFeeRate, uint112 amount) internal pure returns(uint112) { + uint256 fee = uint256(baseFee) + uint256(liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; + require(fee < type(uint112).max, "overflow fee"); + return uint112(fee); + } + + function safeTransfer( + address token, + address receiver, + uint256 amount + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector( + IERC20.transfer.selector, + receiver, + amount + )); + require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); + } + + function safeTransferFrom( + address token, + address sender, + address receiver, + uint256 amount + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector( + IERC20.transferFrom.selector, + sender, + receiver, + amount + )); + require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); + } + + function safeTransferNative( + address receiver, + uint256 amount + ) internal { + (bool success,) = payable(receiver).call{value: amount}(""); + require(success, "lnBridgeHelper:transfer native token failed"); + } + + function getProviderKey(uint256 remoteChainId, address provider, address sourceToken, address targetToken) pure internal returns(bytes32) { + return keccak256(abi.encodePacked( + remoteChainId, + provider, + sourceToken, + targetToken + )); + } + + function getTokenKey(uint256 remoteChainId, address sourceToken, address targetToken) pure internal returns(bytes32) { + return keccak256(abi.encodePacked( + remoteChainId, + sourceToken, + targetToken + )); + } +} + +// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) + + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } +} + +// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) + + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract Pausable is Context { + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + bool private _paused; + + /** + * @dev Initializes the contract in unpaused state. + */ + constructor() { + _paused = false; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + _requireNotPaused(); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + _requirePaused(); + _; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Throws if the contract is paused. + */ + function _requireNotPaused() internal view virtual { + require(!paused(), "Pausable: paused"); + } + + /** + * @dev Throws if the contract is not paused. + */ + function _requirePaused() internal view virtual { + require(paused(), "Pausable: not paused"); + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pause() internal virtual whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpause() internal virtual whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} + +// File contracts/ln/base/LnOppositeBridgeSource.sol +// License-Identifier: MIT + + +/// @title LnBridgeSource +/// @notice LnBridgeSource is a contract to help user transfer token to liquidity node and generate proof, +/// then the liquidity node must transfer the same amount of the token to the user on target chain. +/// Otherwise if timeout the slasher can paid for relayer and slash the transfer, then request slash from lnProvider's margin. +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract LnOppositeBridgeSource is Pausable { + // the Liquidity Node provider info + // Liquidity Node need register first + struct SourceProviderConfigure { + uint112 margin; + uint112 baseFee; + // liquidityFeeRate / 100,000 * amount = liquidityFee + // the max liquidity fee rate is 0.255% + uint16 liquidityFeeRate; + bool pause; + } + struct SourceProviderInfo { + SourceProviderConfigure config; + bytes32 lastTransferId; + } + + // the Snapshot is the state of the token bridge when user prepare to transfer across chains. + // If the snapshot updated when the across chain transfer confirmed, it will + // 1. if lastTransferId updated, revert + // 2. if margin decrease or totalFee increase, revert + // 3. if margin increase or totalFee decrease, success + struct Snapshot { + uint256 remoteChainId; + address provider; + address sourceToken; + address targetToken; + bytes32 transferId; + uint112 totalFee; + uint112 depositedMargin; + } + // registered token info + // tokenKey => token info + mapping(bytes32=>LnBridgeHelper.TokenInfo) public tokenInfos; + // registered srcProviders + mapping(bytes32=>SourceProviderInfo) public srcProviders; + // each time cross chain transfer, amount and fee can't be larger than type(uint112).max + struct LockInfo { + // amount + providerFee + penaltyLnCollateral + // the Indexer should be care about this value, it will frozen lnProvider's margin when the transfer not finished. + // and when the slasher slash success, this amount of token will be transfer from lnProvider's margin to slasher. + uint112 amount; + uint112 feeAndPenalty; + uint32 timestamp; + bool hasSlashed; + } + // key: transferId = hash(proviousTransferId, targetToken, receiver, targetAmount) + // * `proviousTransferId` is used to ensure the continuous of the transfer + // * `timestamp` is the block.timestmap to judge timeout on target chain(here we support source and target chain has the same world clock) + // * `targetToken`, `receiver` and `targetAmount` are used on target chain to transfer target token. + mapping(bytes32 => LockInfo) public lockInfos; + + address public protocolFeeReceiver; + + event TokenLocked( + uint256 remoteChainId, + bytes32 transferId, + address provider, + address sourceToken, + address targetToken, + uint112 amount, + uint112 fee, + uint32 timestamp, + address receiver); + event LiquidityWithdrawn(uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint112 amount); + event Slash(uint256 remoteChainId, bytes32 transferId, address provider, address sourceToken, address targetToken, uint112 margin, address slasher); + // relayer + event LnProviderUpdated(uint256 remoteChainId, address provider, address sourceToken, address targetToken, uint112 margin, uint112 baseFee, uint16 liquidityfeeRate); + + modifier allowRemoteCall(uint256 _remoteChainId) { + _verifyRemote(_remoteChainId); + _; + } + + function _verifyRemote(uint256 _remoteChainId) internal virtual {} + + function _updateFeeReceiver(address _feeReceiver) internal { + require(_feeReceiver != address(this), "invalid system fee receiver"); + protocolFeeReceiver = _feeReceiver; + } + + function _setTokenInfo( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _protocolFee, + uint112 _penaltyLnCollateral, + uint8 _sourceDecimals, + uint8 _targetDecimals + ) internal { + bytes32 tokenKey = LnBridgeHelper.getTokenKey(_remoteChainId, _sourceToken, _targetToken); + tokenInfos[tokenKey] = LnBridgeHelper.TokenInfo( + _protocolFee, + _penaltyLnCollateral, + _sourceDecimals, + _targetDecimals, + true + ); + } + + function providerPause(uint256 _remoteChainId, address _sourceToken, address _targetToken) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + srcProviders[providerKey].config.pause = true; + } + + function providerUnpause(uint256 _remoteChainId, address _sourceToken, address _targetToken) external { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + srcProviders[providerKey].config.pause = false; + } + + // lnProvider can register or update its configure by using this function + // * `margin` is the increased value of the deposited margin + function updateProviderFeeAndMargin( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _margin, + uint112 _baseFee, + uint16 _liquidityFeeRate + ) external payable { + require(_liquidityFeeRate < LnBridgeHelper.LIQUIDITY_FEE_RATE_BASE, "liquidity fee too large"); + bytes32 tokenKey = LnBridgeHelper.getTokenKey(_remoteChainId, _sourceToken, _targetToken); + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[tokenKey]; + require(tokenInfo.isRegistered, "token is not registered"); + + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, msg.sender, _sourceToken, _targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + + SourceProviderConfigure memory config = SourceProviderConfigure( + // the margin can be only increased here + _margin + providerInfo.config.margin, + _baseFee, + _liquidityFeeRate, + providerInfo.config.pause + ); + + srcProviders[providerKey].config = config; + + if (_sourceToken == address(0)) { + require(msg.value == _margin, "invalid margin value"); + } else { + if (_margin > 0) { + LnBridgeHelper.safeTransferFrom(_sourceToken, msg.sender, address(this), _margin); + } + } + emit LnProviderUpdated(_remoteChainId, msg.sender, _sourceToken, _targetToken, config.margin, _baseFee, _liquidityFeeRate); + } + + // the fee user should paid when transfer. + // totalFee = providerFee + protocolFee + // providerFee = provider.baseFee + provider.liquidityFeeRate * amount + function totalFee( + uint256 _remoteChainId, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external view returns(uint256) { + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + uint112 providerFee = LnBridgeHelper.calculateProviderFee(providerInfo.config.baseFee, providerInfo.config.liquidityFeeRate, _amount); + bytes32 tokenKey = LnBridgeHelper.getTokenKey(_remoteChainId, _sourceToken, _targetToken); + return providerFee + tokenInfos[tokenKey].protocolFee; + } + + // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. + // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. + // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. + // 1. the state(lastTransferId, fee, margin) must match snapshot + // 2. transferId not exist + function transferAndLockMargin( + Snapshot calldata _snapshot, + uint112 _amount, + address _receiver + ) whenNotPaused external payable { + require(_amount > 0, "invalid amount"); + + bytes32 providerKey = LnBridgeHelper.getProviderKey(_snapshot.remoteChainId, _snapshot.provider, _snapshot.sourceToken, _snapshot.targetToken); + SourceProviderInfo memory providerInfo = srcProviders[providerKey]; + + require(!providerInfo.config.pause, "provider paused"); + + LnBridgeHelper.TokenInfo memory tokenInfo = tokenInfos[ + LnBridgeHelper.getTokenKey(_snapshot.remoteChainId, _snapshot.sourceToken, _snapshot.targetToken) + ]; + + uint112 providerFee = LnBridgeHelper.calculateProviderFee(providerInfo.config.baseFee, providerInfo.config.liquidityFeeRate, _amount); + + // the chain state not match snapshot + require(providerInfo.lastTransferId == _snapshot.transferId, "snapshot expired"); + // Note: this requirement is not enough to ensure that the lnProvider's margin is enough because there maybe some frozen margins in other transfers + require(providerInfo.config.margin >= _amount + tokenInfo.penaltyLnCollateral + providerFee, "amount not valid"); + require(_snapshot.depositedMargin <= providerInfo.config.margin, "margin updated"); + require(_snapshot.totalFee >= tokenInfo.protocolFee + providerFee, "fee is invalid"); + + uint112 targetAmount = LnBridgeHelper.sourceAmountToTargetAmount(tokenInfo, _amount); + require(targetAmount > 0, "invalid amount"); + require(block.timestamp < type(uint32).max, "timestamp overflow"); + bytes32 transferId = keccak256(abi.encodePacked( + block.chainid, + _snapshot.remoteChainId, + _snapshot.transferId, + _snapshot.provider, + _snapshot.sourceToken, + _snapshot.targetToken, + _receiver, + targetAmount)); + require(lockInfos[transferId].timestamp == 0, "transferId exist"); + lockInfos[transferId] = LockInfo(_amount, tokenInfo.penaltyLnCollateral + providerFee, uint32(block.timestamp), false); + + // update the state to prevent other transfers using the same snapshot + srcProviders[providerKey].lastTransferId = transferId; + + if (_snapshot.sourceToken == address(0)) { + require(_amount + _snapshot.totalFee == msg.value, "amount unmatched"); + LnBridgeHelper.safeTransferNative(_snapshot.provider, _amount + providerFee); + if (tokenInfo.protocolFee > 0) { + LnBridgeHelper.safeTransferNative(protocolFeeReceiver, tokenInfo.protocolFee); + } + uint256 refund = _snapshot.totalFee - tokenInfo.protocolFee - providerFee; + if ( refund > 0 ) { + LnBridgeHelper.safeTransferNative(msg.sender, refund); + } + } else { + LnBridgeHelper.safeTransferFrom( + _snapshot.sourceToken, + msg.sender, + _snapshot.provider, + _amount + providerFee + ); + if (tokenInfo.protocolFee > 0) { + LnBridgeHelper.safeTransferFrom( + _snapshot.sourceToken, + msg.sender, + protocolFeeReceiver, + tokenInfo.protocolFee + ); + } + } + emit TokenLocked( + _snapshot.remoteChainId, + transferId, + _snapshot.provider, + _snapshot.sourceToken, + _snapshot.targetToken, + targetAmount, + providerFee, + uint32(block.timestamp), + _receiver); + } + + // this slash is called by remote message + // the token should be sent to the slasher who slash and finish the transfer on target chain. + // latestSlashTransferId is the latest slashed transfer trusted from the target chain, and the current slash transfer cannot be executed before the latestSlash transfer. + // after slash, the margin of lnProvider need to be updated + function slash( + bytes32 _latestSlashTransferId, + bytes32 _transferId, + uint256 _remoteChainId, + uint256 _timestamp, + address _sourceToken, + address _targetToken, + address _provider, + address _slasher + ) external allowRemoteCall(_remoteChainId) { + // check lastTransfer + // ensure last slash transfer(checked on target chain) has been slashed + LockInfo memory lastLockInfo = lockInfos[_latestSlashTransferId]; + require(lastLockInfo.hasSlashed || _latestSlashTransferId == LnBridgeHelper.INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); + LockInfo memory lockInfo = lockInfos[_transferId]; + + // ensure transfer exist and not slashed yet + require(!lockInfo.hasSlashed, "transfer has been slashed"); + require(lockInfo.timestamp > 0, "lnBridgeSource:invalid timestamp"); + + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + + SourceProviderInfo memory lnProvider = srcProviders[providerKey]; + lockInfos[_transferId].hasSlashed = true; + // transfer token to the slasher + uint112 slashAmount = (lockInfo.timestamp == _timestamp ? lockInfo.amount + lockInfo.feeAndPenalty : lockInfo.amount); + require(lnProvider.config.margin >= slashAmount, "margin not enough"); + uint112 updatedMargin = lnProvider.config.margin - slashAmount; + srcProviders[providerKey].config.margin = updatedMargin; + + if (_sourceToken == address(0)) { + LnBridgeHelper.safeTransferNative(_slasher, slashAmount); + } else { + LnBridgeHelper.safeTransfer(_sourceToken, _slasher, slashAmount); + } + + emit Slash(_remoteChainId, _transferId, _provider, _sourceToken, _targetToken, updatedMargin, _slasher); + } + + // lastTransfer is the latest slash transfer, all transfer must be relayed or slashed + // if user use the snapshot before this transaction to send cross-chain transfer, it should be reverted because this `withdrawMargin` will decrease margin. + function withdrawMargin( + bytes32 _latestSlashTransferId, + bytes32 _lastTransferId, + uint256 _remoteChainId, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external allowRemoteCall(_remoteChainId) { + // check the latest slash transfer + // ensure latest slash tranfer(verified on target chain) has been slashed on source chain + LockInfo memory lastRefundLockInfo = lockInfos[_latestSlashTransferId]; + require(lastRefundLockInfo.hasSlashed || _latestSlashTransferId == LnBridgeHelper.INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); + + // use this condition to ensure that the withdraw message is sent by the provider + // the parameter provider is the message sender of this remote withdraw call + bytes32 providerKey = LnBridgeHelper.getProviderKey(_remoteChainId, _provider, _sourceToken, _targetToken); + SourceProviderInfo memory lnProvider = srcProviders[providerKey]; + + // ensure all transfer has finished + require(lnProvider.lastTransferId == _lastTransferId, "invalid last transferid"); + require(lnProvider.config.margin >= _amount, "margin not enough"); + uint112 updatedMargin = lnProvider.config.margin - _amount; + srcProviders[providerKey].config.margin = updatedMargin; + if (_sourceToken == address(0)) { + LnBridgeHelper.safeTransferNative(_provider, _amount); + } else { + LnBridgeHelper.safeTransfer(_sourceToken, _provider, _amount); + } + emit LiquidityWithdrawn(_remoteChainId, _provider, _sourceToken, _targetToken, updatedMargin); + } +} + +// File contracts/ln/interface/ILnOppositeBridgeSource.sol +// License-Identifier: MIT + +interface ILnOppositeBridgeSource { + function slash( + bytes32 lastRefundTransferId, + bytes32 transferId, + uint256 remoteChainId, + uint256 timestamp, + address sourceToken, + address targetToken, + address provider, + address slasher + ) external; + + function withdrawMargin( + bytes32 lastRefundTransferId, + bytes32 lastTransferId, + uint256 remoteChainId, + address provider, + address sourceToken, + address targetToken, + uint112 amount + ) external; +} + +// File contracts/ln/base/LnOppositeBridgeTarget.sol +// License-Identifier: MIT + + +contract LnOppositeBridgeTarget { + // if slasher == address(0), this FillTransfer is relayed by lnProvider + // otherwise, this FillTransfer is slashed by slasher + // if there is no slash transfer before, then it's latestSlashTransferId is assigned by INIT_SLASH_TRANSFER_ID, a special flag + struct SlashInfo { + address provider; + address sourceToken; + address targetToken; + address slasher; + uint256 timestamp; + } + + // transferId => latest slash transfer Id + mapping(bytes32 => bytes32) public fillTransfers; + // transferId => Slash info + mapping(bytes32 => SlashInfo) public slashInfos; + + event TransferFilled(bytes32 transferId, address slasher); + event SlashRequest(uint256 remoteChainId, address sourceToken, address targetToken, bytes32 transferId); + event WithdrawMarginRequest(uint256 remoteChainId, address sourceToken, address targetToken, uint112 amount); + + // if slasher is nonzero, then it's a slash fill transfer + function _checkPreviousAndFillTransfer( + bytes32 _transferId, + bytes32 _previousTransferId + ) internal { + // the first fill transfer, we fill the INIT_SLASH_TRANSFER_ID as the latest slash transferId + if (_previousTransferId == bytes32(0)) { + fillTransfers[_transferId] = LnBridgeHelper.INIT_SLASH_TRANSFER_ID; + } else { + // Find the previous slash fill, it is a slash fill if the slasher is not zero address. + bytes32 previousLatestSlashTransferId = fillTransfers[_previousTransferId]; + require(previousLatestSlashTransferId != bytes32(0), "previous fill not exist"); + + SlashInfo memory previousSlashInfo = slashInfos[_previousTransferId]; + // we use latestSlashTransferId to store the latest slash transferId + // if previous.slasher != 0, then previous is slashed + // if previous.slasher == 0, then previous is not slashed + bytes32 latestSlashTransferId = previousSlashInfo.slasher != address(0) ? _previousTransferId : previousLatestSlashTransferId; + + fillTransfers[_transferId] = latestSlashTransferId; + } + } + + // fill transfer + // 1. if transfer is not slashed or relayed, LnProvider relay message to fill the transfer, and the transfer finished on target chain + // 2. if transfer is timeout and not processed, slasher(any account) can fill the transfer and request slash + // if it's filled by slasher, we store the address of the slasher + // expectedTransferId used to ensure the parameter is the same as on source chain + // some cases + // 1) If transferId is not exist on source chain, it'll be rejected by source chain when shashed. + // 2) If transferId exist on source chain. We have the same hash process on source and target chain, so the previousTransferId is trusted. + // 2.1) If transferId is the first transfer Id of this provider, then previousTransferId is zero and the latestSlashTransferId is INIT_SLASH_TRANSFER_ID + // 2.2) If transferId is not the first transfer, then it's latestSlashTransferId has the next two scenarios + // * the previousTransfer is a slash transfer, then latestSlashTransferId is previousTransferId + // * the previousTransfer is a normal relayed transfer, then latestSlashTransferId is previousTransfer's latestSlashTransferId + // I. transferId is trusted => previousTransferId is trusted => previousTransfer.previousTransferId is trusted => ... => firstTransfer is trusted + // II. transferId is trusted => previousTransferId is trusted => latestSlashTransferId is trusted if previousTransfer is a slash transfer + // III. Both I and II => latestSlashTransferId is trusted if previousTransfer is normal relayed tranfer + function _fillTransfer( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId + ) internal { + bytes32 transferId = keccak256(abi.encodePacked( + _remoteChainId, + block.chainid, + _params.previousTransferId, + _params.provider, + _params.sourceToken, + _params.targetToken, + _params.receiver, + _params.amount)); + require(_expectedTransferId == transferId, "check expected transferId failed"); + // Make sure this transfer was never filled before + require(fillTransfers[transferId] == bytes32(0), "fill exist"); + + _checkPreviousAndFillTransfer(transferId, _params.previousTransferId); + + if (_params.targetToken == address(0)) { + require(msg.value >= _params.amount, "invalid amount"); + LnBridgeHelper.safeTransferNative(_params.receiver, _params.amount); + } else { + LnBridgeHelper.safeTransferFrom(_params.targetToken, msg.sender, _params.receiver, uint256(_params.amount)); + } + } + + function transferAndReleaseMargin( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId + ) payable external { + // normal relay message, fill slasher as zero + require(_params.provider == msg.sender, "invalid provider"); + _fillTransfer(_params, _remoteChainId, _expectedTransferId); + + emit TransferFilled(_expectedTransferId, address(0)); + } + + // The condition for slash is that the transfer has timed out + // Meanwhile we need to request a slash transaction to the source chain to withdraw the LnProvider's margin + // On the source chain, we need to verify all the transfers before has been relayed or slashed. + // So we needs to carry the the previous shash transferId to ensure that the slash is continuous. + function _slashAndRemoteReleaseCall( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId + ) internal returns(bytes memory message) { + require(block.timestamp > _params.timestamp + LnBridgeHelper.SLASH_EXPIRE_TIME, "slash time not expired"); + _fillTransfer(_params, _remoteChainId, _expectedTransferId); + + // slasher = msg.sender + slashInfos[_expectedTransferId] = SlashInfo(_params.provider, _params.sourceToken, _params.targetToken, msg.sender, _params.timestamp); + + // Do not slash `transferId` in source chain unless `latestSlashTransferId` has been slashed + message = encodeSlashCall( + fillTransfers[_expectedTransferId], + _expectedTransferId, + _params.timestamp, + _params.sourceToken, + _params.targetToken, + _params.provider, + msg.sender + ); + emit TransferFilled(_expectedTransferId, msg.sender); + } + + // we use this to verify that the transfer has been slashed by user and it can resend the slash request + function _retrySlashAndRemoteReleaseCall(bytes32 _transferId) internal view returns(bytes memory message) { + bytes32 latestSlashTransferId = fillTransfers[_transferId]; + // transfer must be filled + require(latestSlashTransferId != bytes32(0), "invalid transfer id"); + // transfer must be slashed + SlashInfo memory slashInfo = slashInfos[_transferId]; + require(slashInfo.slasher != address(0), "slasher not exist"); + message = encodeSlashCall( + latestSlashTransferId, + _transferId, + slashInfo.timestamp, + slashInfo.sourceToken, + slashInfo.targetToken, + slashInfo.provider, + slashInfo.slasher + ); + } + + function encodeSlashCall( + bytes32 _latestSlashTransferId, + bytes32 _transferId, + uint256 _timestamp, + address _sourceToken, + address _targetToken, + address _provider, + address _slasher + ) public view returns(bytes memory) { + return abi.encodeWithSelector( + ILnOppositeBridgeSource.slash.selector, + _latestSlashTransferId, + _transferId, + block.chainid, + _timestamp, + _sourceToken, + _targetToken, + _provider, + _slasher + ); + } + + function _sendMessageToTarget(uint256 _remoteChainId, bytes memory _payload, bytes memory _extParams) internal virtual {} + + function requestSlashAndRemoteRelease( + LnBridgeHelper.TransferParameter calldata _params, + uint256 _remoteChainId, + bytes32 _expectedTransferId, + bytes memory _extParams + ) payable external { + bytes memory slashCallMessage = _slashAndRemoteReleaseCall( + _params, + _remoteChainId, + _expectedTransferId + ); + _sendMessageToTarget(_remoteChainId, slashCallMessage, _extParams); + emit SlashRequest(_remoteChainId, _params.sourceToken, _params.targetToken, _expectedTransferId); + } + + function requestRetrySlashAndRemoteRelease( + uint256 remoteChainId, + bytes32 _transferId, + bytes memory _extParams + ) payable external { + bytes memory retryCallMessage = _retrySlashAndRemoteReleaseCall(_transferId); + _sendMessageToTarget(remoteChainId, retryCallMessage, _extParams); + } + + function encodeWithdrawMargin( + bytes32 _lastTransferId, + address _sourceToken, + address _targetToken, + uint112 _amount + ) public view returns(bytes memory message) { + bytes32 latestSlashTransferId = LnBridgeHelper.INIT_SLASH_TRANSFER_ID; + if (_lastTransferId != bytes32(0)) { + latestSlashTransferId = fillTransfers[_lastTransferId]; + require(latestSlashTransferId != bytes32(0), "invalid last transfer"); + } + + return abi.encodeWithSelector( + ILnOppositeBridgeSource.withdrawMargin.selector, + latestSlashTransferId, + _lastTransferId, + block.chainid, + msg.sender, + _sourceToken, + _targetToken, + _amount + ); + } + + function requestWithdrawMargin( + uint256 _remoteChainId, + bytes32 _lastTransferId, + address _sourceToken, + address _targetToken, + uint112 _amount, + bytes memory _extParams + ) payable external { + bytes memory withdrawCallMessage = encodeWithdrawMargin( + _lastTransferId, + _sourceToken, + _targetToken, + _amount + ); + _sendMessageToTarget(_remoteChainId, withdrawCallMessage, _extParams); + emit WithdrawMarginRequest(_remoteChainId, _sourceToken, _targetToken, _amount); + } +} + +// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) + + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + * + * [IMPORTANT] + * ==== + * You shouldn't rely on `isContract` to protect against flash loan attacks! + * + * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets + * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract + * constructor. + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize/address.code.length, which returns 0 + // for contracts in construction, since the code is only stored at the end + // of the constructor execution. + + return account.code.length > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + /// @solidity memory-safe-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) + + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be + * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in + * case an upgrade adds a module that needs to be initialized. + * + * For example: + * + * [.hljs-theme-light.nopadding] + * ``` + * contract MyToken is ERC20Upgradeable { + * function initialize() initializer public { + * __ERC20_init("MyToken", "MTK"); + * } + * } + * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { + * function initializeV2() reinitializer(2) public { + * __ERC20Permit_init("MyToken"); + * } + * } + * ``` + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * [CAUTION] + * ==== + * Avoid leaving a contract uninitialized. + * + * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation + * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke + * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() { + * _disableInitializers(); + * } + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + * @custom:oz-retyped-from bool + */ + uint8 private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Triggered when the contract has been initialized or reinitialized. + */ + event Initialized(uint8 version); + + /** + * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, + * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. + */ + modifier initializer() { + bool isTopLevelCall = !_initializing; + require( + (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), + "Initializable: contract is already initialized" + ); + _initialized = 1; + if (isTopLevelCall) { + _initializing = true; + } + _; + if (isTopLevelCall) { + _initializing = false; + emit Initialized(1); + } + } + + /** + * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the + * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be + * used to initialize parent contracts. + * + * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original + * initialization step. This is essential to configure modules that are added through upgrades and that require + * initialization. + * + * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in + * a contract, executing them in the right order is up to the developer or operator. + */ + modifier reinitializer(uint8 version) { + require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); + _initialized = version; + _initializing = true; + _; + _initializing = false; + emit Initialized(version); + } + + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} and {reinitializer} modifiers, directly or indirectly. + */ + modifier onlyInitializing() { + require(_initializing, "Initializable: contract is not initializing"); + _; + } + + /** + * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. + * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized + * to any version. It is recommended to use this to lock implementation contracts that are designed to be called + * through proxies. + */ + function _disableInitializers() internal virtual { + require(!_initializing, "Initializable: contract is initializing"); + if (_initialized < type(uint8).max) { + _initialized = type(uint8).max; + emit Initialized(type(uint8).max); + } + } +} + +// File contracts/ln/LnOppositeBridge.sol +// License-Identifier: MIT + + + + + +contract LnOppositeBridge is Initializable, LnAccessController, LnOppositeBridgeSource, LnOppositeBridgeTarget { + struct MessagerService { + address sendService; + address receiveService; + } + mapping(uint256=>MessagerService) public messagers; + + receive() external payable {} + + function initialize(address _dao) public initializer { + _initialize(_dao); + _updateFeeReceiver(_dao); + } + + function unpause() external onlyOperator { + _unpause(); + } + + function pause() external onlyOperator { + _pause(); + } + + function setSendService(uint256 _remoteChainId, address _remoteBridge, address _service) external onlyDao { + messagers[_remoteChainId].sendService = _service; + ILowLevelMessageSender(_service).registerRemoteReceiver(_remoteChainId, _remoteBridge); + } + + function setReceiveService(uint256 _remoteChainId, address _remoteBridge, address _service) external onlyDao { + messagers[_remoteChainId].receiveService = _service; + ILowLevelMessageReceiver(_service).registerRemoteSender(_remoteChainId, _remoteBridge); + } + + function updateFeeReceiver(address _receiver) external onlyDao { + _updateFeeReceiver(_receiver); + } + + function setTokenInfo( + uint256 _remoteChainId, + address _sourceToken, + address _targetToken, + uint112 _protocolFee, + uint112 _penaltyLnCollateral, + uint8 _sourceDecimals, + uint8 _targetDecimals + ) external onlyDao { + _setTokenInfo( + _remoteChainId, + _sourceToken, + _targetToken, + _protocolFee, + _penaltyLnCollateral, + _sourceDecimals, + _targetDecimals + ); + } + + function _sendMessageToTarget(uint256 _remoteChainId, bytes memory _payload, bytes memory _extParams) whenNotPaused internal override { + address sendService = messagers[_remoteChainId].sendService; + require(sendService != address(0), "invalid messager"); + ILowLevelMessageSender(sendService).sendMessage{value: msg.value}(_remoteChainId, _payload, _extParams); + } + + function _verifyRemote(uint256 _remoteChainId) whenNotPaused internal view override { + address receiveService = messagers[_remoteChainId].receiveService; + require(receiveService == msg.sender, "invalid messager"); + } +} \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/Erc20.sol b/helix-contract/flatten/lnv2/TestToken.sol similarity index 96% rename from helix-contract/flatten/sub2eth/Erc20.sol rename to helix-contract/flatten/lnv2/TestToken.sol index db1113f0..15fcc996 100644 --- a/helix-contract/flatten/sub2eth/Erc20.sol +++ b/helix-contract/flatten/lnv2/TestToken.sol @@ -14,93 +14,10 @@ * '----------------' '----------------' '----------------' '----------------' '----------------' ' * * - * 5/12/2023 + * 10/17/2023 **/ -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} +pragma solidity ^0.8.17; // File @zeppelin-solidity/contracts/utils/math/SafeMath.sol@v4.7.3 // License-Identifier: MIT @@ -437,20 +354,107 @@ abstract contract Ownable is Context { } } -// File contracts/mapping-token/v2/erc20-mapping-protocol/Erc20.sol +// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} + +// File contracts/ln/test/TestToken.sol // License-Identifier: MIT -contract Erc20 is IERC20, Ownable { +contract HelixTestErc20 is IERC20, Ownable { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; + mapping (address => uint256) public allowFaucet; + uint256 private _totalSupply; + uint256 public maxFaucetAllowed = 100000; + string public name; string public symbol; uint8 public decimals; @@ -516,6 +520,17 @@ contract Erc20 is IERC20, Ownable { _mint(account, amount); } + function setMaxFaucetAllowed(uint256 allowed) external onlyOwner { + maxFaucetAllowed = allowed; + } + + function faucet(uint256 amount) external { + uint256 alreadyFaucet = allowFaucet[msg.sender]; + require(maxFaucetAllowed * 10**decimals - alreadyFaucet > amount, "exceed faucet amount"); + allowFaucet[msg.sender] = alreadyFaucet + amount; + _mint(msg.sender, amount); + } + function burn(address account, uint256 amount) external { if (account != msg.sender && owner() != msg.sender && _allowances[account][msg.sender] != type(uint256).max) { _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount, "ERC20: decreased allowance below zero")); diff --git a/helix-contract/flatten/lnv2/ZkSync2EthSource.sol b/helix-contract/flatten/lnv2/ZkSync2EthSource.sol deleted file mode 100644 index 6f831dfa..00000000 --- a/helix-contract/flatten/lnv2/ZkSync2EthSource.sol +++ /dev/null @@ -1,2025 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/11/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnOppositeBridgeSource.sol -// License-Identifier: MIT - - -/// @title LnBridgeSource -/// @notice LnBridgeSource is a contract to help user transfer token to liquidity node and generate proof, -/// then the liquidity node must transfer the same amount of the token to the user on target chain. -/// Otherwise if timeout the slasher can paid for relayer and slash the transfer, then request slash from lnProvider's margin. -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnOppositeBridgeSource is LnBridgeHelper { - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; - - // the registered token info - // sourceToken and targetToken is the pair of erc20 token addresses - // if sourceToken == address(0), then it's native token - // if targetToken == address(0), then remote is native token - // * `protocolFee` is the protocol fee charged by system - // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. - struct TokenInfo { - address targetToken; - uint112 protocolFee; - uint112 penaltyLnCollateral; - uint8 sourceDecimals; - uint8 targetDecimals; - bool isRegistered; - } - // the Liquidity Node provider info - // Liquidity Node need register first - struct LnProviderConfigure { - uint112 margin; - uint112 baseFee; - // liquidityFeeRate / 100,000 * amount = liquidityFee - // the max liquidity fee rate is 0.255% - uint8 liquidityFeeRate; - } - struct LnProviderInfo { - LnProviderConfigure config; - bool pause; - bytes32 lastTransferId; - } - - // the Snapshot is the state of the token bridge when user prepare to transfer across chains. - // If the snapshot updated when the across chain transfer confirmed, it will - // 1. if lastTransferId updated, revert - // 2. if margin decrease or totalFee increase, revert - // 3. if margin increase or totalFee decrease, success - struct Snapshot { - address provider; - address sourceToken; - bytes32 transferId; - uint112 depositedMargin; - uint112 totalFee; - } - // registered token info - // sourceToken => token info - mapping(address=>TokenInfo) public tokenInfos; - // registered lnProviders - mapping(bytes32=>LnProviderInfo) public lnProviders; - // each time cross chain transfer, amount and fee can't be larger than type(uint112).max - struct LockInfo { - // amount + providerFee + penaltyLnCollateral - // the Indexer should be care about this value, it will frozen lnProvider's margin when the transfer not finished. - // and when the slasher slash success, this amount of token will be transfer from lnProvider's margin to slasher. - uint112 amountWithFeeAndPenalty; - bool hasSlashed; - } - // key: transferId = hash(proviousTransferId, timestamp, targetToken, receiver, targetAmount) - // * `proviousTransferId` is used to ensure the continuous of the transfer - // * `timestamp` is the block.timestmap to judge timeout on target chain(here we support source and target chain has the same world clock) - // * `targetToken`, `receiver` and `targetAmount` are used on target chain to transfer target token. - mapping(bytes32 => LockInfo) public lockInfos; - address public feeReceiver; - - event TokenLocked( - bytes32 transferId, - address provider, - address sourceToken, - uint112 amount, - uint112 fee, - uint64 timestamp, - address receiver); - event LiquidityWithdrawn(address provider, address token, uint112 amount); - event Slash(bytes32 transferId, address provider, address token, uint112 margin, address slasher); - // relayer - event LnProviderUpdated(address provider, address token, uint112 margin, uint112 baseFee, uint8 liquidityfeeRate); - - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "invalid system fee receiver"); - feeReceiver = _feeReceiver; - } - - function _updateProtocolFee(address _token, uint112 _protocolFee) internal { - require(tokenInfos[_token].isRegistered, "token not registered"); - tokenInfos[_token].protocolFee = _protocolFee; - } - - function _updatePenaltyLnCollateral(address _token, uint112 _penaltyLnCollateral) internal { - require(tokenInfos[_token].isRegistered, "token not registered"); - tokenInfos[_token].penaltyLnCollateral = _penaltyLnCollateral; - } - - function providerPause(address sourceToken) external { - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - lnProviders[providerKey].pause = true; - } - - function providerUnpause(address sourceToken) external { - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - lnProviders[providerKey].pause = false; - } - - // lnProvider can register or update its configure by using this function - // * `margin` is the increased value of the deposited margin - function updateProviderFeeAndMargin( - address sourceToken, - uint112 margin, - uint112 baseFee, - uint8 liquidityFeeRate - ) external payable { - TokenInfo memory tokenInfo = tokenInfos[sourceToken]; - require(tokenInfo.isRegistered, "token is not registered"); - - bytes32 providerKey = getProviderKey(msg.sender, sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - - LnProviderConfigure memory config = LnProviderConfigure( - // the margin can be only increased here - margin + providerInfo.config.margin, - baseFee, - liquidityFeeRate); - - lnProviders[providerKey].config = config; - - if (sourceToken == address(0)) { - require(msg.value == margin, "invalid margin value"); - } else { - if (margin > 0) { - _safeTransferFrom(sourceToken, msg.sender, address(this), margin); - } - } - emit LnProviderUpdated(msg.sender, sourceToken, config.margin, baseFee, liquidityFeeRate); - } - - function _registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) internal { - tokenInfos[sourceToken] = TokenInfo( - targetToken, - protocolFee, - penaltyLnCollateral, - sourceDecimals, - targetDecimals, - true - ); - } - - function calculateProviderFee(LnProviderConfigure memory config, uint112 amount) internal pure returns(uint256) { - return uint256(config.baseFee) + uint256(config.liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; - } - - // the fee user should paid when transfer. - // totalFee = providerFee + protocolFee - // providerFee = provider.baseFee + provider.liquidityFeeRate * amount - function totalFee(address provider, address sourceToken, uint112 amount) external view returns(uint256) { - bytes32 providerKey = getProviderKey(provider, sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - uint256 providerFee = calculateProviderFee(providerInfo.config, amount); - return providerFee + tokenInfos[sourceToken].protocolFee; - } - - // This function transfers tokens from the user to LnProvider and generates a proof on the source chain. - // The snapshot represents the state of the LN bridge for this LnProvider, obtained by the off-chain indexer. - // If the chain state is updated and does not match the snapshot state, the transaction will be reverted. - // 1. the state(lastTransferId, fee, margin) must match snapshot - // 2. transferId not exist - function transferAndLockMargin( - Snapshot calldata snapshot, - uint112 amount, - address receiver - ) external payable { - require(amount > 0, "invalid amount"); - - bytes32 providerKey = getProviderKey(snapshot.provider, snapshot.sourceToken); - LnProviderInfo memory providerInfo = lnProviders[providerKey]; - - require(!providerInfo.pause, "provider paused"); - - TokenInfo memory tokenInfo = tokenInfos[snapshot.sourceToken]; - - uint256 providerFee = calculateProviderFee(providerInfo.config, amount); - - // Note: this requirement is not enough to ensure that the lnProvider's margin is enough because there maybe some frozen margins in other transfers - require(providerInfo.config.margin >= amount + tokenInfo.penaltyLnCollateral + uint112(providerFee), "amount not valid"); - - // the chain state not match snapshot - require(providerInfo.lastTransferId == snapshot.transferId, "snapshot expired"); - require(snapshot.totalFee >= tokenInfo.protocolFee + providerFee, "fee is invalid"); - require(snapshot.depositedMargin <= providerInfo.config.margin, "margin updated"); - - uint256 targetAmount = uint256(amount) * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; - require(targetAmount < MAX_TRANSFER_AMOUNT, "overflow amount"); - uint64 timestamp = uint64(block.timestamp); - bytes32 transferId = keccak256(abi.encodePacked( - snapshot.transferId, - snapshot.provider, - snapshot.sourceToken, - tokenInfo.targetToken, - receiver, - timestamp, - uint112(targetAmount))); - require(lockInfos[transferId].amountWithFeeAndPenalty == 0, "transferId exist"); - lockInfos[transferId] = LockInfo(amount + tokenInfo.penaltyLnCollateral + uint112(providerFee), false); - - // update the state to prevent other transfers using the same snapshot - lnProviders[providerKey].lastTransferId = transferId; - - if (snapshot.sourceToken == address(0)) { - require(amount + snapshot.totalFee == msg.value, "amount unmatched"); - _safeTransferNative(snapshot.provider, amount + providerFee); - if (tokenInfo.protocolFee > 0) { - _safeTransferNative(feeReceiver, tokenInfo.protocolFee); - } - uint256 refund = snapshot.totalFee - tokenInfo.protocolFee - providerFee; - if ( refund > 0 ) { - _safeTransferNative(msg.sender, refund); - } - } else { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - snapshot.provider, - amount + providerFee - ); - if (tokenInfo.protocolFee > 0) { - _safeTransferFrom( - snapshot.sourceToken, - msg.sender, - feeReceiver, - tokenInfo.protocolFee - ); - } - } - emit TokenLocked( - transferId, - snapshot.provider, - snapshot.sourceToken, - amount, - uint112(providerFee), - timestamp, - receiver); - } - - // this slash is called by remote message - // the token should be sent to the slasher who slash and finish the transfer on target chain. - // latestSlashTransferId is the latest slashed transfer trusted from the target chain, and the current slash transfer cannot be executed before the latestSlash transfer. - // after slash, the margin of lnProvider need to be updated - function _slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address sourceToken, - address provider, - address slasher - ) internal { - // check lastTransfer - // ensure last slash transfer(checked on target chain) has been slashed - LockInfo memory lastLockInfo = lockInfos[latestSlashTransferId]; - require(lastLockInfo.hasSlashed || latestSlashTransferId == INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); - LockInfo memory lockInfo = lockInfos[transferId]; - - // ensure transfer exist and not slashed yet - require(!lockInfo.hasSlashed, "transfer has been slashed"); - require(lockInfo.amountWithFeeAndPenalty > 0, "lnBridgeSource:invalid transferId"); - - bytes32 providerKey = getProviderKey(provider, sourceToken); - - LnProviderInfo memory lnProvider = lnProviders[providerKey]; - lockInfos[transferId].hasSlashed = true; - // transfer token to the slasher - uint256 slashAmount = lockInfo.amountWithFeeAndPenalty; - require(lnProvider.config.margin >= slashAmount, "margin not enough"); - uint112 updatedMargin = lnProvider.config.margin - uint112(slashAmount); - lnProviders[providerKey].config.margin = updatedMargin; - - if (sourceToken == address(0)) { - _safeTransferNative(slasher, slashAmount); - } else { - _safeTransfer(sourceToken, slasher, slashAmount); - } - - emit Slash(transferId, provider, sourceToken, updatedMargin, slasher); - } - - // lastTransfer is the latest slash transfer, all transfer must be relayed or slashed - // if user use the snapshot before this transaction to send cross-chain transfer, it should be reverted because this `_withdrawMargin` will decrease margin. - function _withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) internal { - // check the latest slash transfer - // ensure latest slash tranfer(verified on target chain) has been slashed on source chain - LockInfo memory lastRefundLockInfo = lockInfos[latestSlashTransferId]; - require(lastRefundLockInfo.hasSlashed || latestSlashTransferId == INIT_SLASH_TRANSFER_ID, "latest slash transfer invalid"); - - // use this condition to ensure that the withdraw message is sent by the provider - // the parameter provider is the message sender of this remote withdraw call - bytes32 providerKey = getProviderKey(provider, sourceToken); - LnProviderInfo memory lnProvider = lnProviders[providerKey]; - - // ensure all transfer has finished - require(lnProvider.lastTransferId == lastTransferId, "invalid last transferid"); - require(lnProvider.config.margin >= amount, "margin not enough"); - uint112 updatedMargin = lnProvider.config.margin - amount; - lnProviders[providerKey].config.margin = updatedMargin; - if (sourceToken == address(0)) { - _safeTransferNative(provider, amount); - } else { - _safeTransfer(sourceToken, provider, amount); - } - emit LiquidityWithdrawn(provider, sourceToken, updatedMargin); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/ZkSync2EthSource.sol -// License-Identifier: MIT - - - -contract ZkSync2EthSource is Initializable, LnAccessController, LnOppositeBridgeSource { - uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); - address public remoteBridge; - address public remoteBridgeAlias; - - receive() external payable {} - - modifier onlyRemoteBridge() { - require(msg.sender == remoteBridgeAlias, "invalid remote caller"); - _; - } - - function initialize(address dao) public initializer { - _initialize(dao); - _setFeeReceiver(dao); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function updateProtocolFee(address token, uint112 _protocolFee) external onlyDao { - _updateProtocolFee(token, _protocolFee); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - remoteBridgeAlias = address(uint160(_remoteBridge) + offset); - } - - function setRemoteBridgeAlias(address _remoteBridgeAlias) external onlyDao { - remoteBridgeAlias = _remoteBridgeAlias; - } - - function registerToken( - address sourceToken, - address targetToken, - uint112 protocolFee, - uint112 penaltyLnCollateral, - uint8 sourceDecimals, - uint8 targetDecimals - ) external onlyOperator { - _registerToken(sourceToken, targetToken, protocolFee, penaltyLnCollateral, sourceDecimals, targetDecimals); - } - - function slash( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external onlyRemoteBridge whenNotPaused { - _slash(latestSlashTransferId, transferId, sourceToken, provider, slasher); - } - - function withdrawMargin( - bytes32 latestSlashTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external onlyRemoteBridge whenNotPaused { - _withdrawMargin(latestSlashTransferId, lastTransferId, provider, sourceToken, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/ZkSync2EthTarget.sol b/helix-contract/flatten/lnv2/ZkSync2EthTarget.sol deleted file mode 100644 index 53d52b19..00000000 --- a/helix-contract/flatten/lnv2/ZkSync2EthTarget.sol +++ /dev/null @@ -1,1931 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 8/11/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/ln/base/LnAccessController.sol -// License-Identifier: MIT - - -/// @title LnAccessController -/// @notice LnAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LnAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File contracts/ln/interface/ILnOppositeBridgeSource.sol -// License-Identifier: MIT - - -interface ILnOppositeBridgeSource { - function slash( - bytes32 lastRefundTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) external; - - function withdrawMargin( - bytes32 lastRefundTransferId, - bytes32 lastTransferId, - address provider, - address sourceToken, - uint112 amount - ) external; -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/ln/base/LnBridgeHelper.sol -// License-Identifier: MIT - -contract LnBridgeHelper { - bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); - - struct TransferParameter { - bytes32 previousTransferId; - address provider; - address sourceToken; - address targetToken; - uint112 amount; - uint64 timestamp; - address receiver; - } - - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); - } - - function _safeTransferNative( - address receiver, - uint256 amount - ) internal { - (bool success,) = payable(receiver).call{value: amount}(""); - require(success, "lnBridgeHelper:transfer native token failed"); - } - - function getProviderKey(address provider, address sourceToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken - )); - } - - function getDefaultProviderKey(address provider, address sourceToken, address targetToken) pure public returns(bytes32) { - return keccak256(abi.encodePacked( - provider, - sourceToken, - targetToken - )); - } -} - -// File contracts/ln/base/LnOppositeBridgeTarget.sol -// License-Identifier: MIT - - -contract LnOppositeBridgeTarget is LnBridgeHelper { - uint256 constant public MIN_REFUND_TIMESTAMP = 30 * 60; - - // if slasher == address(0), this FillTransfer is relayed by lnProvider - // otherwise, this FillTransfer is slashed by slasher - // if there is no slash transfer before, then it's latestSlashTransferId is assigned by INIT_SLASH_TRANSFER_ID, a special flag - struct SlashInfo { - address provider; - address sourceToken; - address slasher; - } - - // transferId => latest slash transfer Id - mapping(bytes32 => bytes32) public fillTransfers; - // transferId => Slash info - mapping(bytes32 => SlashInfo) public slashInfos; - - event TransferFilled(bytes32 transferId, address slasher); - - // if slasher is nonzero, then it's a slash fill transfer - function _checkPreviousAndFillTransfer( - bytes32 transferId, - bytes32 previousTransferId - ) internal { - // the first fill transfer, we fill the INIT_SLASH_TRANSFER_ID as the latest slash transferId - if (previousTransferId == bytes32(0)) { - fillTransfers[transferId] = INIT_SLASH_TRANSFER_ID; - } else { - // Find the previous slash fill, it is a slash fill if the slasher is not zero address. - bytes32 previousLatestSlashTransferId = fillTransfers[previousTransferId]; - require(previousLatestSlashTransferId != bytes32(0), "previous fill not exist"); - - SlashInfo memory previousSlashInfo = slashInfos[previousTransferId]; - // we use latestSlashTransferId to store the latest slash transferId - // if previous.slasher != 0, then previous is slashed - // if previous.slasher == 0, then previous is not slashed - bytes32 latestSlashTransferId = previousSlashInfo.slasher != address(0) ? previousTransferId : previousLatestSlashTransferId; - - fillTransfers[transferId] = latestSlashTransferId; - } - } - - // fill transfer - // 1. if transfer is not slashed or relayed, LnProvider relay message to fill the transfer, and the transfer finished on target chain - // 2. if transfer is timeout and not processed, slasher(any account) can fill the transfer and request slash - // if it's filled by slasher, we store the address of the slasher - // expectedTransferId used to ensure the parameter is the same as on source chain - // some cases - // 1) If transferId is not exist on source chain, it'll be rejected by source chain when shashed. - // 2) If transferId exist on source chain. We have the same hash process on source and target chain, so the previousTransferId is trusted. - // 2.1) If transferId is the first transfer Id of this provider, then previousTransferId is zero and the latestSlashTransferId is INIT_SLASH_TRANSFER_ID - // 2.2) If transferId is not the first transfer, then it's latestSlashTransferId has the next two scenarios - // * the previousTransfer is a slash transfer, then latestSlashTransferId is previousTransferId - // * the previousTransfer is a normal relayed transfer, then latestSlashTransferId is previousTransfer's latestSlashTransferId - // I. transferId is trusted => previousTransferId is trusted => previousTransfer.previousTransferId is trusted => ... => firstTransfer is trusted - // II. transferId is trusted => previousTransferId is trusted => latestSlashTransferId is trusted if previousTransfer is a slash transfer - // III. Both I and II => latestSlashTransferId is trusted if previousTransfer is normal relayed tranfer - function _fillTransfer( - TransferParameter calldata params, - bytes32 expectedTransferId - ) internal { - bytes32 transferId = keccak256(abi.encodePacked( - params.previousTransferId, - params.provider, - params.sourceToken, - params.targetToken, - params.receiver, - params.timestamp, - params.amount)); - require(expectedTransferId == transferId, "check expected transferId failed"); - // Make sure this transfer was never filled before - require(fillTransfers[transferId] == bytes32(0), "fill exist"); - - _checkPreviousAndFillTransfer(transferId, params.previousTransferId); - - if (params.targetToken == address(0)) { - require(msg.value >= params.amount, "invalid amount"); - _safeTransferNative(params.receiver, params.amount); - } else { - _safeTransferFrom(params.targetToken, msg.sender, params.receiver, uint256(params.amount)); - } - } - - function transferAndReleaseMargin( - TransferParameter calldata params, - bytes32 expectedTransferId - ) payable external { - // normal relay message, fill slasher as zero - require(params.provider == msg.sender, "invalid provider"); - _fillTransfer(params, expectedTransferId); - - emit TransferFilled(expectedTransferId, address(0)); - } - - // The condition for slash is that the transfer has timed out - // Meanwhile we need to request a slash transaction to the source chain to withdraw the LnProvider's margin - // On the source chain, we need to verify all the transfers before has been relayed or slashed. - // So we needs to carry the the previous shash transferId to ensure that the slash is continuous. - function _slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId - ) internal returns(bytes memory message) { - require(block.timestamp > params.timestamp + MIN_REFUND_TIMESTAMP, "slash time not expired"); - _fillTransfer(params, expectedTransferId); - - // slasher = msg.sender - slashInfos[expectedTransferId] = SlashInfo(params.provider, params.sourceToken, msg.sender); - - // Do not slash `transferId` in source chain unless `latestSlashTransferId` has been slashed - message = _encodeSlashCall( - fillTransfers[expectedTransferId], - expectedTransferId, - params.provider, - params.sourceToken, - msg.sender - ); - emit TransferFilled(expectedTransferId, msg.sender); - } - - // we use this to verify that the transfer has been slashed by user and it can resend the slash request - function _retrySlashAndRemoteRefund(bytes32 transferId) internal view returns(bytes memory message) { - bytes32 latestSlashTransferId = fillTransfers[transferId]; - // transfer must be filled - require(latestSlashTransferId != bytes32(0), "invalid transfer id"); - // transfer must be slashed - SlashInfo memory slashInfo = slashInfos[transferId]; - require(slashInfo.slasher != address(0), "slasher not exist"); - message = _encodeSlashCall( - latestSlashTransferId, - transferId, - slashInfo.provider, - slashInfo.sourceToken, - slashInfo.slasher - ); - } - - function _encodeSlashCall( - bytes32 latestSlashTransferId, - bytes32 transferId, - address provider, - address sourceToken, - address slasher - ) internal pure returns(bytes memory) { - return abi.encodeWithSelector( - ILnOppositeBridgeSource.slash.selector, - latestSlashTransferId, - transferId, - provider, - sourceToken, - slasher - ); - } - - function _requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount - ) internal view returns(bytes memory message) { - bytes32 latestSlashTransferId = fillTransfers[lastTransferId]; - require(latestSlashTransferId != bytes32(0), "invalid last transfer"); - - return abi.encodeWithSelector( - ILnOppositeBridgeSource.withdrawMargin.selector, - latestSlashTransferId, - lastTransferId, - msg.sender, - sourceToken, - amount - ); - } -} - -// File contracts/ln/interface/IZksyncMailbox.sol -// License-Identifier: MIT - -interface IMailbox { - function requestL2Transaction( - address _contractL2, - uint256 _l2Value, - bytes calldata _calldata, - uint256 _l2GasLimit, - uint256 _l2GasPerPubdataByteLimit, - bytes[] calldata _factoryDeps, - address _refundRecipient - ) external payable returns (bytes32 canonicalTxHash); - - // this function is used to calculate the fee on L2 - function l2TransactionBaseCost( - uint256 _gasPrice, - uint256 _l2GasLimit, - uint256 _l2GasPerPubdataByteLimit - ) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/ln/ZkSync2EthTarget.sol -// License-Identifier: MIT - - - - -contract ZkSync2EthTarget is Initializable, LnAccessController, LnOppositeBridgeTarget { - IMailbox public mailbox; - address public remoteBridge; - - event WithdrawMargin(bytes32 lastTransferId, uint112 amount); - - receive() external payable {} - - function initialize(address _dao, address _mailBox) public initializer { - mailbox = IMailbox(_mailBox); - _initialize(_dao); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - function l2Fee( - uint256 gasPrice, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) external view returns(uint256) { - return mailbox.l2TransactionBaseCost(gasPrice, l2GasLimit, l2GasPerPubdataByteLimit); - } - - function _sendMessage( - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit, - bytes memory message, - uint256 prepaid - ) internal returns(bytes32) { - return mailbox.requestL2Transaction{ value: prepaid }( - remoteBridge, - 0, - message, - l2GasLimit, - l2GasPerPubdataByteLimit, - new bytes[](0), - msg.sender - ); - } - - function slashAndRemoteRefund( - TransferParameter calldata params, - bytes32 expectedTransferId, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory refundCallMessage = _slashAndRemoteRefund( - params, - expectedTransferId - ); - uint256 valueUsed = address(0) == params.targetToken ? params.amount : 0; - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, refundCallMessage, msg.value - valueUsed); - } - - function retryRemoteRefund( - bytes32 transferId, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory refundCallMessage = _retrySlashAndRemoteRefund(transferId); - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, refundCallMessage, msg.value); - } - - function requestWithdrawMargin( - bytes32 lastTransferId, - address sourceToken, - uint112 amount, - uint256 l2GasLimit, - uint256 l2GasPerPubdataByteLimit - ) payable external whenNotPaused { - bytes memory cancelWithdrawMarginCall = _requestWithdrawMargin( - lastTransferId, - sourceToken, - amount - ); - _sendMessage(l2GasLimit, l2GasPerPubdataByteLimit, cancelWithdrawMarginCall, msg.value); - emit WithdrawMargin(lastTransferId, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lnv2/debugMessager.sol b/helix-contract/flatten/lnv2/debugMessager.sol new file mode 100644 index 00000000..a57dcc78 --- /dev/null +++ b/helix-contract/flatten/lnv2/debugMessager.sol @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * + * 10/17/2023 + **/ + +pragma solidity ^0.8.17; + +// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} + +// File contracts/ln/base/LnBridgeHelper.sol +// License-Identifier: MIT + +library LnBridgeHelper { + // the time(seconds) for liquidity provider to delivery message + // if timeout, slasher can work. + uint256 constant public SLASH_EXPIRE_TIME = 30 * 60; + bytes32 constant public INIT_SLASH_TRANSFER_ID = bytes32(uint256(1)); + // liquidity fee base rate + // liquidityFee = liquidityFeeRate / LIQUIDITY_FEE_RATE_BASE * sendAmount + uint256 constant public LIQUIDITY_FEE_RATE_BASE = 100000; + + struct TransferParameter { + bytes32 previousTransferId; + address provider; + address sourceToken; + address targetToken; + uint112 amount; + uint256 timestamp; + address receiver; + } + + // sourceToken and targetToken is the pair of erc20 token(or native) addresses + // if sourceToken == address(0), then it's native token + // if targetToken == address(0), then remote is native token + // * `protocolFee` is the protocol fee charged by system + // * `penaltyLnCollateral` is penalty from lnProvider when the transfer slashed, if we adjust this value, it'll not affect the old transfers. + struct TokenInfo { + uint112 protocolFee; + uint112 penaltyLnCollateral; + uint8 sourceDecimals; + uint8 targetDecimals; + bool isRegistered; + } + + function sourceAmountToTargetAmount( + TokenInfo memory tokenInfo, + uint112 amount + ) internal pure returns(uint112) { + uint256 targetAmount = uint256(amount) * 10**tokenInfo.targetDecimals / 10**tokenInfo.sourceDecimals; + require(targetAmount < type(uint112).max, "overflow amount"); + return uint112(targetAmount); + } + + function calculateProviderFee(uint112 baseFee, uint16 liquidityFeeRate, uint112 amount) internal pure returns(uint112) { + uint256 fee = uint256(baseFee) + uint256(liquidityFeeRate) * uint256(amount) / LIQUIDITY_FEE_RATE_BASE; + require(fee < type(uint112).max, "overflow fee"); + return uint112(fee); + } + + function safeTransfer( + address token, + address receiver, + uint256 amount + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector( + IERC20.transfer.selector, + receiver, + amount + )); + require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transfer token failed"); + } + + function safeTransferFrom( + address token, + address sender, + address receiver, + uint256 amount + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector( + IERC20.transferFrom.selector, + sender, + receiver, + amount + )); + require(success && (data.length == 0 || abi.decode(data, (bool))), "lnBridgeHelper:transferFrom token failed"); + } + + function safeTransferNative( + address receiver, + uint256 amount + ) internal { + (bool success,) = payable(receiver).call{value: amount}(""); + require(success, "lnBridgeHelper:transfer native token failed"); + } + + function getProviderKey(uint256 remoteChainId, address provider, address sourceToken, address targetToken) pure internal returns(bytes32) { + return keccak256(abi.encodePacked( + remoteChainId, + provider, + sourceToken, + targetToken + )); + } + + function getTokenKey(uint256 remoteChainId, address sourceToken, address targetToken) pure internal returns(bytes32) { + return keccak256(abi.encodePacked( + remoteChainId, + sourceToken, + targetToken + )); + } +} + +// File contracts/ln/interface/ILnDefaultBridgeTarget.sol +// License-Identifier: MIT + +interface ILnDefaultBridgeTarget { + function slash( + LnBridgeHelper.TransferParameter memory params, + uint256 remoteChainId, + address slasher, + uint112 fee, + uint112 penalty + ) external; + + function withdraw( + uint256 _sourceChainId, + bytes32 lastTransferId, + uint64 withdrawNonce, + address provider, + address sourceToken, + address targetToken, + uint112 amount + ) external; +} + +// File contracts/ln/interface/ILnOppositeBridgeSource.sol +// License-Identifier: MIT + +interface ILnOppositeBridgeSource { + function slash( + bytes32 lastRefundTransferId, + bytes32 transferId, + uint256 remoteChainId, + uint256 timestamp, + address sourceToken, + address targetToken, + address provider, + address slasher + ) external; + + function withdrawMargin( + bytes32 lastRefundTransferId, + bytes32 lastTransferId, + uint256 remoteChainId, + address provider, + address sourceToken, + address targetToken, + uint112 amount + ) external; +} + +// File contracts/ln/messager/DebugMessager.sol +// License-Identifier: MIT + + + +contract DebugMessager { + address public owner; + address public app; + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner, "invalid owner"); + _; + } + + function slashDefault( + LnBridgeHelper.TransferParameter memory _params, + uint256 _remoteChainId, + address _slasher, + uint112 _fee, + uint112 _penalty + ) external onlyOwner { + ILnDefaultBridgeTarget(app).slash(_params, _remoteChainId, _slasher, _fee, _penalty); + } + + function slashOpposite( + bytes32 _latestSlashTransferId, + bytes32 _transferId, + uint256 _remoteChainId, + uint256 _timestamp, + address _sourceToken, + address _targetToken, + address _provider, + address _slasher + ) external onlyOwner { + ILnOppositeBridgeSource(app).slash( + _latestSlashTransferId, + _transferId, + _remoteChainId, + _timestamp, + _sourceToken, + _targetToken, + _provider, + _slasher + ); + } + + function withdrawDefault( + uint256 _remoteChainId, + bytes32 _lastTransferId, + uint64 _withdrawNonce, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external onlyOwner { + ILnDefaultBridgeTarget(app).withdraw( + _remoteChainId, + _lastTransferId, + _withdrawNonce, + _provider, + _sourceToken, + _targetToken, + _amount + ); + } + + function withdrawOpposite( + bytes32 _latestSlashTransferId, + bytes32 _lastTransferId, + uint256 _remoteChainId, + address _provider, + address _sourceToken, + address _targetToken, + uint112 _amount + ) external onlyOwner { + ILnOppositeBridgeSource(app).withdrawMargin( + _latestSlashTransferId, + _lastTransferId, + _remoteChainId, + _provider, + _sourceToken, + _targetToken, + _amount + ); + } + + function registerRemoteReceiver(uint256 _remoteChainId, address _remoteBridge) external { + } + + function registerRemoteSender(uint256 _remoteChainId, address _remoteBridge) external { + app = msg.sender; + } +} \ No newline at end of file diff --git a/helix-contract/flatten/lp/LpSub2EthBridge.sol b/helix-contract/flatten/lp/LpSub2EthBridge.sol deleted file mode 100644 index 8e4836c3..00000000 --- a/helix-contract/flatten/lp/LpSub2EthBridge.sol +++ /dev/null @@ -1,1992 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 2/10/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/mapping-token/interfaces/IHelixMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixMessageEndpoint { - function sendMessage(address receiver, bytes calldata encoded) external payable returns (uint256); -} - -// File contracts/mapping-token/interfaces/IHelixSub2EthMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixSub2EthMessageEndpoint is IHelixMessageEndpoint { - function fee() external view returns (uint256); - function currentDeliveredMessageId() external view returns (uint256); - function isMessageDelivered(uint256 messageId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File contracts/mapping-token/v2/lp/base/LpAccessController.sol -// License-Identifier: MIT - - -/// @title LpAccessController -/// @notice LpAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LpAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File contracts/mapping-token/interfaces/IWToken.sol -// License-Identifier: MIT - - -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/mapping-token/v2/lp/base/LpBridgeHelper.sol -// License-Identifier: MIT - -contract LpBridgeHelper { - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lpBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lpBridgeHelper:transferFrom token failed"); - } -} - -// File contracts/mapping-token/v2/lp/base/LpBridgeBacking.sol -// License-Identifier: MIT - - - -/// @title LpBridgeBacking -/// @notice LpBridgeBacking is a contract to help user lock token and then trigger remote chain issuing -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LpBridgeBacking is LpBridgeHelper { - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - uint32 constant public INVALID_TOKEN_INDEX = type(uint32).max; - // the registered token info - // localToken and remoteToken is the pair of erc20 token addresses - // helixFee is charged by system, if it's bigger then half of user's fee, descrease it to the half - // remoteChainId is the remote block.chainid - // remoteIsNative is true when the remoteToken is the remote wrapped native token - struct TokenInfo { - address localToken; - address remoteToken; - uint112 helixFee; - uint64 remoteChainId; - uint8 localDecimals; - uint8 remoteDecimals; - bool remoteIsNative; - } - // registered token info - TokenInfo[] public tokens; - // each time cross chain transfer, amount and fee can't be larger than type(uint112).max - struct LockInfo { - uint32 tokenIndex; - uint112 amount; - uint112 fee; - } - mapping(bytes32 => LockInfo) public lockInfos; - address public feeReceiver; - uint32 public wTokenIndex = INVALID_TOKEN_INDEX; - - event TokenLocked( - uint64 toChainId, - bool isNative, - bool issuingNative, - uint256 nonce, - bytes32 transferId, - address token, - uint112 amount, - uint112 fee, - address receiver); - event FeeUpdated(bytes32 transferId, uint256 fee); - event LiquidityWithdrawn(bytes32 transferId, address receiver); - - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "lpBridgeBacking:invalid helix fee receiver"); - feeReceiver = _feeReceiver; - } - - function _updateHelixFee(uint32 _tokenIndex, uint112 _helixFee) internal { - require(_tokenIndex < tokens.length, "lpBridgeBacking:invalid token index"); - tokens[_tokenIndex].helixFee = _helixFee; - } - - function _setwTokenIndex(uint32 _wTokenIndex) internal { - wTokenIndex = _wTokenIndex; - } - - function _registerToken( - address localToken, - address remoteToken, - uint112 helixFee, - uint64 remoteChainId, - uint8 localDecimals, - uint8 remoteDecimals, - bool remoteIsNative - ) internal { - tokens.push(TokenInfo(localToken, remoteToken, helixFee, remoteChainId, localDecimals, remoteDecimals, remoteIsNative)); - } - - function _lockAndRemoteIssuing( - bool lockNative, - uint256 nonce, - uint32 tokenIndex, - uint112 amount, - uint112 fee, - address receiver, - bool issuingNative - ) internal returns(TokenInfo memory tokenInfo) { - tokenInfo = tokens[tokenIndex]; - require(fee > tokenInfo.helixFee && amount > 0, "lpBridgeBacking:fee or amount is not enough"); - require(!issuingNative || tokenInfo.remoteIsNative, "lpBridgeBacking:remote not native"); - uint256 remoteAmount = uint256(amount) * 10**tokenInfo.remoteDecimals / 10**tokenInfo.localDecimals; - require(remoteAmount < MAX_TRANSFER_AMOUNT, "lpBridgeBacking:overflow amount"); - bytes32 transferId = keccak256(abi.encodePacked( - nonce, - issuingNative, - tokenInfo.remoteToken, - msg.sender, - receiver, - uint112(remoteAmount), - uint64(block.chainid), - tokenInfo.remoteChainId)); - require(lockInfos[transferId].amount == 0, "lpBridgeBacking:transferId exist"); - lockInfos[transferId] = LockInfo(tokenIndex, amount, fee); - emit TokenLocked(tokenInfo.remoteChainId, lockNative, issuingNative, nonce, transferId, tokenInfo.localToken, amount, fee, receiver); - } - - function lockAndRemoteIssuing( - uint256 nonce, - address receiver, - uint112 amount, - uint112 fee, - uint32 tokenIndex, - bool issuingNative - ) external { - require(tokens.length > tokenIndex, "lpBridgeBacking:token not registered"); - TokenInfo memory info = _lockAndRemoteIssuing(false, nonce, tokenIndex, amount, fee, receiver, issuingNative); - _safeTransferFrom(info.localToken, msg.sender, address(this), amount + fee); - } - - function lockNativeAndRemoteIssuing( - uint112 amount, - uint112 fee, - address receiver, - uint256 nonce, - bool issuingNative - ) external payable { - require(amount + fee == msg.value, "lpBridgeBacking:amount unmatched"); - require(wTokenIndex != INVALID_TOKEN_INDEX, "lpBridgeBacking:not support"); - TokenInfo memory info = _lockAndRemoteIssuing(true, nonce, wTokenIndex, amount, fee, receiver, issuingNative); - IWToken(info.localToken).deposit{value: amount + fee}(); - } - - function _increaseFee(bytes32 transferId, uint256 fee) internal returns(uint32 tokenIndex) { - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.amount > 0 && lockInfo.tokenIndex < tokens.length, "lpBridgeBacking:invalid transferId"); - uint256 newFee = lockInfo.fee + fee; - require(newFee < MAX_TRANSFER_AMOUNT, "lpBridgeBacking:fee too large"); - lockInfos[transferId].fee = uint112(newFee); - tokenIndex = lockInfo.tokenIndex; - emit FeeUpdated(transferId, newFee); - } - - function increaseFee(bytes32 transferId, uint256 fee) external { - uint32 tokenIndex = _increaseFee(transferId, fee); - TokenInfo memory tokenInfo = tokens[tokenIndex]; - _safeTransferFrom(tokenInfo.localToken, msg.sender, address(this), fee); - } - - function increaseNativeFee(bytes32 transferId) external payable { - uint32 tokenIndex = _increaseFee(transferId, msg.value); - require(tokenIndex == wTokenIndex && wTokenIndex != INVALID_TOKEN_INDEX, "lpBridgeBacking:invalid token index"); - TokenInfo memory tokenInfo = tokens[tokenIndex]; - IWToken(tokenInfo.localToken).deposit{value: msg.value}(); - } - - // we require the same token to withdrawn - function _withdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver - ) internal { - require(transferIds.length > 0, "lpBridgeBacking:invalid transferIds size"); - uint32 tokenIndex = lockInfos[transferIds[0]].tokenIndex; - require(tokenIndex < tokens.length, "lpBridgeBacking:out of token size"); - uint256 amount = 0; - uint256 fee = 0; - for (uint i = 0; i < transferIds.length; i++) { - bytes32 transferId = transferIds[i]; - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.amount > 0 && lockInfo.tokenIndex < tokens.length, "lpBridgeBacking:invalid transferId"); - require(lockInfo.tokenIndex == tokenIndex, "lpBridgeBacking:invalid tokenindex"); - //can't delete lockInfos directly - lockInfos[transferId].tokenIndex = INVALID_TOKEN_INDEX; - amount += lockInfo.amount; - fee += lockInfo.fee; - emit LiquidityWithdrawn(transferId, receiver); - } - TokenInfo memory tokenInfo = tokens[tokenIndex]; - uint256 helixFee = transferIds.length * tokenInfo.helixFee; - if (helixFee > fee / 2) { - helixFee = fee / 2; - } - uint256 lpFee = amount + fee - helixFee; - if (withdrawNative && tokenIndex == wTokenIndex) { - IWToken(tokenInfo.localToken).withdraw(lpFee); - payable(receiver).transfer(lpFee); - } else { - _safeTransfer(tokenInfo.localToken, receiver, lpFee); - } - _safeTransfer(tokenInfo.localToken, feeReceiver, helixFee); - } - - function tokenLength() external view returns (uint) { - return tokens.length; - } -} - -// File contracts/mapping-token/v2/lp/interface/ILpBridgeBacking.sol -// License-Identifier: MIT - - -interface ILpBridgeBacking { - function withdrawLiquidity(bytes32[] memory hashes, bool withdrawNative, address liquidityProvider) external; -} - -// File contracts/mapping-token/v2/lp/base/LpBridgeIssuing.sol -// License-Identifier: MIT - - -contract LpBridgeIssuing is LpBridgeHelper { - mapping(bytes32 => address) public issuedMessages; - - event TransferRelayed(bytes32 transferId, address relayer); - - function relay( - uint256 nonce, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId, - bool issuingNative - ) payable external { - bytes32 transferId = keccak256(abi.encodePacked(nonce, issuingNative, token, sender, receiver, amount, sourceChainId, uint64(block.chainid))); - require(issuedMessages[transferId] == address(0), "lpBridgeIssuing:message exist"); - issuedMessages[transferId] = msg.sender; - if (issuingNative) { - require(msg.value == amount, "lpBridgeIssuing:invalid amount"); - payable(receiver).transfer(amount); - } else { - _safeTransferFrom(token, msg.sender, receiver, uint256(amount)); - } - emit TransferRelayed(transferId, msg.sender); - } - - // only lpProvider can request withdraw liquidity - function _encodeWithdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver) internal view returns(bytes memory) { - for (uint idx = 0; idx < transferIds.length; idx++) { - address lpProvider = issuedMessages[transferIds[idx]]; - require(lpProvider == msg.sender, "invalid lpProvider"); - } - return abi.encodeWithSelector(ILpBridgeBacking.withdrawLiquidity.selector, transferIds, withdrawNative, receiver); - } - - // we only allowed token sender or receiver cancel the transaction - function _cancelIssuing( - uint256 nonce, - bool issuingNative, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId - ) internal returns(bytes32 transferId) { - require(sender == msg.sender || receiver == msg.sender, "lpBridgeIssuing:only sender or receiver allowed"); - transferId = keccak256(abi.encodePacked(nonce, issuingNative, token, sender, receiver, amount, sourceChainId, uint64(block.chainid))); - if (issuedMessages[transferId] == msg.sender) { - return transferId; - } - require(issuedMessages[transferId] == address(0), "lpBridgeIssuing:message exist"); - issuedMessages[transferId] = msg.sender; - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/mapping-token/v2/lp/LpSub2EthBridge.sol -// License-Identifier: MIT - - - - - -contract LpSub2EthBridge is Initializable, LpAccessController, LpBridgeBacking, LpBridgeIssuing { - address public localEndpoint; - address public remoteEndpoint; - address public remoteBridge; - - event TransferCanceled(bytes32 transferId, address sender); - - receive() external payable {} - - modifier onlyEndpoint() { - require(localEndpoint == msg.sender, "LpSub2EthBridge:invalid endpoint"); - _; - } - - function fee() external view returns(uint256) { - return IHelixSub2EthMessageEndpoint(localEndpoint).fee(); - } - - function _sendMessage(bytes memory message, uint256 prepaid) internal returns(uint256) { - uint256 bridgeFee = IHelixSub2EthMessageEndpoint(localEndpoint).fee(); - require(prepaid >= bridgeFee, "backing:the fee is not enough"); - if (prepaid > bridgeFee) { - // refund fee to msgSender - payable(msg.sender).transfer(prepaid - bridgeFee); - } - uint256 transferId = IHelixSub2EthMessageEndpoint(localEndpoint).sendMessage{value: bridgeFee}( - remoteBridge, - message); - return transferId; - } - - function initialize(address _localEndpoint, address _remoteEndpoint, address dao) public initializer { - localEndpoint = _localEndpoint; - remoteEndpoint = _remoteEndpoint; - _initialize(dao); - _setFeeReceiver(dao); - } - - function updateMessageEndpoint(address _localEndpoint, address _remoteEndpoint) external onlyDao { - localEndpoint = _localEndpoint; - remoteEndpoint = _remoteEndpoint; - } - - function setwTokenIndex(uint32 _wTokenIndex) external onlyDao { - _setwTokenIndex(_wTokenIndex); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function updateHelixFee(uint32 _tokenIndex, uint112 _helixFee) external onlyDao { - _updateHelixFee(_tokenIndex, _helixFee); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - // backing mode called - function registerToken( - address local, - address remote, - uint112 helixFee, - uint32 remoteChainId, - uint8 localDecimals, - uint8 remoteDecimals, - bool remoteIsNative - ) external onlyDao { - _registerToken(local, remote, helixFee, remoteChainId, localDecimals, remoteDecimals, remoteIsNative); - } - - function withdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver - ) external onlyEndpoint whenNotPaused { - _withdrawLiquidity(transferIds, withdrawNative, receiver); - } - - function requestWithdrawLiquidity(bytes32[] memory transferIds, bool withdrawNative, address receiver) payable external whenNotPaused { - bytes memory withdrawCall = _encodeWithdrawLiquidity(transferIds, withdrawNative, receiver); - _sendMessage(withdrawCall, msg.value); - } - - function requestCancelIssuing( - uint256 nonce, - bool issuingNative, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId, - bool withdrawNative - ) payable external whenNotPaused { - bytes32 transferId = _cancelIssuing(nonce, issuingNative, token, sender, receiver, amount, sourceChainId); - bytes32[] memory transferIds = new bytes32[](1); - transferIds[0] = transferId; - // return token to the source sender - bytes memory withdrawCall = _encodeWithdrawLiquidity(transferIds, withdrawNative, sender); - _sendMessage(withdrawCall, msg.value); - emit TransferCanceled(transferId, msg.sender); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/lp/LpSub2SubBridge.sol b/helix-contract/flatten/lp/LpSub2SubBridge.sol deleted file mode 100644 index 4596d9f9..00000000 --- a/helix-contract/flatten/lp/LpSub2SubBridge.sol +++ /dev/null @@ -1,2002 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 2/10/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/mapping-token/interfaces/IHelixMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixMessageEndpoint { - function sendMessage(address receiver, bytes calldata encoded) external payable returns (uint256); -} - -// File contracts/mapping-token/interfaces/IHelixSub2SubMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixSub2SubMessageEndpoint is IHelixMessageEndpoint { - function fee() external view returns (uint256); - function lastDeliveredMessageId() external view returns (uint256); - function isMessageDelivered(uint256 messageId) external view returns(bool); - function sendMessage( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address receiver, - bytes calldata encoded) external payable returns (uint256); -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/lp/base/LpAccessController.sol -// License-Identifier: MIT - - -/// @title LpAccessController -/// @notice LpAccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LpAccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - modifier onlyDao() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "lpBridge:Bad dao role"); - _; - } - - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "lpBridge:Bad operator role"); - _; - } - - function _initialize(address dao) internal { - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, dao); - _setupRole(OPERATOR_ROLE, msg.sender); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File contracts/mapping-token/interfaces/IWToken.sol -// License-Identifier: MIT - - -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/mapping-token/v2/lp/base/LpBridgeHelper.sol -// License-Identifier: MIT - -contract LpBridgeHelper { - function _safeTransfer( - address token, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transfer.selector, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lpBridgeHelper:transfer token failed"); - } - - function _safeTransferFrom( - address token, - address sender, - address receiver, - uint256 amount - ) internal { - (bool success, bytes memory data) = token.call(abi.encodeWithSelector( - IERC20.transferFrom.selector, - sender, - receiver, - amount - )); - require(success && (data.length == 0 || abi.decode(data, (bool))), "lpBridgeHelper:transferFrom token failed"); - } -} - -// File contracts/mapping-token/v2/lp/base/LpBridgeBacking.sol -// License-Identifier: MIT - - - -/// @title LpBridgeBacking -/// @notice LpBridgeBacking is a contract to help user lock token and then trigger remote chain issuing -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract LpBridgeBacking is LpBridgeHelper { - uint256 constant public MAX_TRANSFER_AMOUNT = type(uint112).max; - uint32 constant public INVALID_TOKEN_INDEX = type(uint32).max; - // the registered token info - // localToken and remoteToken is the pair of erc20 token addresses - // helixFee is charged by system, if it's bigger then half of user's fee, descrease it to the half - // remoteChainId is the remote block.chainid - // remoteIsNative is true when the remoteToken is the remote wrapped native token - struct TokenInfo { - address localToken; - address remoteToken; - uint112 helixFee; - uint64 remoteChainId; - uint8 localDecimals; - uint8 remoteDecimals; - bool remoteIsNative; - } - // registered token info - TokenInfo[] public tokens; - // each time cross chain transfer, amount and fee can't be larger than type(uint112).max - struct LockInfo { - uint32 tokenIndex; - uint112 amount; - uint112 fee; - } - mapping(bytes32 => LockInfo) public lockInfos; - address public feeReceiver; - uint32 public wTokenIndex = INVALID_TOKEN_INDEX; - - event TokenLocked( - uint64 toChainId, - bool isNative, - bool issuingNative, - uint256 nonce, - bytes32 transferId, - address token, - uint112 amount, - uint112 fee, - address receiver); - event FeeUpdated(bytes32 transferId, uint256 fee); - event LiquidityWithdrawn(bytes32 transferId, address receiver); - - function _setFeeReceiver(address _feeReceiver) internal { - require(_feeReceiver != address(this), "lpBridgeBacking:invalid helix fee receiver"); - feeReceiver = _feeReceiver; - } - - function _updateHelixFee(uint32 _tokenIndex, uint112 _helixFee) internal { - require(_tokenIndex < tokens.length, "lpBridgeBacking:invalid token index"); - tokens[_tokenIndex].helixFee = _helixFee; - } - - function _setwTokenIndex(uint32 _wTokenIndex) internal { - wTokenIndex = _wTokenIndex; - } - - function _registerToken( - address localToken, - address remoteToken, - uint112 helixFee, - uint64 remoteChainId, - uint8 localDecimals, - uint8 remoteDecimals, - bool remoteIsNative - ) internal { - tokens.push(TokenInfo(localToken, remoteToken, helixFee, remoteChainId, localDecimals, remoteDecimals, remoteIsNative)); - } - - function _lockAndRemoteIssuing( - bool lockNative, - uint256 nonce, - uint32 tokenIndex, - uint112 amount, - uint112 fee, - address receiver, - bool issuingNative - ) internal returns(TokenInfo memory tokenInfo) { - tokenInfo = tokens[tokenIndex]; - require(fee > tokenInfo.helixFee && amount > 0, "lpBridgeBacking:fee or amount is not enough"); - require(!issuingNative || tokenInfo.remoteIsNative, "lpBridgeBacking:remote not native"); - uint256 remoteAmount = uint256(amount) * 10**tokenInfo.remoteDecimals / 10**tokenInfo.localDecimals; - require(remoteAmount < MAX_TRANSFER_AMOUNT, "lpBridgeBacking:overflow amount"); - bytes32 transferId = keccak256(abi.encodePacked( - nonce, - issuingNative, - tokenInfo.remoteToken, - msg.sender, - receiver, - uint112(remoteAmount), - uint64(block.chainid), - tokenInfo.remoteChainId)); - require(lockInfos[transferId].amount == 0, "lpBridgeBacking:transferId exist"); - lockInfos[transferId] = LockInfo(tokenIndex, amount, fee); - emit TokenLocked(tokenInfo.remoteChainId, lockNative, issuingNative, nonce, transferId, tokenInfo.localToken, amount, fee, receiver); - } - - function lockAndRemoteIssuing( - uint256 nonce, - address receiver, - uint112 amount, - uint112 fee, - uint32 tokenIndex, - bool issuingNative - ) external { - require(tokens.length > tokenIndex, "lpBridgeBacking:token not registered"); - TokenInfo memory info = _lockAndRemoteIssuing(false, nonce, tokenIndex, amount, fee, receiver, issuingNative); - _safeTransferFrom(info.localToken, msg.sender, address(this), amount + fee); - } - - function lockNativeAndRemoteIssuing( - uint112 amount, - uint112 fee, - address receiver, - uint256 nonce, - bool issuingNative - ) external payable { - require(amount + fee == msg.value, "lpBridgeBacking:amount unmatched"); - require(wTokenIndex != INVALID_TOKEN_INDEX, "lpBridgeBacking:not support"); - TokenInfo memory info = _lockAndRemoteIssuing(true, nonce, wTokenIndex, amount, fee, receiver, issuingNative); - IWToken(info.localToken).deposit{value: amount + fee}(); - } - - function _increaseFee(bytes32 transferId, uint256 fee) internal returns(uint32 tokenIndex) { - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.amount > 0 && lockInfo.tokenIndex < tokens.length, "lpBridgeBacking:invalid transferId"); - uint256 newFee = lockInfo.fee + fee; - require(newFee < MAX_TRANSFER_AMOUNT, "lpBridgeBacking:fee too large"); - lockInfos[transferId].fee = uint112(newFee); - tokenIndex = lockInfo.tokenIndex; - emit FeeUpdated(transferId, newFee); - } - - function increaseFee(bytes32 transferId, uint256 fee) external { - uint32 tokenIndex = _increaseFee(transferId, fee); - TokenInfo memory tokenInfo = tokens[tokenIndex]; - _safeTransferFrom(tokenInfo.localToken, msg.sender, address(this), fee); - } - - function increaseNativeFee(bytes32 transferId) external payable { - uint32 tokenIndex = _increaseFee(transferId, msg.value); - require(tokenIndex == wTokenIndex && wTokenIndex != INVALID_TOKEN_INDEX, "lpBridgeBacking:invalid token index"); - TokenInfo memory tokenInfo = tokens[tokenIndex]; - IWToken(tokenInfo.localToken).deposit{value: msg.value}(); - } - - // we require the same token to withdrawn - function _withdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver - ) internal { - require(transferIds.length > 0, "lpBridgeBacking:invalid transferIds size"); - uint32 tokenIndex = lockInfos[transferIds[0]].tokenIndex; - require(tokenIndex < tokens.length, "lpBridgeBacking:out of token size"); - uint256 amount = 0; - uint256 fee = 0; - for (uint i = 0; i < transferIds.length; i++) { - bytes32 transferId = transferIds[i]; - LockInfo memory lockInfo = lockInfos[transferId]; - require(lockInfo.amount > 0 && lockInfo.tokenIndex < tokens.length, "lpBridgeBacking:invalid transferId"); - require(lockInfo.tokenIndex == tokenIndex, "lpBridgeBacking:invalid tokenindex"); - //can't delete lockInfos directly - lockInfos[transferId].tokenIndex = INVALID_TOKEN_INDEX; - amount += lockInfo.amount; - fee += lockInfo.fee; - emit LiquidityWithdrawn(transferId, receiver); - } - TokenInfo memory tokenInfo = tokens[tokenIndex]; - uint256 helixFee = transferIds.length * tokenInfo.helixFee; - if (helixFee > fee / 2) { - helixFee = fee / 2; - } - uint256 lpFee = amount + fee - helixFee; - if (withdrawNative && tokenIndex == wTokenIndex) { - IWToken(tokenInfo.localToken).withdraw(lpFee); - payable(receiver).transfer(lpFee); - } else { - _safeTransfer(tokenInfo.localToken, receiver, lpFee); - } - _safeTransfer(tokenInfo.localToken, feeReceiver, helixFee); - } - - function tokenLength() external view returns (uint) { - return tokens.length; - } -} - -// File contracts/mapping-token/v2/lp/interface/ILpBridgeBacking.sol -// License-Identifier: MIT - - -interface ILpBridgeBacking { - function withdrawLiquidity(bytes32[] memory hashes, bool withdrawNative, address liquidityProvider) external; -} - -// File contracts/mapping-token/v2/lp/base/LpBridgeIssuing.sol -// License-Identifier: MIT - - -contract LpBridgeIssuing is LpBridgeHelper { - mapping(bytes32 => address) public issuedMessages; - - event TransferRelayed(bytes32 transferId, address relayer); - - function relay( - uint256 nonce, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId, - bool issuingNative - ) payable external { - bytes32 transferId = keccak256(abi.encodePacked(nonce, issuingNative, token, sender, receiver, amount, sourceChainId, uint64(block.chainid))); - require(issuedMessages[transferId] == address(0), "lpBridgeIssuing:message exist"); - issuedMessages[transferId] = msg.sender; - if (issuingNative) { - require(msg.value == amount, "lpBridgeIssuing:invalid amount"); - payable(receiver).transfer(amount); - } else { - _safeTransferFrom(token, msg.sender, receiver, uint256(amount)); - } - emit TransferRelayed(transferId, msg.sender); - } - - // only lpProvider can request withdraw liquidity - function _encodeWithdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver) internal view returns(bytes memory) { - for (uint idx = 0; idx < transferIds.length; idx++) { - address lpProvider = issuedMessages[transferIds[idx]]; - require(lpProvider == msg.sender, "invalid lpProvider"); - } - return abi.encodeWithSelector(ILpBridgeBacking.withdrawLiquidity.selector, transferIds, withdrawNative, receiver); - } - - // we only allowed token sender or receiver cancel the transaction - function _cancelIssuing( - uint256 nonce, - bool issuingNative, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId - ) internal returns(bytes32 transferId) { - require(sender == msg.sender || receiver == msg.sender, "lpBridgeIssuing:only sender or receiver allowed"); - transferId = keccak256(abi.encodePacked(nonce, issuingNative, token, sender, receiver, amount, sourceChainId, uint64(block.chainid))); - if (issuedMessages[transferId] == msg.sender) { - return transferId; - } - require(issuedMessages[transferId] == address(0), "lpBridgeIssuing:message exist"); - issuedMessages[transferId] = msg.sender; - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/mapping-token/v2/lp/LpSub2SubBridge.sol -// License-Identifier: MIT - - - - - -contract LpSub2SubBridge is Initializable, LpAccessController, LpBridgeBacking, LpBridgeIssuing { - address localEndpoint; - address remoteEndpoint; - address remoteBridge; - - event TransferCanceled(bytes32 transferId, address sender); - - receive() external payable {} - - modifier onlyEndpoint() { - require(localEndpoint == msg.sender, "LpSub2SubBridge:invalid endpoint"); - _; - } - - function fee() external view returns(uint256) { - return IHelixSub2SubMessageEndpoint(localEndpoint).fee(); - } - - function _sendMessage( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - bytes memory message, - uint256 prepaid) internal returns(uint256) { - uint256 bridgeFee = IHelixSub2SubMessageEndpoint(localEndpoint).fee(); - require(prepaid >= bridgeFee, "backing:the fee is not enough"); - if (prepaid > bridgeFee) { - // refund fee to msgSender - payable(msg.sender).transfer(prepaid - bridgeFee); - } - uint256 transferId = IHelixSub2SubMessageEndpoint(localEndpoint).sendMessage{value: bridgeFee}( - remoteSpecVersion, - remoteReceiveGasLimit, - remoteBridge, - message); - return transferId; - } - - function initialize(address _localEndpoint, address _remoteEndpoint, address dao) public initializer { - localEndpoint = _localEndpoint; - remoteEndpoint = _remoteEndpoint; - _initialize(dao); - _setFeeReceiver(dao); - } - - - function setwTokenIndex(uint32 _wTokenIndex) external onlyDao { - _setwTokenIndex(_wTokenIndex); - } - - function updateFeeReceiver(address _receiver) external onlyDao { - _setFeeReceiver(_receiver); - } - - function setRemoteBridge(address _remoteBridge) external onlyDao { - remoteBridge = _remoteBridge; - } - - // backing mode called - function registerToken( - address local, - address remote, - uint112 helixFee, - uint32 remoteChainId, - uint8 localDecimals, - uint8 remoteDecimals, - bool remoteIsNative - ) external onlyDao { - _registerToken(local, remote, helixFee, remoteChainId, localDecimals, remoteDecimals, remoteIsNative); - } - - function withdrawLiquidity( - bytes32[] memory transferIds, - bool withdrawNative, - address receiver - ) external onlyEndpoint whenNotPaused { - _withdrawLiquidity(transferIds, withdrawNative, receiver); - } - - function requestWithdrawLiquidity( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - bytes32[] memory transferIds, - bool withdrawNative, - address receiver) payable external whenNotPaused { - bytes memory withdrawCall = _encodeWithdrawLiquidity(transferIds, withdrawNative, receiver); - _sendMessage(remoteSpecVersion, remoteReceiveGasLimit, withdrawCall, msg.value); - } - - function requestCancelIssuing( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - uint256 nonce, - bool issuingNative, - address token, - address sender, - address receiver, - uint112 amount, - uint64 sourceChainId, - bool withdrawNative - ) payable external whenNotPaused { - bytes32 transferId = _cancelIssuing(nonce, issuingNative, token, sender, receiver, amount, sourceChainId); - bytes32[] memory transferIds = new bytes32[](1); - transferIds[0] = transferId; - // return token to the source sender - bytes memory withdrawCall = _encodeWithdrawLiquidity(transferIds, withdrawNative, sender); - _sendMessage(remoteSpecVersion, remoteReceiveGasLimit, withdrawCall, msg.value); - emit TransferCanceled(transferId, msg.sender); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/DarwiniaSub2EthMessageEndpoint.sol b/helix-contract/flatten/sub2eth/DarwiniaSub2EthMessageEndpoint.sol deleted file mode 100644 index 56d6e9ea..00000000 --- a/helix-contract/flatten/sub2eth/DarwiniaSub2EthMessageEndpoint.sol +++ /dev/null @@ -1,1324 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 5/12/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/mapping-token/interfaces/IInboundLane.sol -// License-Identifier: MIT - - -interface IInboundLane { - struct RelayersRange { - uint64 front; - uint64 back; - } - - struct InboundLaneNonce { - uint64 last_confirmed_nonce; - uint64 last_delivered_nonce; - RelayersRange relayer_range; - } - - function inboundLaneNonce() view external returns(InboundLaneNonce memory); - function encodeMessageKey(uint64 nonce) view external returns(uint256); -} - -// File contracts/mapping-token/interfaces/IOutboundLane.sol -// License-Identifier: MIT - - -interface IOutboundLane { - struct OutboundLaneNonce { - uint64 latest_received_nonce; - uint64 latest_generated_nonce; - uint64 oldest_unpruned_nonce; - } - - function outboundLaneNonce() view external returns(OutboundLaneNonce memory); - - function send_message(address targetContract, bytes calldata encoded) external payable returns (uint64); -} - -// File contracts/mapping-token/interfaces/ICrossChainFilter.sol -// License-Identifier: MIT - - -/** - * @title A interface for message layer to filter unsafe message - * @author echo - * @notice The app layer must implement the interface `ICrossChainFilter` - */ -interface ICrossChainFilter { - /** - * @notice Verify the source sender and payload of source chain messages, - * Generally, app layer cross-chain messages require validation of sourceAccount - * @param bridgedChainPosition The source chain position which send the message - * @param bridgedLanePosition The source lane position which send the message - * @param sourceAccount The source contract address which send the message - * @param payload The calldata which encoded by ABI Encoding - * @return Can call target contract if returns true - */ - function cross_chain_filter(uint32 bridgedChainPosition, uint32 bridgedLanePosition, address sourceAccount, bytes calldata payload) external view returns (bool); -} - -// File contracts/mapping-token/interfaces/IFeeMarket.sol -// License-Identifier: MIT - -pragma abicoder v2; - -interface IFeeMarket { - // Relayer which delivery the messages - struct DeliveredRelayer { - // relayer account - address relayer; - // encoded message key begin - uint256 begin; - // encoded message key end - uint256 end; - } - function market_fee() external view returns (uint256 fee); - - function assign(uint256 nonce) external payable returns(bool); - function settle(DeliveredRelayer[] calldata delivery_relayers, address confirm_relayer) external returns(bool); -} - -// File contracts/mapping-token/interfaces/IHelixApp.sol -// License-Identifier: MIT - - -interface IHelixAppSupportWithdrawFailed { - function handleUnlockFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; - function handleUnlockFailureFromRemoteNative( - uint256 messageId, - address sender, - uint256 amount - ) external; - function handleIssuingFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/AccessController.sol -// License-Identifier: MIT - - -contract AccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - bytes32 public constant CALLER_ROLE = keccak256("CALLER_ROLE"); - bytes32 public constant CALLEE_ROLE = keccak256("CALLEE_ROLE"); - - // access controller - // admin is helix Dao - modifier onlyAdmin() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "AccessController:Bad admin role"); - _; - } - - // operator - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "AccessController:Bad operator role"); - _; - } - - modifier onlyCaller() { - require(hasRole(CALLER_ROLE, msg.sender), "AccessController:Bad caller role"); - _; - } - - modifier onlyCallee() { - require(hasRole(CALLEE_ROLE, msg.sender), "AccessController:Bad callee role"); - _; - } - - function _initialize(address admin) internal { - _setRoleAdmin(CALLER_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(CALLEE_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, admin); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File contracts/mapping-token/v2/message-endpoints/DarwiniaSub2EthMessageEndpoint.sol -// License-Identifier: MIT - - - - - - -contract DarwiniaSub2EthMessageEndpoint is ICrossChainFilter, AccessController { - address immutable public inboundLane; - address immutable public outboundLane; - address immutable public feeMarket; - uint16 immutable public version; - - address public remoteEndpoint; - - constructor( - uint16 _version, - address _inboundLane, - address _outboundLane, - address _feeMarket - ) { - version = _version; - inboundLane = _inboundLane; - outboundLane = _outboundLane; - feeMarket = _feeMarket; - _initialize(msg.sender); - } - - modifier onlyInboundLane() { - require(inboundLane == msg.sender, "DarwiniaSub2EthMessageEndpoint:caller is not the inboundLane account"); - _; - } - - modifier onlyOutBoundLane() { - require(outboundLane == msg.sender, "DarwiniaSub2EthMessageEndpoint:caller is not the outboundLane account"); - _; - } - - function setRemoteEndpoint(address _remoteEndpoint) external onlyAdmin { - require(remoteEndpoint == address(0), "DarwiniaSub2EthMessageEndpoint:can only set once"); - remoteEndpoint = _remoteEndpoint; - } - - function cross_chain_filter( - uint32, - uint32, - address sourceAccount, - bytes calldata - ) external view returns (bool) { - return inboundLane == msg.sender && remoteEndpoint == sourceAccount; - } - - function fee() public view returns(uint256) { - return IFeeMarket(feeMarket).market_fee(); - } - - function nonceToMessageId(uint256 nonce) internal view returns(uint256) { - return (uint256(version) << 64) + nonce; - } - - function sendMessage(address receiver, bytes calldata message) external onlyCaller payable returns (uint256) { - bytes memory messageWithCaller = abi.encodeWithSelector( - DarwiniaSub2EthMessageEndpoint.recvMessage.selector, - receiver, - message - ); - uint256 nonce = IOutboundLane(outboundLane).send_message{value: msg.value}( - remoteEndpoint, - messageWithCaller - ); - return nonceToMessageId(nonce); - } - - function recvMessage( - address receiver, - bytes calldata message - ) external onlyInboundLane whenNotPaused { - require(hasRole(CALLEE_ROLE, receiver), "DarwiniaSub2EthMessageEndpoint:receiver is not callee"); - (bool result,) = receiver.call(message); - require(result, "DarwiniaSub2EthMessageEndpoint:call app failed"); - } - - // we use nonce as message id - function currentDeliveredMessageId() public view returns(uint256) { - IInboundLane.InboundLaneNonce memory inboundLaneNonce = IInboundLane(inboundLane).inboundLaneNonce(); - return nonceToMessageId(inboundLaneNonce.last_delivered_nonce + 1); - } - - function isMessageDelivered(uint256 messageId) public view returns (bool) { - IInboundLane.InboundLaneNonce memory inboundLaneNonce = IInboundLane(inboundLane).inboundLaneNonce(); - uint256 lastMessageNonce = inboundLaneNonce.last_delivered_nonce; - return messageId <= nonceToMessageId(lastMessageNonce); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/Erc20Sub2EthBacking.sol b/helix-contract/flatten/sub2eth/Erc20Sub2EthBacking.sol deleted file mode 100644 index afa7b8c4..00000000 --- a/helix-contract/flatten/sub2eth/Erc20Sub2EthBacking.sol +++ /dev/null @@ -1,2105 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 5/12/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/utils/DailyLimit.sol -// License-Identifier: MIT - - -/// @title relay with daily limit - Allows the relay to mint token in a daily limit. -contract DailyLimit { - - event DailyLimitChange(address token, uint dailyLimit); - - mapping(address => uint) public dailyLimit; - // deprecated, slot for upgrade - mapping(address => uint) public _slotReserved; - mapping(address => uint) public spentToday; - - uint constant public SPEND_BIT_LENGTH = 192; - uint constant public LASTDAY_BIT_LENGTH = 64; - - /// ==== Internal functions ==== - - /// @dev Contract constructor sets initial owners, required number of confirmations and daily mint limit. - /// @param _token Token address. - /// @param _dailyLimit Amount in wei, which can be mint without confirmations on a daily basis. - function _setDailyLimit(address _token, uint _dailyLimit) - internal - { - require(_dailyLimit < type(uint192).max, "DaliyLimit: overflow uint192"); - dailyLimit[_token] = _dailyLimit; - } - - /// @dev Allows to change the daily limit. - /// @param _token Token address. - /// @param _dailyLimit Amount in wei. - function _changeDailyLimit(address _token, uint _dailyLimit) - internal - { - require(_dailyLimit < type(uint192).max, "DaliyLimit: overflow uint192"); - dailyLimit[_token] = _dailyLimit; - emit DailyLimitChange(_token, _dailyLimit); - } - - /// @dev Allows to change the daily limit. - /// @param token Token address. - /// @param amount Amount in wei. - function expendDailyLimit(address token, uint amount) - internal - { - uint spentInfo = spentToday[token]; - uint lastday = spentInfo >> SPEND_BIT_LENGTH; - uint lastspent = spentInfo << LASTDAY_BIT_LENGTH >> LASTDAY_BIT_LENGTH; - if (block.timestamp > lastday + 24 hours) { - require(amount <= dailyLimit[token], "DailyLimit: amount exceed daily limit"); - spentToday[token] = (block.timestamp << SPEND_BIT_LENGTH) + amount; - return; - } - require(lastspent + amount <= dailyLimit[token] && amount <= dailyLimit[token], "DailyLimit: exceed daily limit"); - spentToday[token] = spentInfo + amount; - } - - /// ==== Web3 call functions ==== - - /// @dev Returns maximum withdraw amount. - /// @param token Token address. - /// @return Returns amount. - function calcMaxWithdraw(address token) - public - view - returns (uint) - { - uint spentInfo = spentToday[token]; - uint lastday = spentInfo >> SPEND_BIT_LENGTH; - uint lastspent = spentInfo << LASTDAY_BIT_LENGTH >> LASTDAY_BIT_LENGTH; - if (block.timestamp > lastday + 24 hours) { - return dailyLimit[token]; - } - - if (dailyLimit[token] < lastspent) { - return 0; - } - - return dailyLimit[token] - lastspent; - } -} - -// File contracts/mapping-token/interfaces/IHelixApp.sol -// License-Identifier: MIT - - -interface IHelixAppSupportWithdrawFailed { - function handleUnlockFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; - function handleUnlockFailureFromRemoteNative( - uint256 messageId, - address sender, - uint256 amount - ) external; - function handleIssuingFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; -} - -// File contracts/mapping-token/interfaces/IBacking.sol -// License-Identifier: MIT - - -interface IBacking { - function unlockFromRemote( - address originalToken, - address recipient, - uint256 amount) external; -} - -interface IBackingSupportNative { - function unlockFromRemoteNative( - address recipient, - uint256 amount) external; -} - -// File contracts/mapping-token/interfaces/IGuard.sol -// License-Identifier: MIT - - -interface IGuard { - function deposit(uint256 id, address token, address recipient, uint256 amount) external; -} - -// File contracts/mapping-token/interfaces/IErc20MappingTokenFactory.sol -// License-Identifier: MIT - - -interface IErc20MappingTokenFactory { - function newErc20Contract( - address originalToken, - string memory bridgedChainName, - string memory name, - string memory symbol, - uint8 decimals, - uint256 dailyLimit - ) external returns (address mappingToken); - function issueMappingToken( - address originalToken, - address recipient, - uint256 amount - ) external; -} - -// File contracts/mapping-token/interfaces/IHelixMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixMessageEndpoint { - function sendMessage(address receiver, bytes calldata encoded) external payable returns (uint256); -} - -// File contracts/mapping-token/interfaces/IHelixSub2EthMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixSub2EthMessageEndpoint is IHelixMessageEndpoint { - function fee() external view returns (uint256); - function currentDeliveredMessageId() external view returns (uint256); - function isMessageDelivered(uint256 messageId) external view returns (bool); -} - -// File contracts/mapping-token/interfaces/IWToken.sol -// License-Identifier: MIT - - -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/AccessController.sol -// License-Identifier: MIT - - -contract AccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - bytes32 public constant CALLER_ROLE = keccak256("CALLER_ROLE"); - bytes32 public constant CALLEE_ROLE = keccak256("CALLEE_ROLE"); - - // access controller - // admin is helix Dao - modifier onlyAdmin() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "AccessController:Bad admin role"); - _; - } - - // operator - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "AccessController:Bad operator role"); - _; - } - - modifier onlyCaller() { - require(hasRole(CALLER_ROLE, msg.sender), "AccessController:Bad caller role"); - _; - } - - modifier onlyCallee() { - require(hasRole(CALLEE_ROLE, msg.sender), "AccessController:Bad callee role"); - _; - } - - function _initialize(address admin) internal { - _setRoleAdmin(CALLER_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(CALLEE_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, admin); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/mapping-token/v2/Backing.sol -// License-Identifier: MIT - - -contract Backing is AccessController, Initializable { - address public messageEndpoint; - address public remoteMappingTokenFactory; - - uint256 internal locked; - modifier nonReentrant { - require(locked == 0, "backing: locked"); - locked = 1; - _; - locked = 0; - } - - modifier onlyMessageEndpoint() { - require(messageEndpoint == msg.sender, "Backing:Bad message handle"); - _; - } - - function initialize(address _messageEndpoint) public initializer { - messageEndpoint = _messageEndpoint; - _initialize(msg.sender); - } - - function _setMessageEndpoint(address _messageEndpoint) internal { - messageEndpoint = _messageEndpoint; - } - - function setRemoteMappingTokenFactory(address _remoteMappingTokenFactory) external onlyAdmin { - remoteMappingTokenFactory = _remoteMappingTokenFactory; - } -} - -// File @zeppelin-solidity/contracts/utils/structs/BitMaps.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/structs/BitMaps.sol) - -/** - * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. - * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. - */ -library BitMaps { - struct BitMap { - mapping(uint256 => uint256) _data; - } - - /** - * @dev Returns whether the bit at `index` is set. - */ - function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - return bitmap._data[bucket] & mask != 0; - } - - /** - * @dev Sets the bit at `index` to the boolean `value`. - */ - function setTo( - BitMap storage bitmap, - uint256 index, - bool value - ) internal { - if (value) { - set(bitmap, index); - } else { - unset(bitmap, index); - } - } - - /** - * @dev Sets the bit at `index`. - */ - function set(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] |= mask; - } - - /** - * @dev Unsets the bit at `index`. - */ - function unset(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] &= ~mask; - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthBacking.sol -// License-Identifier: MIT - - - - - - - - - - -contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { - struct LockedInfo { - bytes32 hash; - bool hasRefundForFailed; - } - address public guard; - uint256 public helixFee; - // This is the wrapped native token address - address public wToken; - - // (transferId => lockedInfo) - mapping(uint256 => LockedInfo) public lockedMessages; - BitMaps.BitMap unlockedTransferIds; - - event TokenLocked(uint256 transferId, bool isNative, address token, address sender, address recipient, uint256 amount, uint256 fee); - event TokenUnlocked(uint256 transferId, bool isNative, address token, address recipient, uint256 amount); - event RemoteIssuingFailure(uint256 refundId, uint256 transferId, address mappingToken, address originalSender, uint256 amount, uint256 fee); - event TokenUnlockedForFailed(uint256 transferId, bool isNative, address token, address recipient, uint256 amount); - - receive() external payable {} - - // !!! admin must check the nonce of the newEndpoint is larger than the old one - function setMessageEndpoint(address _messageEndpoint) external onlyAdmin { - _setMessageEndpoint(_messageEndpoint); - } - - function changeDailyLimit(address token, uint amount) public onlyAdmin { - _changeDailyLimit(token, amount); - } - - function setHelixFee(uint256 _helixFee) external onlyAdmin { - helixFee = _helixFee; - } - - function setNativeWrappedToken(address _wToken) external onlyAdmin { - wToken = _wToken; - } - - function updateGuard(address newGuard) external onlyAdmin { - guard = newGuard; - } - - function currentFee() external view returns(uint256) { - return IHelixSub2EthMessageEndpoint(messageEndpoint).fee() + helixFee; - } - - // we use messageId as transferId directly here - function _sendMessage(bytes memory message, uint256 prepaid) internal nonReentrant returns(uint256, uint256) { - uint256 bridgeFee = IHelixSub2EthMessageEndpoint(messageEndpoint).fee(); - uint256 totalFee = bridgeFee + helixFee; - require(prepaid >= totalFee, "backing:the fee is not enough"); - if (prepaid > totalFee) { - // refund fee to msgSender - payable(msg.sender).transfer(prepaid - totalFee); - } - uint256 transferId = IHelixSub2EthMessageEndpoint(messageEndpoint).sendMessage{value: bridgeFee}( - remoteMappingTokenFactory, - message); - return (transferId, totalFee); - } - - function _lockAndRemoteIssuing( - address token, - address recipient, - uint256 amount, - uint256 prepaid, - bool isNative - ) internal { - bytes memory issueMappingToken = abi.encodeWithSelector( - IErc20MappingTokenFactory.issueMappingToken.selector, - token, - recipient, - amount - ); - (uint256 transferId, uint256 fee) = _sendMessage(issueMappingToken, prepaid); - bytes32 lockMessageHash = hash(abi.encodePacked(transferId, token, msg.sender, amount)); - lockedMessages[transferId] = LockedInfo(lockMessageHash, false); - emit TokenLocked(transferId, isNative, token, msg.sender, recipient, amount, fee); - } - - /** - * @notice lock original token and issuing mapping token from bridged chain - * @dev maybe some tokens will take some fee when transfer - * @param token the original token address - * @param recipient the recipient who will receive the issued mapping token - * @param amount amount of the locked token - */ - function lockAndRemoteIssuing( - address token, - address recipient, - uint256 amount - ) external payable whenNotPaused { - uint256 balanceBefore = IERC20(token).balanceOf(address(this)); - require(IERC20(token).transferFrom(msg.sender, address(this), amount), "Backing:transfer tokens failed"); - uint256 balanceAfter = IERC20(token).balanceOf(address(this)); - require(balanceBefore + amount == balanceAfter, "Backing:Transfer amount is invalid"); - _lockAndRemoteIssuing(token, recipient, amount, msg.value, false); - } - - /** - * @notice lock original native token and issuing mapping token from bridged chain - * @dev maybe some tokens will take some fee when transfer - * @param recipient the recipient who will receive the issued mapping token - * @param amount amount of the locked token - */ - function lockAndRemoteIssuingNative( - address recipient, - uint256 amount - ) external payable whenNotPaused { - require(msg.value > amount, "Backing: msg.value must larger than amount"); - IWToken(wToken).deposit{value: amount}(); - _lockAndRemoteIssuing(wToken, recipient, amount, msg.value - amount, true); - } - - /** - * @notice this will be called by inboundLane when the remote mapping token burned and want to unlock the original token - * @param token the original token address - * @param recipient the recipient who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function unlockFromRemote( - address token, - address recipient, - uint256 amount - ) public onlyMessageEndpoint whenNotPaused { - uint256 transferId = IHelixSub2EthMessageEndpoint(messageEndpoint).currentDeliveredMessageId(); - require(BitMaps.get(unlockedTransferIds, transferId) == false, "Backing:message has been accepted"); - BitMaps.set(unlockedTransferIds, transferId); - expendDailyLimit(token, amount); - if (guard != address(0)) { - // see https://github.com/helix-bridge/contracts/issues/18 - uint allowance = IERC20(token).allowance(address(this), guard); - require(IERC20(token).approve(guard, allowance + amount), "Backing:approve token transfer to guard failed"); - IGuard(guard).deposit(transferId, token, recipient, amount); - } else { - require(IERC20(token).transfer(recipient, amount), "Backing:unlock transfer failed"); - } - emit TokenUnlocked(transferId, false, token, recipient, amount); - } - - /** - * @notice this will be called by inboundLane when the remote mapping token burned and want to unlock the original native token - * @param recipient the recipient who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function unlockFromRemoteNative( - address payable recipient, - uint256 amount - ) public onlyMessageEndpoint whenNotPaused { - expendDailyLimit(wToken, amount); - uint256 transferId = IHelixSub2EthMessageEndpoint(messageEndpoint).currentDeliveredMessageId(); - require(BitMaps.get(unlockedTransferIds, transferId) == false, "Backing:message has been accepted"); - BitMaps.set(unlockedTransferIds, transferId); - if (guard != address(0)) { - uint allowance = IERC20(wToken).allowance(address(this), guard); - require(IERC20(wToken).approve(guard, allowance + amount), "Backing:approve token transfer to guard failed"); - IGuard(guard).deposit(transferId, wToken, recipient, amount); - } else { - IWToken(wToken).withdraw(amount); - recipient.transfer(amount); - } - emit TokenUnlocked(transferId, true, wToken, recipient, amount); - } - - function remoteIssuingFailure( - uint256 transferId, - address mappingToken, - address originalSender, - uint256 amount - ) external payable whenNotPaused { - // must not exist in successful issue list - require(BitMaps.get(unlockedTransferIds, transferId) == false, "Backing:success message can't refund for failed"); - bool messageChecked = IHelixSub2EthMessageEndpoint(messageEndpoint).isMessageDelivered(transferId); - require(messageChecked, "Backing:the message is not checked by message layer"); - bytes memory unlockForFailed = abi.encodeWithSelector( - IHelixAppSupportWithdrawFailed.handleIssuingFailureFromRemote.selector, - transferId, - mappingToken, - originalSender, - amount - ); - (uint256 refundId, uint256 fee) = _sendMessage(unlockForFailed, msg.value); - emit RemoteIssuingFailure(refundId, transferId, mappingToken, originalSender, amount, fee); - } - - /** - * @notice this will be called by messageEndpoint when the remote issue failed and want to unlock the original token - * @param token the original token address - * @param originSender the origin sender who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function _handleUnlockFailureFromRemote( - uint256 transferId, - address token, - address originSender, - uint256 amount - ) internal whenNotPaused { - LockedInfo memory lockedMessage = lockedMessages[transferId]; - require(lockedMessage.hasRefundForFailed == false, "Backing: the locked message has been refund"); - bytes32 messageHash = hash(abi.encodePacked(transferId, token, originSender, amount)); - require(lockedMessage.hash == messageHash, "Backing: message is not matched"); - lockedMessages[transferId].hasRefundForFailed = true; - } - - /** - * @notice this will be called by messageEndpoint when the remote issue failed and want to unlock the original token - * @param token the original token address - * @param originSender the origin sender who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function handleUnlockFailureFromRemote( - uint256 transferId, - address token, - address originSender, - uint256 amount - ) external onlyMessageEndpoint { - _handleUnlockFailureFromRemote(transferId, token, originSender, amount); - require(IERC20(token).transfer(originSender, amount), "Backing:unlock transfer failed"); - emit TokenUnlockedForFailed(transferId, false, token, originSender, amount); - } - - /** - * @notice this will be called by messageEndpoint when the remote issue failed and want to unlock the original token - * @param originSender the origin sender who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function handleUnlockFailureFromRemoteNative( - uint256 transferId, - address originSender, - uint256 amount - ) external onlyMessageEndpoint { - _handleUnlockFailureFromRemote(transferId, wToken, originSender, amount); - IWToken(wToken).withdraw(amount); - payable(originSender).transfer(amount); - emit TokenUnlockedForFailed(transferId, true, wToken, originSender, amount); - } - - /** - * @notice this should not be used unless there is a non-recoverable error in the bridge or the target chain - * we use this to protect user's asset from being locked up - */ - function rescueFunds( - address token, - address recipient, - uint256 amount - ) external onlyAdmin { - IERC20(token).transfer(recipient, amount); - } - - - function hash(bytes memory value) public pure returns (bytes32) { - return sha256(value); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/Erc20Sub2EthMappingTokenFactory.sol b/helix-contract/flatten/sub2eth/Erc20Sub2EthMappingTokenFactory.sol deleted file mode 100644 index ddafb873..00000000 --- a/helix-contract/flatten/sub2eth/Erc20Sub2EthMappingTokenFactory.sol +++ /dev/null @@ -1,2593 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 5/12/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/utils/DailyLimit.sol -// License-Identifier: MIT - - -/// @title relay with daily limit - Allows the relay to mint token in a daily limit. -contract DailyLimit { - - event DailyLimitChange(address token, uint dailyLimit); - - mapping(address => uint) public dailyLimit; - // deprecated, slot for upgrade - mapping(address => uint) public _slotReserved; - mapping(address => uint) public spentToday; - - uint constant public SPEND_BIT_LENGTH = 192; - uint constant public LASTDAY_BIT_LENGTH = 64; - - /// ==== Internal functions ==== - - /// @dev Contract constructor sets initial owners, required number of confirmations and daily mint limit. - /// @param _token Token address. - /// @param _dailyLimit Amount in wei, which can be mint without confirmations on a daily basis. - function _setDailyLimit(address _token, uint _dailyLimit) - internal - { - require(_dailyLimit < type(uint192).max, "DaliyLimit: overflow uint192"); - dailyLimit[_token] = _dailyLimit; - } - - /// @dev Allows to change the daily limit. - /// @param _token Token address. - /// @param _dailyLimit Amount in wei. - function _changeDailyLimit(address _token, uint _dailyLimit) - internal - { - require(_dailyLimit < type(uint192).max, "DaliyLimit: overflow uint192"); - dailyLimit[_token] = _dailyLimit; - emit DailyLimitChange(_token, _dailyLimit); - } - - /// @dev Allows to change the daily limit. - /// @param token Token address. - /// @param amount Amount in wei. - function expendDailyLimit(address token, uint amount) - internal - { - uint spentInfo = spentToday[token]; - uint lastday = spentInfo >> SPEND_BIT_LENGTH; - uint lastspent = spentInfo << LASTDAY_BIT_LENGTH >> LASTDAY_BIT_LENGTH; - if (block.timestamp > lastday + 24 hours) { - require(amount <= dailyLimit[token], "DailyLimit: amount exceed daily limit"); - spentToday[token] = (block.timestamp << SPEND_BIT_LENGTH) + amount; - return; - } - require(lastspent + amount <= dailyLimit[token] && amount <= dailyLimit[token], "DailyLimit: exceed daily limit"); - spentToday[token] = spentInfo + amount; - } - - /// ==== Web3 call functions ==== - - /// @dev Returns maximum withdraw amount. - /// @param token Token address. - /// @return Returns amount. - function calcMaxWithdraw(address token) - public - view - returns (uint) - { - uint spentInfo = spentToday[token]; - uint lastday = spentInfo >> SPEND_BIT_LENGTH; - uint lastspent = spentInfo << LASTDAY_BIT_LENGTH >> LASTDAY_BIT_LENGTH; - if (block.timestamp > lastday + 24 hours) { - return dailyLimit[token]; - } - - if (dailyLimit[token] < lastspent) { - return 0; - } - - return dailyLimit[token] - lastspent; - } -} - -// File contracts/mapping-token/interfaces/IBacking.sol -// License-Identifier: MIT - - -interface IBacking { - function unlockFromRemote( - address originalToken, - address recipient, - uint256 amount) external; -} - -interface IBackingSupportNative { - function unlockFromRemoteNative( - address recipient, - uint256 amount) external; -} - -// File contracts/mapping-token/interfaces/IHelixApp.sol -// License-Identifier: MIT - - -interface IHelixAppSupportWithdrawFailed { - function handleUnlockFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; - function handleUnlockFailureFromRemoteNative( - uint256 messageId, - address sender, - uint256 amount - ) external; - function handleIssuingFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; -} - -// File contracts/mapping-token/interfaces/IHelixMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixMessageEndpoint { - function sendMessage(address receiver, bytes calldata encoded) external payable returns (uint256); -} - -// File contracts/mapping-token/interfaces/IHelixSub2EthMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixSub2EthMessageEndpoint is IHelixMessageEndpoint { - function fee() external view returns (uint256); - function currentDeliveredMessageId() external view returns (uint256); - function isMessageDelivered(uint256 messageId) external view returns (bool); -} - -// File contracts/mapping-token/interfaces/IGuard.sol -// License-Identifier: MIT - - -interface IGuard { - function deposit(uint256 id, address token, address recipient, uint256 amount) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/AccessController.sol -// License-Identifier: MIT - - -contract AccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - bytes32 public constant CALLER_ROLE = keccak256("CALLER_ROLE"); - bytes32 public constant CALLEE_ROLE = keccak256("CALLEE_ROLE"); - - // access controller - // admin is helix Dao - modifier onlyAdmin() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "AccessController:Bad admin role"); - _; - } - - // operator - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "AccessController:Bad operator role"); - _; - } - - modifier onlyCaller() { - require(hasRole(CALLER_ROLE, msg.sender), "AccessController:Bad caller role"); - _; - } - - modifier onlyCallee() { - require(hasRole(CALLEE_ROLE, msg.sender), "AccessController:Bad callee role"); - _; - } - - function _initialize(address admin) internal { - _setRoleAdmin(CALLER_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(CALLEE_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, admin); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/access/Ownable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) - - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor() { - _transferOwnership(_msgSender()); - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if the sender is not the owner. - */ - function _checkOwner() internal view virtual { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - address oldOwner = _owner; - _owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/mapping-token/v2/MappingTokenFactory.sol -// License-Identifier: MIT -// This is the Issuing Module(Mapping-token-factory) of the ethereum like bridge. -// We trust the inboundLane/outboundLane when we add them to the module. -// It means that each message from the inboundLane is verified correct and truthly from the sourceAccount. -// Only we need is to verify the sourceAccount is expected. And we add it to the Filter. - - - -contract MappingTokenFactory is AccessController, Initializable { - address[] public allMappingTokens; - // salt=>mappingToken, the salt is derived from origin token on backing chain - // so this is a mapping from origin to mapping token - mapping(bytes32 => address) public salt2MappingToken; - // mappingToken=>info the info is the original token info - // so this is a mapping from mappingToken to original token - mapping(address => address) public mappingToken2OriginalToken; - - address public messageEndpoint; - address public remoteBacking; - - uint256 internal locked; - modifier nonReentrant { - require(locked == 0, "MappingTokenFactory: locked"); - locked = 1; - _; - locked = 0; - } - - modifier onlyMessageEndpoint() { - require(messageEndpoint == msg.sender, "MappingTokenFactory:Bad message handle"); - _; - } - - function initialize(address _messageEndpoint) public initializer { - _setMessageEndpoint(_messageEndpoint); - _initialize(msg.sender); - } - - function _setMessageEndpoint(address _messageEndpoint) internal { - messageEndpoint = _messageEndpoint; - } - - function setRemoteBacking(address _remoteBacking) external onlyAdmin { - remoteBacking = _remoteBacking; - } - - function _transferMappingTokenOwnership(address mappingToken, address new_owner) internal { - Ownable(mappingToken).transferOwnership(new_owner); - } - - /** - * @notice add mapping-token address by owner - * @param salt the salt of the mapping token deployed - * @param originalToken the original token address - * @param mappingToken the mapping token address - */ - function _addMappingToken( - bytes32 salt, - address originalToken, - address mappingToken - ) internal { - // save the mapping tokens in an array so it can be listed - allMappingTokens.push(mappingToken); - // map the originToken to mappingInfo - salt2MappingToken[salt] = mappingToken; - // map the mappingToken to origin info - mappingToken2OriginalToken[mappingToken] = originalToken; - } - - // internal - function _deploy(bytes32 salt, bytes memory bytecodeWithInitdata) internal returns (address addr) { - assembly { - addr := create2(0, add(bytecodeWithInitdata, 0x20), mload(bytecodeWithInitdata), salt) - if iszero(extcodesize(addr)) { revert(0, 0) } - } - } - - function tokenLength() public view returns (uint) { - return allMappingTokens.length; - } - - function getMappingToken(address backingAddress, address originalToken) public view returns (address) { - bytes32 salt = keccak256(abi.encodePacked(backingAddress, originalToken)); - return salt2MappingToken[salt]; - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/math/SafeMath.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) - - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the subtraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} - -// File contracts/mapping-token/v2/erc20-mapping-protocol/Erc20.sol -// License-Identifier: MIT - - - -contract Erc20 is IERC20, Ownable { - using SafeMath for uint256; - - mapping (address => uint256) private _balances; - - mapping (address => mapping (address => uint256)) private _allowances; - - uint256 private _totalSupply; - - string public name; - string public symbol; - uint8 public decimals; - - constructor(string memory _name, string memory _symbol, uint8 _decimals) { - name = _name; - symbol = _symbol; - decimals = _decimals; - _transferOwnership(_msgSender()); - } - - function totalSupply() public view override returns (uint256) { - return _totalSupply; - } - - function balanceOf(address account) public view override returns (uint256) { - return _balances[account]; - } - - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(msg.sender, recipient, amount); - return true; - } - - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - function approve(address spender, uint256 amount) public virtual override returns (bool) { - _approve(msg.sender, spender, amount); - return true; - } - - function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(sender, recipient, amount); - _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance")); - return true; - } - - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); - return true; - } - - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); - return true; - } - - function _transfer(address sender, address recipient, uint256 amount) internal virtual { - require(sender != address(0), "ERC20: transfer from the zero address"); - require(recipient != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(sender, recipient, amount); - - _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); - _balances[recipient] = _balances[recipient].add(amount); - emit Transfer(sender, recipient, amount); - } - - // only factory contract can mint with the lock proof from ethereum - function mint(address account, uint256 amount) external onlyOwner { - _mint(account, amount); - } - - function burn(address account, uint256 amount) external { - if (account != msg.sender && owner() != msg.sender && _allowances[account][msg.sender] != type(uint256).max) { - _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount, "ERC20: decreased allowance below zero")); - } - _burn(account, amount); - } - - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _beforeTokenTransfer(address(0), account, amount); - - _totalSupply = _totalSupply.add(amount); - _balances[account] = _balances[account].add(amount); - emit Transfer(address(0), account, amount); - } - - function _burn(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: burn from the zero address"); - - _beforeTokenTransfer(account, address(0), amount); - - _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); - _totalSupply = _totalSupply.sub(amount); - emit Transfer(account, address(0), amount); - } - - function _approve(address owner, address spender, uint256 amount) internal virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } -} - -// File @zeppelin-solidity/contracts/utils/structs/BitMaps.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/structs/BitMaps.sol) - -/** - * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. - * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. - */ -library BitMaps { - struct BitMap { - mapping(uint256 => uint256) _data; - } - - /** - * @dev Returns whether the bit at `index` is set. - */ - function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - return bitmap._data[bucket] & mask != 0; - } - - /** - * @dev Sets the bit at `index` to the boolean `value`. - */ - function setTo( - BitMap storage bitmap, - uint256 index, - bool value - ) internal { - if (value) { - set(bitmap, index); - } else { - unset(bitmap, index); - } - } - - /** - * @dev Sets the bit at `index`. - */ - function set(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] |= mask; - } - - /** - * @dev Unsets the bit at `index`. - */ - function unset(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] &= ~mask; - } -} - -// File contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthMappingTokenFactory.sol -// License-Identifier: MIT -// This is the Issuing Module(Mapping-token-factory) of the ethereum like bridge. -// We trust the inboundLane/outboundLane when we add them to the module. -// It means that each message from the inboundLane is verified correct and truthly from the sourceAccount. -// Only we need is to verify the sourceAccount is expected. And we add it to the Filter. - - - - - - - - -contract Erc20Sub2EthMappingTokenFactory is DailyLimit, MappingTokenFactory { - struct BurnInfo { - bytes32 hash; - bool hasRefundForFailed; - } - // guard - address public guard; - uint256 public helixFee; - address xwToken; - - mapping(uint256 => BurnInfo) burnMessages; - BitMaps.BitMap issueMessages; - - event IssuingERC20Created(address originalToken, address mappingToken); - event BurnAndRemoteUnlocked(uint256 transferId, bool isNative, address sender, address recipient, address token, uint256 amount, uint256 fee); - event TokenRemintForFailed(uint256 transferId, address token, address recipient, uint256 amount); - event RemoteUnlockFailure(uint256 refundId, uint256 transferId, address originalToken, address originalSender, uint256 amount, uint256 fee); - - receive() external payable {} - - modifier verifyRemoteUnlockFailure(uint256 transferId) { - // must not exist in successful issue list - require(BitMaps.get(issueMessages, transferId) == false, "MappingTokenFactory:success message can't refund for failed"); - // must has been checked by message layer - bool messageChecked = IHelixSub2EthMessageEndpoint(messageEndpoint).isMessageDelivered(transferId); - require(messageChecked, "MappingTokenFactory:the message is not checked by message layer"); - _; - } - - /** - * @notice only admin can transfer the ownership of the mapping token from factory to other account - * generally we should not do this. When we encounter a non-recoverable error, we temporarily transfer the privileges to a maintenance account. - * @param mappingToken the address the mapping token - * @param new_owner the new owner of the mapping token - */ - function transferMappingTokenOwnership(address mappingToken, address new_owner) external onlyAdmin { - _transferMappingTokenOwnership(mappingToken, new_owner); - } - - function updateGuard(address newGuard) external onlyAdmin { - guard = newGuard; - } - - function changeDailyLimit(address mappingToken, uint amount) public onlyAdmin { - _changeDailyLimit(mappingToken, amount); - } - - // !!! admin must check the nonce of the newEndpoint is larger than the old one - function setMessageEndpoint(address _messageEndpoint) external onlyAdmin { - _setMessageEndpoint(_messageEndpoint); - } - - function setMappingNativeWrappedToken(address _xwToken) external onlyAdmin { - xwToken = _xwToken; - } - - function currentFee() external view returns(uint256) { - return IHelixSub2EthMessageEndpoint(messageEndpoint).fee() + helixFee; - } - - function _sendMessage(bytes memory message) internal nonReentrant returns(uint256, uint256) { - uint256 bridgeFee = IHelixSub2EthMessageEndpoint(messageEndpoint).fee(); - uint256 totalFee = bridgeFee + helixFee; - require(msg.value >= totalFee, "MappingTokenFactory:the fee is not enough"); - if (msg.value > totalFee) { - payable(msg.sender).transfer(msg.value - totalFee); - } - uint256 transferId = IHelixSub2EthMessageEndpoint(messageEndpoint).sendMessage{value: bridgeFee}( - remoteBacking, - message); - return (transferId, totalFee); - } - - /** - * @notice create new erc20 mapping contract, this can only be called by operator - * @param originalToken the original token address - * @param name the name of the original erc20 token - * @param symbol the symbol of the original erc20 token - * @param decimals the decimals of the original erc20 token - */ - function register( - address originalToken, - string memory bridgedChainName, - string memory name, - string memory symbol, - uint8 decimals, - uint256 dailyLimit - ) public onlyOperator whenNotPaused returns (address mappingToken) { - bytes32 salt = keccak256(abi.encodePacked(remoteBacking, originalToken)); - require(salt2MappingToken[salt] == address(0), "MappingTokenFactory:contract has been deployed"); - bytes memory bytecode = type(Erc20).creationCode; - bytes memory bytecodeWithInitdata = abi.encodePacked( - bytecode, - abi.encode( - string(abi.encodePacked(name, "[", bridgedChainName, ">")), - string(abi.encodePacked("x", symbol)), - decimals - )); - mappingToken = _deploy(salt, bytecodeWithInitdata); - _addMappingToken(salt, originalToken, mappingToken); - _changeDailyLimit(mappingToken, dailyLimit); - emit IssuingERC20Created(originalToken, mappingToken); - } - - /** - * @notice set erc20 mapping contract directly, this can be only called by admin - * @param originalToken the original token address - * @param mappingToken the mapping token address of the original erc20 token - * @param dailyLimit the daily limit of the mapping erc20 token - */ - function setMappingToken( - address originalToken, - address mappingToken, - uint256 dailyLimit - ) public onlyAdmin { - bytes32 salt = keccak256(abi.encodePacked(remoteBacking, originalToken)); - require(salt2MappingToken[salt] == address(0), "MappingTokenFactory:contract has been deployed"); - _addMappingToken(salt, originalToken, mappingToken); - _changeDailyLimit(mappingToken, dailyLimit); - emit IssuingERC20Created(originalToken, mappingToken); - } - - /** - * @notice issue mapping token, only can be called by inboundLane - * @param originalToken the original token address - * @param recipient the recipient of the issued mapping token - * @param amount the amount of the issued mapping token - */ - function issueMappingToken( - address originalToken, - address recipient, - uint256 amount - ) public onlyMessageEndpoint whenNotPaused { - address mappingToken = getMappingToken(remoteBacking, originalToken); - require(mappingToken != address(0), "MappingTokenFactory:mapping token has not created"); - require(amount > 0, "MappingTokenFactory:can not receive amount zero"); - uint256 transferId = IHelixSub2EthMessageEndpoint(messageEndpoint).currentDeliveredMessageId(); - expendDailyLimit(mappingToken, amount); - require(BitMaps.get(issueMessages, transferId) == false, "MappingTokenFactory:message has been accepted"); - BitMaps.set(issueMessages, transferId); - if (guard != address(0)) { - Erc20(mappingToken).mint(address(this), amount); - uint allowance = IERC20(mappingToken).allowance(address(this), guard); - require(IERC20(mappingToken).approve(guard, allowance + amount), "Backing:approve token transfer to guard failed"); - IGuard(guard).deposit(transferId, mappingToken, recipient, amount); - } else { - Erc20(mappingToken).mint(recipient, amount); - } - } - - function _burnAndRemoteUnlock( - address mappingToken, - address recipient, - uint256 amount, - bytes memory remoteUnlockCall, - bool isNative - ) internal whenNotPaused { - require(amount > 0, "MappingTokenFactory:can not transfer amount zero"); - // transfer to this and then burn - require(IERC20(mappingToken).transferFrom(msg.sender, address(this), amount), "MappingTokenFactory:transfer token failed"); - Erc20(mappingToken).burn(address(this), amount); - (uint256 transferId, uint256 fee) = _sendMessage(remoteUnlockCall); - require(burnMessages[transferId].hash == bytes32(0), "MappingTokenFactory: message exist"); - bytes32 messageHash = hash(abi.encodePacked(transferId, mappingToken, msg.sender, amount)); - burnMessages[transferId] = BurnInfo(messageHash, false); - emit BurnAndRemoteUnlocked(transferId, isNative, msg.sender, recipient, mappingToken, amount, fee); - } - - /** - * @notice burn mapping token and unlock remote original native token - * @param recipient the recipient of the remote unlocked token - * @param amount the amount of the burn and unlock - */ - function burnAndRemoteUnlockNative( - address recipient, - uint256 amount - ) external payable { - require(amount > 0, "MappingTokenFactory:can not transfer amount zero"); - address originalToken = mappingToken2OriginalToken[xwToken]; - require(originalToken != address(0), "MappingTokenFactory:token is not created by factory"); - bytes memory unlockFromRemoteNative = abi.encodeWithSelector( - IBackingSupportNative.unlockFromRemoteNative.selector, - recipient, - amount - ); - - _burnAndRemoteUnlock(xwToken, recipient, amount, unlockFromRemoteNative, true); - } - - /** - * @notice burn mapping token and unlock remote original token - * @param mappingToken the burt mapping token address - * @param recipient the recipient of the remote unlocked token - * @param amount the amount of the burn and unlock - */ - function burnAndRemoteUnlock( - address mappingToken, - address recipient, - uint256 amount - ) external payable { - require(amount > 0, "MappingTokenFactory:can not transfer amount zero"); - address originalToken = mappingToken2OriginalToken[mappingToken]; - require(originalToken != address(0), "MappingTokenFactory:token is not created by factory"); - bytes memory unlockFromRemote = abi.encodeWithSelector( - IBacking.unlockFromRemote.selector, - originalToken, - recipient, - amount - ); - - _burnAndRemoteUnlock(mappingToken, recipient, amount, unlockFromRemote, false); - } - - /** - * @notice send a unlock message to backing when issue mapping token faild to redeem original token. - * @param originalToken the original token address - * @param originalSender the originalSender of the remote unlocked token, must be the same as msg.send of the failed message. - * @param amount the amount of the failed issue token. - */ - function remoteUnlockFailure( - uint256 transferId, - address originalToken, - address originalSender, - uint256 amount - ) external payable verifyRemoteUnlockFailure(transferId) whenNotPaused { - bytes memory handleUnlockForFailed = abi.encodeWithSelector( - IHelixAppSupportWithdrawFailed.handleUnlockFailureFromRemote.selector, - transferId, - originalToken, - originalSender, - amount - ); - (uint256 refundId, uint256 fee) = _sendMessage(handleUnlockForFailed); - emit RemoteUnlockFailure(refundId, transferId, originalToken, originalSender, amount, fee); - } - - /** - * @notice send a unlock message to backing when issue mapping token faild to redeem original token. - * @param originalSender the originalSender of the remote unlocked token, must be the same as msg.send of the failed message. - * @param amount the amount of the failed issue token. - */ - function remoteUnlockFailureNative( - uint256 transferId, - address originalSender, - uint256 amount - ) external payable verifyRemoteUnlockFailure(transferId) whenNotPaused { - bytes memory handleUnlockForFailedNative = abi.encodeWithSelector( - IHelixAppSupportWithdrawFailed.handleUnlockFailureFromRemoteNative.selector, - transferId, - originalSender, - amount - ); - (uint256 refundId, uint256 fee) = _sendMessage(handleUnlockForFailedNative); - emit RemoteUnlockFailure(refundId, transferId, xwToken, originalSender, amount, fee); - } - - /** - * @notice this will be called by messageEndpoint when the remote backing unlock failed and want to unlock the mapping token - * @param token the original token address - * @param origin_sender the origin_sender who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function handleIssuingFailureFromRemote( - uint256 transferId, - address token, - address origin_sender, - uint256 amount - ) external onlyMessageEndpoint whenNotPaused { - BurnInfo memory burnInfo = burnMessages[transferId]; - require(burnInfo.hasRefundForFailed == false, "Backing:the burn message has been refund"); - bytes32 messageHash = hash(abi.encodePacked(transferId, token, origin_sender, amount)); - require(burnInfo.hash == messageHash, "Backing:message is not matched"); - burnMessages[transferId].hasRefundForFailed = true; - Erc20(token).mint(origin_sender, amount); - emit TokenRemintForFailed(transferId, token, origin_sender, amount); - } - - function hash(bytes memory value) public pure returns (bytes32) { - return sha256(value); - } - - function rescueFunds( - address token, - address recipient, - uint256 amount - ) external onlyAdmin { - IERC20(token).transfer(recipient, amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/Guard.sol b/helix-contract/flatten/sub2eth/Guard.sol deleted file mode 100644 index e24d5b73..00000000 --- a/helix-contract/flatten/sub2eth/Guard.sol +++ /dev/null @@ -1,1207 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 5/12/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/mapping-token/interfaces/IWToken.sol -// License-Identifier: MIT - - -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/cryptography/ECDSA.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.3) (utils/cryptography/ECDSA.sol) - - -/** - * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. - * - * These functions can be used to verify that a message was signed by the holder - * of the private keys of a given address. - */ -library ECDSA { - enum RecoverError { - NoError, - InvalidSignature, - InvalidSignatureLength, - InvalidSignatureS, - InvalidSignatureV - } - - function _throwError(RecoverError error) private pure { - if (error == RecoverError.NoError) { - return; // no error: do nothing - } else if (error == RecoverError.InvalidSignature) { - revert("ECDSA: invalid signature"); - } else if (error == RecoverError.InvalidSignatureLength) { - revert("ECDSA: invalid signature length"); - } else if (error == RecoverError.InvalidSignatureS) { - revert("ECDSA: invalid signature 's' value"); - } else if (error == RecoverError.InvalidSignatureV) { - revert("ECDSA: invalid signature 'v' value"); - } - } - - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature` or error string. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - * - * Documentation for signature generation: - * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] - * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] - * - * _Available since v4.3._ - */ - function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { - if (signature.length == 65) { - bytes32 r; - bytes32 s; - uint8 v; - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - /// @solidity memory-safe-assembly - assembly { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - } - return tryRecover(hash, v, r, s); - } else { - return (address(0), RecoverError.InvalidSignatureLength); - } - } - - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature`. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - */ - function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, signature); - _throwError(error); - return recovered; - } - - /** - * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. - * - * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] - * - * _Available since v4.3._ - */ - function tryRecover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address, RecoverError) { - bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - uint8 v = uint8((uint256(vs) >> 255) + 27); - return tryRecover(hash, v, r, s); - } - - /** - * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. - * - * _Available since v4.2._ - */ - function recover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, r, vs); - _throwError(error); - return recovered; - } - - /** - * @dev Overload of {ECDSA-tryRecover} that receives the `v`, - * `r` and `s` signature fields separately. - * - * _Available since v4.3._ - */ - function tryRecover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address, RecoverError) { - // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature - // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines - // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most - // signatures from current libraries generate a unique signature with an s-value in the lower half order. - // - // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value - // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or - // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept - // these malleable signatures as well. - if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { - return (address(0), RecoverError.InvalidSignatureS); - } - if (v != 27 && v != 28) { - return (address(0), RecoverError.InvalidSignatureV); - } - - // If the signature is valid (and not malleable), return the signer address - address signer = ecrecover(hash, v, r, s); - if (signer == address(0)) { - return (address(0), RecoverError.InvalidSignature); - } - - return (signer, RecoverError.NoError); - } - - /** - * @dev Overload of {ECDSA-recover} that receives the `v`, - * `r` and `s` signature fields separately. - */ - function recover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, v, r, s); - _throwError(error); - return recovered; - } - - /** - * @dev Returns an Ethereum Signed Message, created from a `hash`. This - * produces hash corresponding to the one signed with the - * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] - * JSON-RPC method as part of EIP-191. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { - // 32 is the length in bytes of hash, - // enforced by the type signature above - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } - - /** - * @dev Returns an Ethereum Signed Message, created from `s`. This - * produces hash corresponding to the one signed with the - * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] - * JSON-RPC method as part of EIP-191. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); - } - - /** - * @dev Returns an Ethereum Signed Typed Data, created from a - * `domainSeparator` and a `structHash`. This produces hash corresponding - * to the one signed with the - * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] - * JSON-RPC method as part of EIP-712. - * - * See {recover}. - */ - function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } -} - -// File contracts/mapping-token/v2/GuardRegistry.sol -// License-Identifier: Apache-2.0 - -pragma experimental ABIEncoderV2; - -/** - * @title Manages a set of guards and a threshold to double-check BEEFY commitment - * @dev Stores the guards and a threshold - * @author echo - */ -contract GuardRegistry { - event AddedGuard(address guard); - event RemovedGuard(address guard); - event ChangedThreshold(uint256 threshold); - - // keccak256( - // "EIP712Domain(uint256 chainId,address verifyingContract)" - // ); - bytes32 internal constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218; - - address internal constant SENTINEL_GUARDS = address(0x1); - - /** - * @dev Nonce to prevent replay of update operations - */ - uint256 public nonce; - /** - * @dev Store all guards in the linked list - */ - mapping(address => address) internal guards; - /** - * @dev Count of all guards - */ - uint256 internal guardCount; - /** - * @dev Number of required confirmations for update operations - */ - uint256 internal threshold; - - /** - * @dev Sets initial storage of contract. - * @param _guards List of Safe guards. - * @param _threshold Number of required confirmations for check commitment or change guards. - */ - function initialize(address[] memory _guards, uint256 _threshold) internal { - // Threshold can only be 0 at initialization. - // Check ensures that setup function can only be called once. - require(threshold == 0, "Guard: Guards have already been setup"); - // Validate that threshold is smaller than number of added guards. - require(_threshold <= _guards.length, "Guard: Threshold cannot exceed guard count"); - // There has to be at least one Safe guard. - require(_threshold >= 1, "Guard: Threshold needs to be greater than 0"); - // Initializing Safe guards. - address currentGuard = SENTINEL_GUARDS; - for (uint256 i = 0; i < _guards.length; i++) { - // Guard address cannot be null. - address guard = _guards[i]; - require(guard != address(0) && guard != SENTINEL_GUARDS && guard != address(this) && currentGuard != guard, "Guard: Invalid guard address provided"); - // No duplicate guards allowed. - require(guards[guard] == address(0), "Guard: Address is already an guard"); - guards[currentGuard] = guard; - currentGuard = guard; - emit AddedGuard(guard); - } - guards[currentGuard] = SENTINEL_GUARDS; - guardCount = _guards.length; - threshold = _threshold; - } - - /** - * @dev Allows to add a new guard to the registry and update the threshold at the same time. - * This can only be done via multi-sig. - * @notice Adds the guard `guard` to the registry and updates the threshold to `_threshold`. - * @param guard New guard address. - * @param _threshold New threshold. - * @param signatures The signatures of the guards which to add new guard and update the `threshold` . - */ - function addGuardWithThreshold( - address guard, - uint256 _threshold, - bytes[] memory signatures - ) public { - // Guard address cannot be null, the sentinel or the registry itself. - require(guard != address(0) && guard != SENTINEL_GUARDS && guard != address(this), "Guard: Invalid guard address provided"); - // No duplicate guards allowed. - require(guards[guard] == address(0), "Guard: Address is already an guard"); - verifyGuardSignatures(msg.sig, abi.encode(guard, _threshold), signatures); - guards[guard] = guards[SENTINEL_GUARDS]; - guards[SENTINEL_GUARDS] = guard; - guardCount++; - emit AddedGuard(guard); - // Change threshold if threshold was changed. - if (threshold != _threshold) _changeThreshold(_threshold); - } - - /** - * @dev Allows to remove an guard from the registry and update the threshold at the same time. - * This can only be done via multi-sig. - * @notice Removes the guard `guard` from the registry and updates the threshold to `_threshold`. - * @param prevGuard Guard that pointed to the guard to be removed in the linked list - * @param guard Guard address to be removed. - * @param _threshold New threshold. - * @param signatures The signatures of the guards which to remove a guard and update the `threshold` . - */ - function removeGuard( - address prevGuard, - address guard, - uint256 _threshold, - bytes[] memory signatures - ) public { - // Only allow to remove an guard, if threshold can still be reached. - require(guardCount - 1 >= _threshold, "Guard: Threshold cannot exceed guard count"); - // Validate guard address and check that it corresponds to guard index. - require(guard != address(0) && guard != SENTINEL_GUARDS, "Guard: Invalid guard address provided"); - require(guards[prevGuard] == guard, "Guard: Invalid prevGuard, guard pair provided"); - verifyGuardSignatures(msg.sig, abi.encode(prevGuard, guard, _threshold), signatures); - guards[prevGuard] = guards[guard]; - guards[guard] = address(0); - guardCount--; - emit RemovedGuard(guard); - // Change threshold if threshold was changed. - if (threshold != _threshold) _changeThreshold(_threshold); - } - - /** - * @dev Allows to swap/replace a guard from the registry with another address. - * This can only be done via multi-sig. - * @notice Replaces the guard `oldGuard` in the registry with `newGuard`. - * @param prevGuard guard that pointed to the guard to be replaced in the linked list - * @param oldGuard guard address to be replaced. - * @param newGuard New guard address. - * @param signatures The signatures of the guards which to swap/replace a guard and update the `threshold` . - */ - function swapGuard( - address prevGuard, - address oldGuard, - address newGuard, - bytes[] memory signatures - ) public { - // Guard address cannot be null, the sentinel or the registry itself. - require(newGuard != address(0) && newGuard != SENTINEL_GUARDS && newGuard != address(this), "Guard: Invalid guard address provided"); - // No duplicate guards allowed. - require(guards[newGuard] == address(0), "Guard: Address is already an guard"); - // Validate oldGuard address and check that it corresponds to guard index. - require(oldGuard != address(0) && oldGuard != SENTINEL_GUARDS, "Guard: Invalid guard address provided"); - require(guards[prevGuard] == oldGuard, "Guard: Invalid prevGuard, guard pair provided"); - verifyGuardSignatures(msg.sig, abi.encode(prevGuard, oldGuard, newGuard), signatures); - guards[newGuard] = guards[oldGuard]; - guards[prevGuard] = newGuard; - guards[oldGuard] = address(0); - emit RemovedGuard(oldGuard); - emit AddedGuard(newGuard); - } - - /** - * @dev Allows to update the number of required confirmations by guards. - * This can only be done via multi-sig. - * @notice Changes the threshold of the registry to `_threshold`. - * @param _threshold New threshold. - * @param signatures The signatures of the guards which to update the `threshold` . - */ - function changeThreshold(uint256 _threshold, bytes[] memory signatures) public { - verifyGuardSignatures(msg.sig, abi.encode(_threshold), signatures); - _changeThreshold(_threshold); - } - - function _changeThreshold(uint256 _threshold) internal { - // Validate that threshold is smaller than number of owners. - require(_threshold <= guardCount, "Guard: Threshold cannot exceed guard count"); - // There has to be at least one guard. - require(_threshold >= 1, "Guard: Threshold needs to be greater than 0"); - threshold = _threshold; - emit ChangedThreshold(threshold); - } - - function getThreshold() public view returns (uint256) { - return threshold; - } - - function isGuard(address guard) public view returns (bool) { - return guard != SENTINEL_GUARDS && guards[guard] != address(0); - } - - /** - * @dev Returns array of guards. - * @return Array of guards. - */ - function getGuards() public view returns (address[] memory) { - address[] memory array = new address[](guardCount); - - // populate return array - uint256 index = 0; - address currentGuard = guards[SENTINEL_GUARDS]; - while (currentGuard != SENTINEL_GUARDS) { - array[index] = currentGuard; - currentGuard = guards[currentGuard]; - index++; - } - return array; - } - - function verifyGuardSignatures( - bytes4 methodID, - bytes memory params, - bytes[] memory signatures - ) internal { - bytes32 structHash = - keccak256( - abi.encode( - methodID, - params, - nonce - ) - ); - checkGuardSignatures(structHash, signatures); - nonce++; - } - - function verifyGuardSignaturesWithoutNonce( - bytes4 methodID, - bytes memory params, - bytes[] memory signatures - ) view internal { - bytes32 structHash = - keccak256( - abi.encode( - methodID, - params - ) - ); - checkGuardSignatures(structHash, signatures); - } - - /** - * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise. - * @param structHash The struct Hash of the data (could be either a message/commitment hash). - * @param signatures Signature data that should be verified. only ECDSA signature. - * Signers need to be sorted in ascending order - */ - function checkGuardSignatures( - bytes32 structHash, - bytes[] memory signatures - ) public view { - // Load threshold to avoid multiple storage loads - uint256 _threshold = threshold; - // Check that a threshold is set - require(_threshold > 0, "Guard: Threshold needs to be defined"); - bytes32 dataHash = encodeDataHash(structHash); - checkNSignatures(dataHash, signatures, _threshold); - } - - /** - * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise. - * @param dataHash Hash of the data (could be either a message hash or transaction hash). - * @param signatures Signature data that should be verified. only ECDSA signature. - * Signers need to be sorted in ascending order - * @param requiredSignatures Amount of required valid signatures. - */ - function checkNSignatures( - bytes32 dataHash, - bytes[] memory signatures, - uint256 requiredSignatures - ) public view { - // Check that the provided signature data is not too short - require(signatures.length >= requiredSignatures, "GS020"); - // There cannot be an owner with address 0. - address lastGuard = address(0); - address currentGuard; - for (uint256 i = 0; i < requiredSignatures; i++) { - currentGuard = ECDSA.recover(dataHash, signatures[i]); - require(currentGuard > lastGuard && guards[currentGuard] != address(0) && currentGuard != SENTINEL_GUARDS, "Guard: Invalid guard provided"); - lastGuard = currentGuard; - } - } - - /** - * @dev Returns the chain id used by this contract. - */ - function getChainId() public view returns (uint256) { - uint256 id; - // solhint-disable-next-line no-inline-assembly - assembly { - id := chainid() - } - return id; - } - - function domainSeparator() public view returns (bytes32) { - return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), address(this))); - } - - function encodeDataHash(bytes32 structHash) public view returns (bytes32) { - return keccak256(abi.encodePacked(hex"1901", domainSeparator(), structHash)); - } -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/math/SafeMath.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) - - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the subtraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} - -// File contracts/mapping-token/v2/Guard.sol -// License-Identifier: Apache-2.0 - - - - - - -contract Guard is GuardRegistry, Pausable { - using SafeMath for uint256; - - mapping(uint256 => bytes32) depositors; - - uint256 public maxUnclaimableTime; - address public depositor; - address public operator; - - event TokenDeposit(uint256 id, address token, address recipient, uint256 amount); - event TokenClaimed(uint256 id); - - constructor(address[] memory _guards, uint256 _threshold, uint256 _maxUnclaimableTime, address _depositor) { - maxUnclaimableTime = _maxUnclaimableTime; - depositor = _depositor; - operator = msg.sender; - initialize(_guards, _threshold); - } - - modifier onlyDepositor() { - require(msg.sender == depositor, "Guard: Invalid depositor"); - _; - } - - modifier onlyOperator() { - require(msg.sender == operator, "Guard: Invalid operator"); - _; - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } - - function setOperator(address newOperator, bytes[] memory signatures) external { - verifyGuardSignatures(msg.sig, abi.encode(newOperator), signatures); - operator = newOperator; - } - - function setMaxUnclaimableTime(uint256 _maxUnclaimableTime) external onlyOperator { - maxUnclaimableTime = _maxUnclaimableTime; - } - - /** - * @dev deposit token to guard, waiting to claim, only allowed depositor - * @param id the id of the operation, should be siged later by guards - * @param token the erc20 token address - * @param recipient the recipient of the token - * @param amount the amount of the token - */ - function deposit( - uint256 id, - address token, - address recipient, - uint256 amount - ) public onlyDepositor whenNotPaused { - depositors[id] = hash(abi.encodePacked(block.timestamp, token, recipient, amount)); - emit TokenDeposit(id, token, recipient, amount); - } - - function claimById( - uint256 id, - uint256 timestamp, - address token, - address recipient, - uint256 amount, - bool isNative - ) internal { - require(hash(abi.encodePacked(timestamp, token, recipient, amount)) == depositors[id], "Guard: Invalid id to claim"); - require(amount > 0, "Guard: Invalid amount to claim"); - if (isNative) { - require(IERC20(token).transferFrom(depositor, address(this), amount), "Guard: claim native token failed"); - uint256 balanceBefore = address(this).balance; - IWToken(token).withdraw(amount); - require(address(this).balance == balanceBefore.add(amount), "Guard: token is not wrapped by native token"); - payable(recipient).transfer(amount); - } else { - require(IERC20(token).transferFrom(depositor, recipient, amount), "Guard: claim token failed"); - } - delete depositors[id]; - emit TokenClaimed(id); - } - - /** - * @dev claim the tokens in the contract saved by deposit, this acquire signatures from guards - * @param id the id to be claimed - * @param signatures the signatures of the guards which to claim tokens. - */ - function claim( - uint256 id, - uint256 timestamp, - address token, - address recipient, - uint256 amount, - bytes[] memory signatures - ) public { - verifyGuardSignaturesWithoutNonce(msg.sig, abi.encode(id, timestamp, token, recipient, amount), signatures); - claimById(id, timestamp, token, recipient, amount, false); - } - - /** - * @dev claimNative the tokens in the contract saved by deposit, this acquire signatures from guards - * @param id the id to be claimed - * @param signatures the signatures of the guards which to claim tokens. - */ - function claimNative( - uint256 id, - uint256 timestamp, - address token, - address recipient, - uint256 amount, - bytes[] memory signatures - ) public { - verifyGuardSignaturesWithoutNonce(msg.sig, abi.encode(id, timestamp, token, recipient, amount), signatures); - claimById(id, timestamp, token, recipient, amount, true); - } - - /** - * @dev claim the tokens without signatures, this only allowed when timeout - * @param id the id to be claimed - */ - function claimByTimeout( - uint256 id, - uint256 timestamp, - address token, - address recipient, - uint256 amount, - bool isNative - ) public whenNotPaused { - require(timestamp < block.timestamp && block.timestamp - timestamp > maxUnclaimableTime, "Guard: claim at invalid time"); - claimById(id, timestamp, token, recipient, amount, isNative); - } - - function hash(bytes memory value) public pure returns (bytes32) { - return sha256(value); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/ProxyAdmin.sol b/helix-contract/flatten/sub2eth/ProxyAdmin.sol deleted file mode 100644 index f56cfa15..00000000 --- a/helix-contract/flatten/sub2eth/ProxyAdmin.sol +++ /dev/null @@ -1,985 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 9/27/2022 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/proxy/Proxy.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol) - - -/** - * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM - * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to - * be specified by overriding the virtual {_implementation} function. - * - * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a - * different contract through the {_delegate} function. - * - * The success and return data of the delegated call will be returned back to the caller of the proxy. - */ -abstract contract Proxy { - /** - * @dev Delegates the current call to `implementation`. - * - * This function does not return to its internal call site, it will return directly to the external caller. - */ - function _delegate(address implementation) internal virtual { - assembly { - // Copy msg.data. We take full control of memory in this inline assembly - // block because it will not return to Solidity code. We overwrite the - // Solidity scratch pad at memory position 0. - calldatacopy(0, 0, calldatasize()) - - // Call the implementation. - // out and outsize are 0 because we don't know the size yet. - let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) - - // Copy the returned data. - returndatacopy(0, 0, returndatasize()) - - switch result - // delegatecall returns 0 on error. - case 0 { - revert(0, returndatasize()) - } - default { - return(0, returndatasize()) - } - } - } - - /** - * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function - * and {_fallback} should delegate. - */ - function _implementation() internal view virtual returns (address); - - /** - * @dev Delegates the current call to the address returned by `_implementation()`. - * - * This function does not return to its internal call site, it will return directly to the external caller. - */ - function _fallback() internal virtual { - _beforeFallback(); - _delegate(_implementation()); - } - - /** - * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other - * function in the contract matches the call data. - */ - fallback() external payable virtual { - _fallback(); - } - - /** - * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data - * is empty. - */ - receive() external payable virtual { - _fallback(); - } - - /** - * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` - * call, or as part of the Solidity `fallback` or `receive` functions. - * - * If overridden should call `super._beforeFallback()`. - */ - function _beforeFallback() internal virtual {} -} - -// File @zeppelin-solidity/contracts/proxy/beacon/IBeacon.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) - - -/** - * @dev This is the interface that {BeaconProxy} expects of its beacon. - */ -interface IBeacon { - /** - * @dev Must return an address that can be used as a delegate call target. - * - * {BeaconProxy} will check that this address is a contract. - */ - function implementation() external view returns (address); -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/interfaces/draft-IERC1822.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol) - - -/** - * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified - * proxy whose upgrades are fully controlled by the current implementation. - */ -interface IERC1822Proxiable { - /** - * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation - * address. - * - * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks - * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this - * function revert if invoked through a proxy. - */ - function proxiableUUID() external view returns (bytes32); -} - -// File @zeppelin-solidity/contracts/utils/StorageSlot.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol) - - -/** - * @dev Library for reading and writing primitive types to specific storage slots. - * - * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. - * This library helps with reading and writing to such slots without the need for inline assembly. - * - * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. - * - * Example usage to set ERC1967 implementation slot: - * ``` - * contract ERC1967 { - * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - * - * function _getImplementation() internal view returns (address) { - * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - * } - * - * function _setImplementation(address newImplementation) internal { - * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - * } - * } - * ``` - * - * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ - */ -library StorageSlot { - struct AddressSlot { - address value; - } - - struct BooleanSlot { - bool value; - } - - struct Bytes32Slot { - bytes32 value; - } - - struct Uint256Slot { - uint256 value; - } - - /** - * @dev Returns an `AddressSlot` with member `value` located at `slot`. - */ - function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `BooleanSlot` with member `value` located at `slot`. - */ - function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. - */ - function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Uint256Slot` with member `value` located at `slot`. - */ - function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } -} - -// File @zeppelin-solidity/contracts/proxy/ERC1967/ERC1967Upgrade.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol) - - - - - -/** - * @dev This abstract contract provides getters and event emitting update functions for - * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. - * - * _Available since v4.1._ - * - * @custom:oz-upgrades-unsafe-allow delegatecall - */ -abstract contract ERC1967Upgrade { - // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 - bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; - - /** - * @dev Storage slot with the address of the current implementation. - * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @dev Emitted when the implementation is upgraded. - */ - event Upgraded(address indexed implementation); - - /** - * @dev Returns the current implementation address. - */ - function _getImplementation() internal view returns (address) { - return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - } - - /** - * @dev Stores a new address in the EIP1967 implementation slot. - */ - function _setImplementation(address newImplementation) private { - require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - } - - /** - * @dev Perform implementation upgrade - * - * Emits an {Upgraded} event. - */ - function _upgradeTo(address newImplementation) internal { - _setImplementation(newImplementation); - emit Upgraded(newImplementation); - } - - /** - * @dev Perform implementation upgrade with additional setup call. - * - * Emits an {Upgraded} event. - */ - function _upgradeToAndCall( - address newImplementation, - bytes memory data, - bool forceCall - ) internal { - _upgradeTo(newImplementation); - if (data.length > 0 || forceCall) { - Address.functionDelegateCall(newImplementation, data); - } - } - - /** - * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. - * - * Emits an {Upgraded} event. - */ - function _upgradeToAndCallUUPS( - address newImplementation, - bytes memory data, - bool forceCall - ) internal { - // Upgrades from old implementations will perform a rollback test. This test requires the new - // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing - // this special case will break upgrade paths from old UUPS implementation to new ones. - if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { - _setImplementation(newImplementation); - } else { - try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { - require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); - } catch { - revert("ERC1967Upgrade: new implementation is not UUPS"); - } - _upgradeToAndCall(newImplementation, data, forceCall); - } - } - - /** - * @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 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - - /** - * @dev Emitted when the admin account has changed. - */ - event AdminChanged(address previousAdmin, address newAdmin); - - /** - * @dev Returns the current admin. - */ - function _getAdmin() internal view returns (address) { - return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; - } - - /** - * @dev Stores a new address in the EIP1967 admin slot. - */ - function _setAdmin(address newAdmin) private { - require(newAdmin != address(0), "ERC1967: new admin is the zero address"); - StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; - } - - /** - * @dev Changes the admin of the proxy. - * - * Emits an {AdminChanged} event. - */ - function _changeAdmin(address newAdmin) internal { - emit AdminChanged(_getAdmin(), newAdmin); - _setAdmin(newAdmin); - } - - /** - * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. - * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. - */ - bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; - - /** - * @dev Emitted when the beacon is upgraded. - */ - event BeaconUpgraded(address indexed beacon); - - /** - * @dev Returns the current beacon. - */ - function _getBeacon() internal view returns (address) { - return StorageSlot.getAddressSlot(_BEACON_SLOT).value; - } - - /** - * @dev Stores a new beacon in the EIP1967 beacon slot. - */ - function _setBeacon(address newBeacon) private { - require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); - require( - Address.isContract(IBeacon(newBeacon).implementation()), - "ERC1967: beacon implementation is not a contract" - ); - StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; - } - - /** - * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does - * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). - * - * Emits a {BeaconUpgraded} event. - */ - function _upgradeBeaconToAndCall( - address newBeacon, - bytes memory data, - bool forceCall - ) internal { - _setBeacon(newBeacon); - emit BeaconUpgraded(newBeacon); - if (data.length > 0 || forceCall) { - Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); - } - } -} - -// File @zeppelin-solidity/contracts/proxy/ERC1967/ERC1967Proxy.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol) - - - -/** - * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an - * implementation address that can be changed. This address is stored in storage in the location specified by - * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the - * implementation behind the proxy. - */ -contract ERC1967Proxy is Proxy, ERC1967Upgrade { - /** - * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. - * - * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded - * function call, and allows initializing the storage of the proxy like a Solidity constructor. - */ - constructor(address _logic, bytes memory _data) payable { - _upgradeToAndCall(_logic, _data, false); - } - - /** - * @dev Returns the current implementation address. - */ - function _implementation() internal view virtual override returns (address impl) { - return ERC1967Upgrade._getImplementation(); - } -} - -// File @zeppelin-solidity/contracts/proxy/transparent/TransparentUpgradeableProxy.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/transparent/TransparentUpgradeableProxy.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 ERC1967Proxy { - /** - * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and - * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. - */ - constructor( - address _logic, - address admin_, - bytes memory _data - ) payable ERC1967Proxy(_logic, _data) { - _changeAdmin(admin_); - } - - /** - * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. - */ - modifier ifAdmin() { - if (msg.sender == _getAdmin()) { - _; - } 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_ = _getAdmin(); - } - - /** - * @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 { - _changeAdmin(newAdmin); - } - - /** - * @dev Upgrade the implementation of the proxy. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. - */ - function upgradeTo(address newImplementation) external ifAdmin { - _upgradeToAndCall(newImplementation, bytes(""), false); - } - - /** - * @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 ifAdmin { - _upgradeToAndCall(newImplementation, data, true); - } - - /** - * @dev Returns the current admin. - */ - function _admin() internal view virtual returns (address) { - return _getAdmin(); - } - - /** - * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. - */ - function _beforeFallback() internal virtual override { - require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); - super._beforeFallback(); - } -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/access/Ownable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) - - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor() { - _transferOwnership(_msgSender()); - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if the sender is not the owner. - */ - function _checkOwner() internal view virtual { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - address oldOwner = _owner; - _owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} - -// File @zeppelin-solidity/contracts/proxy/transparent/ProxyAdmin.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.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 ProxyAdmin is Ownable { - /** - * @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); - } -} - -// File contracts/mapping-token/deprecated/admin.sol -// License-Identifier: MIT \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/TransparentUpgradeableProxy.sol b/helix-contract/flatten/sub2eth/TransparentUpgradeableProxy.sol deleted file mode 100644 index f211ef22..00000000 --- a/helix-contract/flatten/sub2eth/TransparentUpgradeableProxy.sol +++ /dev/null @@ -1,798 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 9/27/2022 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/proxy/Proxy.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol) - - -/** - * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM - * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to - * be specified by overriding the virtual {_implementation} function. - * - * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a - * different contract through the {_delegate} function. - * - * The success and return data of the delegated call will be returned back to the caller of the proxy. - */ -abstract contract Proxy { - /** - * @dev Delegates the current call to `implementation`. - * - * This function does not return to its internal call site, it will return directly to the external caller. - */ - function _delegate(address implementation) internal virtual { - assembly { - // Copy msg.data. We take full control of memory in this inline assembly - // block because it will not return to Solidity code. We overwrite the - // Solidity scratch pad at memory position 0. - calldatacopy(0, 0, calldatasize()) - - // Call the implementation. - // out and outsize are 0 because we don't know the size yet. - let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) - - // Copy the returned data. - returndatacopy(0, 0, returndatasize()) - - switch result - // delegatecall returns 0 on error. - case 0 { - revert(0, returndatasize()) - } - default { - return(0, returndatasize()) - } - } - } - - /** - * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function - * and {_fallback} should delegate. - */ - function _implementation() internal view virtual returns (address); - - /** - * @dev Delegates the current call to the address returned by `_implementation()`. - * - * This function does not return to its internal call site, it will return directly to the external caller. - */ - function _fallback() internal virtual { - _beforeFallback(); - _delegate(_implementation()); - } - - /** - * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other - * function in the contract matches the call data. - */ - fallback() external payable virtual { - _fallback(); - } - - /** - * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data - * is empty. - */ - receive() external payable virtual { - _fallback(); - } - - /** - * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` - * call, or as part of the Solidity `fallback` or `receive` functions. - * - * If overridden should call `super._beforeFallback()`. - */ - function _beforeFallback() internal virtual {} -} - -// File @zeppelin-solidity/contracts/interfaces/draft-IERC1822.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol) - - -/** - * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified - * proxy whose upgrades are fully controlled by the current implementation. - */ -interface IERC1822Proxiable { - /** - * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation - * address. - * - * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks - * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this - * function revert if invoked through a proxy. - */ - function proxiableUUID() external view returns (bytes32); -} - -// File @zeppelin-solidity/contracts/proxy/beacon/IBeacon.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) - - -/** - * @dev This is the interface that {BeaconProxy} expects of its beacon. - */ -interface IBeacon { - /** - * @dev Must return an address that can be used as a delegate call target. - * - * {BeaconProxy} will check that this address is a contract. - */ - function implementation() external view returns (address); -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/utils/StorageSlot.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol) - - -/** - * @dev Library for reading and writing primitive types to specific storage slots. - * - * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. - * This library helps with reading and writing to such slots without the need for inline assembly. - * - * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. - * - * Example usage to set ERC1967 implementation slot: - * ``` - * contract ERC1967 { - * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - * - * function _getImplementation() internal view returns (address) { - * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - * } - * - * function _setImplementation(address newImplementation) internal { - * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - * } - * } - * ``` - * - * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ - */ -library StorageSlot { - struct AddressSlot { - address value; - } - - struct BooleanSlot { - bool value; - } - - struct Bytes32Slot { - bytes32 value; - } - - struct Uint256Slot { - uint256 value; - } - - /** - * @dev Returns an `AddressSlot` with member `value` located at `slot`. - */ - function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `BooleanSlot` with member `value` located at `slot`. - */ - function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. - */ - function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Uint256Slot` with member `value` located at `slot`. - */ - function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } -} - -// File @zeppelin-solidity/contracts/proxy/ERC1967/ERC1967Upgrade.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol) - - - - - -/** - * @dev This abstract contract provides getters and event emitting update functions for - * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. - * - * _Available since v4.1._ - * - * @custom:oz-upgrades-unsafe-allow delegatecall - */ -abstract contract ERC1967Upgrade { - // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 - bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; - - /** - * @dev Storage slot with the address of the current implementation. - * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @dev Emitted when the implementation is upgraded. - */ - event Upgraded(address indexed implementation); - - /** - * @dev Returns the current implementation address. - */ - function _getImplementation() internal view returns (address) { - return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - } - - /** - * @dev Stores a new address in the EIP1967 implementation slot. - */ - function _setImplementation(address newImplementation) private { - require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - } - - /** - * @dev Perform implementation upgrade - * - * Emits an {Upgraded} event. - */ - function _upgradeTo(address newImplementation) internal { - _setImplementation(newImplementation); - emit Upgraded(newImplementation); - } - - /** - * @dev Perform implementation upgrade with additional setup call. - * - * Emits an {Upgraded} event. - */ - function _upgradeToAndCall( - address newImplementation, - bytes memory data, - bool forceCall - ) internal { - _upgradeTo(newImplementation); - if (data.length > 0 || forceCall) { - Address.functionDelegateCall(newImplementation, data); - } - } - - /** - * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. - * - * Emits an {Upgraded} event. - */ - function _upgradeToAndCallUUPS( - address newImplementation, - bytes memory data, - bool forceCall - ) internal { - // Upgrades from old implementations will perform a rollback test. This test requires the new - // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing - // this special case will break upgrade paths from old UUPS implementation to new ones. - if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { - _setImplementation(newImplementation); - } else { - try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { - require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); - } catch { - revert("ERC1967Upgrade: new implementation is not UUPS"); - } - _upgradeToAndCall(newImplementation, data, forceCall); - } - } - - /** - * @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 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - - /** - * @dev Emitted when the admin account has changed. - */ - event AdminChanged(address previousAdmin, address newAdmin); - - /** - * @dev Returns the current admin. - */ - function _getAdmin() internal view returns (address) { - return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; - } - - /** - * @dev Stores a new address in the EIP1967 admin slot. - */ - function _setAdmin(address newAdmin) private { - require(newAdmin != address(0), "ERC1967: new admin is the zero address"); - StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; - } - - /** - * @dev Changes the admin of the proxy. - * - * Emits an {AdminChanged} event. - */ - function _changeAdmin(address newAdmin) internal { - emit AdminChanged(_getAdmin(), newAdmin); - _setAdmin(newAdmin); - } - - /** - * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. - * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. - */ - bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; - - /** - * @dev Emitted when the beacon is upgraded. - */ - event BeaconUpgraded(address indexed beacon); - - /** - * @dev Returns the current beacon. - */ - function _getBeacon() internal view returns (address) { - return StorageSlot.getAddressSlot(_BEACON_SLOT).value; - } - - /** - * @dev Stores a new beacon in the EIP1967 beacon slot. - */ - function _setBeacon(address newBeacon) private { - require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); - require( - Address.isContract(IBeacon(newBeacon).implementation()), - "ERC1967: beacon implementation is not a contract" - ); - StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; - } - - /** - * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does - * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). - * - * Emits a {BeaconUpgraded} event. - */ - function _upgradeBeaconToAndCall( - address newBeacon, - bytes memory data, - bool forceCall - ) internal { - _setBeacon(newBeacon); - emit BeaconUpgraded(newBeacon); - if (data.length > 0 || forceCall) { - Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); - } - } -} - -// File @zeppelin-solidity/contracts/proxy/ERC1967/ERC1967Proxy.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol) - - - -/** - * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an - * implementation address that can be changed. This address is stored in storage in the location specified by - * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the - * implementation behind the proxy. - */ -contract ERC1967Proxy is Proxy, ERC1967Upgrade { - /** - * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. - * - * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded - * function call, and allows initializing the storage of the proxy like a Solidity constructor. - */ - constructor(address _logic, bytes memory _data) payable { - _upgradeToAndCall(_logic, _data, false); - } - - /** - * @dev Returns the current implementation address. - */ - function _implementation() internal view virtual override returns (address impl) { - return ERC1967Upgrade._getImplementation(); - } -} - -// File @zeppelin-solidity/contracts/proxy/transparent/TransparentUpgradeableProxy.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/transparent/TransparentUpgradeableProxy.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 ERC1967Proxy { - /** - * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and - * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. - */ - constructor( - address _logic, - address admin_, - bytes memory _data - ) payable ERC1967Proxy(_logic, _data) { - _changeAdmin(admin_); - } - - /** - * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. - */ - modifier ifAdmin() { - if (msg.sender == _getAdmin()) { - _; - } 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_ = _getAdmin(); - } - - /** - * @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 { - _changeAdmin(newAdmin); - } - - /** - * @dev Upgrade the implementation of the proxy. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. - */ - function upgradeTo(address newImplementation) external ifAdmin { - _upgradeToAndCall(newImplementation, bytes(""), false); - } - - /** - * @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 ifAdmin { - _upgradeToAndCall(newImplementation, data, true); - } - - /** - * @dev Returns the current admin. - */ - function _admin() internal view virtual returns (address) { - return _getAdmin(); - } - - /** - * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. - */ - function _beforeFallback() internal virtual override { - require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); - super._beforeFallback(); - } -} - -// File contracts/mapping-token/deprecated/proxy.sol -// License-Identifier: MIT \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/WToken.sol b/helix-contract/flatten/sub2eth/WToken.sol deleted file mode 100644 index d061b8dc..00000000 --- a/helix-contract/flatten/sub2eth/WToken.sol +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 5/12/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/mapping-token/v2/erc20-mapping-protocol/WToken.sol -// License-Identifier: MIT - - -contract WToken { - string public name; - string public symbol; - uint8 public decimals; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - event Deposit(address indexed dst, uint wad); - event Withdrawal(address indexed src, uint wad); - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - constructor(string memory _name, string memory _symbol, uint8 _decimals) { - name = _name; - symbol = _symbol; - decimals = _decimals; - } - - receive() external payable { - deposit(); - } - - function deposit() public payable { - balanceOf[msg.sender] += msg.value; - emit Deposit(msg.sender, msg.value); - } - function withdraw(uint wad) public { - require(balanceOf[msg.sender] >= wad); - balanceOf[msg.sender] -= wad; - payable(msg.sender).transfer(wad); - emit Withdrawal(msg.sender, wad); - } - - function totalSupply() public view returns (uint) { - return address(this).balance; - } - - function approve(address guy, uint wad) public returns (bool) { - allowance[msg.sender][guy] = wad; - emit Approval(msg.sender, guy, wad); - return true; - } - - function transfer(address dst, uint wad) public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - - function transferFrom(address src, address dst, uint wad) - public - returns (bool) - { - require(balanceOf[src] >= wad); - - if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { - require(allowance[src][msg.sender] >= wad); - allowance[src][msg.sender] -= wad; - } - - balanceOf[src] -= wad; - balanceOf[dst] += wad; - - emit Transfer(src, dst, wad); - - return true; - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2sub/DarwiniaSub2SubMessageEndpoint.sol b/helix-contract/flatten/sub2sub/DarwiniaSub2SubMessageEndpoint.sol deleted file mode 100644 index be18327c..00000000 --- a/helix-contract/flatten/sub2sub/DarwiniaSub2SubMessageEndpoint.sol +++ /dev/null @@ -1,4547 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 4/28/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/AccessController.sol -// License-Identifier: MIT - - -contract AccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - bytes32 public constant CALLER_ROLE = keccak256("CALLER_ROLE"); - bytes32 public constant CALLEE_ROLE = keccak256("CALLEE_ROLE"); - - // access controller - // admin is helix Dao - modifier onlyAdmin() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "AccessController:Bad admin role"); - _; - } - - // operator - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "AccessController:Bad operator role"); - _; - } - - modifier onlyCaller() { - require(hasRole(CALLER_ROLE, msg.sender), "AccessController:Bad caller role"); - _; - } - - modifier onlyCallee() { - require(hasRole(CALLEE_ROLE, msg.sender), "AccessController:Bad callee role"); - _; - } - - function _initialize(address admin) internal { - _setRoleAdmin(CALLER_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(CALLEE_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, admin); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @darwinia/contracts-utils/contracts/Blake2b.sol@v1.0.4 -/* - * Blake2b library in Solidity using EIP-152 - * - * Copyright (C) 2019 Alex Beregszaszi - * - * License: Apache 2.0 - */ - -// License-Identifier: MIT - -pragma experimental ABIEncoderV2; - -library Blake2b { - struct Instance { - // This is a bit misleadingly called state as it not only includes the Blake2 state, - // but every field needed for the "blake2 f function precompile". - // - // This is a tightly packed buffer of: - // - rounds: 32-bit BE - // - h: 8 x 64-bit LE - // - m: 16 x 64-bit LE - // - t: 2 x 64-bit LE - // - f: 8-bit - bytes state; - // Expected output hash length. (Used in `finalize`.) - uint out_len; - // Data passed to "function F". - // NOTE: this is limited to 24 bits. - uint input_counter; - } - - // Initialise the state with a given `key` and required `out_len` hash length. - function init(bytes memory key, uint out_len) - internal - view - returns (Instance memory instance) - { - // Safety check that the precompile exists. - // TODO: remove this? - // assembly { - // if eq(extcodehash(0x09), 0) { revert(0, 0) } - //} - - reset(instance, key, out_len); - } - - // Initialise the state with a given `key` and required `out_len` hash length. - function reset(Instance memory instance, bytes memory key, uint out_len) - internal - view - { - instance.out_len = out_len; - instance.input_counter = 0; - - // This is entire state transmitted to the precompile. - // It is byteswapped for the encoding requirements, additionally - // the IV has the initial parameter block 0 XOR constant applied, but - // not the key and output length. - instance.state = hex"0000000c08c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - bytes memory state = instance.state; - - // Update parameter block 0 with key length and output length. - uint key_len = key.length; - assembly { - let ptr := add(state, 36) - let tmp := mload(ptr) - let p0 := or(shl(240, key_len), shl(248, out_len)) - tmp := xor(tmp, p0) - mstore(ptr, tmp) - } - - // TODO: support salt and personalization - - if (key_len > 0) { - require(key_len == 64); - // FIXME: the key must be zero padded - assert(key.length == 128); - update(instance, key, key_len); - } - } - - // This calls the blake2 precompile ("function F of the spec"). - // It expects the state was updated with the next block. Upon returning the state will be updated, - // but the supplied block data will not be cleared. - function call_function_f(Instance memory instance) - private - view - { - bytes memory state = instance.state; - assembly { - let state_ptr := add(state, 32) - if iszero(staticcall(not(0), 0x09, state_ptr, 0xd5, add(state_ptr, 4), 0x40)) { - revert(0, 0) - } - } - } - - // This function will split blocks correctly and repeatedly call the precompile. - // NOTE: this is dumb right now and expects `data` to be 128 bytes long and padded with zeroes, - // hence the real length is indicated with `data_len` - function update_loop(Instance memory instance, bytes memory data, uint data_len, bool last_block) - private - view - { - bytes memory state = instance.state; - uint input_counter = instance.input_counter; - - // This is the memory location where the "data block" starts for the precompile. - uint state_ptr; - assembly { - // The `rounds` field is 4 bytes long and the `h` field is 64-bytes long. - // Also adjust for the size of the bytes type. - state_ptr := add(state, 100) - } - - // This is the memory location where the input data resides. - uint data_ptr; - assembly { - data_ptr := add(data, 32) - } - - uint len = data.length; - while (len > 0) { - if (len >= 128) { - assembly { - mstore(state_ptr, mload(data_ptr)) - data_ptr := add(data_ptr, 32) - - mstore(add(state_ptr, 32), mload(data_ptr)) - data_ptr := add(data_ptr, 32) - - mstore(add(state_ptr, 64), mload(data_ptr)) - data_ptr := add(data_ptr, 32) - - mstore(add(state_ptr, 96), mload(data_ptr)) - data_ptr := add(data_ptr, 32) - } - - len -= 128; - // FIXME: remove this once implemented proper padding - if (data_len < 128) { - input_counter += data_len; - } else { - data_len -= 128; - input_counter += 128; - } - } else { - // FIXME: implement support for smaller than 128 byte blocks - revert(); - } - - // Set length field (little-endian) for maximum of 24-bits. - assembly { - mstore8(add(state, 228), and(input_counter, 0xff)) - mstore8(add(state, 229), and(shr(8, input_counter), 0xff)) - mstore8(add(state, 230), and(shr(16, input_counter), 0xff)) - } - - // Set the last block indicator. - // Only if we've processed all input. - if (len == 0) { - assembly { - // Writing byte 212 here. - mstore8(add(state, 244), last_block) - } - } - - // Call the precompile - call_function_f(instance); - } - - instance.input_counter = input_counter; - } - - // Update the state with a non-final block. - // NOTE: the input must be complete blocks. - function update(Instance memory instance, bytes memory data, uint data_len) - internal - view - { - require((data.length % 128) == 0); - update_loop(instance, data, data_len, false); - } - - // Update the state with a final block and return the hash. - function finalize(Instance memory instance, bytes memory data) - internal - view - returns (bytes memory output) - { - // FIXME: support incomplete blocks (zero pad them) - uint input_length = data.length; - if (input_length == 0 || (input_length % 128) != 0) { - data = abi.encodePacked(data, new bytes(128 - (input_length % 128))); - } - assert((data.length % 128) == 0); - update_loop(instance, data, input_length, true); - - // FIXME: support other lengths - // assert(instance.out_len == 64); - - bytes memory state = instance.state; - output = new bytes(instance.out_len); - if (instance.out_len == 16) { - assembly { - mstore(add(output,16), mload(add(state, 20))) - mstore(output, 16) - } - } else if(instance.out_len == 32) { - assembly { - mstore(add(output, 32), mload(add(state, 36))) - } - } else { - assembly { - mstore(add(output, 32), mload(add(state, 36))) - mstore(add(output, 64), mload(add(state, 68))) - } - } - } - - function concat( - bytes memory _preBytes, - bytes memory _postBytes - ) - internal - pure - returns (bytes memory) - { - bytes memory tempBytes; - - assembly { - // Get a location of some free memory and store it in tempBytes as - // Solidity does for memory variables. - tempBytes := mload(0x40) - - // Store the length of the first bytes array at the beginning of - // the memory for tempBytes. - let length := mload(_preBytes) - mstore(tempBytes, length) - - // Maintain a memory counter for the current write location in the - // temp bytes array by adding the 32 bytes for the array length to - // the starting location. - let mc := add(tempBytes, 0x20) - // Stop copying when the memory counter reaches the length of the - // first bytes array. - let end := add(mc, length) - - for { - // Initialize a copy counter to the start of the _preBytes data, - // 32 bytes into its memory. - let cc := add(_preBytes, 0x20) - } lt(mc, end) { - // Increase both counters by 32 bytes each iteration. - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - // Write the _preBytes data into the tempBytes memory 32 bytes - // at a time. - mstore(mc, mload(cc)) - } - - // Add the length of _postBytes to the current length of tempBytes - // and store it as the new length in the first 32 bytes of the - // tempBytes memory. - length := mload(_postBytes) - mstore(tempBytes, add(length, mload(tempBytes))) - - // Move the memory counter back from a multiple of 0x20 to the - // actual end of the _preBytes data. - mc := end - // Stop copying when the memory counter reaches the new combined - // length of the arrays. - end := add(mc, length) - - for { - let cc := add(_postBytes, 0x20) - } lt(mc, end) { - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - mstore(mc, mload(cc)) - } - - // Update the free-memory pointer by padding our last write location - // to 32 bytes: add 31 bytes to the end of tempBytes to move to the - // next 32 byte block, then round down to the nearest multiple of - // 32. If the sum of the length of the two arrays is zero then add - // one before rounding down to leave a blank 32 bytes (the length block with 0). - mstore(0x40, and( - add(add(end, iszero(add(length, mload(_preBytes)))), 31), - not(31) // Round down to the nearest 32 bytes. - )) - } - - return tempBytes; - } - -} - -// File @darwinia/contracts-utils/contracts/Memory.sol@v1.0.4 -// License-Identifier: MIT - - -library Memory { - - uint internal constant WORD_SIZE = 32; - - // Compares the 'len' bytes starting at address 'addr' in memory with the 'len' - // bytes starting at 'addr2'. - // Returns 'true' if the bytes are the same, otherwise 'false'. - function equals(uint addr, uint addr2, uint len) internal pure returns (bool equal) { - assembly { - equal := eq(keccak256(addr, len), keccak256(addr2, len)) - } - } - - // Compares the 'len' bytes starting at address 'addr' in memory with the bytes stored in - // 'bts'. It is allowed to set 'len' to a lower value then 'bts.length', in which case only - // the first 'len' bytes will be compared. - // Requires that 'bts.length >= len' - - function equals(uint addr, uint len, bytes memory bts) internal pure returns (bool equal) { - require(bts.length >= len); - uint addr2; - assembly { - addr2 := add(bts, /*BYTES_HEADER_SIZE*/32) - } - return equals(addr, addr2, len); - } - // Returns a memory pointer to the data portion of the provided bytes array. - function dataPtr(bytes memory bts) internal pure returns (uint addr) { - assembly { - addr := add(bts, /*BYTES_HEADER_SIZE*/32) - } - } - - // Creates a 'bytes memory' variable from the memory address 'addr', with the - // length 'len'. The function will allocate new memory for the bytes array, and - // the 'len bytes starting at 'addr' will be copied into that new memory. - function toBytes(uint addr, uint len) internal pure returns (bytes memory bts) { - bts = new bytes(len); - uint btsptr; - assembly { - btsptr := add(bts, /*BYTES_HEADER_SIZE*/32) - } - copy(addr, btsptr, len); - } - - // Copies 'self' into a new 'bytes memory'. - // Returns the newly created 'bytes memory' - // The returned bytes will be of length '32'. - function toBytes(bytes32 self) internal pure returns (bytes memory bts) { - bts = new bytes(32); - assembly { - mstore(add(bts, /*BYTES_HEADER_SIZE*/32), self) - } - } - - // Copy 'len' bytes from memory address 'src', to address 'dest'. - // This function does not check the or destination, it only copies - // the bytes. - function copy(uint src, uint dest, uint len) internal pure { - // Copy word-length chunks while possible - for (; len >= WORD_SIZE; len -= WORD_SIZE) { - assembly { - mstore(dest, mload(src)) - } - dest += WORD_SIZE; - src += WORD_SIZE; - } - - // Copy remaining bytes - uint mask = len == 0 ? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff : 256 ** (WORD_SIZE - len) - 1; - assembly { - let srcpart := and(mload(src), not(mask)) - let destpart := and(mload(dest), mask) - mstore(dest, or(destpart, srcpart)) - } - } - - // This function does the same as 'dataPtr(bytes memory)', but will also return the - // length of the provided bytes array. - function fromBytes(bytes memory bts) internal pure returns (uint addr, uint len) { - len = bts.length; - assembly { - addr := add(bts, /*BYTES_HEADER_SIZE*/32) - } - } -} - -// File @darwinia/contracts-utils/contracts/Bytes.sol@v1.0.4 -// License-Identifier: MIT - - -library Bytes { - uint256 internal constant BYTES_HEADER_SIZE = 32; - - // Checks if two `bytes memory` variables are equal. This is done using hashing, - // which is much more gas efficient then comparing each byte individually. - // Equality means that: - // - 'self.length == other.length' - // - For 'n' in '[0, self.length)', 'self[n] == other[n]' - function equals(bytes memory self, bytes memory other) internal pure returns (bool equal) { - if (self.length != other.length) { - return false; - } - uint addr; - uint addr2; - assembly { - addr := add(self, /*BYTES_HEADER_SIZE*/32) - addr2 := add(other, /*BYTES_HEADER_SIZE*/32) - } - equal = Memory.equals(addr, addr2, self.length); - } - - // Copies a section of 'self' into a new array, starting at the provided 'startIndex'. - // Returns the new copy. - // Requires that 'startIndex <= self.length' - // The length of the substring is: 'self.length - startIndex' - function substr(bytes memory self, uint256 startIndex) - internal - pure - returns (bytes memory) - { - require(startIndex <= self.length); - uint256 len = self.length - startIndex; - uint256 addr = Memory.dataPtr(self); - return Memory.toBytes(addr + startIndex, len); - } - - // Copies 'len' bytes from 'self' into a new array, starting at the provided 'startIndex'. - // Returns the new copy. - // Requires that: - // - 'startIndex + len <= self.length' - // The length of the substring is: 'len' - function substr( - bytes memory self, - uint256 startIndex, - uint256 len - ) internal pure returns (bytes memory) { - require(startIndex + len <= self.length); - if (len == 0) { - return ""; - } - uint256 addr = Memory.dataPtr(self); - return Memory.toBytes(addr + startIndex, len); - } - - // Combines 'self' and 'other' into a single array. - // Returns the concatenated arrays: - // [self[0], self[1], ... , self[self.length - 1], other[0], other[1], ... , other[other.length - 1]] - // The length of the new array is 'self.length + other.length' - function concat(bytes memory self, bytes memory other) - internal - pure - returns (bytes memory) - { - bytes memory ret = new bytes(self.length + other.length); - uint256 src; - uint256 srcLen; - (src, srcLen) = Memory.fromBytes(self); - uint256 src2; - uint256 src2Len; - (src2, src2Len) = Memory.fromBytes(other); - uint256 dest; - (dest, ) = Memory.fromBytes(ret); - uint256 dest2 = dest + srcLen; - Memory.copy(src, dest, srcLen); - Memory.copy(src2, dest2, src2Len); - return ret; - } - - function toBytes32(bytes memory self) - internal - pure - returns (bytes32 out) - { - require(self.length >= 32, "Bytes:: toBytes32: data is to short."); - assembly { - out := mload(add(self, 32)) - } - } - - function toBytes16(bytes memory self, uint256 offset) - internal - pure - returns (bytes16 out) - { - for (uint i = 0; i < 16; i++) { - out |= bytes16(bytes1(self[offset + i]) & 0xFF) >> (i * 8); - } - } - - function toBytes8(bytes memory self, uint256 offset) - internal - pure - returns (bytes8 out) - { - for (uint i = 0; i < 8; i++) { - out |= bytes8(bytes1(self[offset + i]) & 0xFF) >> (i * 8); - } - } - - function toBytes4(bytes memory self, uint256 offset) - internal - pure - returns (bytes4) - { - bytes4 out; - - for (uint256 i = 0; i < 4; i++) { - out |= bytes4(self[offset + i] & 0xFF) >> (i * 8); - } - return out; - } - - function toBytes2(bytes memory self, uint256 offset) - internal - pure - returns (bytes2) - { - bytes2 out; - - for (uint256 i = 0; i < 2; i++) { - out |= bytes2(self[offset + i] & 0xFF) >> (i * 8); - } - return out; - } - - function removeLeadingZero(bytes memory data) internal pure returns (bytes memory) { - uint length = data.length; - - uint startIndex = 0; - for (uint i = 0; i < length; i++) { - if (data[i] != 0) { - startIndex = i; - break; - } - } - - return substr(data, startIndex); - } - - function removeEndingZero(bytes memory data) internal pure returns (bytes memory) { - uint length = data.length; - - uint endIndex = 0; - for (uint i = length - 1; i >= 0; i--) { - if (data[i] != 0) { - endIndex = i; - break; - } - } - - return substr(data, 0, endIndex + 1); - } - - function reverse(bytes memory inbytes) internal pure returns (bytes memory) { - uint inlength = inbytes.length; - bytes memory outbytes = new bytes(inlength); - - for (uint i = 0; i <= inlength - 1; i++) { - outbytes[i] = inbytes[inlength - i - 1]; - } - - return outbytes; - } -} - -// File @darwinia/contracts-utils/contracts/Hash.sol@v1.0.4 -// License-Identifier: MIT - - -// import "./Memory.sol"; - - -library Hash { - - using Blake2b for Blake2b.Instance; - - // function hash(bytes memory src) internal view returns (bytes memory des) { - // return Memory.toBytes(keccak256(src)); - // Blake2b.Instance memory instance = Blake2b.init(hex"", 32); - // return instance.finalize(src); - // } - - function blake2bHash(bytes memory src) internal view returns (bytes32 des) { - // return keccak256(src); - Blake2b.Instance memory instance = Blake2b.init(hex"", 32); - return abi.decode(instance.finalize(src), (bytes32)); - } - - // Blake2_128 - function blake2b128(bytes memory src) internal view returns (bytes16 des) { - Blake2b.Instance memory instance = Blake2b.init(hex"", 16); - return Bytes.toBytes16(instance.finalize(src), 0); - } - - // Blake2_128Concat - function blake2b128Concat(bytes memory src) internal view returns (bytes memory) { - Blake2b.Instance memory instance = Blake2b.init(hex"", 16); - return abi.encodePacked(instance.finalize(src), src); - } -} - -// File @darwinia/contracts-utils/contracts/ScaleCodec.sol@v1.0.4 -// License-Identifier: MIT - -library ScaleCodec { - // Decodes a SCALE encoded uint256 by converting bytes (big endian) to little endian format - function decodeUint256(bytes memory data) internal pure returns (uint256) { - uint256 number; - for (uint256 i = data.length; i > 0; i--) { - number = number + uint256(uint8(data[i - 1])) * (2**(8 * (i - 1))); - } - return number; - } - - function decodeUint128(bytes memory data) internal pure returns (uint128) { - require(data.length >= 16, "Bad data"); - bytes memory reversed = Bytes.reverse(data); - return uint128(Bytes.toBytes16(reversed, 0)); - } - - function decodeUint64(bytes memory data) internal pure returns (uint64) { - require(data.length >= 8, "Bad data"); - bytes memory reversed = Bytes.reverse(data); - return uint64(Bytes.toBytes8(reversed, 0)); - } - - // Decodes a SCALE encoded compact unsigned integer - function decodeUintCompact(bytes memory data) - internal - pure - returns (uint256 value, uint8 mode) - { - uint8 b = readByteAtIndex(data, 0); // read the first byte - mode = b & 3; // bitwise operation - - if (mode == 0) { - // [0, 63] - value = b >> 2; // right shift to remove mode bits - } else if (mode == 1) { - // [64, 16383] - uint8 bb = readByteAtIndex(data, 1); // read the second byte - uint64 r = bb; // convert to uint64 - r <<= 6; // multiply by * 2^6 - r += b >> 2; // right shift to remove mode bits - value = r; - } else if (mode == 2) { - // [16384, 1073741823] - uint8 b2 = readByteAtIndex(data, 1); // read the next 3 bytes - uint8 b3 = readByteAtIndex(data, 2); - uint8 b4 = readByteAtIndex(data, 3); - - uint32 x1 = uint32(b) | (uint32(b2) << 8); // convert to little endian - uint32 x2 = x1 | (uint32(b3) << 16); - uint32 x3 = x2 | (uint32(b4) << 24); - - x3 >>= 2; // remove the last 2 mode bits - value = uint256(x3); - } else if (mode == 3) { - // [1073741824, 4503599627370496] - uint8 l = b >> 2; // remove mode bits - require( - l > 32, - "Not supported: number cannot be greater than 32 bytes" - ); - } else { - revert("Code should be unreachable"); - } - } - - // The biggest compact supported uint is 2 ** 536 - 1. - // But the biggest value supported by this method is 2 ** 256 - 1(max of uint256) - function encodeUintCompact(uint256 v) internal pure returns (bytes memory) { - if ( v < 64 ) { - return abi.encodePacked(uint8(v << 2)); - } else if ( v < 2 ** 14 ) { - return abi.encodePacked(reverse16(uint16(((v << 2) + 1)))); - } else if ( v < 2 ** 30 ) { - return abi.encodePacked(reverse32(uint32(((v << 2) + 2)))); - } else { // 0b11, The upper six bits are the number of bytes following, plus four - bytes memory valueBytes = - Bytes.removeEndingZero(abi.encodePacked(reverse256(v))); - - uint length = valueBytes.length; - uint8 prefix = uint8(((length - 4) << 2) + 3); - - return abi.encodePacked(prefix, valueBytes); - } - } - - // Read a byte at a specific index and return it as type uint8 - function readByteAtIndex(bytes memory data, uint8 index) - internal - pure - returns (uint8) - { - return uint8(data[index]); - } - - // Sources: - // * https://ethereum.stackexchange.com/questions/15350/how-to-convert-an-bytes-to-address-in-solidity/50528 - // * https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel - - function reverse256(uint256 input) internal pure returns (uint256 v) { - v = input; - - // swap bytes - v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | - ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8); - - // swap 2-byte long pairs - v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | - ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16); - - // swap 4-byte long pairs - v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32) | - ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32); - - // swap 8-byte long pairs - v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64) | - ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64); - - // swap 16-byte long pairs - v = (v >> 128) | (v << 128); - } - - function reverse128(uint128 input) internal pure returns (uint128 v) { - v = input; - - // swap bytes - v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | - ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF) << 8); - - // swap 2-byte long pairs - v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | - ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF) << 16); - - // swap 4-byte long pairs - v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000) >> 32) | - ((v & 0x00000000FFFFFFFF00000000FFFFFFFF) << 32); - - // swap 8-byte long pairs - v = (v >> 64) | (v << 64); - } - - function reverse64(uint64 input) internal pure returns (uint64 v) { - v = input; - - // swap bytes - v = ((v & 0xFF00FF00FF00FF00) >> 8) | - ((v & 0x00FF00FF00FF00FF) << 8); - - // swap 2-byte long pairs - v = ((v & 0xFFFF0000FFFF0000) >> 16) | - ((v & 0x0000FFFF0000FFFF) << 16); - - // swap 4-byte long pairs - v = (v >> 32) | (v << 32); - } - - function reverse32(uint32 input) internal pure returns (uint32 v) { - v = input; - - // swap bytes - v = ((v & 0xFF00FF00) >> 8) | - ((v & 0x00FF00FF) << 8); - - // swap 2-byte long pairs - v = (v >> 16) | (v << 16); - } - - function reverse16(uint16 input) internal pure returns (uint16 v) { - v = input; - - // swap bytes - v = (v >> 8) | (v << 8); - } - - function encode256(uint256 input) internal pure returns (bytes32) { - return bytes32(reverse256(input)); - } - - function encode128(uint128 input) internal pure returns (bytes16) { - return bytes16(reverse128(input)); - } - - function encode64(uint64 input) internal pure returns (bytes8) { - return bytes8(reverse64(input)); - } - - function encode32(uint32 input) internal pure returns (bytes4) { - return bytes4(reverse32(input)); - } - - function encode16(uint16 input) internal pure returns (bytes2) { - return bytes2(reverse16(input)); - } - - function encodeBytes(bytes memory input) internal pure returns (bytes memory) { - return abi.encodePacked( - encodeUintCompact(input.length), - input - ); - } - - -} - -// File @darwinia/contracts-utils/contracts/AccountId.sol@v1.0.4 -// License-Identifier: MIT - - -library AccountId { - bytes private constant prefixBytes = "dvm:"; - bytes private constant zeroBytes = hex"00000000000000"; - - function deriveSubstrateAddress(address addr) internal pure returns (bytes32) { - bytes memory body = abi.encodePacked( - prefixBytes, - zeroBytes, - addr - ); - uint8 checksum = checksumOf(body); - bytes memory result = abi.encodePacked(body, checksum); - return Bytes.toBytes32(result); - } - - function deriveEthereumAddress(bytes32 accountId) internal pure returns (address) { - return address(bytes20(accountId)); - } - - function deriveEthereumAddressFromDvm(bytes32 accountId) internal pure returns (address) { - return address(uint160(uint256(accountId) >> 8)); - } - - function checksumOf(bytes memory accountId) private pure returns (uint8) { - uint8 checksum = uint8(accountId[0]); - for (uint i = 1; i <= 30; i++) { - checksum = checksum ^ uint8(accountId[i]); - } - return checksum; - } -} - -// File @darwinia/contracts-periphery/contracts/s2s/interfaces/IStateStorage.sol@vv2.1.1-fix2 -// License-Identifier: MIT - - -interface IStateStorage { - function state_storage(bytes memory key) external view returns (bytes memory); -} - -// File hardhat/console.sol@v2.12.4 -// License-Identifier: MIT -pragma solidity >= 0.4.22 <0.9.0; - -library console { - address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); - - function _sendLogPayload(bytes memory payload) private view { - uint256 payloadLength = payload.length; - address consoleAddress = CONSOLE_ADDRESS; - assembly { - let payloadStart := add(payload, 32) - let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) - } - } - - function log() internal view { - _sendLogPayload(abi.encodeWithSignature("log()")); - } - - function logInt(int256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); - } - - function logUint(uint256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); - } - - function logString(string memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); - } - - function logBool(bool p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); - } - - function logAddress(address p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); - } - - function logBytes(bytes memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); - } - - function logBytes1(bytes1 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); - } - - function logBytes2(bytes2 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); - } - - function logBytes3(bytes3 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); - } - - function logBytes4(bytes4 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); - } - - function logBytes5(bytes5 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); - } - - function logBytes6(bytes6 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); - } - - function logBytes7(bytes7 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); - } - - function logBytes8(bytes8 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); - } - - function logBytes9(bytes9 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); - } - - function logBytes10(bytes10 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); - } - - function logBytes11(bytes11 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); - } - - function logBytes12(bytes12 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); - } - - function logBytes13(bytes13 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); - } - - function logBytes14(bytes14 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); - } - - function logBytes15(bytes15 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); - } - - function logBytes16(bytes16 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); - } - - function logBytes17(bytes17 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); - } - - function logBytes18(bytes18 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); - } - - function logBytes19(bytes19 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); - } - - function logBytes20(bytes20 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); - } - - function logBytes21(bytes21 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); - } - - function logBytes22(bytes22 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); - } - - function logBytes23(bytes23 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); - } - - function logBytes24(bytes24 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); - } - - function logBytes25(bytes25 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); - } - - function logBytes26(bytes26 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); - } - - function logBytes27(bytes27 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); - } - - function logBytes28(bytes28 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); - } - - function logBytes29(bytes29 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); - } - - function logBytes30(bytes30 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); - } - - function logBytes31(bytes31 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); - } - - function logBytes32(bytes32 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); - } - - function log(uint256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); - } - - function log(string memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); - } - - function log(bool p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); - } - - function log(address p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); - } - - function log(uint256 p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); - } - - function log(uint256 p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); - } - - function log(uint256 p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); - } - - function log(uint256 p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); - } - - function log(string memory p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); - } - - function log(string memory p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); - } - - function log(string memory p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); - } - - function log(string memory p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); - } - - function log(bool p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); - } - - function log(bool p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); - } - - function log(bool p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); - } - - function log(bool p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); - } - - function log(address p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); - } - - function log(address p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); - } - - function log(address p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); - } - - function log(address p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); - } - - function log(uint256 p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); - } - - function log(string memory p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); - } - - function log(string memory p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); - } - - function log(string memory p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); - } - - function log(string memory p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); - } - - function log(bool p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); - } - - function log(bool p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); - } - - function log(bool p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); - } - - function log(bool p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); - } - - function log(bool p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); - } - - function log(bool p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); - } - - function log(bool p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); - } - - function log(bool p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); - } - - function log(address p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); - } - - function log(address p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); - } - - function log(address p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); - } - - function log(address p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); - } - - function log(address p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); - } - - function log(address p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); - } - - function log(address p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); - } - - function log(address p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); - } - - function log(address p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); - } - - function log(address p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); - } - - function log(address p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); - } - - function log(address p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); - } - -} - -// File @darwinia/contracts-periphery/contracts/s2s/types/CommonTypes.sol@vv2.1.1-fix2 -// License-Identifier: MIT - - - - -library CommonTypes { - struct EnumItemWithAccountId { - uint8 index; - address accountId; - } - - function encodeEnumItemWithAccountId( - EnumItemWithAccountId memory _item - ) internal pure returns (bytes memory) { - return abi.encodePacked(_item.index, _item.accountId); - } - - struct EnumItemWithNull { - uint8 index; - } - - function encodeEnumItemWithNull( - EnumItemWithNull memory _item - ) internal pure returns (bytes memory) { - return abi.encodePacked(_item.index); - } - - //////////////////////////////////// - // BitVecU8 - //////////////////////////////////// - function ceilDivide(uint a, uint b) internal pure returns (uint) { - if (a % b == 0) { - return a / b; - } else { - return a / b + 1; - } - } - - // bits: bit amount used, 1: true, 0: false - // bytesLength: bytes used by bits - // result: the bytes - struct BitVecU8 { - uint bits; - bytes result; - uint bytesLength; - } - - function decodeBitVecU8( - bytes memory _data - ) internal pure returns (BitVecU8 memory) { - (uint256 bits, uint8 mode) = ScaleCodec.decodeUintCompact(_data); - uint prefixLength = uint8(2 ** mode); - uint bytesLength = ceilDivide(bits, 8); - require( - _data.length >= prefixLength + bytesLength, - "The data is not enough to decode BitVecU8" - ); - return - BitVecU8( - bits, - Bytes.substr(_data, prefixLength, bytesLength), - prefixLength + bytesLength - ); - } - - //////////////////////////////////// - // Relayer - //////////////////////////////////// - struct Relayer { - bytes20 id; - uint128 collateral; - uint128 fee; - } - - // 52 bytes - function decodeRelayer( - bytes memory _data - ) internal pure returns (Relayer memory) { - require(_data.length >= 52, "The data is not enough to decode Relayer"); - - bytes20 id = bytes20(Bytes.substr(_data, 0, 20)); - - uint128 collateral = ScaleCodec.decodeUint128( - Bytes.substr(_data, 20, 16) - ); - - uint128 fee = ScaleCodec.decodeUint128(Bytes.substr(_data, 36, 16)); - - return Relayer(id, collateral, fee); - } - - function getLastRelayerFromVec( - bytes memory _data - ) internal pure returns (Relayer memory) { - // Option::None - require(_data.length > 0, "No relayers"); - - // Option::Some(Reayler[]) - // _data checking - (uint256 relayersCount, uint8 mode) = ScaleCodec.decodeUintCompact( - _data - ); - require(relayersCount > 0, "No relayers"); - require(mode < 3, "Wrong compact mode"); // Now, mode 3 is not supported yet - uint8 lengthOfPrefixBytes = uint8(2 ** mode); - require( - _data.length >= lengthOfPrefixBytes + relayersCount * 52, - "No enough data" - ); - - // get the bytes of the last Relayer, then decode - Relayer memory relayer = decodeRelayer( - Bytes.substr(_data, lengthOfPrefixBytes + 52 * (relayersCount - 1)) - ); - return relayer; - } - - //////////////////////////////////// - // OutboundLaneData - //////////////////////////////////// - struct OutboundLaneData { - uint64 oldestUnprunedNonce; - uint64 latestReceivedNonce; - uint64 latestGeneratedNonce; - } - - // 24 bytes - function decodeOutboundLaneData( - bytes memory _data - ) internal pure returns (OutboundLaneData memory) { - require( - _data.length >= 24, - "The data is not enough to decode OutboundLaneData" - ); - - uint64 oldestUnprunedNonce = ScaleCodec.decodeUint64( - Bytes.substr(_data, 0, 8) - ); - uint64 latestReceivedNonce = ScaleCodec.decodeUint64( - Bytes.substr(_data, 8, 8) - ); - uint64 latestGeneratedNonce = ScaleCodec.decodeUint64( - Bytes.substr(_data, 16, 8) - ); - - return - OutboundLaneData( - oldestUnprunedNonce, - latestReceivedNonce, - latestGeneratedNonce - ); - } - - //////////////////////////////////// - // DeliveredMessages - //////////////////////////////////// - struct DeliveredMessages { - uint64 begin; - uint64 end; - BitVecU8 dispatchResults; - } - - function decodeDeliveredMessages( - bytes memory _data - ) internal pure returns (DeliveredMessages memory) { - uint64 begin = ScaleCodec.decodeUint64(Bytes.substr(_data, 0, 8)); - uint64 end = ScaleCodec.decodeUint64(Bytes.substr(_data, 8, 8)); - BitVecU8 memory dispatchResults = decodeBitVecU8( - Bytes.substr(_data, 16) - ); - - return DeliveredMessages(begin, end, dispatchResults); - } - - function getBytesLengthOfDeliveredMessages( - DeliveredMessages memory deliveredMessages - ) internal pure returns (uint) { - return 16 + deliveredMessages.dispatchResults.bytesLength; - } - - //////////////////////////////////// - // UnrewardedRelayer - //////////////////////////////////// - struct UnrewardedRelayer { - address relayer; - DeliveredMessages messages; - } - - function decodeUnrewardedRelayer( - bytes memory _data - ) internal pure returns (UnrewardedRelayer memory) { - address relayer = toAddress(_data, 0); - DeliveredMessages memory messages = decodeDeliveredMessages( - Bytes.substr(_data, 20) - ); - - return UnrewardedRelayer(relayer, messages); - } - - function getBytesLengthOfUnrewardedRelayer( - UnrewardedRelayer memory unrewardedRelayer - ) internal pure returns (uint) { - uint bytesLengthOfmessages = getBytesLengthOfDeliveredMessages( - unrewardedRelayer.messages - ); - return 20 + bytesLengthOfmessages; - } - - function toAddress( - bytes memory _bytes, - uint256 _start - ) internal pure returns (address) { - require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); - address tempAddress; - - assembly { - tempAddress := div( - mload(add(add(_bytes, 0x20), _start)), - 0x1000000000000000000000000 - ) - } - - return tempAddress; - } - - //////////////////////////////////// - // InboundLaneData - //////////////////////////////////// - // struct InboundLaneData { - // VecDeque relayers; - // uint64 last_confirmed_nonce; - // } - struct InboundLaneData { - UnrewardedRelayer[] relayers; - uint64 lastConfirmedNonce; - } - - function decodeInboundLaneData( - bytes memory _data - ) internal pure returns (InboundLaneData memory) { - (uint256 numberOfRelayers, uint8 mode) = ScaleCodec.decodeUintCompact( - _data - ); - require(mode < 3, "Wrong compact mode"); // Now, mode 3 is not supported yet - uint consumedLength = uint8(2 ** mode); - - InboundLaneData memory result = InboundLaneData( - new UnrewardedRelayer[](numberOfRelayers), - 0 - ); - - // decode relayers - for (uint i = 0; i < numberOfRelayers; i++) { - UnrewardedRelayer memory relayer = decodeUnrewardedRelayer( - Bytes.substr(_data, consumedLength) - ); - result.relayers[i] = relayer; - consumedLength = - consumedLength + - getBytesLengthOfUnrewardedRelayer(relayer); - } - - // decode lastConfirmedNonce - result.lastConfirmedNonce = ScaleCodec.decodeUint64( - Bytes.substr(_data, consumedLength) - ); - - return result; - } - - function getLastDeliveredNonceFromInboundLaneData( - bytes memory _data - ) internal pure returns (uint64) { - InboundLaneData memory inboundLaneData = decodeInboundLaneData(_data); - if (inboundLaneData.relayers.length == 0) { - return inboundLaneData.lastConfirmedNonce; - } else { - UnrewardedRelayer memory lastRelayer = inboundLaneData.relayers[ - inboundLaneData.relayers.length - 1 - ]; - return lastRelayer.messages.end; - } - } - - //////////////////////////////////// - // Message - //////////////////////////////////// - struct Message { - uint32 specVersion; - uint64 weightRefTime; - uint64 weightProofSize; - EnumItemWithAccountId origin; - EnumItemWithNull dispatchFeePayment; - bytes call; - } - - function encodeMessage( - Message memory _message - ) internal pure returns (bytes memory) { - return - abi.encodePacked( - ScaleCodec.encode32(_message.specVersion), - // weight - ScaleCodec.encodeUintCompact(_message.weightRefTime), - ScaleCodec.encodeUintCompact(_message.weightProofSize), - // origin - _message.origin.index, - _message.origin.accountId, - // dispatchFeePayment - _message.dispatchFeePayment.index, - ScaleCodec.encodeBytes(_message.call) - ); - } -} - -// File @darwinia/contracts-periphery/contracts/s2s/types/PalletBridgeMessages.sol@vv2.1.1-fix2 -// License-Identifier: MIT - - - -library PalletBridgeMessages { - struct SendMessageCall { - bytes2 callIndex; // pallet index and call func index - bytes4 laneId; - bytes message; - uint128 deliveryAndDispatchFee; - } - - function encodeSendMessageCall(SendMessageCall memory _call) - internal - pure - returns (bytes memory) - { - return - abi.encodePacked( - _call.callIndex, - _call.laneId, - _call.message, - ScaleCodec.encode128(_call.deliveryAndDispatchFee) - ); - } -} - -// File @darwinia/contracts-periphery/contracts/s2s/MessageLib.sol@vv2.1.1-fix2 -// License-Identifier: MIT - - - - - - -library MessageLib { - bytes public constant ACCOUNT_DERIVATION_PREFIX = - "pallet-bridge/account-derivation/account"; - - // Send message over lane by calling the `send_message` dispatch call on - // the source chain which is identified by the `callIndex` param. - // Note: `XxxMessages`.`send_message`, the origin must equals to the origin in the message - function sendMessage( - address _srcDispatchPrecompileAddress, - bytes2 _callIndex, - bytes4 _laneId, - uint256 _deliveryAndDispatchFee, - bytes memory _message - ) internal { - // encode send_message call - PalletBridgeMessages.SendMessageCall - memory sendMessageCall = PalletBridgeMessages.SendMessageCall( - _callIndex, - _laneId, - _message, - uint128(_deliveryAndDispatchFee) - ); - - bytes memory sendMessageCallEncoded = PalletBridgeMessages - .encodeSendMessageCall(sendMessageCall); - - // dispatch the send_message call - dispatch( - _srcDispatchPrecompileAddress, - sendMessageCallEncoded, - "Dispatch send_message failed" - ); - } - - // Build the scale encoded message for the target chain. - function buildMessage( - uint32 _specVersion, - uint64 _weight, - bytes memory _call - ) internal view returns (bytes memory) { - // enum CallOrigin - // 0: SourceRoot - // 1: TargetAccount - // 2: SourceAccount - CommonTypes.EnumItemWithAccountId memory origin = CommonTypes - .EnumItemWithAccountId( - 2, // CallOrigin::SourceAccount - address(this) // UserApp contract address - ); - - // enum DispatchFeePayment - // 0: AtSourceChain - // 1: AtTargetChain - CommonTypes.EnumItemWithNull memory dispatchFeePayment = CommonTypes - .EnumItemWithNull(0); // DispatchFeePayment::AtSourceChain - - return - CommonTypes.encodeMessage( - CommonTypes.Message( - _specVersion, - _weight, - 0, - origin, - dispatchFeePayment, - _call - ) - ); - } - - // Get market fee from state storage of the substrate chain - function marketFee( - address _srcStoragePrecompileAddress, - bytes32 _storageKey - ) internal view returns (uint128) { - bytes memory data = getStateStorage( - _srcStoragePrecompileAddress, - abi.encodePacked(_storageKey), - "Get market fee failed" - ); - - CommonTypes.Relayer memory relayer = CommonTypes.getLastRelayerFromVec( - data - ); - return relayer.fee; - } - - // Get the latest nonce from state storage - function latestNonce( - address _srcStoragePrecompileAddress, - bytes32 _storageKey, - bytes4 _laneId - ) internal view returns (uint64) { - // 1. Get `OutboundLaneData` from storage - // Full storage key == storageKey + Blake2_128Concat(laneId) - bytes memory hashedLaneId = Hash.blake2b128Concat( - abi.encodePacked(_laneId) - ); - bytes memory fullStorageKey = abi.encodePacked( - _storageKey, - hashedLaneId - ); - - // Do get data by calling state storage precompile - bytes memory data = getStateStorage( - _srcStoragePrecompileAddress, - fullStorageKey, - "Get OutboundLaneData failed" - ); - - // 2. Decode `OutboundLaneData` and return the latest nonce - CommonTypes.OutboundLaneData memory outboundLaneData = CommonTypes - .decodeOutboundLaneData(data); - return outboundLaneData.latestGeneratedNonce; - } - - function deriveAccountId( - bytes4 _srcChainId, - bytes32 _accountId - ) internal view returns (bytes32) { - bytes memory data = abi.encodePacked( - bytes1(0xa0), // compact length of ACCOUNT_DERIVATION_PREFIX - ACCOUNT_DERIVATION_PREFIX, - _srcChainId, - _accountId - ); - return Hash.blake2bHash(data); - } - - function revertIfFailed( - bool _success, - bytes memory _resultData, - string memory _revertMsg - ) internal pure { - if (!_success) { - if (_resultData.length > 0) { - assembly { - let resultDataSize := mload(_resultData) - revert(add(32, _resultData), resultDataSize) - } - } else { - revert(_revertMsg); - } - } - } - - event DispatchCall(bytes); - - // Dispatch pallet dispatch-call - function dispatch( - address _srcDispatchPrecompileAddress, - bytes memory _callEncoded, - string memory _errMsg - ) internal { - emit DispatchCall(_callEncoded); - // Dispatch the call - (bool success, bytes memory data) = _srcDispatchPrecompileAddress.call( - _callEncoded - ); - revertIfFailed(success, data, _errMsg); - } - - function deriveSender( - bytes4 _srcChainId, - address _srcMessageSender - ) internal view returns (address) { - // H160(sender on the sourc chain) > AccountId32 - bytes32 derivedSubstrateAddress = AccountId.deriveSubstrateAddress( - _srcMessageSender - ); - - // AccountId32 > derived AccountId32 - bytes32 derivedAccountId = deriveAccountId( - _srcChainId, - derivedSubstrateAddress - ); - - // derived AccountId32 > H160 - address result = AccountId.deriveEthereumAddress(derivedAccountId); - - return result; - } - - // Get the last delivered nonce from the state storage of the target chain's inbound lane - function lastDeliveredNonce( - address _tgtStoragePrecompileAddress, - bytes32 _storageKey, - bytes4 _inboundLaneId - ) internal view returns (uint64) { - // 1. Get `inboundLaneData` from storage - // Full storage key == storageKey + Blake2_128Concat(laneId) - bytes memory hashedLaneId = Hash.blake2b128Concat( - abi.encodePacked(_inboundLaneId) - ); - bytes memory fullStorageKey = abi.encodePacked( - _storageKey, - hashedLaneId - ); - - // Do get data by calling state storage precompile - bytes memory data = getStateStorage( - _tgtStoragePrecompileAddress, - fullStorageKey, - "Get InboundLaneData failed" - ); - - // 2. Decode `InboundLaneData` and return the last delivered nonce - return CommonTypes.getLastDeliveredNonceFromInboundLaneData(data); - } - - function getStateStorage( - address _storagePrecompileAddress, - bytes memory _storageKey, - string memory _failedMsg - ) internal view returns (bytes memory) { - (bool success, bytes memory data) = _storagePrecompileAddress - .staticcall( - abi.encodeWithSelector( - IStateStorage.state_storage.selector, - _storageKey - ) - ); - - // TODO: Use try/catch instead for error - revertIfFailed(success, data, _failedMsg); - - return abi.decode(data, (bytes)); - } -} - -// File @darwinia/contracts-periphery/contracts/s2s/types/PalletEthereum.sol@vv2.1.1-fix2 -// License-Identifier: MIT - - - - -library PalletEthereum { - struct TransactCall { - bytes2 callIndex; - EnumItemTransactionV2WithLegacyTransaction transaction; - } - - function encodeTransactCall(TransactCall memory _call) - internal - pure - returns (bytes memory) - { - return - abi.encodePacked( - _call.callIndex, - encodeEnumItemTransactionV2WithLegacyTransaction( - _call.transaction - ) - ); - } - - struct MessageTransactCall { - bytes2 callIndex; - EnumItemTransactionV2WithLegacyTransaction transaction; - } - - function encodeMessageTransactCall(MessageTransactCall memory _call) - internal - pure - returns (bytes memory) - { - return - abi.encodePacked( - _call.callIndex, - encodeEnumItemTransactionV2WithLegacyTransaction( - _call.transaction - ) - ); - } - - struct EnumItemTransactionActionWithAddress { - uint8 index; - address h160; - } - - function encodeEnumItemTransactionActionWithAddress( - EnumItemTransactionActionWithAddress memory _item - ) internal pure returns (bytes memory) { - return abi.encodePacked(_item.index, _item.h160); - } - - struct LegacyTransaction { - uint256 nonce; - uint256 gasPrice; - uint256 gasLimit; - EnumItemTransactionActionWithAddress action; - uint256 value; - bytes input; - uint64 v; - bytes32 r; - bytes32 s; - } - - function encodeLegacyTransaction(LegacyTransaction memory _transaction) - internal - pure - returns (bytes memory) - { - return - abi.encodePacked( - ScaleCodec.encode256(_transaction.nonce), - ScaleCodec.encode256(_transaction.gasPrice), - ScaleCodec.encode256(_transaction.gasLimit), - encodeEnumItemTransactionActionWithAddress(_transaction.action), - ScaleCodec.encode256(_transaction.value), - ScaleCodec.encodeBytes(_transaction.input), - ScaleCodec.encode64(_transaction.v), - _transaction.r, - _transaction.s - ); - } - - struct EnumItemTransactionV2WithLegacyTransaction { - uint8 index; - LegacyTransaction legacyTransaction; - } - - function encodeEnumItemTransactionV2WithLegacyTransaction( - EnumItemTransactionV2WithLegacyTransaction memory _item - ) internal pure returns (bytes memory) { - return - abi.encodePacked( - _item.index, - encodeLegacyTransaction(_item.legacyTransaction) - ); - } - - function buildTransactionV2( - uint256 _nonce, - uint256 _gasPrice, - uint256 _gasLimit, - address _to, - uint256 _value, - uint64 _smartChainId, - bytes memory _input - ) - internal - pure - returns (EnumItemTransactionV2WithLegacyTransaction memory) - { - LegacyTransaction memory transaction = LegacyTransaction( - _nonce, - _gasPrice, - _gasLimit, - PalletEthereum.EnumItemTransactionActionWithAddress( - 0, - _to - ), - _value, - _input, - _smartChainId * 2 + 36, // v - 0x3737373737373737373737373737373737373737373737373737373737373737, // r - 0x3737373737373737373737373737373737373737373737373737373737373737 // s - ); - - return - EnumItemTransactionV2WithLegacyTransaction( - 0, // enum index - transaction // legacyTransaction - ); - } - - function buildTransactionV2ForMessageTransact( - uint256 _gasLimit, - address _to, - uint64 _smartChainId, - bytes memory _input - ) - internal - pure - returns (EnumItemTransactionV2WithLegacyTransaction memory) - { - // nonce and gasPrice will be set by target chain - return buildTransactionV2(0, 0, _gasLimit, _to, 0, _smartChainId, _input); - } -} - -// File @darwinia/contracts-periphery/contracts/s2s/MessageEndpoint.sol@vv2.1.1-fix2 -// License-Identifier: MIT - - - -// srcDapp > endpoint[outboundLaneId] > substrate.send_message -// -> -// substrate.message_transact > remoteEndpoint[inboundLaneId] > TgtDapp.function -abstract contract MessageEndpoint { - // REMOTE - address public remoteEndpoint; - // message sender derived from remoteEndpoint - address public derivedMessageSender; - // call indices - bytes2 public remoteMessageTransactCallIndex; - // remote smart chain id - uint64 public remoteSmartChainId; - - // 1 gas ~= 18_750 weight - uint64 public constant REMOTE_WEIGHT_PER_GAS = 18_750; - - // LOCAL - // storage keys - bytes32 public storageKeyForMarketFee; - bytes32 public storageKeyForLatestNonce; - bytes32 public storageKeyForLastDeliveredNonce; - // call indices - bytes2 public sendMessageCallIndex; - - // lane ids - bytes4 public immutable OUTBOUND_LANE_ID; - bytes4 public immutable INBOUND_LANE_ID; - uint16 public immutable VERSION; - // precompile addresses - address public constant STORAGE_ADDRESS = - 0x0000000000000000000000000000000000000400; - address public constant DISPATCH_ADDRESS = - 0x0000000000000000000000000000000000000401; - - constructor(uint16 version, bytes4 outboundLaneId, bytes4 inboundLaneId) { - VERSION = version; - OUTBOUND_LANE_ID = outboundLaneId; - INBOUND_LANE_ID = inboundLaneId; - } - - /////////////////////////////// - // Outbound - /////////////////////////////// - function fee() public view returns (uint128) { - return MessageLib.marketFee(STORAGE_ADDRESS, storageKeyForMarketFee); - } - - // srcDapp > endpoint[outboundLaneId] > substrate.send_message - // -> - // substrate.message_transact(input) > remoteEndpoint[inboundLaneId] > TgtDapp.function - function _remoteExecute( - uint32 tgtSpecVersion, - address callReceiver, - bytes calldata callPayload, - uint256 gasLimit - ) internal returns (uint256) { - bytes memory input = abi.encodeWithSelector( - this.execute.selector, - callReceiver, - callPayload - ); - - return _remoteTransact(tgtSpecVersion, input, gasLimit); - } - - function _remoteTransact( - uint32 tgtSpecVersion, - bytes memory input, - uint256 gasLimit - ) internal returns (uint256) { - PalletEthereum.MessageTransactCall memory call = PalletEthereum - .MessageTransactCall( - // the call index of message_transact - remoteMessageTransactCallIndex, - // the evm transaction to transact - PalletEthereum.buildTransactionV2ForMessageTransact( - gasLimit, - remoteEndpoint, - remoteSmartChainId, - input - ) - ); - bytes memory callEncoded = PalletEthereum.encodeMessageTransactCall( - call - ); - - uint64 weight = uint64(gasLimit * REMOTE_WEIGHT_PER_GAS); - - return _remoteDispatch(tgtSpecVersion, callEncoded, weight); - } - - function _remoteDispatch( - uint32 tgtSpecVersion, - bytes memory tgtCallEncoded, - uint64 tgtCallWeight - ) internal returns (uint256) { - // Build the encoded message to be sent - bytes memory message = MessageLib.buildMessage( - tgtSpecVersion, - tgtCallWeight, - tgtCallEncoded - ); - - // Send the message - MessageLib.sendMessage( - DISPATCH_ADDRESS, - sendMessageCallIndex, - OUTBOUND_LANE_ID, - msg.value, - message - ); - - // Get nonce from storage - uint64 nonce = MessageLib.latestNonce( - STORAGE_ADDRESS, - storageKeyForLatestNonce, - OUTBOUND_LANE_ID - ); - - return encodeMessageId(OUTBOUND_LANE_ID, nonce); - } - - function _dispatch(bytes memory call) public { - MessageLib.dispatch(DISPATCH_ADDRESS, call, "!dispatch"); - } - - /////////////////////////////// - // Inbound - /////////////////////////////// - modifier onlyMessageSender() { - require( - derivedMessageSender == msg.sender, - "MessageEndpoint: Invalid sender" - ); - _; - } - - function execute( - address callReceiver, - bytes calldata callPayload - ) external onlyMessageSender { - if (_canBeExecuted(callReceiver, callPayload)) { - (bool success, ) = callReceiver.call(callPayload); - require(success, "MessageEndpoint: Call execution failed"); - } else { - revert("MessageEndpoint: Unapproved call"); - } - } - - // Check if the call can be executed - function _canBeExecuted( - address callReceiver, - bytes calldata callPayload - ) internal view virtual returns (bool); - - // Get the last delivered inbound message id - function lastDeliveredMessageId() public view returns (uint256) { - uint64 nonce = MessageLib.lastDeliveredNonce( - STORAGE_ADDRESS, - storageKeyForLastDeliveredNonce, - INBOUND_LANE_ID - ); - return encodeMessageId(INBOUND_LANE_ID, nonce); - } - - // Check if an inbound message has been delivered - function isMessageDelivered(uint256 messageId) public view returns (bool) { - (bytes4 laneId, uint64 nonce) = decodeMessageId(messageId); - uint64 lastNonce = MessageLib.lastDeliveredNonce( - STORAGE_ADDRESS, - storageKeyForLastDeliveredNonce, - laneId - ); - return nonce <= lastNonce; - } - - /////////////////////////////// - // Common functions - /////////////////////////////// - function decodeMessageId( - uint256 messageId - ) public view returns (bytes4, uint64) { - uint16 version = uint16(messageId >> 240); - require(version == VERSION, "MessageEndpoint: Invalid Version"); - return ( - bytes4(uint32(messageId >> 64)), - uint64(messageId & 0xffffffffffffffff) - ); - } - - function encodeMessageId( - bytes4 laneId, - uint64 nonce - ) public view returns (uint256) { - return - (uint256(uint32(laneId)) << 64) + - (uint256(VERSION) << 240) + - uint256(nonce); - } - - /////////////////////////////// - // Setters - /////////////////////////////// - function _setRemoteEndpoint( - bytes4 _remoteChainId, - address _remoteEndpoint - ) internal { - remoteEndpoint = _remoteEndpoint; - derivedMessageSender = MessageLib.deriveSender( - _remoteChainId, - _remoteEndpoint - ); - } - - function _setRemoteMessageTransactCallIndex( - bytes2 _remoteMessageTransactCallIndex - ) internal { - remoteMessageTransactCallIndex = _remoteMessageTransactCallIndex; - } - - function _setSendMessageCallIndex(bytes2 _sendMessageCallIndex) internal { - sendMessageCallIndex = _sendMessageCallIndex; - } - - function _setStorageKeyForMarketFee( - bytes32 _storageKeyForMarketFee - ) internal { - storageKeyForMarketFee = _storageKeyForMarketFee; - } - - function _setStorageKeyForLatestNonce( - bytes32 _storageKeyForLatestNonce - ) internal { - storageKeyForLatestNonce = _storageKeyForLatestNonce; - } - - function _setRemoteSmartChainId(uint64 _remoteSmartChainId) internal { - remoteSmartChainId = _remoteSmartChainId; - } - - function _setStorageKeyForLastDeliveredNonce( - bytes32 _storageKeyForLastDeliveredNonce - ) internal { - storageKeyForLastDeliveredNonce = _storageKeyForLastDeliveredNonce; - } -} - -// File contracts/mapping-token/v2/message-endpoints/DarwiniaSub2SubMessageEndpoint.sol -// License-Identifier: MIT - - -contract DarwiniaSub2SubMessageEndpoint is AccessController, MessageEndpoint { - constructor(uint16 _version, bytes4 _outboundLaneId, bytes4 _inboundLaneId) MessageEndpoint(_version, _outboundLaneId, _inboundLaneId) { - _initialize(msg.sender); - } - - function setRemoteHelix(bytes4 _remoteChainId, uint64 _remoteSmartChainId, address _remoteHelix) external onlyAdmin { - _setRemoteEndpoint(_remoteChainId, _remoteHelix); - _setRemoteSmartChainId(_remoteSmartChainId); - } - - function setRemoteCallIndex(bytes2 _remoteMessageTransactCallIndex) external onlyAdmin { - _setRemoteMessageTransactCallIndex(_remoteMessageTransactCallIndex); - } - - function setLocalCallInfo(bytes2 _callIndexOfSendMessage) external onlyAdmin { - _setSendMessageCallIndex(_callIndexOfSendMessage); - } - - function setLocalStorageKey( - bytes32 _srcStorageKeyForMarketFee, - bytes32 _srcStorageKeyForLatestNonce, - bytes32 _dstStorageKeyForLastDeliveredNonce - ) external onlyAdmin { - _setStorageKeyForMarketFee(_srcStorageKeyForMarketFee); - _setStorageKeyForLatestNonce(_srcStorageKeyForLatestNonce); - _setStorageKeyForLastDeliveredNonce(_dstStorageKeyForLastDeliveredNonce); - } - - function sendMessage( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address receiver, - bytes calldata callPayload - ) external onlyCaller whenNotPaused payable returns(uint256) { - return _remoteExecute(remoteSpecVersion, receiver, callPayload, remoteReceiveGasLimit); - } - - function _canBeExecuted(address callReceiver, bytes calldata) internal view override whenNotPaused returns (bool) { - return hasRole(CALLEE_ROLE, callReceiver); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2sub/Erc20.sol b/helix-contract/flatten/sub2sub/Erc20.sol deleted file mode 100644 index 1a680ce4..00000000 --- a/helix-contract/flatten/sub2sub/Erc20.sol +++ /dev/null @@ -1,555 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 4/28/2023 - **/ - -pragma solidity ^0.8.10; - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/access/Ownable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) - - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor() { - _transferOwnership(_msgSender()); - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if the sender is not the owner. - */ - function _checkOwner() internal view virtual { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - address oldOwner = _owner; - _owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/math/SafeMath.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) - - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the subtraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} - -// File contracts/mapping-token/v2/erc20-mapping-protocol/Erc20.sol -// License-Identifier: MIT - - - -contract Erc20 is IERC20, Ownable { - using SafeMath for uint256; - - mapping (address => uint256) private _balances; - - mapping (address => mapping (address => uint256)) private _allowances; - - uint256 private _totalSupply; - - string public name; - string public symbol; - uint8 public decimals; - - constructor(string memory _name, string memory _symbol, uint8 _decimals) { - name = _name; - symbol = _symbol; - decimals = _decimals; - _transferOwnership(_msgSender()); - } - - function totalSupply() public view override returns (uint256) { - return _totalSupply; - } - - function balanceOf(address account) public view override returns (uint256) { - return _balances[account]; - } - - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(msg.sender, recipient, amount); - return true; - } - - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - function approve(address spender, uint256 amount) public virtual override returns (bool) { - _approve(msg.sender, spender, amount); - return true; - } - - function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(sender, recipient, amount); - _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance")); - return true; - } - - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); - return true; - } - - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); - return true; - } - - function _transfer(address sender, address recipient, uint256 amount) internal virtual { - require(sender != address(0), "ERC20: transfer from the zero address"); - require(recipient != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(sender, recipient, amount); - - _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); - _balances[recipient] = _balances[recipient].add(amount); - emit Transfer(sender, recipient, amount); - } - - // only factory contract can mint with the lock proof from ethereum - function mint(address account, uint256 amount) external onlyOwner { - _mint(account, amount); - } - - function burn(address account, uint256 amount) external { - if (account != msg.sender && owner() != msg.sender && _allowances[account][msg.sender] != type(uint256).max) { - _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount, "ERC20: decreased allowance below zero")); - } - _burn(account, amount); - } - - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _beforeTokenTransfer(address(0), account, amount); - - _totalSupply = _totalSupply.add(amount); - _balances[account] = _balances[account].add(amount); - emit Transfer(address(0), account, amount); - } - - function _burn(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: burn from the zero address"); - - _beforeTokenTransfer(account, address(0), amount); - - _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); - _totalSupply = _totalSupply.sub(amount); - emit Transfer(account, address(0), amount); - } - - function _approve(address owner, address spender, uint256 amount) internal virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2sub/Erc20Sub2SubBacking.sol b/helix-contract/flatten/sub2sub/Erc20Sub2SubBacking.sol deleted file mode 100644 index 76cd3f53..00000000 --- a/helix-contract/flatten/sub2sub/Erc20Sub2SubBacking.sol +++ /dev/null @@ -1,2181 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 4/28/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/utils/DailyLimit.sol -// License-Identifier: MIT - - -/// @title relay with daily limit - Allows the relay to mint token in a daily limit. -contract DailyLimit { - - event DailyLimitChange(address token, uint dailyLimit); - - mapping(address => uint) public dailyLimit; - // deprecated, slot for upgrade - mapping(address => uint) public _slotReserved; - mapping(address => uint) public spentToday; - - uint constant public SPEND_BIT_LENGTH = 192; - uint constant public LASTDAY_BIT_LENGTH = 64; - - /// ==== Internal functions ==== - - /// @dev Contract constructor sets initial owners, required number of confirmations and daily mint limit. - /// @param _token Token address. - /// @param _dailyLimit Amount in wei, which can be mint without confirmations on a daily basis. - function _setDailyLimit(address _token, uint _dailyLimit) - internal - { - require(_dailyLimit < type(uint192).max, "DaliyLimit: overflow uint192"); - dailyLimit[_token] = _dailyLimit; - } - - /// @dev Allows to change the daily limit. - /// @param _token Token address. - /// @param _dailyLimit Amount in wei. - function _changeDailyLimit(address _token, uint _dailyLimit) - internal - { - require(_dailyLimit < type(uint192).max, "DaliyLimit: overflow uint192"); - dailyLimit[_token] = _dailyLimit; - emit DailyLimitChange(_token, _dailyLimit); - } - - /// @dev Allows to change the daily limit. - /// @param token Token address. - /// @param amount Amount in wei. - function expendDailyLimit(address token, uint amount) - internal - { - uint spentInfo = spentToday[token]; - uint lastday = spentInfo >> SPEND_BIT_LENGTH; - uint lastspent = spentInfo << LASTDAY_BIT_LENGTH >> LASTDAY_BIT_LENGTH; - if (block.timestamp > lastday + 24 hours) { - require(amount <= dailyLimit[token], "DailyLimit: amount exceed daily limit"); - spentToday[token] = (block.timestamp << SPEND_BIT_LENGTH) + amount; - return; - } - require(lastspent + amount <= dailyLimit[token] && amount <= dailyLimit[token], "DailyLimit: exceed daily limit"); - spentToday[token] = spentInfo + amount; - } - - /// ==== Web3 call functions ==== - - /// @dev Returns maximum withdraw amount. - /// @param token Token address. - /// @return Returns amount. - function calcMaxWithdraw(address token) - public - view - returns (uint) - { - uint spentInfo = spentToday[token]; - uint lastday = spentInfo >> SPEND_BIT_LENGTH; - uint lastspent = spentInfo << LASTDAY_BIT_LENGTH >> LASTDAY_BIT_LENGTH; - if (block.timestamp > lastday + 24 hours) { - return dailyLimit[token]; - } - - if (dailyLimit[token] < lastspent) { - return 0; - } - - return dailyLimit[token] - lastspent; - } -} - -// File contracts/mapping-token/interfaces/IGuard.sol -// License-Identifier: MIT - - -interface IGuard { - function deposit(uint256 id, address token, address recipient, uint256 amount) external; -} - -// File contracts/mapping-token/interfaces/IErc20MappingTokenFactory.sol -// License-Identifier: MIT - - -interface IErc20MappingTokenFactory { - function newErc20Contract( - address originalToken, - string memory bridgedChainName, - string memory name, - string memory symbol, - uint8 decimals, - uint256 dailyLimit - ) external returns (address mappingToken); - function issueMappingToken( - address originalToken, - address recipient, - uint256 amount - ) external; -} - -// File contracts/mapping-token/interfaces/IBacking.sol -// License-Identifier: MIT - - -interface IBacking { - function unlockFromRemote( - address originalToken, - address recipient, - uint256 amount) external; -} - -interface IBackingSupportNative { - function unlockFromRemoteNative( - address recipient, - uint256 amount) external; -} - -// File contracts/mapping-token/interfaces/IHelixApp.sol -// License-Identifier: MIT - - -interface IHelixAppSupportWithdrawFailed { - function handleUnlockFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; - function handleUnlockFailureFromRemoteNative( - uint256 messageId, - address sender, - uint256 amount - ) external; - function handleIssuingFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; -} - -// File contracts/mapping-token/interfaces/IHelixMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixMessageEndpoint { - function sendMessage(address receiver, bytes calldata encoded) external payable returns (uint256); -} - -// File contracts/mapping-token/interfaces/IHelixSub2SubMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixSub2SubMessageEndpoint is IHelixMessageEndpoint { - function fee() external view returns (uint256); - function lastDeliveredMessageId() external view returns (uint256); - function isMessageDelivered(uint256 messageId) external view returns(bool); - function sendMessage( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address receiver, - bytes calldata encoded) external payable returns (uint256); -} - -// File contracts/mapping-token/interfaces/IWToken.sol -// License-Identifier: MIT - - -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/AccessController.sol -// License-Identifier: MIT - - -contract AccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - bytes32 public constant CALLER_ROLE = keccak256("CALLER_ROLE"); - bytes32 public constant CALLEE_ROLE = keccak256("CALLEE_ROLE"); - - // access controller - // admin is helix Dao - modifier onlyAdmin() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "AccessController:Bad admin role"); - _; - } - - // operator - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "AccessController:Bad operator role"); - _; - } - - modifier onlyCaller() { - require(hasRole(CALLER_ROLE, msg.sender), "AccessController:Bad caller role"); - _; - } - - modifier onlyCallee() { - require(hasRole(CALLEE_ROLE, msg.sender), "AccessController:Bad callee role"); - _; - } - - function _initialize(address admin) internal { - _setRoleAdmin(CALLER_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(CALLEE_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, admin); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/mapping-token/v2/Backing.sol -// License-Identifier: MIT - - -contract Backing is AccessController, Initializable { - address public messageEndpoint; - address public remoteMappingTokenFactory; - - uint256 internal locked; - modifier nonReentrant { - require(locked == 0, "backing: locked"); - locked = 1; - _; - locked = 0; - } - - modifier onlyMessageEndpoint() { - require(messageEndpoint == msg.sender, "Backing:Bad message handle"); - _; - } - - function initialize(address _messageEndpoint) public initializer { - messageEndpoint = _messageEndpoint; - _initialize(msg.sender); - } - - function _setMessageEndpoint(address _messageEndpoint) internal { - messageEndpoint = _messageEndpoint; - } - - function setRemoteMappingTokenFactory(address _remoteMappingTokenFactory) external onlyAdmin { - remoteMappingTokenFactory = _remoteMappingTokenFactory; - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/structs/BitMaps.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/structs/BitMaps.sol) - -/** - * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. - * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. - */ -library BitMaps { - struct BitMap { - mapping(uint256 => uint256) _data; - } - - /** - * @dev Returns whether the bit at `index` is set. - */ - function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - return bitmap._data[bucket] & mask != 0; - } - - /** - * @dev Sets the bit at `index` to the boolean `value`. - */ - function setTo( - BitMap storage bitmap, - uint256 index, - bool value - ) internal { - if (value) { - set(bitmap, index); - } else { - unset(bitmap, index); - } - } - - /** - * @dev Sets the bit at `index`. - */ - function set(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] |= mask; - } - - /** - * @dev Unsets the bit at `index`. - */ - function unset(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] &= ~mask; - } -} - -// File contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubBacking.sol -// License-Identifier: MIT - - - - - - - - - - -contract Erc20Sub2SubBacking is Backing, DailyLimit, IBacking { - struct LockedInfo { - bytes32 hash; - bool hasRefundForFailed; - } - - address public guard; - string public chainName; - uint256 public helixFee; - - // (transferId => LockedInfo) - mapping(uint256 => LockedInfo) public lockedMessages; - BitMaps.BitMap unlockedMessages; - address public wToken; - - event NewErc20TokenRegistered(uint256 transferId, address token); - event TokenLocked(uint256 transferId, bool isNative, address token, address sender, address recipient, uint256 amount, uint256 fee); - event TokenUnlocked(uint256 transferId, bool isNative, address token, address recipient, uint256 amount); - event RemoteIssuingFailure(uint256 refundId, uint256 transferId, uint256 fee); - event TokenUnlockedForFailed(uint256 transferId, bool isNative, address token, address recipient, uint256 amount); - - receive() external payable {} - - function setMessageEndpoint(address _messageEndpoint) external onlyAdmin { - _setMessageEndpoint(_messageEndpoint); - } - - function setChainName(string memory _chainName) external onlyAdmin { - chainName = _chainName; - } - - function changeDailyLimit(address token, uint amount) public onlyAdmin { - _changeDailyLimit(token, amount); - } - - function setHelixFee(uint256 _helixFee) external onlyAdmin { - helixFee = _helixFee; - } - - function updateGuard(address newGuard) external onlyAdmin { - guard = newGuard; - } - - function setWToken(address _wToken) external onlyAdmin { - wToken = _wToken; - } - - function fee() external view returns(uint256) { - return IHelixSub2SubMessageEndpoint(messageEndpoint).fee() + helixFee; - } - - function _sendMessage( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - bytes memory message, - uint256 prepaid - ) internal nonReentrant returns(uint256, uint256) { - uint256 bridgeFee = IHelixSub2SubMessageEndpoint(messageEndpoint).fee(); - uint256 totalFee = bridgeFee + helixFee; - require(prepaid >= totalFee, "Backing:the fee is not enough"); - if (prepaid > totalFee) { - // refund fee to msgSender - payable(msg.sender).transfer(prepaid - totalFee); - } - uint256 transferId = IHelixSub2SubMessageEndpoint(messageEndpoint).sendMessage{value: bridgeFee}( - remoteSpecVersion, - remoteReceiveGasLimit, - remoteMappingTokenFactory, - message); - return (transferId, totalFee); - } - - /** - * @notice reigister new erc20 token to the bridge. Only owner can do this. - * @param token the original token address - * @param name the name of the original token - * @param symbol the symbol of the original token - * @param decimals the decimals of the original token - */ - function register( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address token, - string memory name, - string memory symbol, - uint8 decimals, - uint256 dailyLimit - ) external payable onlyOperator { - bytes memory newErc20Contract = abi.encodeWithSelector( - IErc20MappingTokenFactory.newErc20Contract.selector, - token, - chainName, - name, - symbol, - decimals, - dailyLimit - ); - (uint256 transferId,) = _sendMessage( - remoteSpecVersion, - remoteReceiveGasLimit, - newErc20Contract, - msg.value - ); - _changeDailyLimit(token, dailyLimit); - emit NewErc20TokenRegistered(transferId, token); - } - - function _lockAndRemoteIssuing( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address token, - address recipient, - uint256 amount, - uint256 prepaid, - bool isNative - ) internal { - bytes memory issueMappingToken = abi.encodeWithSelector( - IErc20MappingTokenFactory.issueMappingToken.selector, - token, - recipient, - amount - ); - (uint256 transferId, uint256 totalFee) = _sendMessage( - remoteSpecVersion, - remoteReceiveGasLimit, - issueMappingToken, - prepaid - ); - require(lockedMessages[transferId].hash == bytes32(0), "backing: message exist"); - bytes32 lockMessageHash = hash(abi.encodePacked(transferId, token, msg.sender, amount)); - lockedMessages[transferId] = LockedInfo(lockMessageHash, false); - emit TokenLocked(transferId, isNative, token, msg.sender, recipient, amount, totalFee); - } - - - /** - * @notice lock original token and issuing mapping token from bridged chain - * @dev maybe some tokens will take some fee when transfer - * @param token the original token address - * @param recipient the recipient who will receive the issued mapping token - * @param amount amount of the locked token - */ - function lockAndRemoteIssuing( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address token, - address recipient, - uint256 amount - ) external payable whenNotPaused { - require(IERC20(token).transferFrom(msg.sender, address(this), amount), "Backing:transfer tokens failed"); - _lockAndRemoteIssuing( - remoteSpecVersion, - remoteReceiveGasLimit, - token, - recipient, - amount, - msg.value, - false); - } - - function lockAndRemoteIssuingNative( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address recipient, - uint256 amount - ) external payable whenNotPaused { - IWToken(wToken).deposit{value: amount}(); - _lockAndRemoteIssuing( - remoteSpecVersion, - remoteReceiveGasLimit, - wToken, - recipient, - amount, - msg.value - amount, - true); - } - - /** - * @notice this will be called by inboundLane when the remote mapping token burned and want to unlock the original token - * @param token the original token address - * @param recipient the recipient who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function unlockFromRemote( - address token, - address recipient, - uint256 amount - ) public onlyMessageEndpoint whenNotPaused { - expendDailyLimit(token, amount); - // current message id is last message id + 1 - uint256 transferId = IHelixSub2SubMessageEndpoint(messageEndpoint).lastDeliveredMessageId() + 1; - require(BitMaps.get(unlockedMessages, transferId) == false, "Backing:message has been accepted"); - BitMaps.set(unlockedMessages, transferId); - if (guard != address(0)) { - uint allowance = IERC20(token).allowance(address(this), guard); - require(IERC20(token).approve(guard, amount + allowance), "Backing:approve token transfer to guard failed"); - IGuard(guard).deposit(transferId, token, recipient, amount); - } else { - require(IERC20(token).transfer(recipient, amount), "Backing:unlock transfer failed"); - } - emit TokenUnlocked(transferId, false, token, recipient, amount); - } - - /** - * @notice this will be called by inboundLane when the remote mapping token burned and want to unlock the original native token - * @param recipient the recipient who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function unlockFromRemoteNative( - address payable recipient, - uint256 amount - ) public onlyMessageEndpoint whenNotPaused { - expendDailyLimit(wToken, amount); - uint256 transferId = IHelixSub2SubMessageEndpoint(messageEndpoint).lastDeliveredMessageId() + 1; - require(BitMaps.get(unlockedMessages, transferId) == false, "Backing:message has been accepted"); - BitMaps.set(unlockedMessages, transferId); - if (guard != address(0)) { - uint allowance = IERC20(wToken).allowance(address(this), guard); - require(IERC20(wToken).approve(guard, amount + allowance), "Backing:approve token transfer to guard failed"); - IGuard(guard).deposit(transferId, wToken, recipient, amount); - } else { - IWToken(wToken).withdraw(amount); - recipient.transfer(amount); - } - emit TokenUnlocked(transferId, true, wToken, recipient, amount); - } - - function _handleUnlockFailureFromRemote( - uint256 transferId, - address token, - address originSender, - uint256 amount - ) internal { - LockedInfo memory lockedMessage = lockedMessages[transferId]; - require(lockedMessage.hasRefundForFailed == false, "Backing: the locked message has been refund"); - bytes32 messageHash = hash(abi.encodePacked(transferId, token, originSender, amount)); - require(lockedMessage.hash == messageHash, "Backing: message is not matched"); - lockedMessages[transferId].hasRefundForFailed = true; - } - - /** - * @notice this will be called by messageEndpoint when the remote issue failed and want to unlock the original token - * @param token the original token address - * @param originSender the originSender who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function handleUnlockFailureFromRemote( - uint256 transferId, - address token, - address originSender, - uint256 amount - ) external onlyMessageEndpoint whenNotPaused { - _handleUnlockFailureFromRemote(transferId, token, originSender, amount); - require(IERC20(token).transfer(originSender, amount), "Backing:unlock transfer failed"); - emit TokenUnlockedForFailed(transferId, false, token, originSender, amount); - } - - /** - * @notice this will be called by messageEndpoint when the remote issue failed and want to unlock the original native token - * @param originSender the originSender who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function handleUnlockFailureFromRemoteNative( - uint256 transferId, - address originSender, - uint256 amount - ) external onlyMessageEndpoint whenNotPaused { - _handleUnlockFailureFromRemote(transferId, wToken, originSender, amount); - IWToken(wToken).withdraw(amount); - payable(originSender).transfer(amount); - emit TokenUnlockedForFailed(transferId, true, wToken, originSender, amount); - } - - function remoteIssuingFailure( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - uint256 transferId, - address mappingToken, - address originalSender, - uint256 amount - ) external payable whenNotPaused { - // must not exist in successful issue list - require(BitMaps.get(unlockedMessages, transferId) == false, "Backing:success message can't refund for failed"); - // must has been checked by message layer - bool messageChecked = IHelixSub2SubMessageEndpoint(messageEndpoint).isMessageDelivered(transferId); - require(messageChecked, "Backing:the message is not checked by message layer"); - bytes memory unlockForFailed = abi.encodeWithSelector( - IHelixAppSupportWithdrawFailed.handleIssuingFailureFromRemote.selector, - transferId, - mappingToken, - originalSender, - amount - ); - (uint256 refundId, uint256 totalFee) = _sendMessage( - remoteSpecVersion, - remoteReceiveGasLimit, - unlockForFailed, - msg.value - ); - emit RemoteIssuingFailure(refundId, transferId, totalFee); - } - - /** - * @notice this should not be used unless there is a non-recoverable error in the bridge or the target chain - * we use this to protect user's asset from being locked up - */ - function rescueFunds( - address token, - address recipient, - uint256 amount - ) external onlyAdmin { - IERC20(token).transfer(recipient, amount); - } - - function withdrawFee( - address payable recipient, - uint256 amount - ) external onlyAdmin { - recipient.transfer(amount); - } - - function hash(bytes memory value) public pure returns (bytes32) { - return sha256(value); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2sub/Erc20Sub2SubMappingTokenFactory.sol b/helix-contract/flatten/sub2sub/Erc20Sub2SubMappingTokenFactory.sol deleted file mode 100644 index ff9ffbe8..00000000 --- a/helix-contract/flatten/sub2sub/Erc20Sub2SubMappingTokenFactory.sol +++ /dev/null @@ -1,2662 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 4/28/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/utils/DailyLimit.sol -// License-Identifier: MIT - - -/// @title relay with daily limit - Allows the relay to mint token in a daily limit. -contract DailyLimit { - - event DailyLimitChange(address token, uint dailyLimit); - - mapping(address => uint) public dailyLimit; - // deprecated, slot for upgrade - mapping(address => uint) public _slotReserved; - mapping(address => uint) public spentToday; - - uint constant public SPEND_BIT_LENGTH = 192; - uint constant public LASTDAY_BIT_LENGTH = 64; - - /// ==== Internal functions ==== - - /// @dev Contract constructor sets initial owners, required number of confirmations and daily mint limit. - /// @param _token Token address. - /// @param _dailyLimit Amount in wei, which can be mint without confirmations on a daily basis. - function _setDailyLimit(address _token, uint _dailyLimit) - internal - { - require(_dailyLimit < type(uint192).max, "DaliyLimit: overflow uint192"); - dailyLimit[_token] = _dailyLimit; - } - - /// @dev Allows to change the daily limit. - /// @param _token Token address. - /// @param _dailyLimit Amount in wei. - function _changeDailyLimit(address _token, uint _dailyLimit) - internal - { - require(_dailyLimit < type(uint192).max, "DaliyLimit: overflow uint192"); - dailyLimit[_token] = _dailyLimit; - emit DailyLimitChange(_token, _dailyLimit); - } - - /// @dev Allows to change the daily limit. - /// @param token Token address. - /// @param amount Amount in wei. - function expendDailyLimit(address token, uint amount) - internal - { - uint spentInfo = spentToday[token]; - uint lastday = spentInfo >> SPEND_BIT_LENGTH; - uint lastspent = spentInfo << LASTDAY_BIT_LENGTH >> LASTDAY_BIT_LENGTH; - if (block.timestamp > lastday + 24 hours) { - require(amount <= dailyLimit[token], "DailyLimit: amount exceed daily limit"); - spentToday[token] = (block.timestamp << SPEND_BIT_LENGTH) + amount; - return; - } - require(lastspent + amount <= dailyLimit[token] && amount <= dailyLimit[token], "DailyLimit: exceed daily limit"); - spentToday[token] = spentInfo + amount; - } - - /// ==== Web3 call functions ==== - - /// @dev Returns maximum withdraw amount. - /// @param token Token address. - /// @return Returns amount. - function calcMaxWithdraw(address token) - public - view - returns (uint) - { - uint spentInfo = spentToday[token]; - uint lastday = spentInfo >> SPEND_BIT_LENGTH; - uint lastspent = spentInfo << LASTDAY_BIT_LENGTH >> LASTDAY_BIT_LENGTH; - if (block.timestamp > lastday + 24 hours) { - return dailyLimit[token]; - } - - if (dailyLimit[token] < lastspent) { - return 0; - } - - return dailyLimit[token] - lastspent; - } -} - -// File contracts/mapping-token/interfaces/IGuard.sol -// License-Identifier: MIT - - -interface IGuard { - function deposit(uint256 id, address token, address recipient, uint256 amount) external; -} - -// File contracts/mapping-token/interfaces/IHelixMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixMessageEndpoint { - function sendMessage(address receiver, bytes calldata encoded) external payable returns (uint256); -} - -// File contracts/mapping-token/interfaces/IHelixSub2SubMessageEndpoint.sol -// License-Identifier: MIT - - -interface IHelixSub2SubMessageEndpoint is IHelixMessageEndpoint { - function fee() external view returns (uint256); - function lastDeliveredMessageId() external view returns (uint256); - function isMessageDelivered(uint256 messageId) external view returns(bool); - function sendMessage( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address receiver, - bytes calldata encoded) external payable returns (uint256); -} - -// File contracts/mapping-token/interfaces/IBacking.sol -// License-Identifier: MIT - - -interface IBacking { - function unlockFromRemote( - address originalToken, - address recipient, - uint256 amount) external; -} - -interface IBackingSupportNative { - function unlockFromRemoteNative( - address recipient, - uint256 amount) external; -} - -// File contracts/mapping-token/interfaces/IErc20MappingTokenFactory.sol -// License-Identifier: MIT - - -interface IErc20MappingTokenFactory { - function newErc20Contract( - address originalToken, - string memory bridgedChainName, - string memory name, - string memory symbol, - uint8 decimals, - uint256 dailyLimit - ) external returns (address mappingToken); - function issueMappingToken( - address originalToken, - address recipient, - uint256 amount - ) external; -} - -// File contracts/mapping-token/interfaces/IHelixApp.sol -// License-Identifier: MIT - - -interface IHelixAppSupportWithdrawFailed { - function handleUnlockFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; - function handleUnlockFailureFromRemoteNative( - uint256 messageId, - address sender, - uint256 amount - ) external; - function handleIssuingFailureFromRemote( - uint256 messageId, - address token, - address sender, - uint256 amount - ) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) - - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} - -// File @zeppelin-solidity/contracts/access/IAccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) - - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/utils/introspection/IERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/introspection/ERC165.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/access/AccessControl.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol) - - - - - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view virtual override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `_msgSender()` is missing `role`. - * Overriding this function changes the behavior of the {onlyRole} modifier. - * - * Format of the revert message is described in {_checkRole}. - * - * _Available since v4.6._ - */ - function _checkRole(bytes32 role) internal view virtual { - _checkRole(role, _msgSender()); - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view virtual { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleGranted} event. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - * - * May emit a {RoleRevoked} event. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - * - * May emit a {RoleRevoked} event. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * May emit a {RoleGranted} event. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleGranted} event. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - * - * May emit a {RoleRevoked} event. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} - -// File @zeppelin-solidity/contracts/utils/structs/EnumerableSet.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) - - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} - -// File @zeppelin-solidity/contracts/access/AccessControlEnumerable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) - - - - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File contracts/mapping-token/v2/AccessController.sol -// License-Identifier: MIT - - -contract AccessController is AccessControlEnumerable, Pausable { - bytes32 public constant DAO_ADMIN_ROLE = keccak256("DAO_ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - bytes32 public constant CALLER_ROLE = keccak256("CALLER_ROLE"); - bytes32 public constant CALLEE_ROLE = keccak256("CALLEE_ROLE"); - - // access controller - // admin is helix Dao - modifier onlyAdmin() { - require(hasRole(DAO_ADMIN_ROLE, msg.sender), "AccessController:Bad admin role"); - _; - } - - // operator - modifier onlyOperator() { - require(hasRole(OPERATOR_ROLE, msg.sender), "AccessController:Bad operator role"); - _; - } - - modifier onlyCaller() { - require(hasRole(CALLER_ROLE, msg.sender), "AccessController:Bad caller role"); - _; - } - - modifier onlyCallee() { - require(hasRole(CALLEE_ROLE, msg.sender), "AccessController:Bad callee role"); - _; - } - - function _initialize(address admin) internal { - _setRoleAdmin(CALLER_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(CALLEE_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(OPERATOR_ROLE, DAO_ADMIN_ROLE); - _setRoleAdmin(DAO_ADMIN_ROLE, DAO_ADMIN_ROLE); - _setupRole(DAO_ADMIN_ROLE, admin); - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } -} - -// File @zeppelin-solidity/contracts/access/Ownable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) - - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor() { - _transferOwnership(_msgSender()); - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if the sender is not the owner. - */ - function _checkOwner() internal view virtual { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - address oldOwner = _owner; - _owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} - -// File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) - - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -// File @zeppelin-solidity/contracts/proxy/utils/Initializable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) - - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original - * initialization step. This is essential to configure modules that are added through upgrades and that require - * initialization. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } -} - -// File contracts/mapping-token/v2/MappingTokenFactory.sol -// License-Identifier: MIT -// This is the Issuing Module(Mapping-token-factory) of the ethereum like bridge. -// We trust the inboundLane/outboundLane when we add them to the module. -// It means that each message from the inboundLane is verified correct and truthly from the sourceAccount. -// Only we need is to verify the sourceAccount is expected. And we add it to the Filter. - - - -contract MappingTokenFactory is AccessController, Initializable { - address[] public allMappingTokens; - // salt=>mappingToken, the salt is derived from origin token on backing chain - // so this is a mapping from origin to mapping token - mapping(bytes32 => address) public salt2MappingToken; - // mappingToken=>info the info is the original token info - // so this is a mapping from mappingToken to original token - mapping(address => address) public mappingToken2OriginalToken; - - address public messageEndpoint; - address public remoteBacking; - - uint256 internal locked; - modifier nonReentrant { - require(locked == 0, "MappingTokenFactory: locked"); - locked = 1; - _; - locked = 0; - } - - modifier onlyMessageEndpoint() { - require(messageEndpoint == msg.sender, "MappingTokenFactory:Bad message handle"); - _; - } - - function initialize(address _messageEndpoint) public initializer { - _setMessageEndpoint(_messageEndpoint); - _initialize(msg.sender); - } - - function _setMessageEndpoint(address _messageEndpoint) internal { - messageEndpoint = _messageEndpoint; - } - - function setRemoteBacking(address _remoteBacking) external onlyAdmin { - remoteBacking = _remoteBacking; - } - - function _transferMappingTokenOwnership(address mappingToken, address new_owner) internal { - Ownable(mappingToken).transferOwnership(new_owner); - } - - /** - * @notice add mapping-token address by owner - * @param salt the salt of the mapping token deployed - * @param originalToken the original token address - * @param mappingToken the mapping token address - */ - function _addMappingToken( - bytes32 salt, - address originalToken, - address mappingToken - ) internal { - // save the mapping tokens in an array so it can be listed - allMappingTokens.push(mappingToken); - // map the originToken to mappingInfo - salt2MappingToken[salt] = mappingToken; - // map the mappingToken to origin info - mappingToken2OriginalToken[mappingToken] = originalToken; - } - - // internal - function _deploy(bytes32 salt, bytes memory bytecodeWithInitdata) internal returns (address addr) { - assembly { - addr := create2(0, add(bytecodeWithInitdata, 0x20), mload(bytecodeWithInitdata), salt) - if iszero(extcodesize(addr)) { revert(0, 0) } - } - } - - function tokenLength() public view returns (uint) { - return allMappingTokens.length; - } - - function getMappingToken(address backingAddress, address originalToken) public view returns (address) { - bytes32 salt = keccak256(abi.encodePacked(backingAddress, originalToken)); - return salt2MappingToken[salt]; - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File @zeppelin-solidity/contracts/utils/math/SafeMath.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) - - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the subtraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} - -// File contracts/mapping-token/v2/erc20-mapping-protocol/Erc20.sol -// License-Identifier: MIT - - - -contract Erc20 is IERC20, Ownable { - using SafeMath for uint256; - - mapping (address => uint256) private _balances; - - mapping (address => mapping (address => uint256)) private _allowances; - - uint256 private _totalSupply; - - string public name; - string public symbol; - uint8 public decimals; - - constructor(string memory _name, string memory _symbol, uint8 _decimals) { - name = _name; - symbol = _symbol; - decimals = _decimals; - _transferOwnership(_msgSender()); - } - - function totalSupply() public view override returns (uint256) { - return _totalSupply; - } - - function balanceOf(address account) public view override returns (uint256) { - return _balances[account]; - } - - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(msg.sender, recipient, amount); - return true; - } - - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - function approve(address spender, uint256 amount) public virtual override returns (bool) { - _approve(msg.sender, spender, amount); - return true; - } - - function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(sender, recipient, amount); - _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance")); - return true; - } - - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); - return true; - } - - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); - return true; - } - - function _transfer(address sender, address recipient, uint256 amount) internal virtual { - require(sender != address(0), "ERC20: transfer from the zero address"); - require(recipient != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(sender, recipient, amount); - - _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); - _balances[recipient] = _balances[recipient].add(amount); - emit Transfer(sender, recipient, amount); - } - - // only factory contract can mint with the lock proof from ethereum - function mint(address account, uint256 amount) external onlyOwner { - _mint(account, amount); - } - - function burn(address account, uint256 amount) external { - if (account != msg.sender && owner() != msg.sender && _allowances[account][msg.sender] != type(uint256).max) { - _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount, "ERC20: decreased allowance below zero")); - } - _burn(account, amount); - } - - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _beforeTokenTransfer(address(0), account, amount); - - _totalSupply = _totalSupply.add(amount); - _balances[account] = _balances[account].add(amount); - emit Transfer(address(0), account, amount); - } - - function _burn(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: burn from the zero address"); - - _beforeTokenTransfer(account, address(0), amount); - - _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); - _totalSupply = _totalSupply.sub(amount); - emit Transfer(account, address(0), amount); - } - - function _approve(address owner, address spender, uint256 amount) internal virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } -} - -// File @zeppelin-solidity/contracts/utils/structs/BitMaps.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/structs/BitMaps.sol) - -/** - * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. - * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. - */ -library BitMaps { - struct BitMap { - mapping(uint256 => uint256) _data; - } - - /** - * @dev Returns whether the bit at `index` is set. - */ - function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - return bitmap._data[bucket] & mask != 0; - } - - /** - * @dev Sets the bit at `index` to the boolean `value`. - */ - function setTo( - BitMap storage bitmap, - uint256 index, - bool value - ) internal { - if (value) { - set(bitmap, index); - } else { - unset(bitmap, index); - } - } - - /** - * @dev Sets the bit at `index`. - */ - function set(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] |= mask; - } - - /** - * @dev Unsets the bit at `index`. - */ - function unset(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] &= ~mask; - } -} - -// File contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubMappingTokenFactory.sol -// License-Identifier: MIT -// This is the Issuing Module(Mapping-token-factory) of the ethereum like bridge. -// We trust the inboundLane/outboundLane when we add them to the module. -// It means that each message from the inboundLane is verified correct and truthly from the sourceAccount. -// Only we need is to verify the sourceAccount is expected. And we add it to the Filter. - - - - - - - - - -contract Erc20Sub2SubMappingTokenFactory is DailyLimit, IErc20MappingTokenFactory, MappingTokenFactory { - struct BurnInfo { - bytes32 hash; - bool hasRefundForFailed; - } - - // guard - address public guard; - uint256 public helixFee; - - mapping(uint256 => BurnInfo) public burnMessages; - BitMaps.BitMap issueMessages; - address public xwToken; - - event NewLogicSetted(uint32 tokenType, address addr); - event IssuingERC20Created(address originalToken, address mappingToken); - event BurnAndRemoteUnlocked(uint256 transferId, bool isNative, bytes32 messageHash, address sender, address recipient, address token, uint256 amount, uint256 fee); - event TokenIssued(uint256 transferId, address token, address recipient, uint256 amount); - event TokenRemintForFailed(uint256 transferId, address token, address recipient, uint256 amount); - event RemoteUnlockFailure(uint256 refundId, uint256 transferId, uint256 fee); - - function setMessageEndpoint(address _messageEndpoint) external onlyAdmin { - _setMessageEndpoint(_messageEndpoint); - } - - function setMappingNativeWrappedToken(address _xwToken) external onlyAdmin { - xwToken = _xwToken; - } - - receive() external payable { - } - - /** - * @notice only admin can transfer the ownership of the mapping token from factory to other account - * generally we should not do this. When we encounter a non-recoverable error, we temporarily transfer the privileges to a maintenance account. - * @param mappingToken the address the mapping token - * @param new_owner the new owner of the mapping token - */ - function transferMappingTokenOwnership(address mappingToken, address new_owner) external onlyAdmin { - _transferMappingTokenOwnership(mappingToken, new_owner); - } - - function updateGuard(address newGuard) external onlyAdmin { - guard = newGuard; - } - - function changeDailyLimit(address mappingToken, uint amount) public onlyAdmin { - _changeDailyLimit(mappingToken, amount); - } - - function setHelixFee(uint256 _helixFee) external onlyAdmin { - helixFee = _helixFee; - } - - function fee() external view returns(uint256) { - return IHelixSub2SubMessageEndpoint(messageEndpoint).fee() + helixFee; - } - - function _sendMessage( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - bytes memory message - ) internal nonReentrant returns(uint256, uint256) { - uint256 bridgeFee = IHelixSub2SubMessageEndpoint(messageEndpoint).fee(); - uint256 totalFee = bridgeFee + helixFee; - require(msg.value >= totalFee, "MTF:the fee is not enough"); - if (msg.value > totalFee) { - // refund fee to msgSender - payable(msg.sender).transfer(msg.value - totalFee); - } - uint256 transferId = IHelixSub2SubMessageEndpoint(messageEndpoint).sendMessage{value: bridgeFee}( - remoteSpecVersion, - remoteReceiveGasLimit, - remoteBacking, - message); - return (transferId, totalFee); - } - - /** - * @notice create new erc20 mapping contract, this can only be called by inboundLane - * @param originalToken the original token address - * @param name the name of the original erc20 token - * @param symbol the symbol of the original erc20 token - * @param decimals the decimals of the original erc20 token - */ - function newErc20Contract( - address originalToken, - string memory bridgedChainName, - string memory name, - string memory symbol, - uint8 decimals, - uint256 dailyLimit - ) public onlyMessageEndpoint whenNotPaused returns (address mappingToken) { - bytes32 salt = keccak256(abi.encodePacked(remoteBacking, originalToken)); - require(salt2MappingToken[salt] == address(0), "MTF:contract has been deployed"); - - bytes memory bytecode = type(Erc20).creationCode; - bytes memory bytecodeWithInitdata = abi.encodePacked( - bytecode, - abi.encode( - string(abi.encodePacked(name, "[", bridgedChainName, ">")), - string(abi.encodePacked("x", symbol)), - decimals - )); - mappingToken = _deploy(salt, bytecodeWithInitdata); - _addMappingToken(salt, originalToken, mappingToken); - _changeDailyLimit(mappingToken, dailyLimit); - emit IssuingERC20Created(originalToken, mappingToken); - } - - /** - * @notice issue mapping token, only can be called by inboundLane - * @param originalToken the original token address - * @param recipient the recipient of the issued mapping token - * @param amount the amount of the issued mapping token - */ - function issueMappingToken( - address originalToken, - address recipient, - uint256 amount - ) public onlyMessageEndpoint whenNotPaused { - address mappingToken = getMappingToken(remoteBacking, originalToken); - require(mappingToken != address(0), "MTF:mapping token has not created"); - require(amount > 0, "MTF:can not receive amount zero"); - expendDailyLimit(mappingToken, amount); - uint256 transferId = IHelixSub2SubMessageEndpoint(messageEndpoint).lastDeliveredMessageId() + 1; - require(BitMaps.get(issueMessages, transferId) == false, "MTF:message has been accepted"); - BitMaps.set(issueMessages, transferId); - if (guard != address(0)) { - Erc20(mappingToken).mint(address(this), amount); - uint allowance = IERC20(mappingToken).allowance(address(this), guard); - require(IERC20(mappingToken).approve(guard, allowance + amount), "MTF:approve token transfer to guard failed"); - IGuard(guard).deposit(transferId, mappingToken, recipient, amount); - } else { - Erc20(mappingToken).mint(recipient, amount); - } - emit TokenIssued(transferId, mappingToken, recipient, amount); - } - - /** - * @notice burn mapping token and unlock remote original token - * @param mappingToken the burt mapping token address - * @param recipient the recipient of the remote unlocked token - * @param amount the amount of the burn and unlock - */ - function _burnAndRemoteUnlock( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address mappingToken, - address recipient, - uint256 amount, - bytes memory remoteUnlockCall, - bool isNative - ) internal { - require(amount > 0, "MTF:can not transfer amount zero"); - // transfer to this and then burn - require(IERC20(mappingToken).transferFrom(msg.sender, address(this), amount), "MTF:transfer token failed"); - Erc20(mappingToken).burn(address(this), amount); - - (uint256 transferId, uint256 totalFee) = _sendMessage( - remoteSpecVersion, - remoteReceiveGasLimit, - remoteUnlockCall - ); - require(burnMessages[transferId].hash == bytes32(0), "MTF: message exist"); - bytes32 messageHash = hash(abi.encodePacked(transferId, mappingToken, msg.sender, amount)); - burnMessages[transferId] = BurnInfo(messageHash, false); - emit BurnAndRemoteUnlocked(transferId, isNative, messageHash, msg.sender, recipient, mappingToken, amount, totalFee); - } - - /** - * @notice burn mapping token and unlock remote original token - * @param mappingToken the burt mapping token address - * @param recipient the recipient of the remote unlocked token - * @param amount the amount of the burn and unlock - */ - function burnAndRemoteUnlock( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address mappingToken, - address recipient, - uint256 amount - ) external payable whenNotPaused { - address originalToken = mappingToken2OriginalToken[mappingToken]; - require(originalToken != address(0), "MTF:token is not created by factory"); - bytes memory unlockFromRemote = abi.encodeWithSelector( - IBacking.unlockFromRemote.selector, - originalToken, - recipient, - amount - ); - _burnAndRemoteUnlock( - remoteSpecVersion, - remoteReceiveGasLimit, - mappingToken, - recipient, - amount, - unlockFromRemote, - false - ); - } - - function burnAndRemoteUnlockNative( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - address recipient, - uint256 amount - ) external payable whenNotPaused { - require(amount > 0, "MTF:can not transfer amount zero"); - address originalToken = mappingToken2OriginalToken[xwToken]; - require(originalToken != address(0), "MTF:token is not created by factory"); - bytes memory unlockFromRemoteNative = abi.encodeWithSelector( - IBackingSupportNative.unlockFromRemoteNative.selector, - recipient, - amount - ); - _burnAndRemoteUnlock( - remoteSpecVersion, - remoteReceiveGasLimit, - xwToken, - recipient, - amount, - unlockFromRemoteNative, - true); - } - - /** - * @notice send a unlock message to backing when issue mapping token faild to redeem original token. - * @param originalToken the original token address - * @param originalSender the originalSender of the remote unlocked token, must be the same as msg.send of the failed message. - * @param amount the amount of the failed issue token. - */ - function remoteUnlockFailure( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - uint256 transferId, - address originalToken, - address originalSender, - uint256 amount - ) external payable whenNotPaused { - // must not exist in successful issue list - require(BitMaps.get(issueMessages, transferId) == false, "MTF:success message can't refund for failed"); - // must has been checked by message layer - bool messageChecked = IHelixSub2SubMessageEndpoint(messageEndpoint).isMessageDelivered(transferId); - require(messageChecked, "MTF:the message is not checked by message layer"); - bytes memory handleUnlockForFailed = abi.encodeWithSelector( - IHelixAppSupportWithdrawFailed.handleUnlockFailureFromRemote.selector, - transferId, - originalToken, - originalSender, - amount - ); - (uint256 refundId, uint256 totalFee) = _sendMessage( - remoteSpecVersion, - remoteReceiveGasLimit, - handleUnlockForFailed - ); - emit RemoteUnlockFailure(refundId, transferId, totalFee); - } - - /** - * @notice send a unlock message to backing when issue mapping token faild to redeem original token. - * @param originalSender the originalSender of the remote unlocked token, must be the same as msg.send of the failed message. - * @param amount the amount of the failed issue token. - */ - function remoteUnlockFailureNative( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, - uint256 transferId, - address originalSender, - uint256 amount - ) external payable whenNotPaused { - // must not exist in successful issue list - require(BitMaps.get(issueMessages, transferId) == false, "MTF:success message can't refund for failed"); - // must has been checked by message layer - bool messageChecked = IHelixSub2SubMessageEndpoint(messageEndpoint).isMessageDelivered(transferId); - require(messageChecked, "MTF:the message is not checked by message layer"); - bytes memory handleUnlockForFailed = abi.encodeWithSelector( - IHelixAppSupportWithdrawFailed.handleUnlockFailureFromRemoteNative.selector, - transferId, - originalSender, - amount - ); - (uint256 refundId, uint256 totalFee) = _sendMessage( - remoteSpecVersion, - remoteReceiveGasLimit, - handleUnlockForFailed - ); - emit RemoteUnlockFailure(refundId, transferId, totalFee); - } - - /** - * @notice this will be called by messageEndpoint when the remote backing unlock failed and want to unlock the mapping token - * @param token the original token address - * @param origin_sender the origin_sender who will receive the unlocked token - * @param amount amount of the unlocked token - */ - function handleIssuingFailureFromRemote( - uint256 transferId, - address token, - address origin_sender, - uint256 amount - ) external onlyMessageEndpoint whenNotPaused { - BurnInfo memory burnInfo = burnMessages[transferId]; - require(burnInfo.hasRefundForFailed == false, "MTF:the burn message has been refund"); - bytes32 messageHash = hash(abi.encodePacked(transferId, token, origin_sender, amount)); - require(burnInfo.hash == messageHash, "MTF:message is not matched"); - burnMessages[transferId].hasRefundForFailed = true; - // remint token - Erc20(token).mint(origin_sender, amount); - emit TokenRemintForFailed(transferId, token, origin_sender, amount); - } - - function hash(bytes memory value) public pure returns (bytes32) { - return sha256(value); - } - - function rescueFunds( - address token, - address recipient, - uint256 amount - ) external onlyAdmin { - IERC20(token).transfer(recipient, amount); - } - - function withdrawFee( - address payable recipient, - uint256 amount - ) external onlyAdmin { - recipient.transfer(amount); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2sub/Guard.sol b/helix-contract/flatten/sub2sub/Guard.sol deleted file mode 100644 index 95995d8f..00000000 --- a/helix-contract/flatten/sub2sub/Guard.sol +++ /dev/null @@ -1,1207 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 4/28/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/mapping-token/interfaces/IWToken.sol -// License-Identifier: MIT - - -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; -} - -// File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} - -// File @zeppelin-solidity/contracts/utils/cryptography/ECDSA.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.3) (utils/cryptography/ECDSA.sol) - - -/** - * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. - * - * These functions can be used to verify that a message was signed by the holder - * of the private keys of a given address. - */ -library ECDSA { - enum RecoverError { - NoError, - InvalidSignature, - InvalidSignatureLength, - InvalidSignatureS, - InvalidSignatureV - } - - function _throwError(RecoverError error) private pure { - if (error == RecoverError.NoError) { - return; // no error: do nothing - } else if (error == RecoverError.InvalidSignature) { - revert("ECDSA: invalid signature"); - } else if (error == RecoverError.InvalidSignatureLength) { - revert("ECDSA: invalid signature length"); - } else if (error == RecoverError.InvalidSignatureS) { - revert("ECDSA: invalid signature 's' value"); - } else if (error == RecoverError.InvalidSignatureV) { - revert("ECDSA: invalid signature 'v' value"); - } - } - - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature` or error string. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - * - * Documentation for signature generation: - * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] - * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] - * - * _Available since v4.3._ - */ - function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { - if (signature.length == 65) { - bytes32 r; - bytes32 s; - uint8 v; - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - /// @solidity memory-safe-assembly - assembly { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - } - return tryRecover(hash, v, r, s); - } else { - return (address(0), RecoverError.InvalidSignatureLength); - } - } - - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature`. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - */ - function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, signature); - _throwError(error); - return recovered; - } - - /** - * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. - * - * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] - * - * _Available since v4.3._ - */ - function tryRecover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address, RecoverError) { - bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - uint8 v = uint8((uint256(vs) >> 255) + 27); - return tryRecover(hash, v, r, s); - } - - /** - * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. - * - * _Available since v4.2._ - */ - function recover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, r, vs); - _throwError(error); - return recovered; - } - - /** - * @dev Overload of {ECDSA-tryRecover} that receives the `v`, - * `r` and `s` signature fields separately. - * - * _Available since v4.3._ - */ - function tryRecover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address, RecoverError) { - // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature - // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines - // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most - // signatures from current libraries generate a unique signature with an s-value in the lower half order. - // - // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value - // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or - // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept - // these malleable signatures as well. - if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { - return (address(0), RecoverError.InvalidSignatureS); - } - if (v != 27 && v != 28) { - return (address(0), RecoverError.InvalidSignatureV); - } - - // If the signature is valid (and not malleable), return the signer address - address signer = ecrecover(hash, v, r, s); - if (signer == address(0)) { - return (address(0), RecoverError.InvalidSignature); - } - - return (signer, RecoverError.NoError); - } - - /** - * @dev Overload of {ECDSA-recover} that receives the `v`, - * `r` and `s` signature fields separately. - */ - function recover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, v, r, s); - _throwError(error); - return recovered; - } - - /** - * @dev Returns an Ethereum Signed Message, created from a `hash`. This - * produces hash corresponding to the one signed with the - * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] - * JSON-RPC method as part of EIP-191. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { - // 32 is the length in bytes of hash, - // enforced by the type signature above - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } - - /** - * @dev Returns an Ethereum Signed Message, created from `s`. This - * produces hash corresponding to the one signed with the - * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] - * JSON-RPC method as part of EIP-191. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); - } - - /** - * @dev Returns an Ethereum Signed Typed Data, created from a - * `domainSeparator` and a `structHash`. This produces hash corresponding - * to the one signed with the - * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] - * JSON-RPC method as part of EIP-712. - * - * See {recover}. - */ - function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } -} - -// File contracts/mapping-token/v2/GuardRegistry.sol -// License-Identifier: Apache-2.0 - -pragma experimental ABIEncoderV2; - -/** - * @title Manages a set of guards and a threshold to double-check BEEFY commitment - * @dev Stores the guards and a threshold - * @author echo - */ -contract GuardRegistry { - event AddedGuard(address guard); - event RemovedGuard(address guard); - event ChangedThreshold(uint256 threshold); - - // keccak256( - // "EIP712Domain(uint256 chainId,address verifyingContract)" - // ); - bytes32 internal constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218; - - address internal constant SENTINEL_GUARDS = address(0x1); - - /** - * @dev Nonce to prevent replay of update operations - */ - uint256 public nonce; - /** - * @dev Store all guards in the linked list - */ - mapping(address => address) internal guards; - /** - * @dev Count of all guards - */ - uint256 internal guardCount; - /** - * @dev Number of required confirmations for update operations - */ - uint256 internal threshold; - - /** - * @dev Sets initial storage of contract. - * @param _guards List of Safe guards. - * @param _threshold Number of required confirmations for check commitment or change guards. - */ - function initialize(address[] memory _guards, uint256 _threshold) internal { - // Threshold can only be 0 at initialization. - // Check ensures that setup function can only be called once. - require(threshold == 0, "Guard: Guards have already been setup"); - // Validate that threshold is smaller than number of added guards. - require(_threshold <= _guards.length, "Guard: Threshold cannot exceed guard count"); - // There has to be at least one Safe guard. - require(_threshold >= 1, "Guard: Threshold needs to be greater than 0"); - // Initializing Safe guards. - address currentGuard = SENTINEL_GUARDS; - for (uint256 i = 0; i < _guards.length; i++) { - // Guard address cannot be null. - address guard = _guards[i]; - require(guard != address(0) && guard != SENTINEL_GUARDS && guard != address(this) && currentGuard != guard, "Guard: Invalid guard address provided"); - // No duplicate guards allowed. - require(guards[guard] == address(0), "Guard: Address is already an guard"); - guards[currentGuard] = guard; - currentGuard = guard; - emit AddedGuard(guard); - } - guards[currentGuard] = SENTINEL_GUARDS; - guardCount = _guards.length; - threshold = _threshold; - } - - /** - * @dev Allows to add a new guard to the registry and update the threshold at the same time. - * This can only be done via multi-sig. - * @notice Adds the guard `guard` to the registry and updates the threshold to `_threshold`. - * @param guard New guard address. - * @param _threshold New threshold. - * @param signatures The signatures of the guards which to add new guard and update the `threshold` . - */ - function addGuardWithThreshold( - address guard, - uint256 _threshold, - bytes[] memory signatures - ) public { - // Guard address cannot be null, the sentinel or the registry itself. - require(guard != address(0) && guard != SENTINEL_GUARDS && guard != address(this), "Guard: Invalid guard address provided"); - // No duplicate guards allowed. - require(guards[guard] == address(0), "Guard: Address is already an guard"); - verifyGuardSignatures(msg.sig, abi.encode(guard, _threshold), signatures); - guards[guard] = guards[SENTINEL_GUARDS]; - guards[SENTINEL_GUARDS] = guard; - guardCount++; - emit AddedGuard(guard); - // Change threshold if threshold was changed. - if (threshold != _threshold) _changeThreshold(_threshold); - } - - /** - * @dev Allows to remove an guard from the registry and update the threshold at the same time. - * This can only be done via multi-sig. - * @notice Removes the guard `guard` from the registry and updates the threshold to `_threshold`. - * @param prevGuard Guard that pointed to the guard to be removed in the linked list - * @param guard Guard address to be removed. - * @param _threshold New threshold. - * @param signatures The signatures of the guards which to remove a guard and update the `threshold` . - */ - function removeGuard( - address prevGuard, - address guard, - uint256 _threshold, - bytes[] memory signatures - ) public { - // Only allow to remove an guard, if threshold can still be reached. - require(guardCount - 1 >= _threshold, "Guard: Threshold cannot exceed guard count"); - // Validate guard address and check that it corresponds to guard index. - require(guard != address(0) && guard != SENTINEL_GUARDS, "Guard: Invalid guard address provided"); - require(guards[prevGuard] == guard, "Guard: Invalid prevGuard, guard pair provided"); - verifyGuardSignatures(msg.sig, abi.encode(prevGuard, guard, _threshold), signatures); - guards[prevGuard] = guards[guard]; - guards[guard] = address(0); - guardCount--; - emit RemovedGuard(guard); - // Change threshold if threshold was changed. - if (threshold != _threshold) _changeThreshold(_threshold); - } - - /** - * @dev Allows to swap/replace a guard from the registry with another address. - * This can only be done via multi-sig. - * @notice Replaces the guard `oldGuard` in the registry with `newGuard`. - * @param prevGuard guard that pointed to the guard to be replaced in the linked list - * @param oldGuard guard address to be replaced. - * @param newGuard New guard address. - * @param signatures The signatures of the guards which to swap/replace a guard and update the `threshold` . - */ - function swapGuard( - address prevGuard, - address oldGuard, - address newGuard, - bytes[] memory signatures - ) public { - // Guard address cannot be null, the sentinel or the registry itself. - require(newGuard != address(0) && newGuard != SENTINEL_GUARDS && newGuard != address(this), "Guard: Invalid guard address provided"); - // No duplicate guards allowed. - require(guards[newGuard] == address(0), "Guard: Address is already an guard"); - // Validate oldGuard address and check that it corresponds to guard index. - require(oldGuard != address(0) && oldGuard != SENTINEL_GUARDS, "Guard: Invalid guard address provided"); - require(guards[prevGuard] == oldGuard, "Guard: Invalid prevGuard, guard pair provided"); - verifyGuardSignatures(msg.sig, abi.encode(prevGuard, oldGuard, newGuard), signatures); - guards[newGuard] = guards[oldGuard]; - guards[prevGuard] = newGuard; - guards[oldGuard] = address(0); - emit RemovedGuard(oldGuard); - emit AddedGuard(newGuard); - } - - /** - * @dev Allows to update the number of required confirmations by guards. - * This can only be done via multi-sig. - * @notice Changes the threshold of the registry to `_threshold`. - * @param _threshold New threshold. - * @param signatures The signatures of the guards which to update the `threshold` . - */ - function changeThreshold(uint256 _threshold, bytes[] memory signatures) public { - verifyGuardSignatures(msg.sig, abi.encode(_threshold), signatures); - _changeThreshold(_threshold); - } - - function _changeThreshold(uint256 _threshold) internal { - // Validate that threshold is smaller than number of owners. - require(_threshold <= guardCount, "Guard: Threshold cannot exceed guard count"); - // There has to be at least one guard. - require(_threshold >= 1, "Guard: Threshold needs to be greater than 0"); - threshold = _threshold; - emit ChangedThreshold(threshold); - } - - function getThreshold() public view returns (uint256) { - return threshold; - } - - function isGuard(address guard) public view returns (bool) { - return guard != SENTINEL_GUARDS && guards[guard] != address(0); - } - - /** - * @dev Returns array of guards. - * @return Array of guards. - */ - function getGuards() public view returns (address[] memory) { - address[] memory array = new address[](guardCount); - - // populate return array - uint256 index = 0; - address currentGuard = guards[SENTINEL_GUARDS]; - while (currentGuard != SENTINEL_GUARDS) { - array[index] = currentGuard; - currentGuard = guards[currentGuard]; - index++; - } - return array; - } - - function verifyGuardSignatures( - bytes4 methodID, - bytes memory params, - bytes[] memory signatures - ) internal { - bytes32 structHash = - keccak256( - abi.encode( - methodID, - params, - nonce - ) - ); - checkGuardSignatures(structHash, signatures); - nonce++; - } - - function verifyGuardSignaturesWithoutNonce( - bytes4 methodID, - bytes memory params, - bytes[] memory signatures - ) view internal { - bytes32 structHash = - keccak256( - abi.encode( - methodID, - params - ) - ); - checkGuardSignatures(structHash, signatures); - } - - /** - * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise. - * @param structHash The struct Hash of the data (could be either a message/commitment hash). - * @param signatures Signature data that should be verified. only ECDSA signature. - * Signers need to be sorted in ascending order - */ - function checkGuardSignatures( - bytes32 structHash, - bytes[] memory signatures - ) public view { - // Load threshold to avoid multiple storage loads - uint256 _threshold = threshold; - // Check that a threshold is set - require(_threshold > 0, "Guard: Threshold needs to be defined"); - bytes32 dataHash = encodeDataHash(structHash); - checkNSignatures(dataHash, signatures, _threshold); - } - - /** - * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise. - * @param dataHash Hash of the data (could be either a message hash or transaction hash). - * @param signatures Signature data that should be verified. only ECDSA signature. - * Signers need to be sorted in ascending order - * @param requiredSignatures Amount of required valid signatures. - */ - function checkNSignatures( - bytes32 dataHash, - bytes[] memory signatures, - uint256 requiredSignatures - ) public view { - // Check that the provided signature data is not too short - require(signatures.length >= requiredSignatures, "GS020"); - // There cannot be an owner with address 0. - address lastGuard = address(0); - address currentGuard; - for (uint256 i = 0; i < requiredSignatures; i++) { - currentGuard = ECDSA.recover(dataHash, signatures[i]); - require(currentGuard > lastGuard && guards[currentGuard] != address(0) && currentGuard != SENTINEL_GUARDS, "Guard: Invalid guard provided"); - lastGuard = currentGuard; - } - } - - /** - * @dev Returns the chain id used by this contract. - */ - function getChainId() public view returns (uint256) { - uint256 id; - // solhint-disable-next-line no-inline-assembly - assembly { - id := chainid() - } - return id; - } - - function domainSeparator() public view returns (bytes32) { - return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), address(this))); - } - - function encodeDataHash(bytes32 structHash) public view returns (bytes32) { - return keccak256(abi.encodePacked(hex"1901", domainSeparator(), structHash)); - } -} - -// File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} - -// File @zeppelin-solidity/contracts/security/Pausable.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} - -// File @zeppelin-solidity/contracts/utils/math/SafeMath.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) - - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the subtraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -// File contracts/mapping-token/v2/Guard.sol -// License-Identifier: Apache-2.0 - - - - - - -contract Guard is GuardRegistry, Pausable { - using SafeMath for uint256; - - mapping(uint256 => bytes32) depositors; - - uint256 public maxUnclaimableTime; - address public depositor; - address public operator; - - event TokenDeposit(uint256 id, address token, address recipient, uint256 amount); - event TokenClaimed(uint256 id); - - constructor(address[] memory _guards, uint256 _threshold, uint256 _maxUnclaimableTime, address _depositor) { - maxUnclaimableTime = _maxUnclaimableTime; - depositor = _depositor; - operator = msg.sender; - initialize(_guards, _threshold); - } - - modifier onlyDepositor() { - require(msg.sender == depositor, "Guard: Invalid depositor"); - _; - } - - modifier onlyOperator() { - require(msg.sender == operator, "Guard: Invalid operator"); - _; - } - - function unpause() external onlyOperator { - _unpause(); - } - - function pause() external onlyOperator { - _pause(); - } - - function setOperator(address newOperator, bytes[] memory signatures) external { - verifyGuardSignatures(msg.sig, abi.encode(newOperator), signatures); - operator = newOperator; - } - - function setMaxUnclaimableTime(uint256 _maxUnclaimableTime) external onlyOperator { - maxUnclaimableTime = _maxUnclaimableTime; - } - - /** - * @dev deposit token to guard, waiting to claim, only allowed depositor - * @param id the id of the operation, should be siged later by guards - * @param token the erc20 token address - * @param recipient the recipient of the token - * @param amount the amount of the token - */ - function deposit( - uint256 id, - address token, - address recipient, - uint256 amount - ) public onlyDepositor whenNotPaused { - depositors[id] = hash(abi.encodePacked(block.timestamp, token, recipient, amount)); - emit TokenDeposit(id, token, recipient, amount); - } - - function claimById( - uint256 id, - uint256 timestamp, - address token, - address recipient, - uint256 amount, - bool isNative - ) internal { - require(hash(abi.encodePacked(timestamp, token, recipient, amount)) == depositors[id], "Guard: Invalid id to claim"); - require(amount > 0, "Guard: Invalid amount to claim"); - if (isNative) { - require(IERC20(token).transferFrom(depositor, address(this), amount), "Guard: claim native token failed"); - uint256 balanceBefore = address(this).balance; - IWToken(token).withdraw(amount); - require(address(this).balance == balanceBefore.add(amount), "Guard: token is not wrapped by native token"); - payable(recipient).transfer(amount); - } else { - require(IERC20(token).transferFrom(depositor, recipient, amount), "Guard: claim token failed"); - } - delete depositors[id]; - emit TokenClaimed(id); - } - - /** - * @dev claim the tokens in the contract saved by deposit, this acquire signatures from guards - * @param id the id to be claimed - * @param signatures the signatures of the guards which to claim tokens. - */ - function claim( - uint256 id, - uint256 timestamp, - address token, - address recipient, - uint256 amount, - bytes[] memory signatures - ) public { - verifyGuardSignaturesWithoutNonce(msg.sig, abi.encode(id, timestamp, token, recipient, amount), signatures); - claimById(id, timestamp, token, recipient, amount, false); - } - - /** - * @dev claimNative the tokens in the contract saved by deposit, this acquire signatures from guards - * @param id the id to be claimed - * @param signatures the signatures of the guards which to claim tokens. - */ - function claimNative( - uint256 id, - uint256 timestamp, - address token, - address recipient, - uint256 amount, - bytes[] memory signatures - ) public { - verifyGuardSignaturesWithoutNonce(msg.sig, abi.encode(id, timestamp, token, recipient, amount), signatures); - claimById(id, timestamp, token, recipient, amount, true); - } - - /** - * @dev claim the tokens without signatures, this only allowed when timeout - * @param id the id to be claimed - */ - function claimByTimeout( - uint256 id, - uint256 timestamp, - address token, - address recipient, - uint256 amount, - bool isNative - ) public whenNotPaused { - require(timestamp < block.timestamp && block.timestamp - timestamp > maxUnclaimableTime, "Guard: claim at invalid time"); - claimById(id, timestamp, token, recipient, amount, isNative); - } - - function hash(bytes memory value) public pure returns (bytes32) { - return sha256(value); - } -} \ No newline at end of file diff --git a/helix-contract/flatten/sub2sub/WToken.sol b/helix-contract/flatten/sub2sub/WToken.sol deleted file mode 100644 index 304a75a8..00000000 --- a/helix-contract/flatten/sub2sub/WToken.sol +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** - * .----------------. .----------------. .----------------. .----------------. .----------------. - * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | - * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | - * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | - * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | - * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | - * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | - * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | - * | | | || | | || | | || | | || | | | - * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | - * '----------------' '----------------' '----------------' '----------------' '----------------' ' - * - * - * 4/28/2023 - **/ - -pragma solidity ^0.8.10; - -// File contracts/mapping-token/v2/erc20-mapping-protocol/WToken.sol -// License-Identifier: MIT - - -contract WToken { - string public name; - string public symbol; - uint8 public decimals; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - event Deposit(address indexed dst, uint wad); - event Withdrawal(address indexed src, uint wad); - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - constructor(string memory _name, string memory _symbol, uint8 _decimals) { - name = _name; - symbol = _symbol; - decimals = _decimals; - } - - receive() external payable { - deposit(); - } - - function deposit() public payable { - balanceOf[msg.sender] += msg.value; - emit Deposit(msg.sender, msg.value); - } - function withdraw(uint wad) public { - require(balanceOf[msg.sender] >= wad); - balanceOf[msg.sender] -= wad; - payable(msg.sender).transfer(wad); - emit Withdrawal(msg.sender, wad); - } - - function totalSupply() public view returns (uint) { - return address(this).balance; - } - - function approve(address guy, uint wad) public returns (bool) { - allowance[msg.sender][guy] = wad; - emit Approval(msg.sender, guy, wad); - return true; - } - - function transfer(address dst, uint wad) public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - - function transferFrom(address src, address dst, uint wad) - public - returns (bool) - { - require(balanceOf[src] >= wad); - - if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { - require(allowance[src][msg.sender] >= wad); - allowance[src][msg.sender] -= wad; - } - - balanceOf[src] -= wad; - balanceOf[dst] += wad; - - emit Transfer(src, dst, wad); - - return true; - } -} \ No newline at end of file diff --git a/helix-contract/hardhat.config.js b/helix-contract/hardhat.config.js index ae40a8ab..4dc1be5c 100644 --- a/helix-contract/hardhat.config.js +++ b/helix-contract/hardhat.config.js @@ -20,9 +20,9 @@ module.exports = { solidity: { compilers: [ { - version: "0.8.10", + version: "0.8.17", settings: { - evmVersion: "istanbul", + evmVersion: "london", optimizer: { enabled: true, runs: 200 @@ -162,7 +162,7 @@ subtask("flat:get-flattened-sources", "Returns all contracts and their dependenc flattened = flattened.replace(/SPDX-License-Identifier:/gm, "License-Identifier:") flattened = flattened.replace(/pragma solidity [\^>=0-9.]*;\n/gm, "") - flattened = `pragma solidity ^0.8.10;\n\n${flattened}` + flattened = `pragma solidity ^0.8.17;\n\n${flattened}` flattened = `/** * .----------------. .----------------. .----------------. .----------------. .----------------. * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | diff --git a/helix-contract/package.json b/helix-contract/package.json index 052af07c..9dfbed5c 100644 --- a/helix-contract/package.json +++ b/helix-contract/package.json @@ -28,11 +28,14 @@ "shx": "^0.3.3" }, "dependencies": { + "@arbitrum/nitro-contracts": "1.0.1", + "@axelar-network/axelar-gmp-sdk-solidity": "^5.3.1", "@darwinia/contracts-periphery": "@darwinia/contracts-periphery@2.1.1-fix2", "@darwinia/contracts-utils": "^1.0.4", "@zeppelin-solidity/contracts": "npm:@openzeppelin/contracts@v4.7.3", - "@arbitrum/nitro-contracts": "1.0.1", + "hardhat-solidity-create2-deployer": "^1.0.2", "npx": "^10.2.2", - "sgn-v2-contracts": "https://github.com/celer-network/sgn-v2-contracts.git#v0.2.0" + "sgn-v2-contracts": "https://github.com/celer-network/sgn-v2-contracts.git#v0.2.0", + "solidity-create2-deployer": "^0.4.0" } } diff --git a/helix-contract/test/3_test_ln.js b/helix-contract/test/3_test_ln.js deleted file mode 100644 index 96c3a298..00000000 --- a/helix-contract/test/3_test_ln.js +++ /dev/null @@ -1,1227 +0,0 @@ -const { expect } = require("chai"); -const { solidity } = require("ethereum-waffle"); -const chai = require("chai"); -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -chai.use(solidity); - -async function getBlockTimestamp() { - const blockNumber = await ethers.provider.getBlockNumber(); - const block = await ethers.provider.getBlock(blockNumber); - return block.timestamp; -} - -function getTransferId( - lastTransferId, - provider, - sourceToken, - targetToken, - receiver, - timestamp, - amount -) { - const encoded = ethers.utils.solidityPack([ - "bytes32", - "address", - "address", - "address", - "address", - "uint64", - "uint112", - ], [ - lastTransferId, - provider, - sourceToken, - targetToken, - receiver, - timestamp, - amount - ]); - return ethUtil.keccak256(encoded); -} - -describe("arb<>eth lnv2 bridge tests", () => { - before(async () => { - }); - - it("test_lnv2_flow", async function () { - // deploy inboundLane - const inboxContract = await ethers.getContractFactory("MockArbitrumInbox"); - const inbox = await inboxContract.deploy(); - await inbox.deployed(); - console.log("deploy mock inbox success"); - //******* deploy inboundLane/outboundLane finished ******** - - const [owner, relayer, other] = await ethers.getSigners(); - const dao = owner.address; - const feeReceiver = "0x1000000000000000000000000000000000000001"; - const helixFee = 100; - const initTokenBalance = 1000000; - const penaltyLnCollateral = 100; - var margin = 2000; - const baseFee = 20; - const liquidityFeeRate = 100; - const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - const initSlashTransferId = "0x0000000000000000000000000000000000000000000000000000000000000001"; - const zeroAddress = "0x0000000000000000000000000000000000000000"; - //******* deploy lp bridge at ethereum ******* - const lnBridgeContractOnL2 = await ethers.getContractFactory("Arb2EthSource"); - const lnBridgeOnL2 = await lnBridgeContractOnL2.deploy(); - await lnBridgeOnL2.deployed(); - await lnBridgeOnL2.initialize(dao); - console.log("ln bridge on L2 address", lnBridgeOnL2.address); - await lnBridgeOnL2.updateFeeReceiver(feeReceiver); - - const lnBridgeContractOnL1 = await ethers.getContractFactory("Arb2EthTarget"); - const lnBridgeOnL1 = await lnBridgeContractOnL1.deploy(); - await lnBridgeOnL1.deployed(); - await lnBridgeOnL1.initialize(dao, inbox.address); - console.log("ln bridge on L1 address", lnBridgeOnL1.address); - // init owner - //******* deploy ln bridge at end *************** - // - await lnBridgeOnL2.setRemoteBridge(lnBridgeOnL1.address); - await lnBridgeOnL1.setRemoteBridge(lnBridgeOnL2.address); - - const tokenNameOnEthereum = "Darwinia Ring On Ethereum"; - const tokenSymbolOnEthereum = "RING.e"; - const ethContract = await ethers.getContractFactory("Erc20"); - const ethToken = await ethContract.deploy(tokenNameOnEthereum, tokenSymbolOnEthereum, 18); - await ethToken.deployed(); - - const tokenNameOnArbitrum = "Darwinia Ring On Arbitrum"; - const tokenSymbolOnArbitrum = "RING.a"; - const arbContract = await ethers.getContractFactory("Erc20"); - const arbToken = await ethContract.deploy(tokenNameOnArbitrum, tokenSymbolOnArbitrum, 18); - await arbToken.deployed(); - console.log("contract deploy finished"); - - // 1. register token - await lnBridgeOnL2.grantRole(lnBridgeOnL2.OPERATOR_ROLE(), owner.address); - await lnBridgeOnL2.registerToken( - arbToken.address, - ethToken.address, - helixFee, - penaltyLnCollateral, - 18, - 18 - ); - console.log("register token finished"); - - await ethToken.mint(feeReceiver, initTokenBalance); - await arbToken.mint(feeReceiver, initTokenBalance); - // 2. register lnbridger - // 2.1 mint some tokens on source chain and target chain for relayer - await ethToken.mint(relayer.address, initTokenBalance); - await arbToken.mint(relayer.address, initTokenBalance); - await ethToken.mint(other.address, initTokenBalance); - await arbToken.mint(other.address, initTokenBalance); - await ethToken.mint(owner.address, initTokenBalance); - - await arbToken.connect(relayer).approve(lnBridgeOnL2.address, initTokenBalance * 1000); - await lnBridgeOnL2.connect(relayer).updateProviderFeeAndMargin( - arbToken.address, // tokenIndex - margin, // margin - baseFee, // basefee - liquidityFeeRate // liquidity fee rate x/100,000 - ); - console.log("register provider finished"); - - // 3. transfer and lock margin - // 3.1 normal - const transferAmount01 = 300; - const expectedFee = Math.floor(transferAmount01 * 100 / 100000 + 20 + helixFee); - // 3.1.1 lock - await arbToken.connect(other).approve(lnBridgeOnL2.address, 1000000000); - const lockTransaction = await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - initTransferId, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - ); - const blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - let lockReceipt = await lockTransaction.wait(); - let lockGasUsed = lockReceipt.cumulativeGasUsed; - console.log("transferAndLockMargin gas used", lockGasUsed); - const transferId01 = getTransferId( - initTransferId, // lastTransferId - relayer.address, // provider - arbToken.address, // sourceToken - ethToken.address, // targetToken - other.address, // receiver - blockTimestamp, - transferAmount01, // amount - ); - // 3.1.2 relay - await ethToken.connect(relayer).approve(lnBridgeOnL1.address, 1000000000); - const relayTransaction = await lnBridgeOnL1.connect(relayer).transferAndReleaseMargin( - [ - initTransferId, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp, - other.address - ], - transferId01 - ); - let relayReceipt = await relayTransaction.wait(); - let relayGasUsed = relayReceipt.cumulativeGasUsed; - console.log("relay gas used", relayGasUsed); - - // relay finished, check the id and balance - // check balance - // arbtoken: relayer -> lnbridge (margin) - // other -> relayer (transferAmount01 + baseFee + liquidityFeeRate * amount) - // other -> feeReceive (helixFee) - // ethtoken: relayer -> other (transferAmount01) - const relayerFee01 = baseFee + Math.floor(liquidityFeeRate * transferAmount01/100000); - const relayerArbToken01 = initTokenBalance - margin + transferAmount01 + relayerFee01; - const lnBridgeArbToken01 = margin; - const otherArbToken01 = initTokenBalance - transferAmount01 - relayerFee01 - helixFee; - const feeReceiverArbToken01 = initTokenBalance + helixFee; - const relayerEthToken01 = initTokenBalance - transferAmount01; - const otherEthToken01 = initTokenBalance + transferAmount01; - expect(await arbToken.balanceOf(relayer.address)).to.equal(relayerArbToken01); - expect(await arbToken.balanceOf(lnBridgeOnL2.address)).to.equal(lnBridgeArbToken01); - expect(await arbToken.balanceOf(other.address)).to.equal(otherArbToken01); - expect(await arbToken.balanceOf(feeReceiver)).to.equal(feeReceiverArbToken01); - expect(await ethToken.balanceOf(relayer.address)).to.equal(relayerEthToken01); - expect(await ethToken.balanceOf(other.address)).to.equal(otherEthToken01); - // check transferId - // source chain - const lockInfo01 = await lnBridgeOnL2.lockInfos(transferId01); - expect(lockInfo01.amountWithFeeAndPenalty).to.equal(transferAmount01 + relayerFee01 + penaltyLnCollateral); - // target chain - const transferInfo01 = await lnBridgeOnL1.fillTransfers(transferId01); - expect(transferInfo01).to.equal(initSlashTransferId); - const slashInfo01 = await lnBridgeOnL1.slashInfos(transferId01); - expect(slashInfo01.slasher).to.equal(zeroAddress); - - // slash 02 - // lock - // must be continuous - console.log("check continuous"); - await expect(lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - initTransferId, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - )).to.be.revertedWith("snapshot expired"); - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - transferId01, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - ); - const blockTimestamp02 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId02 = getTransferId( - transferId01, // lastTransferId - relayer.address, // provider - arbToken.address, // sourceToken - ethToken.address, // targetToken - other.address, // receiver - blockTimestamp02, - transferAmount01, // amount - ); - // cannot slash before expired - await expect(lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId01, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp02, - other.address - ], - transferId01, - 0, - 0, - 200 - )).to.be.revertedWith("slash time not expired"); - console.log("check expired slash finished"); - await hre.network.provider.request({ - method: "evm_increaseTime", - //params: [await lnBridgeOnL1.MIN_REFUND_TIMESTAMP() + 1], - params: [18001], - }); - // slash - // start slash - // 1. relayed transfer cannot slash, try to slash transfer01 failed - await expect(lnBridgeOnL1.slashAndRemoteRefund( - [ - initTransferId, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp, - other.address - ], - transferId01, - 0, - 0, - 0 - )).to.be.revertedWith("fill exist"); - console.log("check exist message slash finished"); - - // mock sender address - await lnBridgeOnL2.setRemoteBridgeAlias(inbox.address); - // request success - await ethToken.approve(lnBridgeOnL1.address, 1000000000); - await lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId01, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp02, - other.address - ], - transferId02, - 0, - 0, - 200 - ); - // check balance - // arbtoken: other -> relayer (transferAmount01 + baseFee + liquidityFeeRate * transferAmount01) - // lnBridgeOnL2 -> owner (transferAmount01 + baseFee + liquidityFeeRate * transferAmount01 + penaltyLnCollateral) - // other -> feeReceive (helixFee) - // ethtoken: owner -> other (transferAmount01) - const relayerFee02 = baseFee + Math.floor(liquidityFeeRate * transferAmount01/100000); - const relayerArbToken02 = relayerArbToken01 + transferAmount01 + relayerFee02; - const lnBridgeArbToken02 = lnBridgeArbToken01 - transferAmount01 - relayerFee02 - penaltyLnCollateral; - const otherArbToken02 = otherArbToken01 - transferAmount01 - relayerFee02 - helixFee; - const feeReceiverArbToken02 = feeReceiverArbToken01 + helixFee; - const ownerArbToken02 = transferAmount01 + relayerFee02 + penaltyLnCollateral; - const relayerEthToken02 = relayerEthToken01; - const otherEthToken02 = otherEthToken01 + transferAmount01; - const ownerEthToken02 = initTokenBalance - transferAmount01; - expect(await arbToken.balanceOf(relayer.address)).to.equal(relayerArbToken02); - expect(await arbToken.balanceOf(lnBridgeOnL2.address)).to.equal(lnBridgeArbToken02); - expect(await arbToken.balanceOf(other.address)).to.equal(otherArbToken02); - expect(await arbToken.balanceOf(feeReceiver)).to.equal(feeReceiverArbToken02); - expect(await arbToken.balanceOf(owner.address)).to.equal(ownerArbToken02); - expect(await ethToken.balanceOf(relayer.address)).to.equal(relayerEthToken02); - expect(await ethToken.balanceOf(other.address)).to.equal(otherEthToken02); - expect(await ethToken.balanceOf(owner.address)).to.equal(ownerEthToken02); - margin -= (transferAmount01 + relayerFee02 + penaltyLnCollateral); - console.log("check normal slash finished"); - - // check slash continous - // 3 slash, 4 relayed, 5 slash - // locks - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - transferId02, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - ); - const blockTimestamp03 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId03 = getTransferId( - transferId02, // lastTransferId - relayer.address, // provider - arbToken.address, // sourceToken - ethToken.address, // targetToken - other.address, // receiver - blockTimestamp03, - transferAmount01, // amount - ); - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - transferId03, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - ); - const blockTimestamp04 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId04 = getTransferId( - transferId03, // lastTransferId - relayer.address, // provider - arbToken.address, // sourceToken - ethToken.address, // targetToken - other.address, // receiver - blockTimestamp04, - transferAmount01, // amount - ); - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - transferId04, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - ); - const blockTimestamp05 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId05 = getTransferId( - transferId04, // lastTransferId - relayer.address, // provider - arbToken.address, // sourceToken - ethToken.address, // targetToken - other.address, // receiver - blockTimestamp05, - transferAmount01, // amount - ); - // slash 3 - await hre.network.provider.request({ - method: "evm_increaseTime", - //params: [await lnBridgeOnL1.MIN_REFUND_TIMESTAMP() + 1], - params: [18001], - }); - - // slash 03 failed on remote - await lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId02, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp03, - other.address - ], - transferId03, - 0, - 0, - 0 - ); - - // relay 4 - await lnBridgeOnL1.connect(relayer).transferAndReleaseMargin( - [ - transferId03, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp04, - other.address - ], - transferId04 - ); - // slash 5 must after 3 - await expect(lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId04, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp05, - other.address - ], - transferId05, - 0, - 0, - 200 - )).to.be.revertedWith("arbitrum mock call failed"); - - console.log("check retry function"); - // retry 3 - await lnBridgeOnL1.retryRemoteRefund( - transferId03, - 0, - 0, - 200 - ); - - // then slash 5 - await lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId04, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp05, - other.address - ], - transferId05, - 0, - 0, - 200 - ); - // cannot relay the slashed - await expect(lnBridgeOnL1.connect(relayer).transferAndReleaseMargin( - [ - transferId04, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - transferAmount01, - blockTimestamp05, - other.address - ], - transferId05 - )).to.revertedWith("fill exist"); - - // test paused - await lnBridgeOnL2.connect(relayer).providerPause(arbToken.address); - await expect(lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - transferId05, - margin, - expectedFee - ], - transferAmount01, // amount - other.address, // receiver - { value: transferAmount01 + expectedFee } - )).to.be.revertedWith("provider paused"); - await lnBridgeOnL2.connect(relayer).providerUnpause(arbToken.address); - await expect(lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - transferId05, - margin, - expectedFee - ], - transferAmount01, // amount - other.address, // receiver - { value: transferAmount01 + expectedFee } - )).to.be.revertedWith("margin updated"); - - console.log("ln bridge test finished"); - }); - - it("test_lnv2_native", async function () { - // deploy inboundLane - const inboxContract = await ethers.getContractFactory("MockArbitrumInbox"); - const inbox = await inboxContract.deploy(); - await inbox.deployed(); - console.log("deploy mock inbox success"); - //******* deploy inboundLane/outboundLane finished ******** - - const [owner, relayer, other] = await ethers.getSigners(); - const dao = owner.address; - const feeReceiver = "0x1000000000000000000000000000000000000001"; - const helixFee = 100; - const initTokenBalance = 1000000; - const penaltyLnCollateral = 100; - var margin = 2000; - const baseFee = 20; - const liquidityFeeRate = 100; - const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - const initSlashTransferId = "0x0000000000000000000000000000000000000000000000000000000000000001"; - const nativeTokenAddress = "0x0000000000000000000000000000000000000000"; - const zeroAddress = "0x0000000000000000000000000000000000000000"; - //******* deploy lp bridge at ethereum ******* - const lnBridgeContractOnL2 = await ethers.getContractFactory("Arb2EthSource"); - const lnBridgeOnL2 = await lnBridgeContractOnL2.deploy(); - await lnBridgeOnL2.deployed(); - await lnBridgeOnL2.initialize(dao); - console.log("ln bridge on L2 address", lnBridgeOnL2.address); - await lnBridgeOnL2.updateFeeReceiver(feeReceiver); - - const lnBridgeContractOnL1 = await ethers.getContractFactory("Arb2EthTarget"); - const lnBridgeOnL1 = await lnBridgeContractOnL1.deploy(); - await lnBridgeOnL1.deployed(); - await lnBridgeOnL1.initialize(dao, inbox.address); - console.log("ln bridge on L1 address", lnBridgeOnL1.address); - // init owner - //******* deploy ln bridge at end *************** - // - await lnBridgeOnL2.setRemoteBridge(lnBridgeOnL1.address); - await lnBridgeOnL1.setRemoteBridge(lnBridgeOnL2.address); - - // eth on both ethereum and atbitrum are native token - // 1. register native token - await lnBridgeOnL2.grantRole(lnBridgeOnL2.OPERATOR_ROLE(), owner.address); - await lnBridgeOnL2.registerToken( - nativeTokenAddress, - nativeTokenAddress, - helixFee, - penaltyLnCollateral, - 18, - 18 - ); - console.log("register native token finished"); - - await lnBridgeOnL2.connect(relayer).updateProviderFeeAndMargin( - zeroAddress, - margin, // margin - baseFee, // basefee - liquidityFeeRate, // liquidity fee rate x/100,000 - { value: margin } - ); - console.log("register provider finished"); - - // 3. transfer and lock margin - // 3.1 normal - const transferAmount01 = 300; - const expectedFee = Math.floor(transferAmount01 * 100 / 100000 + 20 + helixFee); - // 3.1.1 lock - const lockTransaction = await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - zeroAddress, - initTransferId, - margin, - expectedFee - ], - transferAmount01, // amount - other.address, // receiver - { value: transferAmount01 + expectedFee } - ); - const blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - let lockReceipt = await lockTransaction.wait(); - let lockGasUsed = lockReceipt.cumulativeGasUsed; - console.log("transferAndLockMargin gas used", lockGasUsed); - // 3.1.2 relay - const balanceOtherBefore = await ethers.provider.getBalance(other.address); - const balanceRelayBefore = await ethers.provider.getBalance(relayer.address); - await hre.network.provider.request({ - method: "evm_increaseTime", - params: [18001], - }); - const transferId01 = getTransferId( - initTransferId, // lastTransferId - relayer.address, // provider - zeroAddress, // sourceToken - zeroAddress, // targetToken - other.address, // receiver - blockTimestamp, - transferAmount01, // amount - ); - const relayTransaction = await lnBridgeOnL1.connect(relayer).transferAndReleaseMargin( - [ - initTransferId, // lastTransferId - relayer.address, // provider - zeroAddress, - zeroAddress, // token - transferAmount01, - blockTimestamp, - other.address - ], - transferId01, - { value: transferAmount01 } - ); - let relayReceipt = await relayTransaction.wait(); - let relayGasUsed = relayReceipt.cumulativeGasUsed; - console.log("relay gas used", relayGasUsed); - const balanceOtherAfter = await ethers.provider.getBalance(other.address); - const balanceRelayAfter = await ethers.provider.getBalance(relayer.address); - let gasFeeRelayer01 = relayReceipt.cumulativeGasUsed.mul(relayReceipt.effectiveGasPrice); - // check balance - expect(balanceOtherAfter.sub(balanceOtherBefore)).to.equal(transferAmount01); - expect(balanceRelayBefore.sub(balanceRelayAfter).sub(gasFeeRelayer01)).to.equal(transferAmount01); - - // relay finished, check the id and balance - // check balance - // arbtoken: relayer -> lnbridge (margin) - // other -> relayer (transferAmount01 + baseFee + liquidityFeeRate * amount) - // other -> feeReceive (helixFee) - // ethtoken: relayer -> other (transferAmount01) - - // check transferId - // source chain - - const lockInfo01 = await lnBridgeOnL2.lockInfos(transferId01); - const relayerFee01 = baseFee + Math.floor(liquidityFeeRate * transferAmount01/100000); - expect(lockInfo01.amountWithFeeAndPenalty).to.equal(transferAmount01 + relayerFee01 + penaltyLnCollateral); - // target chain - const latestSlashTransferId = await lnBridgeOnL1.fillTransfers(transferId01); - expect(latestSlashTransferId).to.equal(initSlashTransferId); - const slashInfo = await lnBridgeOnL1.slashInfos(transferId01); - expect(slashInfo.slasher).to.equal(zeroAddress); - - console.log("start to check continuous"); - // slash 02 - // lock - // must be continuous - await expect(lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - zeroAddress, - initTransferId, - margin, - expectedFee - ], - transferAmount01, // amount - other.address, // receiver - { value: transferAmount01 + expectedFee } - )).to.be.revertedWith("snapshot expired"); - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - zeroAddress, - transferId01, - margin, - expectedFee - ], - transferAmount01, // amount - other.address, // receiver - { value: transferAmount01 + expectedFee } - ); - const blockTimestamp02 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId02 = getTransferId( - transferId01, // lastTransferId - relayer.address, // provider - zeroAddress, // sourceToken - zeroAddress, // targetToken - other.address, // receiver - blockTimestamp02, - transferAmount01, // amount - ); - // slash - // start slash - // 1. relayed transfer cannot slash, try to slash transfer01 failed - await expect(lnBridgeOnL1.slashAndRemoteRefund( - [ - initTransferId, // lastTransferId - relayer.address, // provider - zeroAddress, - zeroAddress, // token - transferAmount01, - blockTimestamp, - other.address - ], - transferId01, - 0, - 0, - 200 - )).to.be.revertedWith("fill exist"); - - await expect(lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId01, // lastTransferId - relayer.address, // provider - nativeTokenAddress, - nativeTokenAddress, // token - transferAmount01, - blockTimestamp02, - other.address - ], - transferId02, - 0, - 0, - 200 - )).to.be.revertedWith("slash time not expired"); - - // 3. wait for timeout - await hre.network.provider.request({ - method: "evm_increaseTime", - //params: [await lnBridgeOnL1.MIN_REFUND_TIMESTAMP() + 1], - params: [18001], - }); - // mock sender address - await lnBridgeOnL2.setRemoteBridgeAlias(inbox.address); - // request success - const balanceOtherBeforeCancel = await ethers.provider.getBalance(other.address); - const balanceBridgeBeforeCancel = await ethers.provider.getBalance(lnBridgeOnL2.address); - await lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId01, // lastTransferId - relayer.address, // provider - nativeTokenAddress, - nativeTokenAddress, // token - transferAmount01, - blockTimestamp02, - other.address - ], - transferId02, - 0, - 0, - 200, - { value: transferAmount01 } - ); - const balanceOtherAfterCancel = await ethers.provider.getBalance(other.address); - const balanceBridgeAfterCancel = await ethers.provider.getBalance(lnBridgeOnL2.address); - const relayerFee02 = baseFee + Math.floor(liquidityFeeRate * transferAmount01/100000); - margin -= (transferAmount01 + relayerFee02 + penaltyLnCollateral); - margin -= (transferAmount01 + relayerFee02 + penaltyLnCollateral); - // check balance - // arbtoken: other -> relayer (transferAmount01 + baseFee + liquidityFeeRate * transferAmount01) - // slasher -> other (transferAmount01) - // other -> feeReceive (helixFee) - expect(balanceOtherAfterCancel.sub(balanceOtherBeforeCancel)).to.equal(transferAmount01); - expect(balanceBridgeBeforeCancel.sub(balanceBridgeAfterCancel)).to.equal(transferAmount01 + relayerFee02 + penaltyLnCollateral); - - await lnBridgeOnL2.connect(relayer).updateProviderFeeAndMargin( - nativeTokenAddress, - 0, // margin - baseFee, // basefee - liquidityFeeRate, // liquidity fee rate x/100,000 - { value: 0 } - ); - // check slash continous - // 3 slash, 4 relayed, 5 slash - // locks - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - nativeTokenAddress, - transferId02, - margin, - expectedFee - ], - transferAmount01, // amount - other.address, // receiver - { value: transferAmount01 + expectedFee } - ); - const blockTimestamp03 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId03 = getTransferId( - transferId02, // lastTransferId - relayer.address, // provider - nativeTokenAddress, // sourceToken - nativeTokenAddress, // targetToken - other.address, // receiver - blockTimestamp03, - transferAmount01, // amount - ); - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - nativeTokenAddress, - transferId03, - margin, - expectedFee - ], - transferAmount01, // amount - other.address, // receiver - { value: transferAmount01 + expectedFee } - ); - const blockTimestamp04 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId04 = getTransferId( - transferId03, // lastTransferId - relayer.address, // provider - nativeTokenAddress, // sourceToken - nativeTokenAddress, // targetToken - other.address, // receiver - blockTimestamp04, - transferAmount01, // amount - ); - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - nativeTokenAddress, - transferId04, - margin, - expectedFee - ], - transferAmount01, // amount - other.address, // receiver - { value: transferAmount01 + expectedFee } - ); - const blockTimestamp05 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId05 = getTransferId( - transferId04, // lastTransferId - relayer.address, // provider - nativeTokenAddress, // sourceToken - nativeTokenAddress, // targetToken - other.address, // receiver - blockTimestamp05, - transferAmount01, // amount - ); - await hre.network.provider.request({ - method: "evm_increaseTime", - //params: [await lnBridgeOnL1.MIN_REFUND_TIMESTAMP() + 1], - params: [18001], - }); - // slash 3 failed on remote - await lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId02, // lastTransferId - relayer.address, // provider - nativeTokenAddress, - nativeTokenAddress, // token - transferAmount01, - blockTimestamp03, - other.address - ], - transferId03, - 0, - 0, - 0, - { value: transferAmount01 } - ) - // relay 4 - await lnBridgeOnL1.connect(relayer).transferAndReleaseMargin( - [ - transferId03, // lastTransferId - relayer.address, // provider - nativeTokenAddress, - nativeTokenAddress, // token - transferAmount01, - blockTimestamp04, - other.address - ], - transferId04, - { value: transferAmount01 } - ); - // slash 5 - // slash 5 must after 3 - await expect(lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId04, // lastTransferId - relayer.address, // provider - nativeTokenAddress, - nativeTokenAddress, // token - transferAmount01, - blockTimestamp05, - other.address - ], - transferId05, - 0, - 0, - 200, - { value: transferAmount01 } - )).to.be.revertedWith("arbitrum mock call failed"); - - // retry 3 - await lnBridgeOnL1.retryRemoteRefund( - transferId03, - 0, - 0, - 200 - ); - // then slash 5 - lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId04, // lastTransferId - relayer.address, // provider - nativeTokenAddress, - nativeTokenAddress, // token - transferAmount01, - blockTimestamp05, - other.address - ], - transferId05, - 0, - 0, - 200, - { value: transferAmount01 } - ) - // cannot relay the slashed - await expect(lnBridgeOnL1.connect(relayer).transferAndReleaseMargin( - [ - transferId04, // lastTransferId - relayer.address, // provider - nativeTokenAddress, - nativeTokenAddress, // token - transferAmount01, - blockTimestamp05, - other.address - ], - transferId05, - { value: transferAmount01 } - )).to.revertedWith("fill exist"); - - console.log("ln bridge test native finished"); - }); - - it("test_lnv2_different_decimals", async function () { - // deploy inboundLane - const inboxContract = await ethers.getContractFactory("MockArbitrumInbox"); - const inbox = await inboxContract.deploy(); - await inbox.deployed(); - console.log("deploy mock inbox success"); - //******* deploy inboundLane/outboundLane finished ******** - - const [owner, relayer, other] = await ethers.getSigners(); - const dao = owner.address; - const feeReceiver = "0x1000000000000000000000000000000000000001"; - const helixFee = ethers.utils.parseEther("100.0"); - const initTokenBalance = ethers.utils.parseEther("10000000.0"); - const penaltyLnCollateral = ethers.utils.parseEther("10000"); - var margin = ethers.utils.parseEther("200000"); - const baseFee = ethers.utils.parseEther("200"); - const liquidityFeeRate = 100; - const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - const initSlashTransferId = "0x0000000000000000000000000000000000000000000000000000000000000001"; - const zeroAddress = "0x0000000000000000000000000000000000000000"; - //******* deploy lp bridge at ethereum ******* - const lnBridgeContractOnL2 = await ethers.getContractFactory("Arb2EthSource"); - const lnBridgeOnL2 = await lnBridgeContractOnL2.deploy(); - await lnBridgeOnL2.deployed(); - await lnBridgeOnL2.initialize(dao); - console.log("ln bridge on L2 address", lnBridgeOnL2.address); - await lnBridgeOnL2.updateFeeReceiver(feeReceiver); - - const lnBridgeContractOnL1 = await ethers.getContractFactory("Arb2EthTarget"); - const lnBridgeOnL1 = await lnBridgeContractOnL1.deploy(); - await lnBridgeOnL1.deployed(); - await lnBridgeOnL1.initialize(dao, inbox.address); - console.log("ln bridge on L1 address", lnBridgeOnL1.address); - // init owner - //******* deploy ln bridge at end *************** - // - await lnBridgeOnL2.setRemoteBridge(lnBridgeOnL1.address); - await lnBridgeOnL1.setRemoteBridge(lnBridgeOnL2.address); - - const tokenNameOnEthereum = "Darwinia Ring On Ethereum"; - const tokenSymbolOnEthereum = "RING.e"; - const ethContract = await ethers.getContractFactory("Erc20"); - const ethToken = await ethContract.deploy(tokenNameOnEthereum, tokenSymbolOnEthereum, 6); - await ethToken.deployed(); - - const tokenNameOnArbitrum = "Darwinia Ring On Arbitrum"; - const tokenSymbolOnArbitrum = "RING.a"; - const arbContract = await ethers.getContractFactory("Erc20"); - const arbToken = await ethContract.deploy(tokenNameOnArbitrum, tokenSymbolOnArbitrum, 18); - await arbToken.deployed(); - console.log("contract deploy finished"); - - // 1. register token - await lnBridgeOnL2.grantRole(lnBridgeOnL2.OPERATOR_ROLE(), owner.address); - await lnBridgeOnL2.registerToken( - arbToken.address, - ethToken.address, - helixFee, - penaltyLnCollateral, - 18, - 6 - ); - console.log("register token finished"); - - await ethToken.mint(feeReceiver, initTokenBalance); - await arbToken.mint(feeReceiver, initTokenBalance); - // 2. register lnbridger - // 2.1 mint some tokens on source chain and target chain for relayer - await ethToken.mint(relayer.address, initTokenBalance); - await arbToken.mint(relayer.address, initTokenBalance); - await ethToken.mint(other.address, initTokenBalance); - await arbToken.mint(other.address, initTokenBalance); - await ethToken.mint(owner.address, initTokenBalance); - - await arbToken.connect(relayer).approve(lnBridgeOnL2.address, initTokenBalance); - await lnBridgeOnL2.connect(relayer).updateProviderFeeAndMargin( - arbToken.address, // tokenIndex - margin, // margin - baseFee, // basefee - liquidityFeeRate // liquidity fee rate x/100,000 - ); - console.log("register provider finished"); - - // 3. transfer and lock margin - // 3.1 normal - const transferAmount01 = ethers.utils.parseEther("500"); - const targetAmount01 = transferAmount01.div(1e12); - const expectedFee = transferAmount01.mul(liquidityFeeRate).div(100000).add(baseFee.add(helixFee)); - // 3.1.1 lock - await arbToken.connect(other).approve(lnBridgeOnL2.address, initTokenBalance); - const lockTransaction = await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - initTransferId, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - ); - const blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - let lockReceipt = await lockTransaction.wait(); - let lockGasUsed = lockReceipt.cumulativeGasUsed; - console.log("transferAndLockMargin gas used", lockGasUsed); - const transferId01 = getTransferId( - initTransferId, // lastTransferId - relayer.address, // provider - arbToken.address, // sourceToken - ethToken.address, // targetToken - other.address, // receiver - blockTimestamp, - targetAmount01, // amount - ); - // 3.1.2 relay - await ethToken.connect(relayer).approve(lnBridgeOnL1.address, initTokenBalance); - const relayTransaction = await lnBridgeOnL1.connect(relayer).transferAndReleaseMargin( - [ - initTransferId, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - targetAmount01, - blockTimestamp, - other.address - ], - transferId01 - ); - let relayReceipt = await relayTransaction.wait(); - let relayGasUsed = relayReceipt.cumulativeGasUsed; - console.log("relay gas used", relayGasUsed); - - // relay finished, check the id and balance - // check balance - // arbtoken: relayer -> lnbridge (margin) - // other -> relayer (transferAmount01 + baseFee + liquidityFeeRate * amount) - // other -> feeReceive (helixFee) - // ethtoken: relayer -> other (transferAmount01) - const relayerFee01 = baseFee.add(transferAmount01.mul(liquidityFeeRate).div(100000)); - const relayerArbToken01 = initTokenBalance.sub(margin).add(transferAmount01).add(relayerFee01); - const lnBridgeArbToken01 = margin; - const otherArbToken01 = initTokenBalance.sub(transferAmount01).sub(relayerFee01).sub(helixFee); - const feeReceiverArbToken01 = initTokenBalance.add(helixFee); - const relayerEthToken01 = initTokenBalance.sub(targetAmount01); - const otherEthToken01 = initTokenBalance.add(targetAmount01); - expect(await arbToken.balanceOf(relayer.address)).to.equal(relayerArbToken01); - expect(await arbToken.balanceOf(lnBridgeOnL2.address)).to.equal(lnBridgeArbToken01); - expect(await arbToken.balanceOf(other.address)).to.equal(otherArbToken01); - expect(await arbToken.balanceOf(feeReceiver)).to.equal(feeReceiverArbToken01); - expect(await ethToken.balanceOf(relayer.address)).to.equal(relayerEthToken01); - expect(await ethToken.balanceOf(other.address)).to.equal(otherEthToken01); - // check transferId - // source chain - const lockInfo01 = await lnBridgeOnL2.lockInfos(transferId01); - expect(lockInfo01.amountWithFeeAndPenalty).to.equal(transferAmount01.add(relayerFee01).add(penaltyLnCollateral)); - // target chain - const transferInfo01 = await lnBridgeOnL1.fillTransfers(transferId01); - expect(transferInfo01).to.equal(initSlashTransferId); - const slashInfo01 = await lnBridgeOnL1.slashInfos(transferId01); - expect(slashInfo01.slasher).to.equal(zeroAddress); - - // slash 02 - console.log("check continuous"); - await expect(lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - initTransferId, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - )).to.be.revertedWith("snapshot expired"); - await lnBridgeOnL2.connect(other).transferAndLockMargin( - [ - relayer.address, - arbToken.address, - transferId01, - margin, - expectedFee - ], - transferAmount01, // amount - other.address // receiver - ); - const blockTimestamp02 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId02 = getTransferId( - transferId01, // lastTransferId - relayer.address, // provider - arbToken.address, // sourceToken - ethToken.address, // targetToken - other.address, // receiver - blockTimestamp02, - targetAmount01, // amount - ); - // cannot slash before expired - await expect(lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId01, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - targetAmount01, - blockTimestamp02, - other.address - ], - transferId01, - 0, - 0, - 200 - )).to.be.revertedWith("slash time not expired"); - console.log("check expired slash finished"); - await hre.network.provider.request({ - method: "evm_increaseTime", - //params: [await lnBridgeOnL1.MIN_REFUND_TIMESTAMP() + 1], - params: [18001], - }); - // slash - // start slash - // 1. relayed transfer cannot slash, try to slash transfer01 failed - await expect(lnBridgeOnL1.slashAndRemoteRefund( - [ - initTransferId, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - targetAmount01, - blockTimestamp, - other.address - ], - transferId01, - 0, - 0, - 0 - )).to.be.revertedWith("fill exist"); - console.log("check exist message slash finished"); - - // mock sender address - await lnBridgeOnL2.setRemoteBridgeAlias(inbox.address); - // request success - await ethToken.approve(lnBridgeOnL1.address, 1000000000); - await lnBridgeOnL1.slashAndRemoteRefund( - [ - transferId01, // lastTransferId - relayer.address, // provider - arbToken.address, - ethToken.address, // token - targetAmount01, - blockTimestamp02, - other.address - ], - transferId02, - 0, - 0, - 200 - ); - // check balance - // arbtoken: other -> relayer (transferAmount01 + baseFee + liquidityFeeRate * transferAmount01) - // lnBridgeOnL2 -> owner (transferAmount01 + baseFee + liquidityFeeRate * transferAmount01 + penaltyLnCollateral) - // other -> feeReceive (helixFee) - // ethtoken: owner -> other (transferAmount01) - const relayerFee02 = baseFee.add(transferAmount01.mul(liquidityFeeRate).div(100000)); - const relayerArbToken02 = relayerArbToken01.add(transferAmount01).add(relayerFee02); - const lnBridgeArbToken02 = lnBridgeArbToken01.sub(transferAmount01).sub(relayerFee02).sub(penaltyLnCollateral); - const otherArbToken02 = otherArbToken01.sub(transferAmount01).sub(relayerFee02).sub(helixFee); - const feeReceiverArbToken02 = feeReceiverArbToken01.add(helixFee); - const ownerArbToken02 = transferAmount01.add(relayerFee02).add(penaltyLnCollateral); - const relayerEthToken02 = relayerEthToken01; - const otherEthToken02 = otherEthToken01.add(targetAmount01); - const ownerEthToken02 = initTokenBalance.sub(targetAmount01); - expect(await arbToken.balanceOf(relayer.address)).to.equal(relayerArbToken02); - expect(await arbToken.balanceOf(lnBridgeOnL2.address)).to.equal(lnBridgeArbToken02); - expect(await arbToken.balanceOf(other.address)).to.equal(otherArbToken02); - expect(await arbToken.balanceOf(feeReceiver)).to.equal(feeReceiverArbToken02); - expect(await arbToken.balanceOf(owner.address)).to.equal(ownerArbToken02); - expect(await ethToken.balanceOf(relayer.address)).to.equal(relayerEthToken02); - expect(await ethToken.balanceOf(other.address)).to.equal(otherEthToken02); - expect(await ethToken.balanceOf(owner.address)).to.equal(ownerEthToken02); - console.log("check normal slash finished"); - }); -}); diff --git a/helix-contract/test/3_test_ln_opposite.js b/helix-contract/test/3_test_ln_opposite.js new file mode 100644 index 00000000..1471d939 --- /dev/null +++ b/helix-contract/test/3_test_ln_opposite.js @@ -0,0 +1,560 @@ +const { expect } = require("chai"); +const { solidity } = require("ethereum-waffle"); +const chai = require("chai"); +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const secp256k1 = require('secp256k1'); + +chai.use(solidity); + +// ethereum -> arbitrum using arbitrum L1->L2 message +// arbitrum -> ethereum using layerzero message + +async function getBlockTimestamp() { + const blockNumber = await ethers.provider.getBlockNumber(); + const block = await ethers.provider.getBlock(blockNumber); + return block.timestamp; +} + +function getTransferId( + localChainId, + remoteChainId, + lastTransferId, // lastTransferId + provider, // provider + sourceToken, // sourceToken + targetToken, // targetToken + receiver, // receiver + amount, // amount +) { + const encoded = ethers.utils.solidityPack([ + "uint256", + "uint256", + "bytes32", + "address", + "address", + "address", + "address", + "uint112", + ], [localChainId, remoteChainId, lastTransferId, provider, sourceToken, targetToken, receiver, amount]); + return ethUtil.keccak256(encoded); +} + +function getProviderKey( + remoteChainId, + provider, + sourceToken, + remoteToken +) { + const encode = ethers.utils.solidityPack([ + "uint256", + "address", + "address", + "address", + ], [remoteChainId, provider, sourceToken, remoteToken]); + return ethUtil.keccak256(encode); +} + +describe("eth->arb lnv2 positive bridge tests", () => { + before(async () => { + }); + + it("test_lnv2_flow", async function () { + const [owner, relayer, user, slasher] = await ethers.getSigners(); + const dao = owner.address; + const protocolFee = 100; + const penalty = 200; + const feeReceiver = "0x1000000000000000000000000000000000000001"; + const nullAddress = "0x0000000000000000000000000000000000000000"; + const baseFee = 300; + const liquidityFeeRate = 1; + const initTokenBalance = 1000000; + const initMargin = 10000; + const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; + const transferAmount = 30; + const ethChainId = 31337; + const arbChainId = 31337; + + // deploy erc20 token contract + const tokenNameOnEthereum = "Darwinia Ring On Ethereum"; + const tokenSymbolOnEthereum = "RING.e"; + const ethContract = await ethers.getContractFactory("Erc20"); + const ethToken = await ethContract.deploy(tokenNameOnEthereum, tokenSymbolOnEthereum, 18); + await ethToken.deployed(); + + const tokenNameOnArbitrum = "Darwinia Ring On Arbitrum"; + const tokenSymbolOnArbitrum = "RING.a"; + const arbContract = await ethers.getContractFactory("Erc20"); + const arbToken = await ethContract.deploy(tokenNameOnArbitrum, tokenSymbolOnArbitrum, 9); + await arbToken.deployed(); + console.log("contract deploy erc20 finished"); + + // mint some tokens on source chain and target chain for relayer + await ethToken.mint(feeReceiver, initTokenBalance); + await arbToken.mint(feeReceiver, initTokenBalance); + await ethToken.mint(relayer.address, initTokenBalance); + await arbToken.mint(relayer.address, initTokenBalance); + await ethToken.mint(user.address, initTokenBalance); + await arbToken.mint(user.address, initTokenBalance); + await ethToken.mint(owner.address, initTokenBalance); + await arbToken.mint(owner.address, initTokenBalance); + await ethToken.mint(slasher.address, initTokenBalance); + await arbToken.mint(slasher.address, initTokenBalance); + + // deploy inboundLane + const inboxContract = await ethers.getContractFactory("MockArbitrumInbox"); + const inbox = await inboxContract.deploy(); + await inbox.deployed(); + console.log("deploy mock inbox success"); + //******* deploy inboundLane/outboundLane finished ******** + + + const bridgeContract = await ethers.getContractFactory("LnOppositeBridge"); + + const ethBridge = await bridgeContract.deploy(); + await ethBridge.deployed(); + await ethBridge.initialize(dao); + await ethBridge.updateFeeReceiver(feeReceiver); + const arbBridge = await bridgeContract.deploy(); + await arbBridge.deployed(); + await arbBridge.initialize(dao); + await arbBridge.updateFeeReceiver(feeReceiver); + + // eth -> arb messager service + console.log("deploy etherum to arbitrum l1->l2 message service"); + const eth2arbSendServiceContract = await ethers.getContractFactory("Eth2ArbSendService"); + const eth2arbSendService = await eth2arbSendServiceContract.deploy(dao, inbox.address, arbChainId); + await eth2arbSendService.deployed(); + const eth2arbRecvServiceContract = await ethers.getContractFactory("MockEth2ArbReceiveService"); + const eth2arbRecvService = await eth2arbRecvServiceContract.deploy(dao, ethChainId); + await eth2arbRecvService.deployed(); + + await eth2arbSendService.setRemoteMessager(eth2arbRecvService.address); + await eth2arbRecvService.setRemoteMessagerAlias(inbox.address); + + // arb -> eth message service + console.log("deploy arbitrum to ethereum layerzero message service"); + const endpointContract = await ethers.getContractFactory("LayerZeroEndpointMock"); + const endpoint = await endpointContract.deploy(arbChainId); + await endpoint.deployed(); + console.log("deploy mock endpoint success"); + //******* deploy endpoint finished ******** + + // deploy layerzero messager + const lzMessagerContract = await ethers.getContractFactory("LayerZeroMessager"); + const lzMessagerEth = await lzMessagerContract.deploy(dao, endpoint.address); + await lzMessagerEth.deployed(); + const lzMessagerArb = await lzMessagerContract.deploy(dao, endpoint.address); + await lzMessagerArb.deployed(); + + await lzMessagerEth.setRemoteMessager(arbChainId, arbChainId, lzMessagerArb.address); + await lzMessagerArb.setRemoteMessager(ethChainId, ethChainId, lzMessagerEth.address); + console.log("messager service deploy finished"); + + console.log("configure message service for token bridge"); + // authorise + await eth2arbSendService.authoriseAppCaller(ethBridge.address, true); + await eth2arbRecvService.authoriseAppCaller(arbBridge.address, true); + await lzMessagerEth.authoriseAppCaller(ethBridge.address, true); + await lzMessagerArb.authoriseAppCaller(arbBridge.address, true); + + await ethBridge.setSendService(arbChainId, arbBridge.address, eth2arbSendService.address); + await ethBridge.setReceiveService(arbChainId, arbBridge.address, lzMessagerEth.address); + await arbBridge.setSendService(ethChainId, ethBridge.address, lzMessagerArb.address); + await arbBridge.setReceiveService(ethChainId, ethBridge.address, eth2arbRecvService.address); + + // configure + // register token + console.log("register token info"); + await ethBridge.setTokenInfo( + arbChainId, + ethToken.address, + arbToken.address, + protocolFee, + penalty, + 18, + 18 + ); + await arbBridge.setTokenInfo( + ethChainId, + arbToken.address, + ethToken.address, + protocolFee, + penalty, + 18, + 18 + ); + + console.log("provider register"); + // provider + await ethToken.connect(relayer).approve(ethBridge.address, initTokenBalance); + await arbToken.connect(relayer).approve(arbBridge.address, initTokenBalance); + // register on source chain + await arbBridge.connect(relayer).updateProviderFeeAndMargin( + ethChainId, + arbToken.address, + ethToken.address, + initMargin, + baseFee, + liquidityFeeRate + ); + + await ethBridge.connect(relayer).updateProviderFeeAndMargin( + arbChainId, + ethToken.address, + arbToken.address, + initMargin, + baseFee, + liquidityFeeRate + ); + + async function getChainInfo(direction) { + if (direction === 'eth2arb') { + return { + srcChainId: ethChainId, + dstChainId: arbChainId, + srcToken: ethToken, + dstToken: arbToken, + srcBridge: ethBridge, + dstBridge: arbBridge, + extParams: relayer.address, + }; + } else { + return { + srcChainId: arbChainId, + dstChainId: ethChainId, + srcToken: arbToken, + dstToken: ethToken, + srcBridge: arbBridge, + dstBridge: ethBridge, + extParams: await eth2arbSendService.encodeParams(0, 200, 200, relayer.address), + }; + } + } + + async function getCurrentTransferId(direction, lastTransferId) { + const chainInfo = await getChainInfo(direction); + const blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; + + const transferId = getTransferId( + chainInfo.srcChainId, + chainInfo.dstChainId, + lastTransferId, // lastTransferId + relayer.address, // provider + chainInfo.srcToken.address, // sourceToken + chainInfo.dstToken.address, // targetToken + user.address, // receiver + transferAmount, // amount + ); + + // check transferId exist on source chain + const lockInfo = await chainInfo.srcBridge.lockInfos(transferId); + expect(lockInfo.timestamp).to.equal(blockTimestamp); + return transferId; + } + + async function transfer(direction, lastTransferId, leftMargin) { + const chainInfo = await getChainInfo(direction); + const totalFee = Number(await chainInfo.srcBridge.totalFee( + chainInfo.dstChainId, + relayer.address, + chainInfo.srcToken.address, + chainInfo.dstToken.address, + transferAmount + )); + const balanceOfUser = await chainInfo.srcToken.balanceOf(user.address); + const balanceOfRelayer = await chainInfo.srcToken.balanceOf(relayer.address); + const tx = await chainInfo.srcBridge.connect(user).transferAndLockMargin( + [ + chainInfo.dstChainId, + relayer.address, + chainInfo.srcToken.address, + chainInfo.dstToken.address, + lastTransferId, + totalFee, + leftMargin + ], + transferAmount, + user.address, + ); + const balanceOfUserAfter = await chainInfo.srcToken.balanceOf(user.address); + const balanceOfRelayerAfter = await chainInfo.srcToken.balanceOf(relayer.address); + expect(balanceOfUser - balanceOfUserAfter).to.equal(totalFee + transferAmount); + expect(balanceOfRelayerAfter - balanceOfRelayer).to.equal(transferAmount + totalFee - protocolFee); + return tx; + } + + async function relay(direction, lastTransferId, transferId, timestamp) { + const chainInfo = await getChainInfo(direction); + let blockTimestamp = timestamp; + if (blockTimestamp === null) { + blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; + } + const balanceOfUser = await chainInfo.dstToken.balanceOf(user.address); + const balanceOfRelayer = await chainInfo.dstToken.balanceOf(relayer.address); + const relayTransaction = await chainInfo.dstBridge.connect(relayer).transferAndReleaseMargin( + [ + lastTransferId, // lastTransferId + relayer.address, // provider + chainInfo.srcToken.address, // sourceToken + chainInfo.dstToken.address, // targetToken + transferAmount, + blockTimestamp, + user.address + ], + chainInfo.srcChainId, + transferId + ); + + // check relay result + const relayTimestamp = (await ethers.provider.getBlock("latest")).timestamp; + //const fillInfo = await chainInfo.dstBridge.fillTransfers(transferId); + //expect(fillInfo.timestamp).to.equal(relayTimestamp); + const slashInfo = await chainInfo.dstBridge.slashInfos(transferId); + expect(slashInfo.slasher).to.equal(nullAddress); + const balanceOfUserAfter = await chainInfo.dstToken.balanceOf(user.address); + const balanceOfRelayerAfter = await chainInfo.dstToken.balanceOf(relayer.address); + expect(balanceOfUserAfter - balanceOfUser).to.equal(transferAmount); + expect(balanceOfRelayer - balanceOfRelayerAfter).to.equal(transferAmount); + return relayTransaction; + } + + async function slash(direction, lastTransferId, expectedTransferId, timestamp) { + const chainInfo = await getChainInfo(direction); + let blockTimestamp = timestamp; + if (blockTimestamp === null) { + blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; + } + const balanceOfUser = await chainInfo.dstToken.balanceOf(user.address); + const balanceOfSlasher = await chainInfo.dstToken.balanceOf(slasher.address); + const balanceOfSlasherOnSrc = await chainInfo.srcToken.balanceOf(slasher.address); + const slashTransaction = await chainInfo.dstBridge.connect(slasher).requestSlashAndRemoteRelease( + [ + lastTransferId, + relayer.address, + chainInfo.srcToken.address, + chainInfo.dstToken.address, + transferAmount, + blockTimestamp, + user.address + ], + chainInfo.srcChainId, + expectedTransferId, + chainInfo.extParams, + ); + const slashInfo = await chainInfo.dstBridge.slashInfos(expectedTransferId); + const balanceOfUserAfter = await chainInfo.dstToken.balanceOf(user.address); + const balanceOfSlasherAfter = await chainInfo.dstToken.balanceOf(slasher.address); + const balanceOfSlasherAfterOnSrc = await chainInfo.srcToken.balanceOf(slasher.address); + const totalFee = Number(await chainInfo.srcBridge.totalFee( + chainInfo.dstChainId, + relayer.address, + chainInfo.srcToken.address, + chainInfo.dstToken.address, + transferAmount + )); + expect(balanceOfUserAfter - balanceOfUser).to.equal(transferAmount); + expect(balanceOfSlasher - balanceOfSlasherAfter).to.equal(transferAmount); + expect(slashInfo.slasher).to.equal(slasher.address); + expect(balanceOfSlasherAfterOnSrc - balanceOfSlasherOnSrc).to.equal(transferAmount + penalty + totalFee - protocolFee); + return slashTransaction; + } + + async function withdraw(direction, lastTransferId, amount) { + const chainInfo = await getChainInfo(direction); + const providerKey = getProviderKey(chainInfo.dstChainId, relayer.address, chainInfo.srcToken.address, chainInfo.dstToken.address); + const marginBefore = (await chainInfo.srcBridge.srcProviders(providerKey)).config.margin; + + const balanceOfRelayerBefore = await chainInfo.srcToken.balanceOf(relayer.address); + const withdrawTransaction = await chainInfo.dstBridge.connect(relayer).requestWithdrawMargin( + chainInfo.srcChainId, + lastTransferId, + chainInfo.srcToken.address, + chainInfo.dstToken.address, + amount, + chainInfo.extParams, + ); + const balanceOfRelayerAfter = await chainInfo.srcToken.balanceOf(relayer.address); + const marginAfter = (await chainInfo.srcBridge.srcProviders(providerKey)).config.margin; + + let successWithdrawAmount = amount; + if (marginBefore.lt(amount)) { + // if withdraw failed + successWithdrawAmount = 0; + } + expect(balanceOfRelayerAfter - balanceOfRelayerBefore).to.equal(successWithdrawAmount); + expect(marginBefore - marginAfter).to.equal(successWithdrawAmount); + return successWithdrawAmount > 0; + } + + // eth -> arb + { + await ethToken.connect(user).approve(ethBridge.address, initTokenBalance); + const totalFee = Number(await ethBridge.totalFee( + arbChainId, + relayer.address, + ethToken.address, + arbToken.address, + transferAmount + )); + // 1. transfer from eth to arb + const lockTransaction = await transfer('eth2arb', initTransferId, initMargin); + let lockReceipt = await lockTransaction.wait(); + let lockGasUsed = lockReceipt.cumulativeGasUsed; + console.log("transferAndLockMargin gas used", lockGasUsed); + const blockTimestamp01 = (await ethers.provider.getBlock("latest")).timestamp; + + const transferId01 = await getCurrentTransferId('eth2arb', initTransferId); + + // 2. relay "transfer from eth to arb" + const relayTransaction = await relay('eth2arb', initTransferId, transferId01, null); + let relayReceipt = await relayTransaction.wait(); + let relayGasUsed = relayReceipt.cumulativeGasUsed; + console.log("relay gas used", relayGasUsed); + + // check balance + const userEthBalance = initTokenBalance - transferAmount - totalFee; + const relayerEthBalance = initTokenBalance + transferAmount + totalFee - protocolFee - initMargin ; + const userArbBalance = initTokenBalance + transferAmount; + const relayerArbBalance = initTokenBalance - transferAmount - initMargin; + expect(await ethToken.balanceOf(user.address)).to.equal(userEthBalance); + expect(await ethToken.balanceOf(relayer.address)).to.equal(relayerEthBalance); + expect(await arbToken.balanceOf(user.address)).to.equal(userArbBalance); + expect(await arbToken.balanceOf(relayer.address)).to.equal(relayerArbBalance); + console.log("normal lock and release test finished"); + + // check unique and continuous + await expect(transfer("eth2arb", initTransferId, initMargin)).to.be.revertedWith("snapshot expired"); + await expect(transfer("eth2arb", transferId01, initMargin + 1)).to.be.revertedWith("margin updated"); + + const lockTransaction1 = await transfer("eth2arb", transferId01, initMargin) + lockReceipt = await lockTransaction1.wait(); + lockGasUsed = lockReceipt.cumulativeGasUsed; + console.log("transferAndLockMargin 01 gas used", lockGasUsed); + const blockTimestamp02 = (await ethers.provider.getBlock("latest")).timestamp; + const transferId02 = await getCurrentTransferId("eth2arb", transferId01); + await transfer("eth2arb", transferId02, 0) + const blockTimestamp03 = (await ethers.provider.getBlock("latest")).timestamp; + const transferId03 = await getCurrentTransferId("eth2arb", transferId02); + + // release transfer02 failed + await expect(relay("eth2arb", transferId02, transferId03, null)).to.be.revertedWith("previous fill not exist"); + // 1. slash when not timeout + await expect(slash("eth2arb", transferId02, transferId03, null)).to.be.revertedWith("slash time not expired"); + + await hre.network.provider.request({ + method: "evm_increaseTime", + params: [18001], + }); + await expect(slash("eth2arb", transferId02, transferId03, blockTimestamp03)).to.be.revertedWith("previous fill not exist"); + console.log("check continuous success"); + + // 2. slash when timeout, but relayed + await expect(slash("eth2arb", initTransferId, transferId01, blockTimestamp01)).to.be.revertedWith("fill exist"); + // relay 02 && slash 02 + await relay("eth2arb", transferId01, transferId02, blockTimestamp02); + // can't relay twice + await expect(relay("eth2arb", transferId01, transferId02, blockTimestamp02)).to.be.revertedWith("fill exist"); + // 3. slash when timeout but relayed(timeout) + // can't slash event if relayed when timeout + await expect(slash("eth2arb", transferId01, transferId02, blockTimestamp02)).to.be.revertedWith("fill exist"); + // slash 03 + // 4. slash when timeout and not relayed + // can slash if not relayed when timeout + await arbToken.connect(slasher).approve(arbBridge.address, initTokenBalance); + await slash("eth2arb", transferId02, transferId03, blockTimestamp03); + + expect(await withdraw('eth2arb', transferId03, 15000)).to.equal(false); + expect(await withdraw('eth2arb', transferId03, 5000)).to.equal(true); + console.log("ln bridge test eth2arb finished"); + } + + // test arb2eth direction + { + await arbToken.connect(user).approve(arbBridge.address, initTokenBalance); + const totalFee = Number(await arbBridge.totalFee( + ethChainId, + relayer.address, + arbToken.address, + ethToken.address, + transferAmount + )); + // 1. transfer from eth to arb + const userArbBalanceBefore = await arbToken.balanceOf(user.address); + const relayerArbBalanceBefore = await arbToken.balanceOf(relayer.address); + const userEthBalanceBefore = await ethToken.balanceOf(user.address); + const relayerEthBalanceBefore = await ethToken.balanceOf(relayer.address); + + const lockTransaction = await transfer('arb2eth', initTransferId, 0); + let lockReceipt = await lockTransaction.wait(); + let lockGasUsed = lockReceipt.cumulativeGasUsed; + console.log("transferAndLockMargin gas used", lockGasUsed); + const blockTimestamp01 = (await ethers.provider.getBlock("latest")).timestamp; + + const transferId01 = await getCurrentTransferId('arb2eth', initTransferId); + + // 2. relay "transfer from eth to arb" + const relayTransaction = await relay('arb2eth', initTransferId, transferId01, null); + let relayReceipt = await relayTransaction.wait(); + let relayGasUsed = relayReceipt.cumulativeGasUsed; + console.log("relay gas used", relayGasUsed); + + console.log("total fee", totalFee); + // check balance + const userArbBalance = userArbBalanceBefore - totalFee - transferAmount; + const relayerArbBalance = relayerArbBalanceBefore.add(totalFee).sub(protocolFee).add(transferAmount); + const userEthBalance = userEthBalanceBefore.add(transferAmount); + const relayerEthBalance = relayerEthBalanceBefore - transferAmount; + expect(await arbToken.balanceOf(user.address)).to.equal(userArbBalance); + expect(await arbToken.balanceOf(relayer.address)).to.equal(relayerArbBalance); + expect(await ethToken.balanceOf(user.address)).to.equal(userEthBalance); + expect(await ethToken.balanceOf(relayer.address)).to.equal(relayerEthBalance); + console.log("normal lock and release test finished"); + + // check unique and continuous + await expect(transfer("arb2eth", initTransferId, initMargin)).to.be.revertedWith("snapshot expired"); + await expect(transfer("arb2eth", transferId01, initMargin + 1)).to.be.revertedWith("margin updated"); + + const lockTransaction1 = await transfer("arb2eth", transferId01, initMargin) + lockReceipt = await lockTransaction1.wait(); + lockGasUsed = lockReceipt.cumulativeGasUsed; + console.log("transferAndLockMargin 01 gas used", lockGasUsed); + const blockTimestamp02 = (await ethers.provider.getBlock("latest")).timestamp; + const transferId02 = await getCurrentTransferId("arb2eth", transferId01); + await transfer("arb2eth", transferId02, 0) + const blockTimestamp03 = (await ethers.provider.getBlock("latest")).timestamp; + const transferId03 = await getCurrentTransferId("arb2eth", transferId02); + + // release transfer02 failed + await expect(relay("arb2eth", transferId02, transferId03, null)).to.be.revertedWith("previous fill not exist"); + // 1. slash when not timeout + await expect(slash("arb2eth", transferId02, transferId03, null)).to.be.revertedWith("slash time not expired"); + + await hre.network.provider.request({ + method: "evm_increaseTime", + params: [18001], + }); + await expect(slash("arb2eth", transferId02, transferId03, blockTimestamp03)).to.be.revertedWith("previous fill not exist"); + console.log("check continuous success"); + + // 2. slash when timeout, but relayed + await expect(slash("arb2eth", initTransferId, transferId01, blockTimestamp01)).to.be.revertedWith("fill exist"); + // relay 02 && slash 02 + await relay("arb2eth", transferId01, transferId02, blockTimestamp02); + // can't relay twice + await expect(relay("arb2eth", transferId01, transferId02, blockTimestamp02)).to.be.revertedWith("fill exist"); + // 3. slash when timeout but relayed(timeout) + // can't slash event if relayed when timeout + await expect(slash("arb2eth", transferId01, transferId02, blockTimestamp02)).to.be.revertedWith("fill exist"); + // slash 03 + // 4. slash when timeout and not relayed + // can slash if not relayed when timeout + await ethToken.connect(slasher).approve(ethBridge.address, initTokenBalance); + console.log("try slash normal"); + await slash("arb2eth", transferId02, transferId03, blockTimestamp03); + + await expect(withdraw('arb2eth', transferId03, 15000)).to.be.revertedWith("arbitrum mock call failed"); + expect(await withdraw('arb2eth', transferId03, 5000)).to.equal(true); + console.log("ln bridge test finished"); + } + }); +}); diff --git a/helix-contract/test/5_test_ln_layerzero.js b/helix-contract/test/4_test_ln_layerzero.js similarity index 68% rename from helix-contract/test/5_test_ln_layerzero.js rename to helix-contract/test/4_test_ln_layerzero.js index 93eb588d..79fd0be3 100644 --- a/helix-contract/test/5_test_ln_layerzero.js +++ b/helix-contract/test/4_test_ln_layerzero.js @@ -14,26 +14,43 @@ async function getBlockTimestamp() { } function getTransferId( + localChainId, + remoteChainId, lastTransferId, // lastTransferId provider, // provider sourceToken, // sourceToken targetToken, // targetToken receiver, // receiver - timestamp, amount, // amount ) { const encoded = ethers.utils.solidityPack([ + "uint256", + "uint256", "bytes32", "address", "address", "address", "address", - "uint64", "uint112", - ], [lastTransferId, provider, sourceToken, targetToken, receiver, timestamp, amount]); + ], [localChainId, remoteChainId, lastTransferId, provider, sourceToken, targetToken, receiver, amount]); return ethUtil.keccak256(encoded); } +function getProviderKey( + remoteChainId, + provider, + sourceToken, + remoteToken +) { + const encode = ethers.utils.solidityPack([ + "uint256", + "address", + "address", + "address", + ], [remoteChainId, provider, sourceToken, remoteToken]); + return ethUtil.keccak256(encode); +} + describe("eth->arb lnv2 layerzero bridge tests", () => { before(async () => { }); @@ -52,8 +69,8 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { const initSlashReserveFund = 1000; const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; const transferAmount = 30; - const srcChainId = 100; - const dstChainId = 200; + const ethChainId = 31337; + const arbChainId = 31337; // deploy erc20 token contract const tokenNameOnEthereum = "Darwinia Ring On Ethereum"; @@ -83,22 +100,36 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { // deploy LayerZeroEndpointMock const endpointContract = await ethers.getContractFactory("LayerZeroEndpointMock"); - const endpoint = await endpointContract.deploy(srcChainId); + const endpoint = await endpointContract.deploy(ethChainId); await endpoint.deployed(); console.log("deploy mock endpoint success"); //******* deploy endpoint finished ******** - const eth2arbSourceContract = await ethers.getContractFactory("LnBridgeBaseLZ"); - const eth2arbSource = await eth2arbSourceContract.deploy(); - await eth2arbSource.deployed(); + // deploy layerzero messager + const lzMessagerContract = await ethers.getContractFactory("LayerZeroMessager"); + const lzMessagerEth = await lzMessagerContract.deploy(dao, endpoint.address); + await lzMessagerEth.deployed(); + const lzMessagerArb = await lzMessagerContract.deploy(dao, endpoint.address); + await lzMessagerArb.deployed(); + + await lzMessagerEth.setRemoteMessager(arbChainId, arbChainId, lzMessagerArb.address); + await lzMessagerArb.setRemoteMessager(ethChainId, ethChainId, lzMessagerEth.address); + + const lnDefaultBridgeContract = await ethers.getContractFactory("LnDefaultBridge"); + + const lnDefaultBridgeEth = await lnDefaultBridgeContract.deploy(); + await lnDefaultBridgeEth.deployed(); + const lnDefaultBridgeArb = await lnDefaultBridgeContract.deploy(); + await lnDefaultBridgeArb.deployed(); // configure // init // set fee receiver // register token - await eth2arbSource.initialize(dao, endpoint.address, dstChainId); - await eth2arbSource.updateFeeReceiver(feeReceiver); - await eth2arbSource.setTokenInfo( + await lnDefaultBridgeEth.initialize(dao); + await lnDefaultBridgeEth.updateFeeReceiver(feeReceiver); + await lnDefaultBridgeEth.setTokenInfo( + arbChainId, ethToken.address, arbToken.address, protocolFee, @@ -107,30 +138,45 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { 18 ); - const eth2arbTargetContract = await ethers.getContractFactory("LnBridgeBaseLZ"); - const eth2arbTarget = await eth2arbTargetContract.deploy(); - await eth2arbTarget.deployed(); - await eth2arbTarget.initialize(dao, endpoint.address, srcChainId); + await lnDefaultBridgeArb.initialize(dao); + await lnDefaultBridgeArb.updateFeeReceiver(feeReceiver); + await lnDefaultBridgeArb.setTokenInfo( + arbChainId, + arbToken.address, + ethToken.address, + protocolFee, + penalty, + 18, + 18 + ); + // ******************* register token ************** - await eth2arbSource.setRemoteBridge(eth2arbTarget.address); - await eth2arbTarget.setRemoteBridge(eth2arbSource.address); + // set bridge infos + await lzMessagerEth.authoriseAppCaller(lnDefaultBridgeEth.address, true); + await lzMessagerArb.authoriseAppCaller(lnDefaultBridgeArb.address, true); + await lnDefaultBridgeEth.setSendService(arbChainId, lnDefaultBridgeArb.address, lzMessagerEth.address); + await lnDefaultBridgeArb.setReceiveService(ethChainId, lnDefaultBridgeEth.address, lzMessagerArb.address); console.log("deploy bridge finished"); // provider - await ethToken.connect(relayer).approve(eth2arbSource.address, initTokenBalance); - await arbToken.connect(relayer).approve(eth2arbTarget.address, initTokenBalance); + await ethToken.connect(relayer).approve(lnDefaultBridgeEth.address, initTokenBalance); + await arbToken.connect(relayer).approve(lnDefaultBridgeArb.address, initTokenBalance); // register on source chain(set provider fee) - await eth2arbSource.connect(relayer).setProviderFee( + await lnDefaultBridgeEth.connect(relayer).setProviderFee( + arbChainId, ethToken.address, + arbToken.address, baseFee, liquidityFeeRate ); - await eth2arbTarget.connect(relayer).depositProviderMargin( + await lnDefaultBridgeArb.connect(relayer).depositProviderMargin( + ethChainId, ethToken.address, arbToken.address, initMargin ); - await eth2arbTarget.connect(relayer).depositSlashFundReserve( + await lnDefaultBridgeArb.connect(relayer).depositSlashFundReserve( + ethChainId, ethToken.address, arbToken.address, initSlashReserveFund @@ -139,33 +185,38 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { async function getCurrentTransferId(lastTransferId) { const blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; const transferId = getTransferId( + ethChainId, + arbChainId, lastTransferId, // lastTransferId relayer.address, // provider ethToken.address, // sourceToken arbToken.address, // targetToken user.address, // receiver - blockTimestamp, transferAmount, // amount ); // check transferId exist on source chain - const lockInfo = await eth2arbSource.lockInfos(transferId); - expect(lockInfo.isLocked).to.equal(true); + const lockInfo = await lnDefaultBridgeEth.lockInfos(transferId); + expect(lockInfo.timestamp).to.equal(blockTimestamp); return transferId; } async function transfer(lastTransferId, withdrawNonce) { - const totalFee = Number(await eth2arbSource.totalFee( + const totalFee = Number(await lnDefaultBridgeEth.totalFee( + arbChainId, relayer.address, ethToken.address, + arbToken.address, transferAmount )); const balanceOfUser = await ethToken.balanceOf(user.address); const balanceOfRelayer = await ethToken.balanceOf(relayer.address); - const tx = await eth2arbSource.connect(user).transferAndLockMargin( + const tx = await lnDefaultBridgeEth.connect(user).transferAndLockMargin( [ + arbChainId, relayer.address, ethToken.address, + arbToken.address, lastTransferId, totalFee, withdrawNonce @@ -187,7 +238,7 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { } const balanceOfUser = await arbToken.balanceOf(user.address); const balanceOfRelayer = await arbToken.balanceOf(relayer.address); - const relayTransaction = await eth2arbTarget.connect(relayer).transferAndReleaseMargin( + const relayTransaction = await lnDefaultBridgeArb.connect(relayer).transferAndReleaseMargin( [ lastTransferId, // lastTransferId relayer.address, // provider @@ -197,12 +248,13 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { blockTimestamp, user.address ], + ethChainId, transferId ); // check relay result const relayTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - const fillInfo = await eth2arbTarget.fillTransfers(transferId); + const fillInfo = await lnDefaultBridgeArb.fillTransfers(transferId); expect(fillInfo.timestamp).to.equal(relayTimestamp); expect(fillInfo.slasher).to.equal(nullAddress); const balanceOfUserAfter = await arbToken.balanceOf(user.address); @@ -217,11 +269,11 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { if (blockTimestamp === null) { blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; } - const fillInfoBefore = await eth2arbTarget.fillTransfers(expectedTransferId); + const fillInfoBefore = await lnDefaultBridgeArb.fillTransfers(expectedTransferId); const timestampBefore = fillInfoBefore.timestamp; const balanceOfUser = await arbToken.balanceOf(user.address); const balanceOfSlasher = await arbToken.balanceOf(slasher.address); - const slashTransaction = await eth2arbSource.connect(slasher).slashAndRemoteRelease( + const slashTransaction = await lnDefaultBridgeEth.connect(slasher).requestSlashAndRemoteRelease( [ lastTransferId, relayer.address, @@ -231,10 +283,12 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { blockTimestamp, user.address ], - expectedTransferId + ethChainId, + expectedTransferId, + relayer.address ); const relayTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - const fillInfo = await eth2arbTarget.fillTransfers(expectedTransferId); + const fillInfo = await lnDefaultBridgeArb.fillTransfers(expectedTransferId); const balanceOfUserAfter = await arbToken.balanceOf(user.address); const balanceOfSlasherAfter = await arbToken.balanceOf(slasher.address); if (timestampBefore > 0) { @@ -242,24 +296,52 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { expect(balanceOfUserAfter - balanceOfUser).to.equal(0); expect(balanceOfSlasherAfter - balanceOfSlasher).to.equal(penalty/5); } else { - const totalFee = Number(await eth2arbSource.totalFee( + const totalFee = Number(await lnDefaultBridgeEth.totalFee( + arbChainId, relayer.address, ethToken.address, + arbToken.address, transferAmount )); expect(fillInfo.timestamp).to.equal(relayTimestamp); expect(balanceOfUserAfter - balanceOfUser).to.equal(transferAmount); - expect(balanceOfSlasherAfter - balanceOfSlasher).to.equal(penalty + totalFee); + expect(balanceOfSlasherAfter - balanceOfSlasher).to.equal(penalty + totalFee - protocolFee); } expect(fillInfo.slasher).to.equal(slasher.address); return slashTransaction; } + async function withdraw(amount) { + const providerKey = getProviderKey(ethChainId, relayer.address, ethToken.address, arbToken.address); + const marginBefore = (await lnDefaultBridgeArb.tgtProviders(providerKey)).margin; + const balanceOfRelayerBefore = await arbToken.balanceOf(relayer.address); + const withdrawTransaction = await lnDefaultBridgeEth.connect(relayer).requestWithdrawMargin( + ethChainId, + ethToken.address, + arbToken.address, + amount, + relayer.address + ); + const balanceOfRelayerAfter = await arbToken.balanceOf(relayer.address); + const marginAfter = (await lnDefaultBridgeArb.tgtProviders(providerKey)).margin; + + let successWithdrawAmount = amount; + if (marginBefore.lt(amount)) { + // if withdraw failed + successWithdrawAmount = 0; + } + expect(balanceOfRelayerAfter - balanceOfRelayerBefore).to.equal(successWithdrawAmount); + expect(marginBefore - marginAfter).to.equal(successWithdrawAmount); + return successWithdrawAmount > 0; + } + // user lock - await ethToken.connect(user).approve(eth2arbSource.address, initTokenBalance); - const totalFee = Number(await eth2arbSource.totalFee( + await ethToken.connect(user).approve(lnDefaultBridgeEth.address, initTokenBalance); + const totalFee = Number(await lnDefaultBridgeEth.totalFee( + arbChainId, relayer.address, ethToken.address, + arbToken.address, transferAmount )); const lockTransaction = await transfer(initTransferId, 0); @@ -318,9 +400,11 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { // relay 02 && slash 02 await relay(transferId01, transferId02, blockTimestamp02); // can't relay twice + console.log("test relay twice error"); await expect(relay(transferId01, transferId02, blockTimestamp02)).to.be.revertedWith("transfer has been filled"); // 3. slash when timeout but relayed(timeout) // can slash if relayed when timeout + console.log("test can slash when relayed timeout"); await slash(transferId01, transferId02, blockTimestamp02); // 4. slash when slash has finished // can't slash twice @@ -328,8 +412,11 @@ describe("eth->arb lnv2 layerzero bridge tests", () => { // slash 03 // 5. slash when timeout and not relayed // can slash if not relayed when timeout + console.log("test slash when not relayed and timeout"); await slash(transferId02, transferId03, blockTimestamp03); + expect(await withdraw(15000)).to.equal(false); + expect(await withdraw(5000)).to.equal(true); console.log("ln bridge test finished"); }); }); diff --git a/helix-contract/test/4_test_ln_positive.js b/helix-contract/test/4_test_ln_positive.js deleted file mode 100644 index add4698a..00000000 --- a/helix-contract/test/4_test_ln_positive.js +++ /dev/null @@ -1,336 +0,0 @@ -const { expect } = require("chai"); -const { solidity } = require("ethereum-waffle"); -const chai = require("chai"); -const ethUtil = require('ethereumjs-util'); -const abi = require('ethereumjs-abi'); -const secp256k1 = require('secp256k1'); - -chai.use(solidity); - -async function getBlockTimestamp() { - const blockNumber = await ethers.provider.getBlockNumber(); - const block = await ethers.provider.getBlock(blockNumber); - return block.timestamp; -} - -function getTransferId( - lastTransferId, // lastTransferId - provider, // provider - sourceToken, // sourceToken - targetToken, // targetToken - receiver, // receiver - timestamp, - amount, // amount -) { - const encoded = ethers.utils.solidityPack([ - "bytes32", - "address", - "address", - "address", - "address", - "uint64", - "uint112", - ], [lastTransferId, provider, sourceToken, targetToken, receiver, timestamp, amount]); - return ethUtil.keccak256(encoded); -} - -describe("eth->arb lnv2 positive bridge tests", () => { - before(async () => { - }); - - it("test_lnv2_flow", async function () { - const [owner, relayer, user, slasher] = await ethers.getSigners(); - const dao = owner.address; - const protocolFee = 100; - const penalty = 200; - const feeReceiver = "0x1000000000000000000000000000000000000001"; - const nullAddress = "0x0000000000000000000000000000000000000000"; - const baseFee = 300; - const liquidityFeeRate = 1; - const initTokenBalance = 1000000; - const initMargin = 10000; - const initSlashReserveFund = 1000; - const initTransferId = "0x0000000000000000000000000000000000000000000000000000000000000000"; - const transferAmount = 30; - - // deploy erc20 token contract - const tokenNameOnEthereum = "Darwinia Ring On Ethereum"; - const tokenSymbolOnEthereum = "RING.e"; - const ethContract = await ethers.getContractFactory("Erc20"); - const ethToken = await ethContract.deploy(tokenNameOnEthereum, tokenSymbolOnEthereum, 18); - await ethToken.deployed(); - - const tokenNameOnArbitrum = "Darwinia Ring On Arbitrum"; - const tokenSymbolOnArbitrum = "RING.a"; - const arbContract = await ethers.getContractFactory("Erc20"); - const arbToken = await ethContract.deploy(tokenNameOnArbitrum, tokenSymbolOnArbitrum, 9); - await arbToken.deployed(); - console.log("contract deploy erc20 finished"); - - // mint some tokens on source chain and target chain for relayer - await ethToken.mint(feeReceiver, initTokenBalance); - await arbToken.mint(feeReceiver, initTokenBalance); - await ethToken.mint(relayer.address, initTokenBalance); - await arbToken.mint(relayer.address, initTokenBalance); - await ethToken.mint(user.address, initTokenBalance); - await arbToken.mint(user.address, initTokenBalance); - await ethToken.mint(owner.address, initTokenBalance); - await arbToken.mint(owner.address, initTokenBalance); - await ethToken.mint(slasher.address, initTokenBalance); - await arbToken.mint(slasher.address, initTokenBalance); - - // deploy inboundLane - const inboxContract = await ethers.getContractFactory("MockArbitrumInbox"); - const inbox = await inboxContract.deploy(); - await inbox.deployed(); - console.log("deploy mock inbox success"); - //******* deploy inboundLane/outboundLane finished ******** - - const eth2arbSourceContract = await ethers.getContractFactory("Eth2ArbSource"); - const eth2arbSource = await eth2arbSourceContract.deploy(); - await eth2arbSource.deployed(); - - // configure - // init - // set fee receiver - // register token - await eth2arbSource.initialize(dao, inbox.address); - await eth2arbSource.updateFeeReceiver(feeReceiver); - await eth2arbSource.setTokenInfo( - ethToken.address, - arbToken.address, - protocolFee, - penalty, - 18, - 18 - ); - - const eth2arbTargetContract = await ethers.getContractFactory("Eth2ArbTarget"); - const eth2arbTarget = await eth2arbTargetContract.deploy(); - await eth2arbTarget.deployed(); - await eth2arbTarget.initialize(dao); - - await eth2arbSource.setRemoteBridge(eth2arbTarget.address); - await eth2arbTarget.setRemoteBridge(eth2arbSource.address); - await eth2arbTarget.setRemoteBridgeAlias(inbox.address); - console.log("deploy bridge finished"); - - // provider - await ethToken.connect(relayer).approve(eth2arbSource.address, initTokenBalance); - await arbToken.connect(relayer).approve(eth2arbTarget.address, initTokenBalance); - // register on source chain(set provider fee) - await eth2arbSource.connect(relayer).setProviderFee( - ethToken.address, - baseFee, - liquidityFeeRate - ); - await eth2arbTarget.connect(relayer).depositProviderMargin( - ethToken.address, - arbToken.address, - initMargin - ); - await eth2arbTarget.connect(relayer).depositSlashFundReserve( - ethToken.address, - arbToken.address, - initSlashReserveFund - ); - - async function getCurrentTransferId(lastTransferId) { - const blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - const transferId = getTransferId( - lastTransferId, // lastTransferId - relayer.address, // provider - ethToken.address, // sourceToken - arbToken.address, // targetToken - user.address, // receiver - blockTimestamp, - transferAmount, // amount - ); - - // check transferId exist on source chain - const lockInfo = await eth2arbSource.lockInfos(transferId); - expect(lockInfo.isLocked).to.equal(true); - return transferId; - } - - async function transfer(lastTransferId, withdrawNonce) { - const totalFee = Number(await eth2arbSource.totalFee( - relayer.address, - ethToken.address, - transferAmount - )); - const balanceOfUser = await ethToken.balanceOf(user.address); - const balanceOfRelayer = await ethToken.balanceOf(relayer.address); - const tx = await eth2arbSource.connect(user).transferAndLockMargin( - [ - relayer.address, - ethToken.address, - lastTransferId, - totalFee, - withdrawNonce - ], - transferAmount, - user.address, - ); - const balanceOfUserAfter = await ethToken.balanceOf(user.address); - const balanceOfRelayerAfter = await ethToken.balanceOf(relayer.address); - expect(balanceOfUser - balanceOfUserAfter).to.equal(totalFee + transferAmount); - expect(balanceOfRelayerAfter - balanceOfRelayer).to.equal(transferAmount + totalFee - protocolFee); - return tx; - } - - async function relay(lastTransferId, transferId, timestamp) { - let blockTimestamp = timestamp; - if (blockTimestamp === null) { - blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - } - const balanceOfUser = await arbToken.balanceOf(user.address); - const balanceOfRelayer = await arbToken.balanceOf(relayer.address); - const relayTransaction = await eth2arbTarget.connect(relayer).transferAndReleaseMargin( - [ - lastTransferId, // lastTransferId - relayer.address, // provider - ethToken.address, // sourceToken - arbToken.address, // targetToken - transferAmount, - blockTimestamp, - user.address - ], - transferId - ); - - // check relay result - const relayTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - const fillInfo = await eth2arbTarget.fillTransfers(transferId); - expect(fillInfo.timestamp).to.equal(relayTimestamp); - expect(fillInfo.slasher).to.equal(nullAddress); - const balanceOfUserAfter = await arbToken.balanceOf(user.address); - const balanceOfRelayerAfter = await arbToken.balanceOf(relayer.address); - expect(balanceOfUserAfter - balanceOfUser).to.equal(transferAmount); - expect(balanceOfRelayer - balanceOfRelayerAfter).to.equal(transferAmount); - return relayTransaction; - } - - async function slash(lastTransferId, expectedTransferId, timestamp) { - let blockTimestamp = timestamp; - if (blockTimestamp === null) { - blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - } - const fillInfoBefore = await eth2arbTarget.fillTransfers(expectedTransferId); - const timestampBefore = fillInfoBefore.timestamp; - const balanceOfUser = await arbToken.balanceOf(user.address); - const balanceOfSlasher = await arbToken.balanceOf(slasher.address); - const slashTransaction = await eth2arbSource.connect(slasher).slashAndRemoteRelease( - [ - lastTransferId, - relayer.address, - ethToken.address, - arbToken.address, - transferAmount, - blockTimestamp, - user.address - ], - expectedTransferId, - 0, - 0, - 200 - ); - const relayTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - const fillInfo = await eth2arbTarget.fillTransfers(expectedTransferId); - const balanceOfUserAfter = await arbToken.balanceOf(user.address); - const balanceOfSlasherAfter = await arbToken.balanceOf(slasher.address); - if (timestampBefore > 0) { - expect(fillInfo.timestamp).to.equal(timestampBefore); - expect(balanceOfUserAfter - balanceOfUser).to.equal(0); - expect(balanceOfSlasherAfter - balanceOfSlasher).to.equal(penalty/5); - } else { - const totalFee = Number(await eth2arbSource.totalFee( - relayer.address, - ethToken.address, - transferAmount - )); - expect(fillInfo.timestamp).to.equal(relayTimestamp); - expect(balanceOfUserAfter - balanceOfUser).to.equal(transferAmount); - expect(balanceOfSlasherAfter - balanceOfSlasher).to.equal(penalty + totalFee); - } - expect(fillInfo.slasher).to.equal(slasher.address); - return slashTransaction; - } - - // user lock - await ethToken.connect(user).approve(eth2arbSource.address, initTokenBalance); - const totalFee = Number(await eth2arbSource.totalFee( - relayer.address, - ethToken.address, - transferAmount - )); - const lockTransaction = await transfer(initTransferId, 0); - let lockReceipt = await lockTransaction.wait(); - let lockGasUsed = lockReceipt.cumulativeGasUsed; - console.log("transferAndLockMargin gas used", lockGasUsed); - const blockTimestamp01 = (await ethers.provider.getBlock("latest")).timestamp; - - const transferId01 = await getCurrentTransferId(initTransferId); - - const relayTransaction = await relay(initTransferId, transferId01, null); - let relayReceipt = await relayTransaction.wait(); - let relayGasUsed = relayReceipt.cumulativeGasUsed; - console.log("relay gas used", relayGasUsed); - - // check balance - const userEthBalance = initTokenBalance - transferAmount - totalFee; - const relayerEthBalance = initTokenBalance + transferAmount + totalFee - protocolFee; - const userArbBalance = initTokenBalance + transferAmount; - const relayerArbBalance = initTokenBalance - transferAmount - initMargin - initSlashReserveFund; - expect(await ethToken.balanceOf(user.address)).to.equal(userEthBalance); - expect(await ethToken.balanceOf(relayer.address)).to.equal(relayerEthBalance); - expect(await arbToken.balanceOf(user.address)).to.equal(userArbBalance); - expect(await arbToken.balanceOf(relayer.address)).to.equal(relayerArbBalance); - console.log("normal lock and release test finished"); - - // check unique and continuous - await expect(transfer(initTransferId, 0)).to.be.revertedWith("snapshot expired:transfer"); - await expect(transfer(transferId01, 1)).to.be.revertedWith("snapshot expired:withdraw"); - - const lockTransaction1 = await transfer(transferId01, 0) - lockReceipt = await lockTransaction1.wait(); - lockGasUsed = lockReceipt.cumulativeGasUsed; - console.log("transferAndLockMargin 01 gas used", lockGasUsed); - const blockTimestamp02 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId02 = await getCurrentTransferId(transferId01); - await transfer(transferId02, 0) - const blockTimestamp03 = (await ethers.provider.getBlock("latest")).timestamp; - const transferId03 = await getCurrentTransferId(transferId02); - - // release transfer02 failed - await expect(relay(transferId02, transferId03, null)).to.be.revertedWith("last transfer not filled"); - // 1. slash when not timeout - await expect(slash(transferId02, transferId03, null)).to.be.revertedWith("invalid timestamp"); - - await hre.network.provider.request({ - method: "evm_increaseTime", - params: [18001], - }); - await expect(slash(transferId02, transferId03, blockTimestamp03)).to.be.revertedWith("arbitrum mock call failed"); - console.log("check continuous success"); - - // 2. slash when timeout, but relayed - await expect(slash(initTransferId, transferId01, blockTimestamp01)).to.be.revertedWith("arbitrum mock call failed"); - // relay 02 && slash 02 - await relay(transferId01, transferId02, blockTimestamp02); - // can't relay twice - await expect(relay(transferId01, transferId02, blockTimestamp02)).to.be.revertedWith("transfer has been filled"); - // 3. slash when timeout but relayed(timeout) - // can slash if relayed when timeout - await slash(transferId01, transferId02, blockTimestamp02); - // 4. slash when slash has finished - // can't slash twice - await expect(slash(transferId01, transferId02, blockTimestamp02)).to.be.revertedWith("arbitrum mock call failed"); - // slash 03 - // 5. slash when timeout and not relayed - // can slash if not relayed when timeout - await slash(transferId02, transferId03, blockTimestamp03); - - console.log("ln bridge test finished"); - }); -}); diff --git a/helix-contract/yarn.lock b/helix-contract/yarn.lock index 03c3d716..04adf7ff 100644 --- a/helix-contract/yarn.lock +++ b/helix-contract/yarn.lock @@ -19,6 +19,11 @@ optionalDependencies: sol2uml "2.2.0" +"@axelar-network/axelar-gmp-sdk-solidity@^5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.3.1.tgz#66d46d6ff750a031d83f4f459007ebccd815f910" + integrity sha512-pwhSKbHjByavmJdETxf21lRekclAO5RBJkCYGVT/MmsfacNEAKbVFdOVbJ/JPLTIc5bPKWdp9ak4ET3GKrSjhg== + "@darwinia/contracts-periphery@@darwinia/contracts-periphery@2.1.1-fix2": version "2.1.1-fix2" resolved "https://registry.yarnpkg.com/@darwinia/contracts-periphery/-/contracts-periphery-2.1.1-fix2.tgz#e150414fe2422a8ca08bb446907f10aaf9ee5e18" @@ -4083,7 +4088,7 @@ ethereumjs-wallet@0.6.5: utf8 "^3.0.0" uuid "^3.3.2" -ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.6.9: +ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.14, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.6.9: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -4967,6 +4972,13 @@ hardhat-abi-exporter@^2.2.1: "@ethersproject/abi" "^5.5.0" delete-empty "^3.0.0" +hardhat-solidity-create2-deployer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hardhat-solidity-create2-deployer/-/hardhat-solidity-create2-deployer-1.0.2.tgz#deef793b01cb99d032dab6a272cb8339a0a0e593" + integrity sha512-u0tY4cE59GqoxbjB5vIRARZYdUcwBCKqu7ULv5V/Ad2t/GGH5buR1hLQrfaZNqh+hSppnwWbnnoIzAmkJFH9IA== + dependencies: + ethers "^5.0.14" + hardhat@^2.11.2: version "2.12.4" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.12.4.tgz#e539ba58bee9ba1a1ced823bfdcec0b3c5a3e70f" @@ -8932,6 +8944,13 @@ solc@^0.6.3: semver "^5.5.0" tmp "0.0.33" +solidity-create2-deployer@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/solidity-create2-deployer/-/solidity-create2-deployer-0.4.0.tgz#d966256e3ade848d840ed231cad3d3cd364d3b99" + integrity sha512-bB5d8fPt4dWsOoRodrsyfWKcjiv77IFl84+e6EckMMGYlfL2ZFqUoMz6tnqqiUFrM9abF1p6dWFOgJ/3zVc8yQ== + dependencies: + ethers "^5.0.14" + sorted-object@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/sorted-object/-/sorted-object-2.0.1.tgz#7d631f4bd3a798a24af1dffcfbfe83337a5df5fc" diff --git a/zksync-deployer/copy_zksync_contract.sh b/zksync-deployer/copy_zksync_contract.sh index c15094a4..8f2aa335 100644 --- a/zksync-deployer/copy_zksync_contract.sh +++ b/zksync-deployer/copy_zksync_contract.sh @@ -1,13 +1,14 @@ rm -rf ./contracts mkdir -p contracts/ln +mkdir -p contracts/ln/messager +mkdir -p contracts/ln/test rm -rf ./cache-zk rm -rf ./artifacts-zk cp -r ../helix-contract/contracts/ln/base ./contracts/ln/ cp -r ../helix-contract/contracts/ln/interface ./contracts/ln/ -cp -r ../helix-contract/contracts/ln/Eth2ZkSyncSource.sol ./contracts/ln/ -cp -r ../helix-contract/contracts/ln/Eth2ZkSyncTarget.sol ./contracts/ln/ -cp -r ../helix-contract/contracts/ln/ZkSync2EthSource.sol ./contracts/ln/ -cp -r ../helix-contract/contracts/ln/ZkSync2EthTarget.sol ./contracts/ln/ -cp -r ../helix-contract/contracts/ln/ProxyAdmin.sol ./contracts/ln/ -cp -r ../helix-contract/contracts/ln/TransparentUpgradeableProxy.sol ./contracts/ln/ -cp -r ../helix-contract/contracts/ln/LnBridgeBaseLZ.sol ./contracts/ln/ +cp -r ../helix-contract/contracts/ln/tool ./contracts/ln/ +cp -r ../helix-contract/contracts/ln/messager/interface ./contracts/ln/messager/ +cp -r ../helix-contract/contracts/ln/messager/LayerZeroMessager.sol ./contracts/ln/messager/ +cp -r ../helix-contract/contracts/ln/LnDefaultBridge.sol ./contracts/ln/ +cp -r ../helix-contract/contracts/ln/LnOppositeBridge.sol ./contracts/ln/ +cp -r ../helix-contract/contracts/ln/test/TestToken.sol ./contracts/ln/test/ diff --git a/zksync-deployer/deploy/deploy_eth2zksync_target.ts b/zksync-deployer/deploy/deploy_eth2zksync_target.ts deleted file mode 100644 index 7ce856b0..00000000 --- a/zksync-deployer/deploy/deploy_eth2zksync_target.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Wallet, utils, ContractFactory } from "zksync-web3"; -import * as ethers from "ethers"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; -import { ProxyDeployer } from "./proxy.ts"; - -// load wallet private key from env file -const privateKey = process.env.PRIKEY - -// An example of a deploy script that will deploy and call a simple contract. -export default async function (hre: HardhatRuntimeEnvironment) { - // deploy proxy admin contract - console.log(`Running deploy script for the eth2zksync target contract`); - const proxyAdmin = "0x96892F3EaD26515592Da38432cFABad991BBd69d"; - const dao = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; - - // Initialize the wallet. - const wallet = new Wallet(privateKey); - - // Create deployer object and load the artifact of the contract you want to deploy. - const deployer = new Deployer(hre, wallet); - // deploy logic contract - const artifact = await deployer.loadArtifact("Eth2ZkSyncTarget"); - const logicContract = await deployer.deploy(artifact, []); - const logicContractAddress = logicContract.address; - console.log(`logic contract was deployed to ${logicContractAddress}`); - const logicVerificationId = await hre.run("verify:verify", { - address: logicContractAddress, - contract: "contracts/ln/Eth2ZkSyncTarget.sol:Eth2ZkSyncTarget", - constructorArguments: [], - }); - - console.log(`Logic Verification ID: ${logicVerificationId}`); - - // deploy proxy contract - const logicFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet); - const proxyAddress = await ProxyDeployer.deployProxyContract(deployer, proxyAdmin, logicFactory, logicContractAddress, [dao]); - console.log(`proxy contract was deployed to ${proxyAddress}`); - - const calldata = ProxyDeployer.getInitializerData(logicFactory.interface, [dao], "initialize"); - const proxyVerificationId = await hre.run("verify:verify", { - address: proxyAddress, - constructorArguments: [logicContractAddress, proxyAdmin, calldata], - }); - console.log(`Proxy Verification ID: ${logicVerificationId}`); -} - diff --git a/zksync-deployer/deploy/deploy_layerzero_ln.ts b/zksync-deployer/deploy/deploy_layerzero_ln.ts deleted file mode 100644 index 0b2cd44e..00000000 --- a/zksync-deployer/deploy/deploy_layerzero_ln.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Wallet, utils, ContractFactory } from "zksync-web3"; -import * as ethers from "ethers"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; -import { ProxyDeployer } from "./proxy.ts"; - -const privateKey = process.env.PRIKEY - -const zkSyncNetwork = { - url: "https://zksync2-testnet.zksync.dev", - proxyAdmin: "0x96892F3EaD26515592Da38432cFABad991BBd69d", - dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", - endpoint: "0x093D2CF57f764f09C3c2Ac58a42A2601B8C79281", - usdc: "0x0faF6df7054946141266420b43783387A78d82A9", - // we should update this remote chain id when deploy - // arbitrum-goerli: 10143 - // linea-goerli: 10157 - remoteChainId: 10143, -}; - -export default async function (hre: HardhatRuntimeEnvironment) { - // deploy proxy admin contract - console.log(`Running deploy script for the zksync Layerzero contract`); - - // Initialize the wallet. - const wallet = new Wallet(privateKey); - - // Create deployer object and load the artifact of the contract you want to deploy. - const deployer = new Deployer(hre, wallet); - // deploy logic contract - const artifact = await deployer.loadArtifact("LnBridgeBaseLZ"); - const logicContract = await deployer.deploy(artifact, []); - const logicContractAddress = logicContract.address; - console.log(`logic contract was deployed to ${logicContractAddress}`); - const logicVerificationId = await hre.run("verify:verify", { - address: logicContractAddress, - contract: "contracts/ln/LnBridgeBaseLZ.sol:LnBridgeBaseLZ", - constructorArguments: [], - }); - - console.log(`Logic Verification ID: ${logicVerificationId}`); - - // deploy proxy contract - const logicFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet); - const proxyAddress = await ProxyDeployer.deployProxyContract(deployer, zkSyncNetwork.proxyAdmin, logicFactory, logicContractAddress, [zkSyncNetwork.dao, zkSyncNetwork.endpoint, zkSyncNetwork.remoteChainId]); - console.log(`proxy contract was deployed to ${proxyAddress}`); - - const calldata = ProxyDeployer.getInitializerData(logicFactory.interface, [zkSyncNetwork.dao, zkSyncNetwork.endpoint, zkSyncNetwork.remoteChainId], "initialize"); - const proxyVerificationId = await hre.run("verify:verify", { - address: proxyAddress, - constructorArguments: [logicContractAddress, zkSyncNetwork.proxyAdmin, calldata], - }); - console.log(`Proxy Verification ID: ${logicVerificationId}`); -} - diff --git a/zksync-deployer/deploy/deploy_ln_default_logic.ts b/zksync-deployer/deploy/deploy_ln_default_logic.ts new file mode 100644 index 00000000..65eff008 --- /dev/null +++ b/zksync-deployer/deploy/deploy_ln_default_logic.ts @@ -0,0 +1,30 @@ +import { Wallet } from "zksync-web3"; +import * as ethers from "ethers"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; + +const privateKey = process.env.PRIKEY + +export default async function (hre: HardhatRuntimeEnvironment) { + // deploy proxy admin contract + console.log(`Running deploy script for the zksync ln default logic contract`); + + // Initialize the wallet. + const wallet = new Wallet(privateKey); + + // Create deployer object and load the artifact of the contract you want to deploy. + const deployer = new Deployer(hre, wallet); + // deploy create2 tool contract + const artifact = await deployer.loadArtifact("LnDefaultBridge"); + const contract = await deployer.deploy(artifact, []); + const contractAddress = contract.address; + console.log(`ln default bridge logic contract was deployed to ${contractAddress}`); + const verificationId = await hre.run("verify:verify", { + address: contractAddress, + contract: "contracts/ln/LnDefaultBridge.sol:LnDefaultBridge", + constructorArguments: [], + }); + + console.log(`LnDefaultBridge Logic Verification ID: ${verificationId}`); +} + diff --git a/zksync-deployer/deploy/deploy_ln_default_proxy.ts b/zksync-deployer/deploy/deploy_ln_default_proxy.ts new file mode 100644 index 00000000..04a1d372 --- /dev/null +++ b/zksync-deployer/deploy/deploy_ln_default_proxy.ts @@ -0,0 +1,39 @@ +import { Wallet, utils, ContractFactory } from "zksync-web3"; +import * as ethers from "ethers"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; +import { ProxyDeployer } from "./proxy.ts"; + +const privateKey = process.env.PRIKEY + +const zkSyncNetwork = { + url: "https://zksync2-testnet.zksync.dev", + proxyAdmin: "0xd7b3aC0c9E99e9B2EF1C9D2a5ff397867c8c8A3E", + dao: "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4", + logicAddress: "0x6213E3bc566f7d7A73Fd7565c97ac5Ffb8624674", +}; + +export default async function (hre: HardhatRuntimeEnvironment) { + // deploy proxy admin contract + console.log(`Running deploy script for the zksync default bridge proxy contract`); + + // Initialize the wallet. + const wallet = new Wallet(privateKey); + + // Create deployer object and load the artifact of the contract you want to deploy. + const deployer = new Deployer(hre, wallet); + const artifact = await deployer.loadArtifact("LnDefaultBridge"); + + // deploy proxy contract + const logicFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet); + const proxyAddress = await ProxyDeployer.deployProxyContract(deployer, zkSyncNetwork.proxyAdmin, logicFactory, zkSyncNetwork.logicAddress, [zkSyncNetwork.dao]); + console.log(`proxy contract was deployed to ${proxyAddress}`); + + const calldata = ProxyDeployer.getInitializerData(logicFactory.interface, [zkSyncNetwork.dao], "initialize"); + const proxyVerificationId = await hre.run("verify:verify", { + address: proxyAddress, + constructorArguments: [zkSyncNetwork.logicAddress, zkSyncNetwork.proxyAdmin, calldata], + }); + console.log(`Proxy Verification ID: ${proxyVerificationId}`); +} + diff --git a/zksync-deployer/deploy/deploy_ln_layerzero.ts b/zksync-deployer/deploy/deploy_ln_layerzero.ts new file mode 100644 index 00000000..3a3061b0 --- /dev/null +++ b/zksync-deployer/deploy/deploy_ln_layerzero.ts @@ -0,0 +1,32 @@ +import { Wallet } from "zksync-web3"; +import * as ethers from "ethers"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; + +const privateKey = process.env.PRIKEY +const dao = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; +const endpoint = "0x093D2CF57f764f09C3c2Ac58a42A2601B8C79281"; + +export default async function (hre: HardhatRuntimeEnvironment) { + // deploy proxy admin contract + console.log(`Running deploy script for the zksync layerzero contract`); + + // Initialize the wallet. + const wallet = new Wallet(privateKey); + + // Create deployer object and load the artifact of the contract you want to deploy. + const deployer = new Deployer(hre, wallet); + // deploy create2 tool contract + const artifact = await deployer.loadArtifact("LayerZeroMessager"); + const contract = await deployer.deploy(artifact, [dao, endpoint]); + const contractAddress = contract.address; + console.log(`layerzero contract was deployed to ${contractAddress}`); + const verificationId = await hre.run("verify:verify", { + address: contractAddress, + contract: "contracts/ln/messager/LayerZeroMessager.sol:LayerZeroMessager", + constructorArguments: [dao, endpoint], + }); + + console.log(`LayerZeroMessager Verification ID: ${verificationId}`); +} + diff --git a/zksync-deployer/deploy/deploy_ln_opposite_logic.ts b/zksync-deployer/deploy/deploy_ln_opposite_logic.ts new file mode 100644 index 00000000..95138aa0 --- /dev/null +++ b/zksync-deployer/deploy/deploy_ln_opposite_logic.ts @@ -0,0 +1,30 @@ +import { Wallet } from "zksync-web3"; +import * as ethers from "ethers"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; + +const privateKey = process.env.PRIKEY + +export default async function (hre: HardhatRuntimeEnvironment) { + // deploy proxy admin contract + console.log(`Running deploy script for the zksync ln opposite logic contract`); + + // Initialize the wallet. + const wallet = new Wallet(privateKey); + + // Create deployer object and load the artifact of the contract you want to deploy. + const deployer = new Deployer(hre, wallet); + // deploy create2 tool contract + const artifact = await deployer.loadArtifact("LnOppositeBridge"); + const contract = await deployer.deploy(artifact, []); + const contractAddress = contract.address; + console.log(`ln opposite bridge logic contract was deployed to ${contractAddress}`); + const verificationId = await hre.run("verify:verify", { + address: contractAddress, + contract: "contracts/ln/LnOppositeBridge.sol:LnOppositeBridge", + constructorArguments: [], + }); + + console.log(`LnOppositeBridge Logic Verification ID: ${verificationId}`); +} + diff --git a/zksync-deployer/deploy/deploy_ln_test_token.ts b/zksync-deployer/deploy/deploy_ln_test_token.ts new file mode 100644 index 00000000..b4faff5b --- /dev/null +++ b/zksync-deployer/deploy/deploy_ln_test_token.ts @@ -0,0 +1,44 @@ +import { Wallet } from "zksync-web3"; +import * as ethers from "ethers"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; + +const privateKey = process.env.PRIKEY + +const tokens = [ + { + name: "Helix Test Token USDT", + symbol: "USDT", + decimals: 18 + }, + { + name: "Helix Test Token USDC", + symbol: "USDC", + decimals: 18 + } +]; + +export default async function (hre: HardhatRuntimeEnvironment) { + // deploy proxy admin contract + console.log(`Running deploy script for the zksync ln test token contract`); + + // Initialize the wallet. + const wallet = new Wallet(privateKey); + + // Create deployer object and load the artifact of the contract you want to deploy. + const deployer = new Deployer(hre, wallet); + // deploy create2 tool contract + const artifact = await deployer.loadArtifact("HelixTestErc20"); + + for (const token of tokens) { + const contract = await deployer.deploy(artifact, [token.name, token.symbol, token.decimals]); + const contractAddress = contract.address; + console.log(`ln test token contract was deployed to ${contractAddress}`); + const verificationId = await hre.run("verify:verify", { + address: contractAddress, + constructorArguments: [token.name, token.symbol, token.decimals], + }); + console.log(`Ln Test Erc20 Contract Verification ID: ${verificationId}`); + } +} + diff --git a/zksync-deployer/deploy/deploy_proxy.ts b/zksync-deployer/deploy/deploy_proxy_admin.ts similarity index 79% rename from zksync-deployer/deploy/deploy_proxy.ts rename to zksync-deployer/deploy/deploy_proxy_admin.ts index eaeedc79..2e772a41 100644 --- a/zksync-deployer/deploy/deploy_proxy.ts +++ b/zksync-deployer/deploy/deploy_proxy_admin.ts @@ -17,13 +17,19 @@ export default async function (hre: HardhatRuntimeEnvironment) { // Create deployer object and load the artifact of the contract you want to deploy. const deployer = new Deployer(hre, wallet); - /* const artifact = await deployer.loadArtifact("ProxyAdmin"); const proxyAdminContract = await deployer.deploy(artifact, []); // Show the contract info. const contractAddress = proxyAdminContract.address; - */ - const contractAddress = await ProxyDeployer.deployProxyAdmin(deployer); + //const contractAddress = await ProxyDeployer.deployProxyAdmin(deployer); console.log(`deployed to ${contractAddress}`); + + const verificationId = await hre.run("verify:verify", { + address: contractAddress, + constructorArguments: [], + }); + + console.log(`ProxyAdmin Verification ID: ${verificationId}`); + } diff --git a/zksync-deployer/deploy/deploy_zksync2eth_source.ts b/zksync-deployer/deploy/deploy_zksync2eth_source.ts deleted file mode 100644 index e343f199..00000000 --- a/zksync-deployer/deploy/deploy_zksync2eth_source.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Wallet, utils, ContractFactory } from "zksync-web3"; -import * as ethers from "ethers"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; -import { ProxyDeployer } from "./proxy.ts"; - -// load wallet private key from env file -const privateKey = process.env.PRIKEY - -// An example of a deploy script that will deploy and call a simple contract. -export default async function (hre: HardhatRuntimeEnvironment) { - // deploy proxy admin contract - console.log(`Running deploy script for the zksync2eth source contract`); - const proxyAdmin = "0x96892F3EaD26515592Da38432cFABad991BBd69d"; - const dao = "0x88a39B052d477CfdE47600a7C9950a441Ce61cb4"; - - // Initialize the wallet. - const wallet = new Wallet(privateKey); - - // Create deployer object and load the artifact of the contract you want to deploy. - const deployer = new Deployer(hre, wallet); - // deploy logic contract - const artifact = await deployer.loadArtifact("ZkSync2EthSource"); - const logicContract = await deployer.deploy(artifact, []); - const logicContractAddress = logicContract.address; - console.log(`logic contract was deployed to ${logicContractAddress}`); - const logicVerificationId = await hre.run("verify:verify", { - address: logicContractAddress, - contract: "contracts/ln/ZkSync2EthSource.sol:ZkSync2EthSource", - constructorArguments: [], - }); - - console.log(`Logic Verification ID: ${logicVerificationId}`); - - // deploy proxy contract - const logicFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet); - const proxyAddress = await ProxyDeployer.deployProxyContract(deployer, proxyAdmin, logicFactory, logicContractAddress, [dao]); - console.log(`proxy contract was deployed to ${proxyAddress}`); - - const calldata = ProxyDeployer.getInitializerData(logicFactory.interface, [dao], "initialize"); - const proxyVerificationId = await hre.run("verify:verify", { - address: proxyAddress, - constructorArguments: [logicContractAddress, proxyAdmin, calldata], - }); - console.log(`Proxy Verification ID: ${logicVerificationId}`); -} - diff --git a/zksync-deployer/hardhat.config.ts b/zksync-deployer/hardhat.config.ts index 2c6287ac..de4bb9cf 100644 --- a/zksync-deployer/hardhat.config.ts +++ b/zksync-deployer/hardhat.config.ts @@ -39,7 +39,7 @@ const config: HardhatUserConfig = { zkSyncTestnet, }, solidity: { - version: "0.8.10", + version: "0.8.17", }, };