diff --git a/helix-contract/address/sub2eth-v2-dev.json b/helix-contract/address/sub2eth-v2-dev.json index da9d68e1..fb745fa0 100644 --- a/helix-contract/address/sub2eth-v2-dev.json +++ b/helix-contract/address/sub2eth-v2-dev.json @@ -1,17 +1,17 @@ { "pangoro2goerli_sub2eth_goerli": { - "messageEndpoint": "0xa4bAE72077c43F72D168b923Ffb28030FD382F51", - "mappingTokenFactoryLogic": "0x24B8dc40f84210B0375EE8E14620988E5cbd6180", - "mappingTokenFactoryAdmin": "0xbb119dB68F710E035091b94ADa8a48d0868C648A", - "mappingTokenFactoryProxy": "0x1AECF62B14aC33929e74fc7939FA490b3B3E3563", - "ring": "0xB7A9115C0a1D2baBc764957C506c34843eeE8893", - "guard": "0x6aa0e15E88A8EF584304Bb52c3E9C6fA16d3EAc5" + "messageEndpoint": "0x190304Fd18c7185637b055Cd91077374b49E2659", + "mappingTokenFactoryLogic": "0x55F5C311d105eDc5f34eaa940DA1816Ba4de575e", + "mappingTokenFactoryAdmin": "0x81E8220a2Ed7275982F59D316F02C2D301151F7C", + "mappingTokenFactoryProxy": "0xfcAcf3d08275031e5Bd453Cf2509301290858984", + "ring": "0x046D07d53926318d1F06c2c2A0F26a4de83E26c4", + "guard": "0xB63846f957A97eC982b83Bb50957A519878196Ef" }, "pangoro2goerli_sub2eth_pangoro": { - "messageEndpoint": "0x478f210F77e4aCed2069a39B45EBdE54d8A4420A", - "backingLogic": "0xe35b898A65c9868336bf34321373E1DA9401eB9d", - "backingAdmin": "0x73e810ae1a56a2d1BCd55f0F1c304e46ce14a70e", - "backingProxy": "0x41073C282621590b731B69D7DEA9dE5557C22823", - "WRING": "0x1977213Bb561d6e3bB07A9F22BCE758A14D7C693" + "messageEndpoint": "0x528985686C6EC07B8D9f7BfB0dCFed9f74520b83", + "backingLogic": "0x0B458df3B7c32096b1fa532BAFCBF452c3a0DC0C", + "backingAdmin": "0xF4aEF264D0e112D0bD1371278F39B3d80d1B4f8D", + "backingProxy": "0xaafFbF487e9dA8429E2E9502d0907e5fD6b0C320", + "WRING": "0x46f01081e800BF47e43e7bAa6D98d45F6a0251E4" } } diff --git a/helix-contract/contracts/mapping-token/darwinia/Admin.sol b/helix-contract/contracts/mapping-token/darwinia/Admin.sol deleted file mode 100644 index 5011306d..00000000 --- a/helix-contract/contracts/mapping-token/darwinia/Admin.sol +++ /dev/null @@ -1,3 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; -import "@zeppelin-solidity-4.4.0/contracts/proxy/transparent/ProxyAdmin.sol"; diff --git a/helix-contract/contracts/mapping-token/darwinia/BasicMappingTokenFactory.sol b/helix-contract/contracts/mapping-token/darwinia/BasicMappingTokenFactory.sol deleted file mode 100644 index d18ef14c..00000000 --- a/helix-contract/contracts/mapping-token/darwinia/BasicMappingTokenFactory.sol +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import "@zeppelin-solidity-4.4.0/contracts/proxy/utils/Initializable.sol"; -import "@zeppelin-solidity-4.4.0/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import "../../utils/DailyLimit.sol"; -import "../../utils/Ownable.sol"; -import "../../utils/Pausable.sol"; -import "../interfaces/IERC20.sol"; -import "./MappingTokenAddress.sol"; - -contract BasicMappingTokenFactory is Initializable, Ownable, DailyLimit, MappingTokenAddress, Pausable { - struct OriginalInfo { - // 0 - NativeToken - // 1 - Erc20Token - // ... - uint32 tokenType; - address backing_address; - address original_token; - } - // the mapping token list - address[] public allMappingTokens; - // salt=>mapping_token, 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; - // mapping_token=>info the info is the original token info - // so this is a mapping from mapping_token to original token - mapping(address => OriginalInfo) public mappingToken2OriginalInfo; - // tokenType=>Logic - // tokenType comes from original token, the logic contract is used to create the mapping-token contract - mapping(uint32 => address) public tokenType2Logic; - - event NewLogicSetted(uint32 tokenType, address addr); - event IssuingERC20Created(address backing_address, address original_token, address mapping_token); - event MappingTokenUpdated(bytes32 salt, address old_address, address new_address); - - receive() external payable { - } - - function initialize() public initializer { - ownableConstructor(); - } - - /** - * @dev Throws if called by any account other than the system account defined by SYSTEM_ACCOUNT address. - */ - modifier onlySystem() { - require(SYSTEM_ACCOUNT == msg.sender, "System: caller is not the system account"); - _; - } - - function setDailyLimit(address mapping_token, uint amount) public onlyOwner { - _setDailyLimit(mapping_token, amount); - } - - function changeDailyLimit(address mapping_token, uint amount) public onlyOwner { - _changeDailyLimit(mapping_token, amount); - } - - function setTokenContractLogic(uint32 tokenType, address logic) external onlyOwner { - tokenType2Logic[tokenType] = logic; - emit NewLogicSetted(tokenType, logic); - } - - function unpause() external onlyOwner { - _unpause(); - } - - function pause() external onlyOwner { - _pause(); - } - - function transferMappingTokenOwnership(address mapping_token, address new_owner) external onlyOwner { - Ownable(mapping_token).transferOwnership(new_owner); - } - - // add new mapping token address - function addMappingToken(address backing_address, address original_token, address mapping_token, uint32 token_type) external onlyOwner { - bytes32 salt = keccak256(abi.encodePacked(backing_address, original_token)); - address existed = salt2MappingToken[salt]; - require(existed == address(0), "the mapping token exist"); - allMappingTokens.push(mapping_token); - mappingToken2OriginalInfo[mapping_token] = OriginalInfo(token_type, backing_address, original_token); - salt2MappingToken[salt] = mapping_token; - emit MappingTokenUpdated(salt, existed, mapping_token); - } - - // update the mapping token address when the mapping token contract deployed before - function updateMappingToken(address backing_address, address original_token, address new_mapping_token, uint index) external onlyOwner { - bytes32 salt = keccak256(abi.encodePacked(backing_address, original_token)); - address existed = salt2MappingToken[salt]; - require(salt2MappingToken[salt] != address(0), "the mapping token not exist"); - require(tokenLength() > index && allMappingTokens[index] == existed, "invalid index"); - allMappingTokens[index] = new_mapping_token; - OriginalInfo memory info = mappingToken2OriginalInfo[existed]; - delete mappingToken2OriginalInfo[existed]; - mappingToken2OriginalInfo[new_mapping_token] = info; - salt2MappingToken[salt] = new_mapping_token; - emit MappingTokenUpdated(salt, existed, new_mapping_token); - } - - // internal - function deploy(bytes32 salt, bytes memory code) internal returns (address addr) { - assembly { - addr := create2(0, add(code, 0x20), mload(code), salt) - if iszero(extcodesize(addr)) { revert(0, 0) } - } - } - - // view - function tokenLength() public view returns (uint) { - return allMappingTokens.length; - } - - function mappingToken(address backing_address, address original_token) public view returns (address) { - bytes32 salt = keccak256(abi.encodePacked(backing_address, original_token)); - return salt2MappingToken[salt]; - } - - // only system - // create new erc20 mapping token contract - // save and manage the token list - function newErc20Contract( - uint32 tokenType, - string memory name, - string memory symbol, - uint8 decimals, - address backing_address, - address original_token - ) public virtual onlySystem whenNotPaused returns (address mapping_token) { - require(tokenType == 0 || tokenType == 1, "token type cannot mapping to erc20 token"); - // backing_address and original_token pack a unique new contract salt - bytes32 salt = keccak256(abi.encodePacked(backing_address, original_token)); - require(salt2MappingToken[salt] == address(0), "contract has been deployed"); - bytes memory bytecode = type(TransparentUpgradeableProxy).creationCode; - bytes memory bytecodeWithInitdata = abi.encodePacked(bytecode, abi.encode(tokenType2Logic[tokenType], address(DEAD_ADDRESS), "")); - mapping_token = deploy(salt, bytecodeWithInitdata); - IMappingToken(mapping_token).initialize(name, symbol, decimals); - - salt2MappingToken[salt] = mapping_token; - // save the mapping tokens in an array so it can be listed - allMappingTokens.push(mapping_token); - // map the mapping_token to origin info - mappingToken2OriginalInfo[mapping_token] = OriginalInfo(tokenType, backing_address, original_token); - emit IssuingERC20Created(backing_address, original_token, mapping_token); - } - - function issueMappingToken(address mapping_token, address recipient, uint256 amount) public virtual onlySystem whenNotPaused { - require(amount > 0, "can not receive amount zero"); - OriginalInfo memory info = mappingToken2OriginalInfo[mapping_token]; - require(info.original_token != address(0), "token is not created by factory"); - expendDailyLimit(mapping_token, amount); - IERC20(mapping_token).mint(recipient, amount); - } -} - diff --git a/helix-contract/contracts/mapping-token/darwinia/Ethereum2DarwiniaMappingTokenFactory.sol b/helix-contract/contracts/mapping-token/darwinia/Ethereum2DarwiniaMappingTokenFactory.sol deleted file mode 100644 index 6a61331b..00000000 --- a/helix-contract/contracts/mapping-token/darwinia/Ethereum2DarwiniaMappingTokenFactory.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; -import "./BasicMappingTokenFactory.sol"; - -contract Ethereum2DarwiniaMappingTokenFactory is BasicMappingTokenFactory { - // create new erc20 mapping token contract - // save and manage the token list - function newErc20Contract( - uint32 tokenType, - string memory name, - string memory symbol, - uint8 decimals, - address backing_address, - address original_token - ) public virtual override onlySystem returns (address mapping_token) { - mapping_token = super.newErc20Contract( - tokenType, - name, - symbol, - decimals, - backing_address, - original_token - ); - // send the register response message to backing - (bool encodeSuccess, bytes memory register_response) = DISPATCH_ENCODER.call( - abi.encodePacked(bytes4(keccak256("token_register_response(address,address,address)")), - abi.encode(backing_address, original_token, mapping_token))); - require(encodeSuccess, "create: encode e2d_token_register_response failed"); - (bool success, ) = DISPATCH.call(register_response); - require(success, "create: call create erc20 precompile failed"); - } - - // cross transfer to remote chain without waiting any confirm information, - // this require the burn proof can be always verified by the remote chain corrently. - // so, here the user's token burned directly. - function burnAndRemoteUnlock(address mapping_token, bytes memory recipient, uint256 amount) external { - require(amount > 0, "can not transfer amount zero"); - OriginalInfo memory info = mappingToken2OriginalInfo[mapping_token]; - require(info.original_token != address(0), "token is not created by factory"); - // Lock the fund in this before message on remote backing chain get dispatched successfully and burn finally - // If remote backing chain unlock the origin token successfully, then this fund will be burned. - // Otherwise, this fund will be transfered back to the msg.sender. - require(IERC20(mapping_token).transferFrom(msg.sender, address(this), amount), "transfer token failed"); - IERC20(mapping_token).burn(address(this), amount); - - (bool encodeSuccess, bytes memory unlock_message) = DISPATCH_ENCODER.call( - abi.encodePacked(bytes4(keccak256("burn_and_remote_unlock(uint32,address,address,address,bytes,uint256)")), - abi.encode( - info.tokenType, - info.backing_address, - msg.sender, - info.original_token, - recipient, - amount))); - require(encodeSuccess, "burn: encode unlock message failed"); - (bool success, ) = DISPATCH.call(unlock_message); - require(success, "burn: call send unlock message failed"); - } -} - diff --git a/helix-contract/contracts/mapping-token/darwinia/MappingTokenAddress.sol b/helix-contract/contracts/mapping-token/darwinia/MappingTokenAddress.sol deleted file mode 100644 index 31fccb47..00000000 --- a/helix-contract/contracts/mapping-token/darwinia/MappingTokenAddress.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.10; - -contract MappingTokenAddress { - address public constant DISPATCH_ENCODER = 0x0000000000000000000000000000000000000018; - address public constant DISPATCH = 0x0000000000000000000000000000000000000019; - address public constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD; - - // This system account is derived from the dvm pallet id `dar/dvmp`, - // and it has no private key, it comes from internal transaction in dvm. - address public constant SYSTEM_ACCOUNT = 0x6D6F646C6461722f64766D700000000000000000; -} diff --git a/helix-contract/contracts/mapping-token/darwinia/Sub2SubBacking.sol b/helix-contract/contracts/mapping-token/darwinia/Sub2SubBacking.sol deleted file mode 100644 index a1fac4b3..00000000 --- a/helix-contract/contracts/mapping-token/darwinia/Sub2SubBacking.sol +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; -import "@zeppelin-solidity-4.4.0/contracts/proxy/utils/Initializable.sol"; -import "@zeppelin-solidity-4.4.0/contracts/utils/math/SafeMath.sol"; -import "../../utils/DailyLimit.sol"; -import "../../utils/Ownable.sol"; -import "../../utils/Pausable.sol"; -import "../../precompile/sub2sub.sol"; -import "../interfaces/IERC20.sol"; -import "./MappingTokenAddress.sol"; - -contract Sub2SubBacking is Initializable, Ownable, DailyLimit, Pausable, MappingTokenAddress { - using SafeMath for uint256; - struct LockedInfo { - address token; - address sender; - uint256 amount; - } - uint32 public constant NATIVE_TOKEN_TYPE = 0; - uint32 public constant ERC20_TOKEN_TYPE = 1; - address public operator; - - // this can decide an unique remote chain - uint32 public messagePalletIndex; - - // token => IsRegistered - mapping(address => bool) public registeredTokens; - - // (messageId => tokenAddress) - mapping(bytes => address) public registerMessages; - // (messageId => lockedInfo) - mapping(bytes => LockedInfo) public lockMessages; - - event NewErc20TokenRegistered(bytes4 laneId, uint64 nonce, address token); - event TokenLocked(bytes4 laneId, uint64 nonce, address token, address recipient, uint256 amount); - event TokenLockFinished(bytes4 laneId, uint64 nonce, bool result); - event TokenRegisterFinished(bytes4 laneId, uint64 nonce, bool result); - event TokenUnlocked(address token, address recipient, uint256 amount); - - modifier onlyOperatorOrOwner() { - require(operator == msg.sender || owner() == msg.sender, "Backing:caller is not the owner or operator"); - _; - } - - modifier onlySystem() { - require(SYSTEM_ACCOUNT == msg.sender, "System: caller is not the system account"); - _; - } - - function initialize() public initializer { - operator = msg.sender; - ownableConstructor(); - } - - function setMessagePalletIndex(uint32 index) external onlyOwner { - messagePalletIndex = index; - } - - function changeDailyLimit(address token, uint amount) public onlyOwner { - _changeDailyLimit(token, amount); - } - - function unpause() external onlyOperatorOrOwner { - _unpause(); - } - - function pause() external onlyOperatorOrOwner { - _pause(); - } - - function updateOperator(address _operator) external onlyOperatorOrOwner { - operator = _operator; - } - - function decimalConversion(uint256 balance) internal pure returns (uint256) { - return balance/(10**9); - } - - /** - * @notice reigister new erc20 token to the bridge. Only owner or operator can do this. - * @param specVersion the spec_version of the bridged chain - * @param weight the remote dispatch call's weight - * @param laneId the laneId of the message channel - * @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 registerErc20Token( - uint32 specVersion, - uint64 weight, - bytes4 laneId, - address token, - string memory name, - string memory symbol, - uint8 decimals - ) external payable onlyOperatorOrOwner { - require(registeredTokens[token] == false, "Backing:token has been registered"); - - // encode remote register dispatch call - bytes memory registerErc20 = SubToSubBridge(DISPATCH_ENCODER).encode_register_from_remote_dispatch_call( - specVersion, - weight, - ERC20_TOKEN_TYPE, - token, - name, - symbol, - decimals - ); - - // transform fee in contract(decimals is 18) to pallet(decimals is 9) - uint256 fee = decimalConversion(msg.value); - - // encode - // this fee is needed to send to the sub<>sub bridge fund to pay relayer - bytes memory sendMessageCall = SubToSubBridge(DISPATCH_ENCODER).encode_send_message_dispatch_call( - messagePalletIndex, - laneId, - registerErc20, - fee); - - // send s2s message - (bool success, ) = DISPATCH.call(sendMessageCall); - require(success, "burn: send register message failed"); - uint64 nonce = SubToSubBridge(DISPATCH_ENCODER).outbound_latest_generated_nonce(laneId); - bytes memory messageId = abi.encode(laneId, nonce); - registerMessages[messageId] = token; - emit NewErc20TokenRegistered(laneId, nonce, token); - } - - /** - * @notice lock original token and issuing mapping token from bridged chain - * @param specVersion the spec_version of the bridged chain - * @param weight the remote dispatch call's weight - * @param laneId the laneId of the message channel - * @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 specVersion, - uint64 weight, - bytes4 laneId, - address token, - address recipient, - uint256 amount - ) external payable whenNotPaused { - require(registeredTokens[token], "Backing:the token is not registed"); - - 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.add(amount) == balanceAfter, "Backing:Transfer amount is invalid"); - bytes memory issueFromRemote = SubToSubBridge(DISPATCH_ENCODER).encode_issue_from_remote_dispatch_call( - specVersion, - weight, - token, - recipient, - amount - ); - - uint256 fee = decimalConversion(msg.value); - bytes memory sendMessageCall = SubToSubBridge(DISPATCH_ENCODER).encode_send_message_dispatch_call( - messagePalletIndex, - laneId, - issueFromRemote, - fee); - - (bool success, ) = DISPATCH.call(sendMessageCall); - require(success, "burn: send register message failed"); - uint64 nonce = SubToSubBridge(DISPATCH_ENCODER).outbound_latest_generated_nonce(laneId); - bytes memory messageId = abi.encode(laneId, nonce); - - lockMessages[messageId] = LockedInfo(token, msg.sender, amount); - emit TokenLocked(laneId, nonce, token, recipient, amount); - } - - function confirmRemoteLockOrRegister(bytes4 laneId, uint64 nonce, bool result) external onlySystem { - bytes memory messageId = abi.encode(laneId, nonce); - LockedInfo memory lockedInfo = lockMessages[messageId]; - // it is lock message, if result is false, need to transfer back to the user, otherwise will be locked here - if (lockedInfo.token != address(0)) { - delete lockMessages[messageId]; - if (!result) { - IERC20(lockedInfo.token).transfer(lockedInfo.sender, lockedInfo.amount); - } - emit TokenLockFinished(laneId, nonce, result); - return; - } - address registerToken = registerMessages[messageId]; - // it is register message, if result is true, need to save the token - if (registerToken != address(0)) { - delete registerMessages[messageId]; - if (result) { - registeredTokens[registerToken] = true; - } - emit TokenRegisterFinished(laneId, nonce, result); - } - } - - /** - * @notice this will be called by system 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 - ) external onlySystem whenNotPaused { - expendDailyLimit(token, amount); - require(IERC20(token).transfer(recipient, amount), "Backing:unlock transfer failed"); - emit TokenUnlocked(token, recipient, amount); - } -} - diff --git a/helix-contract/contracts/mapping-token/darwinia/Sub2SubMappingTokenFactory.sol b/helix-contract/contracts/mapping-token/darwinia/Sub2SubMappingTokenFactory.sol deleted file mode 100644 index b0232fee..00000000 --- a/helix-contract/contracts/mapping-token/darwinia/Sub2SubMappingTokenFactory.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; -import "../../precompile/sub2sub.sol"; -import "./BasicMappingTokenFactory.sol"; - -contract Sub2SubMappingTokenFactory is BasicMappingTokenFactory { - struct UnconfirmedInfo { - address sender; - address mapping_token; - uint256 amount; - } - mapping(bytes => UnconfirmedInfo) public transferUnconfirmed; - uint32 public message_pallet_index; - bytes4 public lane_id; - event IssuingMappingToken(bytes4 laneid, uint64 nonce, address mapping_token, address recipient, uint256 amount); - event BurnAndWaitingConfirm(bytes4 laneid, uint64 nonce, address sender, bytes recipient, address token, uint256 amount); - event RemoteUnlockConfirmed(bytes4 laneid, uint64 nonce, address sender, address token, uint256 amount, bool result); - - function setMessagePalletIndex(uint32 index) external onlyOwner { - message_pallet_index = index; - } - - // should ensure the `transferUnconfirmed` is empty before call this setLaneId - function setLaneId(bytes4 laneid) external onlyOwner { - lane_id = laneid; - } - - function issueMappingToken(address mapping_token, address recipient, uint256 amount) public override { - super.issueMappingToken(mapping_token, recipient, amount); - // current message's nonce is the latest recieved nonce plus one, because the lastest nonce updated - // after the message dispatch finished - uint64 nonce = SubToSubBridge(DISPATCH_ENCODER).inbound_latest_received_nonce(lane_id) + 1; - emit IssuingMappingToken(lane_id, nonce, mapping_token, recipient, amount); - } - - // Step 1: User lock the mapped token to this contract and waiting the remote backing's unlock result. - function burnAndRemoteUnlockWaitingConfirm( - uint32 specVersion, - uint64 weight, - address mapping_token, - bytes memory recipient, - uint256 amount - ) external payable whenNotPaused { - require(amount > 0, "can not transfer amount zero"); - OriginalInfo memory info = mappingToken2OriginalInfo[mapping_token]; - require(info.original_token != address(0), "token is not created by factory"); - // Lock the fund in this before message on remote backing chain get dispatched successfully and burn finally - // If remote backing chain unlock the origin token successfully, then this fund will be burned. - // Otherwise, this fund will be transfered back to the msg.sender. - require(IERC20(mapping_token).transferFrom(msg.sender, address(this), amount), "transfer token failed"); - - bytes memory unlockMessage = SubToSubBridge(DISPATCH_ENCODER).encode_unlock_from_remote_dispatch_call( - specVersion, - weight, - info.tokenType, - info.original_token, - recipient, - amount); - - // the pricision in contract is 18, and in pallet is 9, transform the fee value - uint256 fee = msg.value/(10**9); - bytes memory sendMessageCall = SubToSubBridge(DISPATCH_ENCODER).encode_send_message_dispatch_call( - message_pallet_index, - lane_id, - unlockMessage, - fee); - - // 1. send unlock message to remote backing across sub<>sub bridge - (bool success, ) = DISPATCH.call(sendMessageCall); - require(success, "burn: send unlock message failed"); - // 2. getting the messageid, saving and waiting confirm - uint64 nonce = SubToSubBridge(DISPATCH_ENCODER).outbound_latest_generated_nonce(lane_id); - bytes memory message_id = abi.encode(lane_id, nonce); - transferUnconfirmed[message_id] = UnconfirmedInfo(msg.sender, mapping_token, amount); - emit BurnAndWaitingConfirm(lane_id, nonce, msg.sender, recipient, mapping_token, amount); - } - - // Step 2: The remote backing's unlock result comes. The result is true(success) or false(failure). - // True: if event is verified and the origin token unlocked successfully on remote chain, then we burn the mapped token - // False: if event is verified, but the origin token unlocked on remote chain failed, then we take back the mapped token to user. - function confirmBurnAndRemoteUnlock(bytes4 laneid, uint64 nonce, bool result) external onlySystem { - bytes memory message_id = abi.encode(laneid, nonce); - UnconfirmedInfo memory info = transferUnconfirmed[message_id]; - require(info.amount > 0 && info.sender != address(0) && info.mapping_token != address(0), "invalid unconfirmed message"); - if (result) { - IERC20(info.mapping_token).burn(address(this), info.amount); - } else { - require(IERC20(info.mapping_token).transfer(info.sender, info.amount), "transfer back failed"); - } - delete transferUnconfirmed[message_id]; - emit RemoteUnlockConfirmed(laneid, nonce, info.sender, info.mapping_token, info.amount, result); - } -} diff --git a/helix-contract/contracts/mapping-token/darwinia/MappingERC20.sol b/helix-contract/contracts/mapping-token/deprecated/MappingERC20.sol similarity index 97% rename from helix-contract/contracts/mapping-token/darwinia/MappingERC20.sol rename to helix-contract/contracts/mapping-token/deprecated/MappingERC20.sol index 2b8199d6..b5c207e1 100644 --- a/helix-contract/contracts/mapping-token/darwinia/MappingERC20.sol +++ b/helix-contract/contracts/mapping-token/deprecated/MappingERC20.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.10; import "@zeppelin-solidity-4.4.0/contracts/token/ERC20/IERC20.sol"; import "@zeppelin-solidity-4.4.0/contracts/utils/math/SafeMath.sol"; import "@zeppelin-solidity-4.4.0/contracts/proxy/utils/Initializable.sol"; -import "../../utils/Ownable.sol"; +import "@zeppelin-solidity-4.4.0/contracts/access/Ownable.sol"; contract MappingERC20 is IERC20, Ownable, Initializable { using SafeMath for uint256; @@ -24,7 +24,7 @@ contract MappingERC20 is IERC20, Ownable, Initializable { string memory _symbol, uint8 _decimals ) public initializer { - ownableConstructor(); + _transferOwnership(_msgSender()); name = _name; symbol = _symbol; decimals = _decimals; @@ -121,4 +121,3 @@ contract MappingERC20 is IERC20, Ownable, Initializable { function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } - diff --git a/helix-contract/contracts/mapping-token/interfaces/IHelixApp.sol b/helix-contract/contracts/mapping-token/interfaces/IHelixApp.sol index 018529bd..42f8c63e 100644 --- a/helix-contract/contracts/mapping-token/interfaces/IHelixApp.sol +++ b/helix-contract/contracts/mapping-token/interfaces/IHelixApp.sol @@ -2,10 +2,6 @@ pragma solidity ^0.8.0; -interface IHelixAppSupportConfirm { - function onMessageDelivered(uint256 messageId, bool result) external; -} - interface IHelixAppSupportWithdrawFailed { function handleUnlockFailureFromRemote( uint256 messageId, diff --git a/helix-contract/contracts/mapping-token/interfaces/IHelixMessageEndpointSupportingConfirm.sol b/helix-contract/contracts/mapping-token/interfaces/IHelixMessageEndpointSupportingConfirm.sol deleted file mode 100644 index b80e69cd..00000000 --- a/helix-contract/contracts/mapping-token/interfaces/IHelixMessageEndpointSupportingConfirm.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./IHelixMessageEndpoint.sol"; - -interface IHelixMessageEndpointSupportingConfirm is IHelixMessageEndpoint { - function lastDeliveredMessageId() external view returns (uint256); -} diff --git a/helix-contract/contracts/mapping-token/interfaces/IMessageCommitment.sol b/helix-contract/contracts/mapping-token/interfaces/IMessageCommitment.sol deleted file mode 100644 index 2c44f98d..00000000 --- a/helix-contract/contracts/mapping-token/interfaces/IMessageCommitment.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -interface IMessageCommitment { - function thisChainPosition() external view returns (uint32); - function thisLanePosition() external view returns (uint32); - function bridgedChainPosition() external view returns (uint32); - function bridgedLanePosition() external view returns (uint32); - function commitment() external view returns (bytes32); - function getLaneInfo() external view returns (uint32,uint32,uint32,uint32); -} diff --git a/helix-contract/contracts/mapping-token/interfaces/IOnMessageDelivered.sol b/helix-contract/contracts/mapping-token/interfaces/IOnMessageDelivered.sol deleted file mode 100644 index 90a84bdc..00000000 --- a/helix-contract/contracts/mapping-token/interfaces/IOnMessageDelivered.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @title A interface for app layer to get message dispatch result - * @author echo - * @notice The app layer could implement the interface `IOnMessageDelivered` to receive message dispatch result (optionally) - */ -interface IOnMessageDelivered { - /** - * @notice Message delivered callback - * @param nonce Nonce of the callback message - * @param dispatch_result Dispatch result of cross chain message - */ - function on_messages_delivered(uint256 nonce, bool dispatch_result) external; -} diff --git a/helix-contract/contracts/mapping-token/test/Erc1155Material.sol b/helix-contract/contracts/mapping-token/test/Erc1155Material.sol index ee47297a..e5eb3b89 100644 --- a/helix-contract/contracts/mapping-token/test/Erc1155Material.sol +++ b/helix-contract/contracts/mapping-token/test/Erc1155Material.sol @@ -2,12 +2,12 @@ pragma solidity >=0.8.10; +import "@zeppelin-solidity-4.4.0/contracts/access/Ownable.sol"; import "../interfaces/IErc1155Metadata.sol"; -import "../../utils/Ownable.sol"; import "../interfaces/IErc1155MappingToken.sol"; contract Erc1155MaterialMetadata is IErc1155Metadata { - function uri(uint256 tokenId) external pure returns(string memory) { + function uri(uint256) external pure returns(string memory) { return ""; } } diff --git a/helix-contract/contracts/mapping-token/test/Erc721Monkey.sol b/helix-contract/contracts/mapping-token/test/Erc721Monkey.sol index b4120773..bd08b673 100644 --- a/helix-contract/contracts/mapping-token/test/Erc721Monkey.sol +++ b/helix-contract/contracts/mapping-token/test/Erc721Monkey.sol @@ -35,7 +35,7 @@ contract Erc721MonkeyAttributeSerializer is IErc721AttrSerializer { return "MKY"; } - function tokenURI(uint256 tokenId) external pure returns(string memory) { + function tokenURI(uint256) external pure returns(string memory) { return ""; } diff --git a/helix-contract/contracts/mapping-token/test/MockDispatchCall.sol b/helix-contract/contracts/mapping-token/test/MockDispatchCall.sol deleted file mode 100644 index 33f60bd5..00000000 --- a/helix-contract/contracts/mapping-token/test/MockDispatchCall.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.6.0; - -import "../darwinia/MappingTokenAddress.sol"; - -contract MockDispatchCall is MappingTokenAddress { - uint64 public generated_nonce = 0; - uint256 minFee = 50 ether; - function send_message(uint32 pallet_id, bytes4 lane_id, bytes memory message, uint256 fee) external { - generated_nonce += 1; - require(minFee <= fee, "fee is too small"); - require(pallet_id == 43, "invalid palletid"); - require(lane_id == 0x726f6c69, "invalid laneid"); - require(message.length > 0, "invalid message"); - } -} - diff --git a/helix-contract/contracts/mapping-token/test/MockInboundLane.sol b/helix-contract/contracts/mapping-token/test/MockInboundLane.sol index d1e4d72d..3fd8bf3f 100644 --- a/helix-contract/contracts/mapping-token/test/MockInboundLane.sol +++ b/helix-contract/contracts/mapping-token/test/MockInboundLane.sol @@ -36,7 +36,8 @@ contract MockInboundLane is MockMessageVerifier { } function test_dispatch(address targetContract, bytes calldata encoded) external { - targetContract.call(encoded); + (bool result, ) = targetContract.call(encoded); + require(result, "test_dispatch failed"); } } diff --git a/helix-contract/contracts/mapping-token/test/MockOutboundLane.sol b/helix-contract/contracts/mapping-token/test/MockOutboundLane.sol index 3b98d8a5..d1700a6e 100644 --- a/helix-contract/contracts/mapping-token/test/MockOutboundLane.sol +++ b/helix-contract/contracts/mapping-token/test/MockOutboundLane.sol @@ -2,8 +2,8 @@ pragma solidity >=0.6.0; import "./MockMessageVerifier.sol"; import "./MockInboundLane.sol"; -import "../interfaces/IOnMessageDelivered.sol"; import "../interfaces/IOutboundLane.sol"; +import "hardhat/console.sol"; contract MockOutboundLane is MockMessageVerifier { IOutboundLane.OutboundLaneNonce public outboundLaneNonce; @@ -30,6 +30,7 @@ contract MockOutboundLane is MockMessageVerifier { function send_message(address targetContract, bytes calldata encoded) external payable returns (uint64) { // call target contract bool result = MockInboundLane(remoteInboundLane).mock_dispatch(msg.sender, targetContract, encoded); + console.log("remote dispatch return %s", result); outboundLaneNonce.latest_generated_nonce += 1; return outboundLaneNonce.latest_generated_nonce; } diff --git a/helix-contract/contracts/mapping-token/test/MockSub2SubMessageEndpoint.sol b/helix-contract/contracts/mapping-token/test/MockSub2SubMessageEndpoint.sol index d915b577..70c84be5 100644 --- a/helix-contract/contracts/mapping-token/test/MockSub2SubMessageEndpoint.sol +++ b/helix-contract/contracts/mapping-token/test/MockSub2SubMessageEndpoint.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.10; import "../v2/AccessController.sol"; +import "hardhat/console.sol"; contract MockSub2SubMessageEndpoint is AccessController { address remoteHelix; @@ -33,8 +34,8 @@ contract MockSub2SubMessageEndpoint is AccessController { } function sendMessage( - uint32 remoteSpecVersion, - uint256 remoteReceiveGasLimit, + uint32, + uint256, address receiver, bytes calldata message) external onlyCaller payable returns(uint256) { require(msg.value == fee(), "fee is not matched"); @@ -45,14 +46,16 @@ contract MockSub2SubMessageEndpoint is AccessController { ); uint64 nonce = outboundMessages[outboundLaneId] + 1; outboundMessages[outboundLaneId] = nonce; - remoteHelix.call(recv); + (bool result, ) = remoteHelix.call(recv); + require(result, "call remote helix failed"); return encodeMessageId(outboundLaneId, nonce); } function recvMessage(address receiver, bytes calldata message) external onlyRemoteHelix { require(hasRole(CALLER_ROLE, receiver), "MockS2sMessageEndpoint:receiver is not caller"); // don't make sure this success to simulate the failed case. - receiver.call(message); + (bool result, ) = receiver.call(message); + console.log("mock recv call return %s", result); inboundMessages[inboundLaneId] = inboundMessages[inboundLaneId] + 1; } @@ -60,7 +63,7 @@ contract MockSub2SubMessageEndpoint is AccessController { return encodeMessageId(inboundLaneId, inboundMessages[inboundLaneId]); } - function fee() public view returns(uint256) { + function fee() public pure returns(uint256) { return 1 ether; } diff --git a/helix-contract/contracts/mapping-token/test/MockSubToSubBridge.sol b/helix-contract/contracts/mapping-token/test/MockSubToSubBridge.sol deleted file mode 100644 index f8cdc041..00000000 --- a/helix-contract/contracts/mapping-token/test/MockSubToSubBridge.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.6.0; - -import "../darwinia/MappingTokenAddress.sol"; - -interface IDispatchCall { - function generated_nonce() view external returns(uint64); - function send_message(uint32 pallet_id, bytes4 lane_id, bytes memory message, uint256 fee) external; -} - -contract MockSubToSubBridge is MappingTokenAddress { - function outbound_latest_generated_nonce(bytes4 laneid) external view returns (uint64) { - return IDispatchCall(DISPATCH).generated_nonce(); - } - - function inbound_latest_received_nonce(bytes4 laneid) external view returns (uint64) { - // suppose received nonce is generated nonce - return IDispatchCall(DISPATCH).generated_nonce(); - } - - function encode_unlock_from_remote_dispatch_call( - uint32 spec_version, - uint64 weight, - uint32 token_type, - address original_token, - bytes memory recipient, - uint256 amount) external pure returns(bytes memory) { - return abi.encode( - spec_version, - weight, - token_type, - original_token, - recipient, - amount); - } - - function encode_send_message_dispatch_call(uint32 msg_pallet_id, bytes4 lane_id, bytes memory message, uint256 fee) external pure returns(bytes memory) { - return abi.encodeWithSelector( - IDispatchCall.send_message.selector, - msg_pallet_id, - lane_id, - message, - fee - ); - } - - function encode_register_from_remote_dispatch_call( - uint32 spec_version, - uint64 weight, - uint32 token_type, - address original_token, - string memory name, - string memory symbol, - uint8 decimals - ) external pure returns(bytes memory) { - return abi.encode( - spec_version, - weight, - token_type, - original_token, - name, - symbol, - decimals - ); - } - - function encode_issue_from_remote_dispatch_call( - uint32 spec_version, - uint64 weight, - address token, - address recipient, - uint256 amount - ) external pure returns(bytes memory) { - return abi.encode( - spec_version, - weight, - token, - recipient, - amount - ); - } -} - diff --git a/helix-contract/contracts/mapping-token/test/MockcBridgeMsgBus.sol b/helix-contract/contracts/mapping-token/test/MockcBridgeMsgBus.sol index 9b7ab7da..b840b5ec 100644 --- a/helix-contract/contracts/mapping-token/test/MockcBridgeMsgBus.sol +++ b/helix-contract/contracts/mapping-token/test/MockcBridgeMsgBus.sol @@ -32,8 +32,8 @@ contract MockcBridgeMsgBus { } function sendMessage( - address _receiver, - uint256 _dstChainId, + address, + uint256, bytes calldata _message ) external payable { IMsgBus(remoteMessageBus).executeMessage(msg.sender, remoteChainId, _message, address(0)); @@ -44,8 +44,9 @@ contract MockcBridgeMsgBus { uint64 srcChainId, bytes memory message, address executor - ) external payable returns (IMessageReceiverApp.ExecutionStatus) { + ) external payable returns (IMessageReceiverApp.ExecutionStatus status) { IMsgBus(receiver).executeMessage(sender, srcChainId, message, executor); + status = IMessageReceiverApp.ExecutionStatus.Success; } } diff --git a/helix-contract/contracts/mapping-token/v2/Guard.sol b/helix-contract/contracts/mapping-token/v2/Guard.sol index c7aa58f5..10c5ccf9 100644 --- a/helix-contract/contracts/mapping-token/v2/Guard.sol +++ b/helix-contract/contracts/mapping-token/v2/Guard.sol @@ -2,11 +2,11 @@ pragma solidity >=0.8.10; +import "@zeppelin-solidity-4.4.0/contracts/security/Pausable.sol"; +import "@zeppelin-solidity-4.4.0/contracts/token/ERC20/IERC20.sol"; import "@zeppelin-solidity-4.4.0/contracts/utils/math/SafeMath.sol"; import "./GuardRegistry.sol"; -import "../interfaces/IERC20.sol"; import "../interfaces/IWToken.sol"; -import "../../utils/Pausable.sol"; contract Guard is GuardRegistry, Pausable { using SafeMath for uint256; diff --git a/helix-contract/contracts/mapping-token/v2/GuardRegistry.sol b/helix-contract/contracts/mapping-token/v2/GuardRegistry.sol index e1ace4bc..8837c93a 100644 --- a/helix-contract/contracts/mapping-token/v2/GuardRegistry.sol +++ b/helix-contract/contracts/mapping-token/v2/GuardRegistry.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.10; pragma experimental ABIEncoderV2; -import "../../utils/ECDSA.sol"; +import "@zeppelin-solidity-4.4.0/contracts/utils/cryptography/ECDSA.sol"; /** * @title Manages a set of guards and a threshold to double-check BEEFY commitment @@ -222,7 +222,7 @@ contract GuardRegistry { bytes4 methodID, bytes memory params, bytes[] memory signatures - ) internal { + ) view internal { bytes32 structHash = keccak256( abi.encode( diff --git a/helix-contract/contracts/mapping-token/v2/erc1155-mapping-protocol/Erc1155BackingSupportingConfirm.sol b/helix-contract/contracts/mapping-token/v2/erc1155-mapping-protocol/Erc1155BackingSupportingConfirm.sol index ec7eb658..209d2a6e 100644 --- a/helix-contract/contracts/mapping-token/v2/erc1155-mapping-protocol/Erc1155BackingSupportingConfirm.sol +++ b/helix-contract/contracts/mapping-token/v2/erc1155-mapping-protocol/Erc1155BackingSupportingConfirm.sol @@ -139,22 +139,22 @@ contract Erc1155BackingSupportingConfirm is Backing, IErc1155Backing { } function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes calldata data - ) external returns (bytes4) { + address, + address, + uint256, + uint256, + bytes calldata + ) external pure returns (bytes4) { return Erc1155BackingSupportingConfirm.onERC1155Received.selector; } function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external returns (bytes4) { + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure returns (bytes4) { return Erc1155BackingSupportingConfirm.onERC1155BatchReceived.selector; } diff --git a/helix-contract/contracts/mapping-token/v2/erc1155-mapping-protocol/Erc1155MappingTokenFactorySupportingConfirm.sol b/helix-contract/contracts/mapping-token/v2/erc1155-mapping-protocol/Erc1155MappingTokenFactorySupportingConfirm.sol index 5c84c2a2..ccb08cd3 100644 --- a/helix-contract/contracts/mapping-token/v2/erc1155-mapping-protocol/Erc1155MappingTokenFactorySupportingConfirm.sol +++ b/helix-contract/contracts/mapping-token/v2/erc1155-mapping-protocol/Erc1155MappingTokenFactorySupportingConfirm.sol @@ -129,22 +129,22 @@ contract Erc1155MappingTokenFactorySupportingConfirm is MappingTokenFactory { } function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes calldata data - ) external returns (bytes4) { + address, + address, + uint256, + uint256, + bytes calldata + ) external pure returns (bytes4) { return Erc1155MappingTokenFactorySupportingConfirm.onERC1155Received.selector; } function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external returns (bytes4) { + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure returns (bytes4) { return Erc1155MappingTokenFactorySupportingConfirm.onERC1155BatchReceived.selector; } } diff --git a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthBacking.sol b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthBacking.sol index c6103c98..e5192978 100644 --- a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthBacking.sol +++ b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthBacking.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.10; -import "@zeppelin-solidity-4.4.0/contracts/utils/math/SafeMath.sol"; +import "@zeppelin-solidity-4.4.0/contracts/token/ERC20/IERC20.sol"; import "@zeppelin-solidity-4.4.0/contracts/utils/structs/BitMaps.sol"; import "../Backing.sol"; import "../../interfaces/IBacking.sol"; -import "../../interfaces/IERC20.sol"; import "../../interfaces/IErc20MappingTokenFactory.sol"; import "../../interfaces/IGuard.sol"; import "../../interfaces/IHelixApp.sol"; @@ -14,7 +13,6 @@ import "../../interfaces/IWToken.sol"; import "../../../utils/DailyLimit.sol"; contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { - using SafeMath for uint256; struct LockedInfo { bytes32 hash; bool hasRefundForFailed; @@ -30,11 +28,10 @@ contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { 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 transferId, address mappingToken, address originalSender, uint256 amount, uint256 fee); + 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 { - } + receive() external payable {} // !!! admin must check the nonce of the newEndpoint is larger than the old one function setMessageEndpoint(address _messageEndpoint) external onlyAdmin { @@ -110,7 +107,7 @@ contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { 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.add(amount) == balanceAfter, "Backing:Transfer amount is invalid"); + require(balanceBefore + amount == balanceAfter, "Backing:Transfer amount is invalid"); _lockAndRemoteIssuing(token, recipient, amount, msg.value, false); } @@ -143,12 +140,13 @@ contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { 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 - require(IERC20(token).increaseAllowance(guard, amount), "Backing:approve token transfer to guard failed"); + 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 { - expendDailyLimit(token, amount); require(IERC20(token).transfer(recipient, amount), "Backing:unlock transfer failed"); } emit TokenUnlocked(transferId, false, token, recipient, amount); @@ -168,7 +166,8 @@ contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { require(BitMaps.get(unlockedTransferIds, transferId) == false, "Backing:message has been accepted"); BitMaps.set(unlockedTransferIds, transferId); if (guard != address(0)) { - require(IERC20(wToken).approve(guard, amount), "Backing:approve token transfer to guard failed"); + 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); @@ -194,8 +193,8 @@ contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { originalSender, amount ); - (, uint256 fee) = _sendMessage(unlockForFailed, msg.value); - emit RemoteIssuingFailure(transferId, mappingToken, originalSender, amount, fee); + (uint256 refundId, uint256 fee) = _sendMessage(unlockForFailed, msg.value); + emit RemoteIssuingFailure(refundId, transferId, mappingToken, originalSender, amount, fee); } /** @@ -228,7 +227,7 @@ contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { address token, address originSender, uint256 amount - ) external onlyMessageEndpoint whenNotPaused { + ) external onlyMessageEndpoint { _handleUnlockFailureFromRemote(transferId, token, originSender, amount); require(IERC20(token).transfer(originSender, amount), "Backing:unlock transfer failed"); emit TokenUnlockedForFailed(transferId, false, token, originSender, amount); @@ -243,7 +242,7 @@ contract Erc20Sub2EthBacking is Backing, DailyLimit, IBacking { uint256 transferId, address originSender, uint256 amount - ) external onlyMessageEndpoint whenNotPaused { + ) external onlyMessageEndpoint { _handleUnlockFailureFromRemote(transferId, wToken, originSender, amount); IWToken(wToken).withdraw(amount); payable(originSender).transfer(amount); diff --git a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthMappingTokenFactory.sol b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthMappingTokenFactory.sol index 7515796b..e4988447 100644 --- a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthMappingTokenFactory.sol +++ b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthMappingTokenFactory.sol @@ -11,9 +11,7 @@ import "../MappingTokenFactory.sol"; import "../../interfaces/IBacking.sol"; import "../../interfaces/IGuard.sol"; import "../../interfaces/IHelixApp.sol"; -import "../../interfaces/IHelixMessageEndpoint.sol"; import "../../interfaces/IHelixSub2EthMessageEndpoint.sol"; -import "../../interfaces/IMessageCommitment.sol"; import "../../../utils/DailyLimit.sol"; contract Erc20Sub2EthMappingTokenFactory is DailyLimit, MappingTokenFactory { @@ -32,10 +30,9 @@ contract Erc20Sub2EthMappingTokenFactory is DailyLimit, MappingTokenFactory { 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 transferId, address originalToken, address originalSender, uint256 amount, uint256 fee); + event RemoteUnlockFailure(uint256 refundId, uint256 transferId, address originalToken, address originalSender, uint256 amount, uint256 fee); - receive() external payable { - } + receive() external payable {} modifier verifyRemoteUnlockFailure(uint256 transferId) { // must not exist in successful issue list @@ -154,14 +151,15 @@ contract Erc20Sub2EthMappingTokenFactory is DailyLimit, MappingTokenFactory { 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); - require(Erc20(mappingToken).increaseAllowance(guard, amount), "Backing:approve token transfer to guard failed"); + 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 { - expendDailyLimit(mappingToken, amount); Erc20(mappingToken).mint(recipient, amount); } } @@ -192,7 +190,7 @@ contract Erc20Sub2EthMappingTokenFactory is DailyLimit, MappingTokenFactory { function burnAndRemoteUnlockNative( address recipient, uint256 amount - ) external payable whenNotPaused { + ) 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"); @@ -215,7 +213,7 @@ contract Erc20Sub2EthMappingTokenFactory is DailyLimit, MappingTokenFactory { address mappingToken, address recipient, uint256 amount - ) external payable whenNotPaused { + ) 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"); @@ -248,8 +246,8 @@ contract Erc20Sub2EthMappingTokenFactory is DailyLimit, MappingTokenFactory { originalSender, amount ); - (, uint256 fee) = _sendMessage(handleUnlockForFailed); - emit RemoteUnlockFailure(transferId, originalToken, originalSender, amount, fee); + (uint256 refundId, uint256 fee) = _sendMessage(handleUnlockForFailed); + emit RemoteUnlockFailure(refundId, transferId, originalToken, originalSender, amount, fee); } /** @@ -268,8 +266,8 @@ contract Erc20Sub2EthMappingTokenFactory is DailyLimit, MappingTokenFactory { originalSender, amount ); - (, uint256 fee) = _sendMessage(handleUnlockForFailedNative); - emit RemoteUnlockFailure(transferId, xwToken, originalSender, amount, fee); + (uint256 refundId, uint256 fee) = _sendMessage(handleUnlockForFailedNative); + emit RemoteUnlockFailure(refundId, transferId, xwToken, originalSender, amount, fee); } /** diff --git a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubBacking.sol b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubBacking.sol index 76ab984f..6a13a9bd 100644 --- a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubBacking.sol +++ b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubBacking.sol @@ -8,7 +8,6 @@ import "../../interfaces/IBacking.sol"; import "../../interfaces/IERC20.sol"; import "../../interfaces/IGuard.sol"; import "../../interfaces/IHelixApp.sol"; -import "../../interfaces/IHelixMessageEndpoint.sol"; import "../../interfaces/IHelixSub2SubMessageEndpoint.sol"; import "../../interfaces/IErc20MappingTokenFactory.sol"; import "../../../utils/DailyLimit.sol"; @@ -140,7 +139,7 @@ contract Erc20Sub2SubBacking is Backing, DailyLimit, IBacking { recipient, amount ); - (uint256 transferId, uint256 fee) = _sendMessage( + (uint256 transferId, uint256 totalFee) = _sendMessage( remoteSpecVersion, remoteReceiveGasLimit, issueMappingToken @@ -148,7 +147,7 @@ contract Erc20Sub2SubBacking is Backing, DailyLimit, IBacking { 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, lockMessageHash, token, msg.sender, recipient, amount, fee); + emit TokenLocked(transferId, lockMessageHash, token, msg.sender, recipient, amount, totalFee); } /** @@ -218,12 +217,12 @@ contract Erc20Sub2SubBacking is Backing, DailyLimit, IBacking { originalSender, amount ); - (, uint256 fee) = _sendMessage( + (, uint256 totalFee) = _sendMessage( remoteSpecVersion, remoteReceiveGasLimit, unlockForFailed ); - emit RemoteIssuingFailure(transferId, mappingToken, originalSender, amount, fee); + emit RemoteIssuingFailure(transferId, mappingToken, originalSender, amount, totalFee); } /** diff --git a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubMappingTokenFactory.sol b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubMappingTokenFactory.sol index d131441d..0987b820 100644 --- a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubMappingTokenFactory.sol +++ b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2SubMappingTokenFactory.sol @@ -12,7 +12,6 @@ import "../../interfaces/IBacking.sol"; import "../../interfaces/IERC20.sol"; import "../../interfaces/IGuard.sol"; import "../../interfaces/IHelixApp.sol"; -import "../../interfaces/IHelixMessageEndpoint.sol"; import "../../interfaces/IHelixSub2SubMessageEndpoint.sol"; import "../../interfaces/IErc20MappingTokenFactory.sol"; import "../../../utils/DailyLimit.sol"; @@ -196,7 +195,7 @@ contract Erc20Sub2SubMappingTokenFactory is DailyLimit, IErc20MappingTokenFactor amount ); - (uint256 transferId, uint256 fee) = _sendMessage( + (uint256 transferId, uint256 totalFee) = _sendMessage( remoteSpecVersion, remoteReceiveGasLimit, unlockFromRemote @@ -204,7 +203,7 @@ contract Erc20Sub2SubMappingTokenFactory is DailyLimit, IErc20MappingTokenFactor 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, messageHash, msg.sender, recipient, mappingToken, amount, fee); + emit BurnAndRemoteUnlocked(transferId, messageHash, msg.sender, recipient, mappingToken, amount, totalFee); } /** @@ -233,12 +232,12 @@ contract Erc20Sub2SubMappingTokenFactory is DailyLimit, IErc20MappingTokenFactor originalSender, amount ); - (, uint256 fee) = _sendMessage( + (, uint256 totalFee) = _sendMessage( remoteSpecVersion, remoteReceiveGasLimit, handleUnlockForFailed ); - emit RemoteUnlockFailure(transferId, originalToken, originalSender, amount, fee); + emit RemoteUnlockFailure(transferId, originalToken, originalSender, amount, totalFee); } /** diff --git a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/WToken.sol b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/WToken.sol index 955d39b5..e366d9c4 100644 --- a/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/WToken.sol +++ b/helix-contract/contracts/mapping-token/v2/erc20-mapping-protocol/WToken.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.4.18; +pragma solidity ^0.8.10; contract WToken { string public name; @@ -15,15 +15,16 @@ contract WToken { mapping (address => uint) public balanceOf; mapping (address => mapping (address => uint)) public allowance; - constructor(string _name, string _symbol, uint8 _decimals) public { + constructor(string memory _name, string memory _symbol, uint8 _decimals) { name = _name; symbol = _symbol; decimals = _decimals; } - function() public payable { + receive() external payable { deposit(); } + function deposit() public payable { balanceOf[msg.sender] += msg.value; emit Deposit(msg.sender, msg.value); @@ -31,7 +32,7 @@ contract WToken { function withdraw(uint wad) public { require(balanceOf[msg.sender] >= wad); balanceOf[msg.sender] -= wad; - msg.sender.transfer(wad); + payable(msg.sender).transfer(wad); emit Withdrawal(msg.sender, wad); } @@ -55,7 +56,7 @@ contract WToken { { require(balanceOf[src] >= wad); - if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { + if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { require(allowance[src][msg.sender] >= wad); allowance[src][msg.sender] -= wad; } diff --git a/helix-contract/contracts/mapping-token/v2/message-endpoints/DarwiniaSub2EthMessageEndpoint.sol b/helix-contract/contracts/mapping-token/v2/message-endpoints/DarwiniaSub2EthMessageEndpoint.sol index d80fe70f..70d28ab7 100644 --- a/helix-contract/contracts/mapping-token/v2/message-endpoints/DarwiniaSub2EthMessageEndpoint.sol +++ b/helix-contract/contracts/mapping-token/v2/message-endpoints/DarwiniaSub2EthMessageEndpoint.sol @@ -6,7 +6,6 @@ import "../../interfaces/ICrossChainFilter.sol"; import "../../interfaces/IFeeMarket.sol"; import "../../interfaces/IHelixApp.sol"; import "../../interfaces/IInboundLane.sol"; -import "../../interfaces/IMessageCommitment.sol"; import "../../interfaces/IOutboundLane.sol"; contract DarwiniaSub2EthMessageEndpoint is ICrossChainFilter, AccessController { @@ -43,8 +42,8 @@ contract DarwiniaSub2EthMessageEndpoint is ICrossChainFilter, AccessController { } function cross_chain_filter( - uint32 bridgedChainPosition, - uint32 bridgedLanePosition, + uint32, + uint32, address sourceAccount, bytes calldata ) external view returns (bool) { diff --git a/helix-contract/contracts/mapping-token/v2/message-endpoints/cBridgeMessageEndpoint.sol b/helix-contract/contracts/mapping-token/v2/message-endpoints/cBridgeMessageEndpoint.sol index 8dbfbce9..4d91857b 100644 --- a/helix-contract/contracts/mapping-token/v2/message-endpoints/cBridgeMessageEndpoint.sol +++ b/helix-contract/contracts/mapping-token/v2/message-endpoints/cBridgeMessageEndpoint.sol @@ -45,7 +45,7 @@ contract cBridgeMessageEndpoint is IHelixMessageEndpoint, AccessController { address sender, uint64 srcChainId, bytes memory message, - address executor + address ) external payable onlyMessageBus onlyBridgeMessageEndpoint(sender) returns (IMessageReceiverApp.ExecutionStatus) { require(remoteChainId == srcChainId, "invalid srcChainId"); (address receiver, bytes memory payload) = abi.decode(message, (address, bytes)); diff --git a/helix-contract/contracts/utils/Context.sol b/helix-contract/contracts/utils/Context.sol deleted file mode 100644 index 9ae695e1..00000000 --- a/helix-contract/contracts/utils/Context.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0; - -/* - * @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 GSN 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. - */ -contract Context { - function _msgSender() internal view returns (address payable) { - return payable(msg.sender); - } - - function _msgData() internal view returns (bytes memory) { - this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 - return msg.data; - } -} diff --git a/helix-contract/contracts/utils/DailyLimit.sol b/helix-contract/contracts/utils/DailyLimit.sol index 4b023d1a..ddf792c9 100644 --- a/helix-contract/contracts/utils/DailyLimit.sol +++ b/helix-contract/contracts/utils/DailyLimit.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.6.0; +pragma solidity >=0.8.10; /// @title relay with daily limit - Allows the relay to mint token in a daily limit. contract DailyLimit { @@ -9,7 +9,7 @@ contract DailyLimit { mapping(address => uint) public dailyLimit; // deprecated, slot for upgrade - mapping(address => uint) public lastDay; + mapping(address => uint) public _slotReserved; mapping(address => uint) public spentToday; uint constant public SPEND_BIT_LENGTH = 192; diff --git a/helix-contract/contracts/utils/ECDSA.sol b/helix-contract/contracts/utils/ECDSA.sol deleted file mode 100644 index b751efc4..00000000 --- a/helix-contract/contracts/utils/ECDSA.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0; - -/** - * @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 { - /** - * @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) { - // Check the signature length - if (signature.length != 65) { - revert("ECDSA: invalid signature length"); - } - - // Divide the signature in r, s and v variables - bytes32 r; - bytes32 s; - uint8 v; - - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - // solhint-disable-next-line no-inline-assembly - assembly { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - } - - // 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 (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): 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. - require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value"); - require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); - - // If the signature is valid (and not malleable), return the signer address - address signer = ecrecover(hash, v, r, s); - require(signer != address(0), "ECDSA: invalid signature"); - - return signer; - } - - /** - * @dev Returns an Ethereum Signed Message, created from a `hash`. This - * replicates the behavior of the - * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] - * JSON-RPC method. - * - * 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)); - } -} diff --git a/helix-contract/contracts/utils/Ownable.sol b/helix-contract/contracts/utils/Ownable.sol deleted file mode 100644 index ffe35660..00000000 --- a/helix-contract/contracts/utils/Ownable.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0; - -import "./Context.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. - */ -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. - */ - function ownableConstructor () internal { - address msgSender = _msgSender(); - _owner = msgSender; - emit OwnershipTransferred(address(0), msgSender); - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view returns (address) { - return _owner; - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - 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 onlyOwner { - emit OwnershipTransferred(_owner, address(0)); - _owner = 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 onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - emit OwnershipTransferred(_owner, newOwner); - _owner = newOwner; - } -} diff --git a/helix-contract/contracts/utils/Pausable.sol b/helix-contract/contracts/utils/Pausable.sol deleted file mode 100644 index 504f2769..00000000 --- a/helix-contract/contracts/utils/Pausable.sol +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0; - -import "./Context.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. - */ -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. - */ - function pausableConstructor () internal { - _paused = false; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view returns (bool) { - return _paused; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - require(!_paused, "Pausable: paused"); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - require(_paused, "Pausable: not paused"); - _; - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} diff --git a/helix-contract/deploy/deploy_pangoro2goerli.js b/helix-contract/deploy/deploy_pangoro2goerli.js index 94612275..b3c21a32 100644 --- a/helix-contract/deploy/deploy_pangoro2goerli.js +++ b/helix-contract/deploy/deploy_pangoro2goerli.js @@ -1,5 +1,13 @@ var ProxyDeployer = require("./proxy.js"); +const backingFeeMarket = "0x25ee4212CfA2DC29E6a5e4A857b9656E439259c9"; +const backingOutboundLane = "0x2f6aE7fDbB7c0c613F7923Ddce3E5b71aFE71f78"; +const backingInboundLane = "0x8F02B779EaD342Bb431A2f6cCc7866EB1928AE88"; + +const mtfFeeMarket = "0xdb5E16A6E25ABF29dbe26e701D1DDCad03180E92"; +const mtfOutboundLane = "0x449337BBe404CaE0bA82f3451661AF7481f37aaC"; +const mtfInboundLane = "0xBEF08069FD1F43CB48Afd436D73F1a59019B87D7"; + async function deployMessageEndpoint(wallet, inboundLane, ouboundLane, feeMarket, existedAddress) { return await deployContract(wallet, "DarwiniaSub2EthMessageEndpoint", existedAddress, inboundLane, ouboundLane, feeMarket, {gasLimit: 2000000}); } @@ -97,13 +105,6 @@ function wallet() { } async function deploy(backingWallet, mtfWallet) { - const backingFeeMarket = "0x6eDcF984eF28C29aa48242B92685244bcD6D7203"; - const backingOutboundLane = "0x671EB5a157328b46518D9D0d070e33404Bae5758"; - const backingInboundLane = "0x3277B7BABbeadF5dC43D5350df25310f3c819965"; - - const mtfFeeMarket = "0x380244554a9C51f0CCaFec90A2766B0C8b698a4a"; - const mtfOutboundLane = "0x33Ae943B5567e0a92928EF5EB1E6151558a086da"; - const mtfInboundLane = "0xD9c96CaDC0710b8cD206d4F24DD8c547c6Ce23af"; // deploy const backingMessageEndpoint = await deployMessageEndpoint(backingWallet, backingInboundLane, backingOutboundLane, backingFeeMarket, null); console.log("deploy backing message handle finished, address: ", backingMessageEndpoint.address); @@ -231,14 +232,6 @@ async function redeployGuard(mtfWallet, mtf) { } async function deployWithExistContract(backingWallet, mtfWallet) { - const backingFeeMarket = "0x6eDcF984eF28C29aa48242B92685244bcD6D7203"; - const backingOutboundLane = "0x671EB5a157328b46518D9D0d070e33404Bae5758"; - const backingInboundLane = "0x3277B7BABbeadF5dC43D5350df25310f3c819965"; - - const mtfFeeMarket = "0x380244554a9C51f0CCaFec90A2766B0C8b698a4a"; - const mtfOutboundLane = "0x33Ae943B5567e0a92928EF5EB1E6151558a086da"; - const mtfInboundLane = "0xD9c96CaDC0710b8cD206d4F24DD8c547c6Ce23af"; - const wringAddress = "0x69e392E057B5994da2b0E9661039970Ac4c26b8c"; const ringErc20 = "0x69e392E057B5994da2b0E9661039970Ac4c26b8c"; @@ -361,33 +354,35 @@ async function main() { const mtfWallet = wallets[1]; const deployed = await deploy(backingWallet, mtfWallet); - //const deployed = await deployWithExistContract(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 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 = "0xF5c874cb3C541aE8C8f5C810BA78E98449A17913"; - //const mtfAddress = "0xe35b898A65c9868336bf34321373E1DA9401eB9d"; - //const backingAddress = "0x7F9096beb4bAd82a63f4275d53c7E8EA03aC1C99"; - //const ringAddress = "0xD08a544fc3baa1dBB34F310c4A941E88D82bc8Fe"; - //const mtf = await ethers.getContractAt("Erc20Sub2EthMappingTokenFactory", mtfAddress, mtfWallet); - //const backing = await ethers.getContractAt("Erc20Sub2EthBacking", backingAddress, backingWallet); + /* + 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("300"); + const dailyLimit = ethers.utils.parseEther("200"); //await mtf.changeDailyLimit(ringAddress, dailyLimit); - //await backing.changeDailyLimit(wethAddress, 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("1.82"), 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() diff --git a/helix-contract/deploy/flatten-sub2eth.sh b/helix-contract/deploy/flatten-sub2eth.sh new file mode 100644 index 00000000..18110832 --- /dev/null +++ b/helix-contract/deploy/flatten-sub2eth.sh @@ -0,0 +1,8 @@ +path=flatten/sub2eth +mkdir -p $path +yarn flat contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthBacking.sol --output $path/Erc20Sub2EthBacking.sol +yarn flat contracts/mapping-token/v2/erc20-mapping-protocol/Erc20Sub2EthMappingTokenFactory.sol --output $path/Erc20Sub2EthMappingTokenFactory.sol +yarn flat contracts/mapping-token/v2/erc20-mapping-protocol/WToken.sol --output $path/WToken.sol +yarn flat contracts/mapping-token/v2/erc20-mapping-protocol/Erc20.sol --output $path/Erc20.sol +yarn flat contracts/mapping-token/v2/message-endpoints/DarwiniaSub2EthMessageEndpoint.sol --output $path/DarwiniaSub2EthMessageEndpoint.sol +yarn flat contracts/mapping-token/v2/Guard.sol --output $path/Guard.sol diff --git a/helix-contract/flatten/sub2eth/DarwiniaSub2EthMessageEndpoint.sol b/helix-contract/flatten/sub2eth/DarwiniaSub2EthMessageEndpoint.sol new file mode 100644 index 00000000..8a2f347f --- /dev/null +++ b/helix-contract/flatten/sub2eth/DarwiniaSub2EthMessageEndpoint.sol @@ -0,0 +1,1274 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * https://helixbridge.app/ + * + * 9/24/2022 + **/ + +pragma solidity ^0.8.10; + +// File @zeppelin-solidity-4.4.0/contracts/utils/Context.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/security/Pausable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + require(!paused(), "Pausable: paused"); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + 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-4.4.0/contracts/access/IAccessControl.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/access/IAccessControlEnumerable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/utils/Strings.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (utils/Strings.sol) + + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + /** + * @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); + } +} + +// File @zeppelin-solidity-4.4.0/contracts/utils/introspection/IERC165.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/utils/introspection/ERC165.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/access/AccessControl.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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, _msgSender()); + _; + } + + /** + * @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 override returns (bool) { + return _roles[role].members[account]; + } + + /** + * @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 { + 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 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. + */ + 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. + */ + 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`. + */ + 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. + * + * [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. + */ + 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. + */ + 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-4.4.0/contracts/utils/structs/EnumerableSet.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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. + */ +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; + + 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; + + assembly { + result := store + } + + return result; + } +} + +// File @zeppelin-solidity-4.4.0/contracts/access/AccessControlEnumerable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 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 override returns (uint256) { + return _roleMembers[role].length(); + } + + /** + * @dev Overload {grantRole} to track enumerable memberships + */ + function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.grantRole(role, account); + _roleMembers[role].add(account); + } + + /** + * @dev Overload {revokeRole} to track enumerable memberships + */ + function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.revokeRole(role, account); + _roleMembers[role].remove(account); + } + + /** + * @dev Overload {renounceRole} to track enumerable memberships + */ + function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.renounceRole(role, account); + _roleMembers[role].remove(account); + } + + /** + * @dev Overload {_setupRole} to track enumerable memberships + */ + function _setupRole(bytes32 role, address account) internal virtual override { + super._setupRole(role, account); + _roleMembers[role].add(account); + } +} + +// 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/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/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/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/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/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/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; + + address public remoteEndpoint; + + constructor( + address _inboundLane, + address _outboundLane, + address _feeMarket + ) { + 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 sendMessage(address receiver, bytes calldata message) external onlyCaller payable returns (uint256) { + bytes memory messageWithCaller = abi.encodeWithSelector( + DarwiniaSub2EthMessageEndpoint.recvMessage.selector, + receiver, + message + ); + return IOutboundLane(outboundLane).send_message{value: msg.value}(remoteEndpoint, messageWithCaller); + } + + 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 inboundLaneNonce.last_delivered_nonce + 1; + } + + function isMessageDelivered(uint256 messageId) public view returns (bool) { + IInboundLane.InboundLaneNonce memory inboundLaneNonce = IInboundLane(inboundLane).inboundLaneNonce(); + uint256 lastMessageId = inboundLaneNonce.last_delivered_nonce; + return messageId <= lastMessageId; + } +} \ No newline at end of file diff --git a/helix-contract/flatten/sub2eth/Erc20.sol b/helix-contract/flatten/sub2eth/Erc20.sol new file mode 100644 index 00000000..4039fc32 --- /dev/null +++ b/helix-contract/flatten/sub2eth/Erc20.sol @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * https://helixbridge.app/ + * + * 9/24/2022 + **/ + +pragma solidity ^0.8.10; + +// File @zeppelin-solidity-4.4.0/contracts/token/ERC20/IERC20.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @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 `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, 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 `sender` to `recipient` 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 sender, + address recipient, + uint256 amount + ) external returns (bool); + + /** + * @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); +} + +// File @zeppelin-solidity-4.4.0/contracts/utils/Context.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/access/Ownable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + 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-4.4.0/contracts/utils/math/SafeMath.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 substraction 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/sub2eth/Erc20Sub2EthBacking.sol b/helix-contract/flatten/sub2eth/Erc20Sub2EthBacking.sol new file mode 100644 index 00000000..41ba4889 --- /dev/null +++ b/helix-contract/flatten/sub2eth/Erc20Sub2EthBacking.sol @@ -0,0 +1,1770 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * https://helixbridge.app/ + * + * 9/24/2022 + **/ + +pragma solidity ^0.8.10; + +// File @zeppelin-solidity-4.4.0/contracts/access/IAccessControl.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/access/IAccessControlEnumerable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/utils/Strings.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (utils/Strings.sol) + + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + /** + * @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); + } +} + +// File @zeppelin-solidity-4.4.0/contracts/utils/Context.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/utils/introspection/IERC165.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/utils/introspection/ERC165.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/access/AccessControl.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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, _msgSender()); + _; + } + + /** + * @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 override returns (bool) { + return _roles[role].members[account]; + } + + /** + * @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 { + 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 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. + */ + 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. + */ + 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`. + */ + 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. + * + * [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. + */ + 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. + */ + 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-4.4.0/contracts/utils/structs/EnumerableSet.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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. + */ +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; + + 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; + + assembly { + result := store + } + + return result; + } +} + +// File @zeppelin-solidity-4.4.0/contracts/access/AccessControlEnumerable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 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 override returns (uint256) { + return _roleMembers[role].length(); + } + + /** + * @dev Overload {grantRole} to track enumerable memberships + */ + function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.grantRole(role, account); + _roleMembers[role].add(account); + } + + /** + * @dev Overload {revokeRole} to track enumerable memberships + */ + function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.revokeRole(role, account); + _roleMembers[role].remove(account); + } + + /** + * @dev Overload {renounceRole} to track enumerable memberships + */ + function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.renounceRole(role, account); + _roleMembers[role].remove(account); + } + + /** + * @dev Overload {_setupRole} to track enumerable memberships + */ + function _setupRole(bytes32 role, address account) internal virtual override { + super._setupRole(role, account); + _roleMembers[role].add(account); + } +} + +// File @zeppelin-solidity-4.4.0/contracts/security/Pausable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + require(!paused(), "Pausable: paused"); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + 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-4.4.0/contracts/proxy/utils/Initializable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 a proxied contract can't have 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. + * + * 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 initialize the implementation contract, you can either invoke the + * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() initializer {} + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + */ + bool private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Modifier to protect an initializer function from being invoked twice. + */ + modifier initializer() { + require(_initializing || !_initialized, "Initializable: contract is already initialized"); + + bool isTopLevelCall = !_initializing; + if (isTopLevelCall) { + _initializing = true; + _initialized = true; + } + + _; + + if (isTopLevelCall) { + _initializing = false; + } + } +} + +// 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 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/IErc20MappingTokenFactory.sol +// License-Identifier: MIT + + +interface IErc20MappingTokenFactory { + function newErc20Contract( + uint32 tokenType, + 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/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/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/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/IWToken.sol +// License-Identifier: MIT + + +interface IWToken { + function deposit() external payable; + function withdraw(uint wad) 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-4.4.0/contracts/token/ERC20/IERC20.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @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 `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, 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 `sender` to `recipient` 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 sender, + address recipient, + uint256 amount + ) external returns (bool); + + /** + * @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); +} + +// File @zeppelin-solidity-4.4.0/contracts/utils/structs/BitMaps.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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/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 new file mode 100644 index 00000000..77e2e2d1 --- /dev/null +++ b/helix-contract/flatten/sub2eth/Erc20Sub2EthMappingTokenFactory.sol @@ -0,0 +1,2250 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * https://helixbridge.app/ + * + * 9/24/2022 + **/ + +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/IGuard.sol +// License-Identifier: MIT + + +interface IGuard { + function deposit(uint256 id, address token, 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 @zeppelin-solidity-4.4.0/contracts/access/IAccessControl.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/access/IAccessControlEnumerable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/utils/structs/EnumerableSet.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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. + */ +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; + + 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; + + assembly { + result := store + } + + return result; + } +} + +// File @zeppelin-solidity-4.4.0/contracts/utils/Context.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/utils/Strings.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (utils/Strings.sol) + + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + /** + * @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); + } +} + +// File @zeppelin-solidity-4.4.0/contracts/utils/introspection/IERC165.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/utils/introspection/ERC165.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/access/AccessControl.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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, _msgSender()); + _; + } + + /** + * @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 override returns (bool) { + return _roles[role].members[account]; + } + + /** + * @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 { + 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 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. + */ + 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. + */ + 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`. + */ + 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. + * + * [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. + */ + 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. + */ + 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-4.4.0/contracts/access/AccessControlEnumerable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 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 override returns (uint256) { + return _roleMembers[role].length(); + } + + /** + * @dev Overload {grantRole} to track enumerable memberships + */ + function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.grantRole(role, account); + _roleMembers[role].add(account); + } + + /** + * @dev Overload {revokeRole} to track enumerable memberships + */ + function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.revokeRole(role, account); + _roleMembers[role].remove(account); + } + + /** + * @dev Overload {renounceRole} to track enumerable memberships + */ + function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { + super.renounceRole(role, account); + _roleMembers[role].remove(account); + } + + /** + * @dev Overload {_setupRole} to track enumerable memberships + */ + function _setupRole(bytes32 role, address account) internal virtual override { + super._setupRole(role, account); + _roleMembers[role].add(account); + } +} + +// File @zeppelin-solidity-4.4.0/contracts/security/Pausable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + require(!paused(), "Pausable: paused"); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + 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-4.4.0/contracts/proxy/utils/Initializable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 a proxied contract can't have 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. + * + * 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 initialize the implementation contract, you can either invoke the + * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() initializer {} + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + */ + bool private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Modifier to protect an initializer function from being invoked twice. + */ + modifier initializer() { + require(_initializing || !_initialized, "Initializable: contract is already initialized"); + + bool isTopLevelCall = !_initializing; + if (isTopLevelCall) { + _initializing = true; + _initialized = true; + } + + _; + + if (isTopLevelCall) { + _initializing = false; + } + } +} + +// File @zeppelin-solidity-4.4.0/contracts/access/Ownable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + 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 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-4.4.0/contracts/token/ERC20/IERC20.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @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 `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, 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 `sender` to `recipient` 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 sender, + address recipient, + uint256 amount + ) external returns (bool); + + /** + * @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); +} + +// File @zeppelin-solidity-4.4.0/contracts/utils/math/SafeMath.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 substraction 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-4.4.0/contracts/utils/structs/BitMaps.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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 new file mode 100644 index 00000000..51bbd91a --- /dev/null +++ b/helix-contract/flatten/sub2eth/Guard.sol @@ -0,0 +1,1198 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * https://helixbridge.app/ + * + * 9/24/2022 + **/ + +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-4.4.0/contracts/utils/Strings.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (utils/Strings.sol) + + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + /** + * @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); + } +} + +// File @zeppelin-solidity-4.4.0/contracts/utils/cryptography/ECDSA.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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) { + // Check the signature length + // - case 65: r,s,v signature (standard) + // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ + 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. + 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 if (signature.length == 64) { + bytes32 r; + bytes32 vs; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + assembly { + r := mload(add(signature, 0x20)) + vs := mload(add(signature, 0x40)) + } + return tryRecover(hash, r, vs); + } 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; + uint8 v; + assembly { + s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + v := add(shr(255, vs), 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-4.4.0/contracts/utils/Context.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (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-4.4.0/contracts/security/Pausable.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + require(!paused(), "Pausable: paused"); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + 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-4.4.0/contracts/utils/math/SafeMath.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.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 substraction 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-4.4.0/contracts/token/ERC20/IERC20.sol@v4.4.0-rc.0 +// License-Identifier: MIT +// OpenZeppelin Contracts v4.4.0-rc.0 (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @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 `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, 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 `sender` to `recipient` 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 sender, + address recipient, + uint256 amount + ) external returns (bool); + + /** + * @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); +} + +// 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; + } + + /** + * @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/WToken.sol b/helix-contract/flatten/sub2eth/WToken.sol new file mode 100644 index 00000000..1f85c4e9 --- /dev/null +++ b/helix-contract/flatten/sub2eth/WToken.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT + +/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \_| | || | | | | || | | | | || | \ \ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > `' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'`\ \_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * https://helixbridge.app/ + * + * 9/24/2022 + **/ + +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 0841ffea..5b17b68e 100644 --- a/helix-contract/hardhat.config.js +++ b/helix-contract/hardhat.config.js @@ -3,6 +3,7 @@ require("@nomiclabs/hardhat-etherscan"); require('hardhat-abi-exporter'); require('dotenv').config({ path: '.env' }) +const fs = require("fs") const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY @@ -88,3 +89,120 @@ module.exports = { } }; + +function getSortedFiles(dependenciesGraph) { + const tsort = require("tsort") + const graph = tsort() + + const filesMap = {} + const resolvedFiles = dependenciesGraph.getResolvedFiles() + resolvedFiles.forEach((f) => (filesMap[f.sourceName] = f)) + + for (const [from, deps] of dependenciesGraph.entries()) { + for (const to of deps) { + graph.add(to.sourceName, from.sourceName) + } + } + + const topologicalSortedNames = graph.sort() + + // If an entry has no dependency it won't be included in the graph, so we + // add them and then dedup the array + const withEntries = topologicalSortedNames.concat(resolvedFiles.map((f) => f.sourceName)) + + const sortedNames = [...new Set(withEntries)] + return sortedNames.map((n) => filesMap[n]) +} + +function getFileWithoutImports(resolvedFile) { + const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+)[\s\S]*?;\s*$/gm + + return resolvedFile.content.rawContent.replace(IMPORT_SOLIDITY_REGEX, "").trim() +} + +subtask("flat:get-flattened-sources", "Returns all contracts and their dependencies flattened") + .addOptionalParam("files", undefined, undefined, types.any) + .addOptionalParam("output", undefined, undefined, types.string) + .setAction(async ({ files, output }, { run }) => { + const dependencyGraph = await run("flat:get-dependency-graph", { files }) + console.log(dependencyGraph) + + let flattened = "" + + if (dependencyGraph.getResolvedFiles().length === 0) { + return flattened + } + + const sortedFiles = getSortedFiles(dependencyGraph) + + let isFirst = true + for (const file of sortedFiles) { + if (!isFirst) { + flattened += "\n" + } + flattened += `// File ${file.getVersionedName()}\n` + flattened += `${getFileWithoutImports(file)}\n` + + isFirst = false + } + + // Remove every line started with "// SPDX-License-Identifier:" + 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 = `/** + * .----------------. .----------------. .----------------. .----------------. .----------------. + * | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | + * | | ____ ____ | || | _________ | || | _____ | || | _____ | || | ____ ____ | | + * | | |_ || _| | || | |_ ___ | | || | |_ _| | || | |_ _| | || | |_ _||_ _| | | + * | | | |__| | | || | | |_ \\_| | || | | | | || | | | | || | \\ \\ / / | | + * | | | __ | | || | | _| _ | || | | | _ | || | | | | || | > \`' < | | + * | | _| | | |_ | || | _| |___/ | | || | _| |__/ | | || | _| |_ | || | _/ /'\`\\ \\_ | | + * | | |____||____| | || | |_________| | || | |________| | || | |_____| | || | |____||____| | | + * | | | || | | || | | || | | || | | | + * | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | + * '----------------' '----------------' '----------------' '----------------' '----------------' ' + * + * https://helixbridge.app/ + * + * ${(new Date()).toLocaleDateString()}\n **/\n\n${flattened}` + flattened = `// SPDX-License-Identifier: MIT\n\n${flattened}` + + // Remove every line started with "pragma experimental ABIEncoderV2;" except the first one + flattened = flattened.replace(/pragma experimental ABIEncoderV2;\n/gm, ((i) => (m) => (!i++ ? m : ""))(0)) + + flattened = flattened.trim() + if (output) { + console.log("Writing to", output) + fs.writeFileSync(output, flattened) + return "" + } + return flattened + }) + +subtask("flat:get-dependency-graph") + .addOptionalParam("files", undefined, undefined, types.any) + .setAction(async ({ files }, { run }) => { + const sourcePaths = files === undefined ? await run("compile:solidity:get-source-paths") : files.map((f) => fs.realpathSync(f)) + + const sourceNames = await run("compile:solidity:get-source-names", { + sourcePaths, + }) + + const dependencyGraph = await run("compile:solidity:get-dependency-graph", { sourceNames }) + + return dependencyGraph + }) + +task("flat", "Flattens and prints contracts and their dependencies") + .addOptionalVariadicPositionalParam("files", "The files to flatten", undefined, types.inputFile) + .addOptionalParam("output", "Specify the output file", undefined, types.string) + .setAction(async ({ files, output }, { run }) => { + console.log( + await run("flat:get-flattened-sources", { + files, + output, + }) + ) + }) diff --git a/helix-contract/package.json b/helix-contract/package.json index 550e61c7..98d2b42b 100644 --- a/helix-contract/package.json +++ b/helix-contract/package.json @@ -9,7 +9,9 @@ "test": "npx hardhat test", "deploy": "npx hardhat run", "clean": "shx rm -rf cache artifacts abi", - "flatten": "npx hardhat flatten" + "flatten": "npx hardhat flatten", + "flat": "npx hardhat flat", + "flat:sub2eth": "sh deploy/flatten-sub2eth.sh" }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.2", @@ -20,7 +22,7 @@ "dotenv": "^10.0.0", "ethereum-waffle": "^3.0.0", "ethers": "^5.0.0", - "hardhat": "^2.6.8", + "hardhat": "^2.11.2", "hardhat-abi-exporter": "^2.2.1", "readline-sync": "^1.4.10", "shx": "^0.3.3" diff --git a/helix-contract/test/1_test_bscv2.js b/helix-contract/test/1_test_bscv2.js index a146d7e3..6b5dcd12 100644 --- a/helix-contract/test/1_test_bscv2.js +++ b/helix-contract/test/1_test_bscv2.js @@ -383,7 +383,6 @@ describe("darwinia<>bsc mapping token tests", () => { await guard.deployed(); await originalToken.approve(guard.address, 1000); - await originalToken.transfer(guard.address, 300); await guard.deposit(1, originalToken.address, wallets[1].address, 100); const timestamp01 = await getBlockTimestamp(); await guard.deposit(2, originalToken.address, wallets[2].address, 200);