From 41fe43d6d05b9cef93bd9b4f2d903dfa961ca8ca Mon Sep 17 00:00:00 2001 From: rain-zxn <206131521@qq.com> Date: Fri, 21 Jul 2023 20:08:51 +0800 Subject: [PATCH 1/2] init poly-assets --- .../assets/erc20_asset/erc20_templete.sol | 52 +++ .../interface/IEthCrossChainManager.sol | 8 + .../interface/IEthCrossChainManagerProxy.sol | 8 + contracts/core/lock_proxy/LockProxy.sol | 189 +++++++++++ contracts/libs/GSN/Context.sol | 28 ++ contracts/libs/common/ZeroCopySink.sol | 185 +++++++++++ contracts/libs/common/ZeroCopySource.sol | 293 +++++++++++++++++ contracts/libs/lifecycle/Pausable.sol | 72 ++++ contracts/libs/math/SafeMath.sol | 156 +++++++++ contracts/libs/ownership/Ownable.sol | 77 +++++ contracts/libs/token/ERC20/ERC20.sol | 231 +++++++++++++ contracts/libs/token/ERC20/ERC20Detailed.sol | 55 ++++ contracts/libs/token/ERC20/ERC20Extended.sol | 65 ++++ contracts/libs/token/ERC20/IERC20.sol | 76 +++++ contracts/libs/token/ERC20/SafeERC20.sol | 74 +++++ contracts/libs/utils/Encoder.sol | 8 + contracts/libs/utils/ReentrancyGuard.sol | 56 ++++ contracts/libs/utils/Utils.sol | 311 ++++++++++++++++++ importingAsset.md | 103 ++++++ source/poly-asset.xlsx | Bin 0 -> 13462 bytes 20 files changed, 2047 insertions(+) create mode 100644 contracts/core/assets/erc20_asset/erc20_templete.sol create mode 100644 contracts/core/cross_chain_manager/interface/IEthCrossChainManager.sol create mode 100644 contracts/core/cross_chain_manager/interface/IEthCrossChainManagerProxy.sol create mode 100644 contracts/core/lock_proxy/LockProxy.sol create mode 100644 contracts/libs/GSN/Context.sol create mode 100644 contracts/libs/common/ZeroCopySink.sol create mode 100644 contracts/libs/common/ZeroCopySource.sol create mode 100644 contracts/libs/lifecycle/Pausable.sol create mode 100644 contracts/libs/math/SafeMath.sol create mode 100644 contracts/libs/ownership/Ownable.sol create mode 100644 contracts/libs/token/ERC20/ERC20.sol create mode 100644 contracts/libs/token/ERC20/ERC20Detailed.sol create mode 100644 contracts/libs/token/ERC20/ERC20Extended.sol create mode 100644 contracts/libs/token/ERC20/IERC20.sol create mode 100644 contracts/libs/token/ERC20/SafeERC20.sol create mode 100644 contracts/libs/utils/Encoder.sol create mode 100644 contracts/libs/utils/ReentrancyGuard.sol create mode 100644 contracts/libs/utils/Utils.sol create mode 100644 importingAsset.md create mode 100644 source/poly-asset.xlsx diff --git a/contracts/core/assets/erc20_asset/erc20_templete.sol b/contracts/core/assets/erc20_asset/erc20_templete.sol new file mode 100644 index 0000000..b692b80 --- /dev/null +++ b/contracts/core/assets/erc20_asset/erc20_templete.sol @@ -0,0 +1,52 @@ +pragma solidity ^0.5.0; + + +import "./../../../libs/GSN/Context.sol"; +import "./../../../libs/token/ERC20/ERC20.sol"; +import "./../../../libs/token/ERC20/ERC20Detailed.sol"; +import "./../../../libs/ownership/Ownable.sol"; + +contract ERC20Template is Context, ERC20, ERC20Detailed, Ownable { + mapping(address => bool) private _blacklist; + + event BlacklistAdded(address indexed account); + event BlacklistRemoved(address indexed account); + + constructor () public ERC20Detailed("Poly-peg-test", "POT", 18) { + _mint(_msgSender(), 1000000000000000000000); + } + + function addToBlacklist(address account) external onlyOwner { + require(account != address(0), "Invalid account address"); + require(!isBlacklisted(account), "Account is already blacklisted"); + + _blacklist[account] = true; + emit BlacklistAdded(account); + } + + function removeFromBlacklist(address account) external onlyOwner { + require(account != address(0), "Invalid account address"); + require(isBlacklisted(account), "Account is not blacklisted"); + + _blacklist[account] = false; + emit BlacklistRemoved(account); + } + + function transfer(address recipient, uint256 amount) public returns (bool) { + require(!isBlacklisted(msg.sender), "Your account is already blacklisted"); + require(!isBlacklisted(recipient), "Recipient's account is already blacklisted"); + + return super.transfer(recipient, amount); + } + + function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { + require(!isBlacklisted(sender), "Sender's account is already blacklisted"); + require(!isBlacklisted(recipient), "Recipient's account is already blacklisted"); + + return super.transferFrom(sender, recipient, amount); + } + + function isBlacklisted(address account) public view returns (bool) { + return _blacklist[account]; + } +} \ No newline at end of file diff --git a/contracts/core/cross_chain_manager/interface/IEthCrossChainManager.sol b/contracts/core/cross_chain_manager/interface/IEthCrossChainManager.sol new file mode 100644 index 0000000..852df55 --- /dev/null +++ b/contracts/core/cross_chain_manager/interface/IEthCrossChainManager.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.5.0; + +/** + * @dev Interface of the EthCrossChainManager contract for business contract like LockProxy to request cross chain transaction + */ +interface IEthCrossChainManager { + function crossChain(uint64 _toChainId, bytes calldata _toContract, bytes calldata _method, bytes calldata _txData) external returns (bool); +} diff --git a/contracts/core/cross_chain_manager/interface/IEthCrossChainManagerProxy.sol b/contracts/core/cross_chain_manager/interface/IEthCrossChainManagerProxy.sol new file mode 100644 index 0000000..73150e1 --- /dev/null +++ b/contracts/core/cross_chain_manager/interface/IEthCrossChainManagerProxy.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.5.0; + +/** + * @dev Interface of the EthCrossChainManagerProxy for business contract like LockProxy to obtain the reliable EthCrossChainManager contract hash. + */ +interface IEthCrossChainManagerProxy { + function getEthCrossChainManager() external view returns (address); +} diff --git a/contracts/core/lock_proxy/LockProxy.sol b/contracts/core/lock_proxy/LockProxy.sol new file mode 100644 index 0000000..f0deeb7 --- /dev/null +++ b/contracts/core/lock_proxy/LockProxy.sol @@ -0,0 +1,189 @@ +pragma solidity ^0.5.0; + +import "./../../libs/ownership/Ownable.sol"; +import "./../../libs/common/ZeroCopySource.sol"; +import "./../../libs/common/ZeroCopySink.sol"; +import "./../../libs/utils/Utils.sol"; +import "./../../libs/token/ERC20/SafeERC20.sol"; +import "./../cross_chain_manager/interface/IEthCrossChainManager.sol"; +import "./../cross_chain_manager/interface/IEthCrossChainManagerProxy.sol"; + + +contract LockProxy is Ownable { + using SafeMath for uint; + using SafeERC20 for IERC20; + + struct TxArgs { + bytes toAssetHash; + bytes toAddress; + uint256 amount; + } + address public managerProxyContract; + mapping(uint64 => bytes) public proxyHashMap; + mapping(address => mapping(uint64 => bytes)) public assetHashMap; + mapping(address => bool) safeTransfer; + + event SetManagerProxyEvent(address manager); + event BindProxyEvent(uint64 toChainId, bytes targetProxyHash); + event BindAssetEvent(address fromAssetHash, uint64 toChainId, bytes targetProxyHash, uint initialAmount); + event UnlockEvent(address toAssetHash, address toAddress, uint256 amount); + event LockEvent(address fromAssetHash, address fromAddress, uint64 toChainId, bytes toAssetHash, bytes toAddress, uint256 amount); + + modifier onlyManagerContract() { + IEthCrossChainManagerProxy ieccmp = IEthCrossChainManagerProxy(managerProxyContract); + require(_msgSender() == ieccmp.getEthCrossChainManager(), "msgSender is not EthCrossChainManagerContract"); + _; + } + + function setManagerProxy(address ethCCMProxyAddr) onlyOwner public { + managerProxyContract = ethCCMProxyAddr; + emit SetManagerProxyEvent(managerProxyContract); + } + + function bindProxyHash(uint64 toChainId, bytes memory targetProxyHash) onlyOwner public returns (bool) { + proxyHashMap[toChainId] = targetProxyHash; + emit BindProxyEvent(toChainId, targetProxyHash); + return true; + } + + function bindAssetHash(address fromAssetHash, uint64 toChainId, bytes memory toAssetHash) onlyOwner public returns (bool) { + assetHashMap[fromAssetHash][toChainId] = toAssetHash; + emit BindAssetEvent(fromAssetHash, toChainId, toAssetHash, getBalanceFor(fromAssetHash)); + return true; + } + + /* @notice This function is meant to be invoked by the user, + * a certin amount teokens will be locked in the proxy contract the invoker/msg.sender immediately. + * Then the same amount of tokens will be unloked from target chain proxy contract at the target chain with chainId later. + * @param fromAssetHash The asset address in current chain, uniformly named as `fromAssetHash` + * @param toChainId The target chain id + * + * @param toAddress The address in bytes format to receive same amount of tokens in target chain + * @param amount The amount of tokens to be crossed from ethereum to the chain with chainId + */ + function lock(address fromAssetHash, uint64 toChainId, bytes memory toAddress, uint256 amount) public payable returns (bool) { + require(amount != 0, "amount cannot be zero!"); + + + require(_transferToContract(fromAssetHash, amount), "transfer asset from fromAddress to lock_proxy contract failed!"); + + bytes memory toAssetHash = assetHashMap[fromAssetHash][toChainId]; + require(toAssetHash.length != 0, "empty illegal toAssetHash"); + + TxArgs memory txArgs = TxArgs({ + toAssetHash: toAssetHash, + toAddress: toAddress, + amount: amount + }); + bytes memory txData = _serializeTxArgs(txArgs); + + IEthCrossChainManagerProxy eccmp = IEthCrossChainManagerProxy(managerProxyContract); + address eccmAddr = eccmp.getEthCrossChainManager(); + IEthCrossChainManager eccm = IEthCrossChainManager(eccmAddr); + + bytes memory toProxyHash = proxyHashMap[toChainId]; + require(toProxyHash.length != 0, "empty illegal toProxyHash"); + require(eccm.crossChain(toChainId, toProxyHash, "unlock", txData), "EthCrossChainManager crossChain executed error!"); + + emit LockEvent(fromAssetHash, _msgSender(), toChainId, toAssetHash, toAddress, amount); + + return true; + + } + + // /* @notice This function is meant to be invoked by the ETH crosschain management contract, + // * then mint a certin amount of tokens to the designated address since a certain amount + // * was burnt from the source chain invoker. + // * @param argsBs The argument bytes recevied by the ethereum lock proxy contract, need to be deserialized. + // * based on the way of serialization in the source chain proxy contract. + // * @param fromContractAddr The source chain contract address + // * @param fromChainId The source chain id + // */ + function unlock(bytes memory argsBs, bytes memory fromContractAddr, uint64 fromChainId) onlyManagerContract public returns (bool) { + TxArgs memory args = _deserializeTxArgs(argsBs); + + require(fromContractAddr.length != 0, "from proxy contract address cannot be empty"); + require(Utils.equalStorage(proxyHashMap[fromChainId], fromContractAddr), "From Proxy contract address error!"); + + require(args.toAssetHash.length != 0, "toAssetHash cannot be empty"); + address toAssetHash = Utils.bytesToAddress(args.toAssetHash); + + require(args.toAddress.length != 0, "toAddress cannot be empty"); + address toAddress = Utils.bytesToAddress(args.toAddress); + + + require(_transferFromContract(toAssetHash, toAddress, args.amount), "transfer asset from lock_proxy contract to toAddress failed!"); + + emit UnlockEvent(toAssetHash, toAddress, args.amount); + return true; + } + + function getBalanceFor(address fromAssetHash) public view returns (uint256) { + if (fromAssetHash == address(0)) { + // return address(this).balance; // this expression would result in error: Failed to decode output: Error: insufficient data for uint256 type + address selfAddr = address(this); + return selfAddr.balance; + } else { + IERC20 erc20Token = IERC20(fromAssetHash); + return erc20Token.balanceOf(address(this)); + } + } + function _transferToContract(address fromAssetHash, uint256 amount) internal returns (bool) { + if (fromAssetHash == address(0)) { + // fromAssetHash === address(0) denotes user choose to lock ether + // passively check if the received msg.value equals amount + require(msg.value != 0, "transferred ether cannot be zero!"); + require(msg.value == amount, "transferred ether is not equal to amount!"); + } else { + // make sure lockproxy contract will decline any received ether + require(msg.value == 0, "there should be no ether transfer!"); + // actively transfer amount of asset from msg.sender to lock_proxy contract + require(_transferERC20ToContract(fromAssetHash, _msgSender(), address(this), amount), "transfer erc20 asset to lock_proxy contract failed!"); + } + return true; + } + function _transferFromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) { + if (toAssetHash == address(0x0000000000000000000000000000000000000000)) { + // toAssetHash === address(0) denotes contract needs to unlock ether to toAddress + // convert toAddress from 'address' type to 'address payable' type, then actively transfer ether + address(uint160(toAddress)).transfer(amount); + } else { + // actively transfer amount of asset from lock_proxy contract to toAddress + require(_transferERC20FromContract(toAssetHash, toAddress, amount), "transfer erc20 asset from lock_proxy contract to toAddress failed!"); + } + return true; + } + + + function _transferERC20ToContract(address fromAssetHash, address fromAddress, address toAddress, uint256 amount) internal returns (bool) { + IERC20 erc20Token = IERC20(fromAssetHash); + // require(erc20Token.transferFrom(fromAddress, toAddress, amount), "trasnfer ERC20 Token failed!"); + erc20Token.safeTransferFrom(fromAddress, toAddress, amount); + return true; + } + function _transferERC20FromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) { + IERC20 erc20Token = IERC20(toAssetHash); + // require(erc20Token.transfer(toAddress, amount), "trasnfer ERC20 Token failed!"); + erc20Token.safeTransfer(toAddress, amount); + return true; + } + + function _serializeTxArgs(TxArgs memory args) internal pure returns (bytes memory) { + bytes memory buff; + buff = abi.encodePacked( + ZeroCopySink.WriteVarBytes(args.toAssetHash), + ZeroCopySink.WriteVarBytes(args.toAddress), + ZeroCopySink.WriteUint255(args.amount) + ); + return buff; + } + + function _deserializeTxArgs(bytes memory valueBs) internal pure returns (TxArgs memory) { + TxArgs memory args; + uint256 off = 0; + (args.toAssetHash, off) = ZeroCopySource.NextVarBytes(valueBs, off); + (args.toAddress, off) = ZeroCopySource.NextVarBytes(valueBs, off); + (args.amount, off) = ZeroCopySource.NextUint255(valueBs, off); + return args; + } +} \ No newline at end of file diff --git a/contracts/libs/GSN/Context.sol b/contracts/libs/GSN/Context.sol new file mode 100644 index 0000000..72b0eeb --- /dev/null +++ b/contracts/libs/GSN/Context.sol @@ -0,0 +1,28 @@ +pragma solidity ^0.5.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. + * Refer from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/GSN/Context.sol + */ +contract Context { + // Empty internal constructor, to prevent people from mistakenly deploying + // an instance of this contract, which should be used via inheritance. + constructor () internal { } + // solhint-disable-previous-line no-empty-blocks + + function _msgSender() internal view returns (address payable) { + return 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/contracts/libs/common/ZeroCopySink.sol b/contracts/libs/common/ZeroCopySink.sol new file mode 100644 index 0000000..7a9b0ae --- /dev/null +++ b/contracts/libs/common/ZeroCopySink.sol @@ -0,0 +1,185 @@ +pragma solidity ^0.5.0; + +/** + * @dev Wrappers over encoding and serialization operation into bytes from bassic types in Solidity for PolyNetwork cross chain utility. + * + * Encode basic types in Solidity into bytes easily. It's designed to be used + * for PolyNetwork cross chain application, and the encoding rules on Ethereum chain + * and the decoding rules on other chains should be consistent. Here we + * follow the underlying serialization rule with implementation found here: + * https://github.com/polynetwork/poly/blob/master/common/zero_copy_sink.go + * + * Using this library instead of the unchecked serialization method can help reduce + * the risk of serious bugs and handfule, so it's recommended to use it. + * + * Please note that risk can be minimized, yet not eliminated. + */ +library ZeroCopySink { + /* @notice Convert boolean value into bytes + * @param b The boolean value + * @return Converted bytes array + */ + function WriteBool(bool b) internal pure returns (bytes memory) { + bytes memory buff; + assembly{ + buff := mload(0x40) + mstore(buff, 1) + switch iszero(b) + case 1 { + mstore(add(buff, 0x20), shl(248, 0x00)) + // mstore8(add(buff, 0x20), 0x00) + } + default { + mstore(add(buff, 0x20), shl(248, 0x01)) + // mstore8(add(buff, 0x20), 0x01) + } + mstore(0x40, add(buff, 0x21)) + } + return buff; + } + + /* @notice Convert byte value into bytes + * @param b The byte value + * @return Converted bytes array + */ + function WriteByte(byte b) internal pure returns (bytes memory) { + return WriteUint8(uint8(b)); + } + + /* @notice Convert uint8 value into bytes + * @param v The uint8 value + * @return Converted bytes array + */ + function WriteUint8(uint8 v) internal pure returns (bytes memory) { + bytes memory buff; + assembly{ + buff := mload(0x40) + mstore(buff, 1) + mstore(add(buff, 0x20), shl(248, v)) + // mstore(add(buff, 0x20), byte(0x1f, v)) + mstore(0x40, add(buff, 0x21)) + } + return buff; + } + + /* @notice Convert uint16 value into bytes + * @param v The uint16 value + * @return Converted bytes array + */ + function WriteUint16(uint16 v) internal pure returns (bytes memory) { + bytes memory buff; + + assembly{ + buff := mload(0x40) + let byteLen := 0x02 + mstore(buff, byteLen) + for { + let mindex := 0x00 + let vindex := 0x1f + } lt(mindex, byteLen) { + mindex := add(mindex, 0x01) + vindex := sub(vindex, 0x01) + }{ + mstore8(add(add(buff, 0x20), mindex), byte(vindex, v)) + } + mstore(0x40, add(buff, 0x22)) + } + return buff; + } + + /* @notice Convert uint32 value into bytes + * @param v The uint32 value + * @return Converted bytes array + */ + function WriteUint32(uint32 v) internal pure returns(bytes memory) { + bytes memory buff; + assembly{ + buff := mload(0x40) + let byteLen := 0x04 + mstore(buff, byteLen) + for { + let mindex := 0x00 + let vindex := 0x1f + } lt(mindex, byteLen) { + mindex := add(mindex, 0x01) + vindex := sub(vindex, 0x01) + }{ + mstore8(add(add(buff, 0x20), mindex), byte(vindex, v)) + } + mstore(0x40, add(buff, 0x24)) + } + return buff; + } + + /* @notice Convert uint64 value into bytes + * @param v The uint64 value + * @return Converted bytes array + */ + function WriteUint64(uint64 v) internal pure returns(bytes memory) { + bytes memory buff; + + assembly{ + buff := mload(0x40) + let byteLen := 0x08 + mstore(buff, byteLen) + for { + let mindex := 0x00 + let vindex := 0x1f + } lt(mindex, byteLen) { + mindex := add(mindex, 0x01) + vindex := sub(vindex, 0x01) + }{ + mstore8(add(add(buff, 0x20), mindex), byte(vindex, v)) + } + mstore(0x40, add(buff, 0x28)) + } + return buff; + } + + /* @notice Convert limited uint256 value into bytes + * @param v The uint256 value + * @return Converted bytes array + */ + function WriteUint255(uint256 v) internal pure returns (bytes memory) { + require(v <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds uint255 range"); + bytes memory buff; + + assembly{ + buff := mload(0x40) + let byteLen := 0x20 + mstore(buff, byteLen) + for { + let mindex := 0x00 + let vindex := 0x1f + } lt(mindex, byteLen) { + mindex := add(mindex, 0x01) + vindex := sub(vindex, 0x01) + }{ + mstore8(add(add(buff, 0x20), mindex), byte(vindex, v)) + } + mstore(0x40, add(buff, 0x40)) + } + return buff; + } + + /* @notice Encode bytes format data into bytes + * @param data The bytes array data + * @return Encoded bytes array + */ + function WriteVarBytes(bytes memory data) internal pure returns (bytes memory) { + uint64 l = uint64(data.length); + return abi.encodePacked(WriteVarUint(l), data); + } + + function WriteVarUint(uint64 v) internal pure returns (bytes memory) { + if (v < 0xFD){ + return WriteUint8(uint8(v)); + } else if (v <= 0xFFFF) { + return abi.encodePacked(WriteByte(0xFD), WriteUint16(uint16(v))); + } else if (v <= 0xFFFFFFFF) { + return abi.encodePacked(WriteByte(0xFE), WriteUint32(uint32(v))); + } else { + return abi.encodePacked(WriteByte(0xFF), WriteUint64(uint64(v))); + } + } +} \ No newline at end of file diff --git a/contracts/libs/common/ZeroCopySource.sol b/contracts/libs/common/ZeroCopySource.sol new file mode 100644 index 0000000..c113dd4 --- /dev/null +++ b/contracts/libs/common/ZeroCopySource.sol @@ -0,0 +1,293 @@ +pragma solidity ^0.5.0; + +/** + * @dev Wrappers over decoding and deserialization operation from bytes into bassic types in Solidity for PolyNetwork cross chain utility. + * + * Decode into basic types in Solidity from bytes easily. It's designed to be used + * for PolyNetwork cross chain application, and the decoding rules on Ethereum chain + * and the encoding rule on other chains should be consistent, and . Here we + * follow the underlying deserialization rule with implementation found here: + * https://github.com/polynetwork/poly/blob/master/common/zero_copy_source.go + * + * Using this library instead of the unchecked serialization method can help reduce + * the risk of serious bugs and handfule, so it's recommended to use it. + * + * Please note that risk can be minimized, yet not eliminated. + */ +library ZeroCopySource { + /* @notice Read next byte as boolean type starting at offset from buff + * @param buff Source bytes array + * @param offset The position from where we read the boolean value + * @return The the read boolean value and new offset + */ + function NextBool(bytes memory buff, uint256 offset) internal pure returns(bool, uint256) { + require(offset + 1 <= buff.length && offset < offset + 1, "Offset exceeds limit"); + // byte === bytes1 + byte v; + assembly{ + v := mload(add(add(buff, 0x20), offset)) + } + bool value; + if (v == 0x01) { + value = true; + } else if (v == 0x00) { + value = false; + } else { + revert("NextBool value error"); + } + return (value, offset + 1); + } + + /* @notice Read next byte starting at offset from buff + * @param buff Source bytes array + * @param offset The position from where we read the byte value + * @return The read byte value and new offset + */ + function NextByte(bytes memory buff, uint256 offset) internal pure returns (byte, uint256) { + require(offset + 1 <= buff.length && offset < offset + 1, "NextByte, Offset exceeds maximum"); + byte v; + assembly{ + v := mload(add(add(buff, 0x20), offset)) + } + return (v, offset + 1); + } + + /* @notice Read next byte as uint8 starting at offset from buff + * @param buff Source bytes array + * @param offset The position from where we read the byte value + * @return The read uint8 value and new offset + */ + function NextUint8(bytes memory buff, uint256 offset) internal pure returns (uint8, uint256) { + require(offset + 1 <= buff.length && offset < offset + 1, "NextUint8, Offset exceeds maximum"); + uint8 v; + assembly{ + let tmpbytes := mload(0x40) + let bvalue := mload(add(add(buff, 0x20), offset)) + mstore8(tmpbytes, byte(0, bvalue)) + mstore(0x40, add(tmpbytes, 0x01)) + v := mload(sub(tmpbytes, 0x1f)) + } + return (v, offset + 1); + } + + /* @notice Read next two bytes as uint16 type starting from offset + * @param buff Source bytes array + * @param offset The position from where we read the uint16 value + * @return The read uint16 value and updated offset + */ + function NextUint16(bytes memory buff, uint256 offset) internal pure returns (uint16, uint256) { + require(offset + 2 <= buff.length && offset < offset + 2, "NextUint16, offset exceeds maximum"); + + uint16 v; + assembly { + let tmpbytes := mload(0x40) + let bvalue := mload(add(add(buff, 0x20), offset)) + mstore8(tmpbytes, byte(0x01, bvalue)) + mstore8(add(tmpbytes, 0x01), byte(0, bvalue)) + mstore(0x40, add(tmpbytes, 0x02)) + v := mload(sub(tmpbytes, 0x1e)) + } + return (v, offset + 2); + } + + + /* @notice Read next four bytes as uint32 type starting from offset + * @param buff Source bytes array + * @param offset The position from where we read the uint32 value + * @return The read uint32 value and updated offset + */ + function NextUint32(bytes memory buff, uint256 offset) internal pure returns (uint32, uint256) { + require(offset + 4 <= buff.length && offset < offset + 4, "NextUint32, offset exceeds maximum"); + uint32 v; + assembly { + let tmpbytes := mload(0x40) + let byteLen := 0x04 + for { + let tindex := 0x00 + let bindex := sub(byteLen, 0x01) + let bvalue := mload(add(add(buff, 0x20), offset)) + } lt(tindex, byteLen) { + tindex := add(tindex, 0x01) + bindex := sub(bindex, 0x01) + }{ + mstore8(add(tmpbytes, tindex), byte(bindex, bvalue)) + } + mstore(0x40, add(tmpbytes, byteLen)) + v := mload(sub(tmpbytes, sub(0x20, byteLen))) + } + return (v, offset + 4); + } + + /* @notice Read next eight bytes as uint64 type starting from offset + * @param buff Source bytes array + * @param offset The position from where we read the uint64 value + * @return The read uint64 value and updated offset + */ + function NextUint64(bytes memory buff, uint256 offset) internal pure returns (uint64, uint256) { + require(offset + 8 <= buff.length && offset < offset + 8, "NextUint64, offset exceeds maximum"); + uint64 v; + assembly { + let tmpbytes := mload(0x40) + let byteLen := 0x08 + for { + let tindex := 0x00 + let bindex := sub(byteLen, 0x01) + let bvalue := mload(add(add(buff, 0x20), offset)) + } lt(tindex, byteLen) { + tindex := add(tindex, 0x01) + bindex := sub(bindex, 0x01) + }{ + mstore8(add(tmpbytes, tindex), byte(bindex, bvalue)) + } + mstore(0x40, add(tmpbytes, byteLen)) + v := mload(sub(tmpbytes, sub(0x20, byteLen))) + } + return (v, offset + 8); + } + + /* @notice Read next 32 bytes as uint256 type starting from offset, + there are limits considering the numerical limits in multi-chain + * @param buff Source bytes array + * @param offset The position from where we read the uint256 value + * @return The read uint256 value and updated offset + */ + function NextUint255(bytes memory buff, uint256 offset) internal pure returns (uint256, uint256) { + require(offset + 32 <= buff.length && offset < offset + 32, "NextUint255, offset exceeds maximum"); + uint256 v; + assembly { + let tmpbytes := mload(0x40) + let byteLen := 0x20 + for { + let tindex := 0x00 + let bindex := sub(byteLen, 0x01) + let bvalue := mload(add(add(buff, 0x20), offset)) + } lt(tindex, byteLen) { + tindex := add(tindex, 0x01) + bindex := sub(bindex, 0x01) + }{ + mstore8(add(tmpbytes, tindex), byte(bindex, bvalue)) + } + mstore(0x40, add(tmpbytes, byteLen)) + v := mload(tmpbytes) + } + require(v <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range"); + return (v, offset + 32); + } + /* @notice Read next variable bytes starting from offset, + the decoding rule coming from multi-chain + * @param buff Source bytes array + * @param offset The position from where we read the bytes value + * @return The read variable bytes array value and updated offset + */ + function NextVarBytes(bytes memory buff, uint256 offset) internal pure returns(bytes memory, uint256) { + uint len; + (len, offset) = NextVarUint(buff, offset); + require(offset + len <= buff.length && offset < offset + len, "NextVarBytes, offset exceeds maximum"); + bytes memory tempBytes; + assembly{ + switch iszero(len) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(len, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, len) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(buff, lengthmod), mul(0x20, iszero(lengthmod))), offset) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, len) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return (tempBytes, offset + len); + } + /* @notice Read next 32 bytes starting from offset, + * @param buff Source bytes array + * @param offset The position from where we read the bytes value + * @return The read bytes32 value and updated offset + */ + function NextHash(bytes memory buff, uint256 offset) internal pure returns (bytes32 , uint256) { + require(offset + 32 <= buff.length && offset < offset + 32, "NextHash, offset exceeds maximum"); + bytes32 v; + assembly { + v := mload(add(buff, add(offset, 0x20))) + } + return (v, offset + 32); + } + + /* @notice Read next 20 bytes starting from offset, + * @param buff Source bytes array + * @param offset The position from where we read the bytes value + * @return The read bytes20 value and updated offset + */ + function NextBytes20(bytes memory buff, uint256 offset) internal pure returns (bytes20 , uint256) { + require(offset + 20 <= buff.length && offset < offset + 20, "NextBytes20, offset exceeds maximum"); + bytes20 v; + assembly { + v := mload(add(buff, add(offset, 0x20))) + } + return (v, offset + 20); + } + + function NextVarUint(bytes memory buff, uint256 offset) internal pure returns(uint, uint256) { + byte v; + (v, offset) = NextByte(buff, offset); + + uint value; + if (v == 0xFD) { + // return NextUint16(buff, offset); + (value, offset) = NextUint16(buff, offset); + require(value >= 0xFD && value <= 0xFFFF, "NextUint16, value outside range"); + return (value, offset); + } else if (v == 0xFE) { + // return NextUint32(buff, offset); + (value, offset) = NextUint32(buff, offset); + require(value > 0xFFFF && value <= 0xFFFFFFFF, "NextVarUint, value outside range"); + return (value, offset); + } else if (v == 0xFF) { + // return NextUint64(buff, offset); + (value, offset) = NextUint64(buff, offset); + require(value > 0xFFFFFFFF, "NextVarUint, value outside range"); + return (value, offset); + } else{ + // return (uint8(v), offset); + value = uint8(v); + require(value < 0xFD, "NextVarUint, value outside range"); + return (value, offset); + } + } +} \ No newline at end of file diff --git a/contracts/libs/lifecycle/Pausable.sol b/contracts/libs/lifecycle/Pausable.sol new file mode 100644 index 0000000..a5218ed --- /dev/null +++ b/contracts/libs/lifecycle/Pausable.sol @@ -0,0 +1,72 @@ +pragma solidity ^0.5.0; + +import "../GSN/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 a pauser (`account`). + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by a pauser (`account`). + */ + event Unpaused(address account); + + bool private _paused; + + /** + * @dev Initializes the contract in unpaused state. + */ + constructor () 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. + */ + modifier whenNotPaused() { + require(!_paused, "Pausable: paused"); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + */ + modifier whenPaused() { + require(_paused, "Pausable: not paused"); + _; + } + + /** + * @dev Called to pause, triggers stopped state. + */ + function _pause() internal whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Called to unpause, returns to normal state. + */ + function _unpause() internal whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} diff --git a/contracts/libs/math/SafeMath.sol b/contracts/libs/math/SafeMath.sol new file mode 100644 index 0000000..96b76f0 --- /dev/null +++ b/contracts/libs/math/SafeMath.sol @@ -0,0 +1,156 @@ +pragma solidity ^0.5.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @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) { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @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 sub(a, b, "SafeMath: subtraction overflow"); + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + * + * _Available since v2.4.0._ + */ + function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b <= a, errorMessage); + uint256 c = a - b; + + return c; + } + + /** + * @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) { + // 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 0; + } + + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts 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) internal pure returns (uint256) { + return div(a, b, "SafeMath: division by zero"); + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts 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. + * + * _Available since v2.4.0._ + */ + function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b != 0, errorMessage); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts 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 mod(a, b, "SafeMath: modulo by zero"); + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts with custom message 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. + * + * _Available since v2.4.0._ + */ + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b != 0, errorMessage); + return a % b; + } +} diff --git a/contracts/libs/ownership/Ownable.sol b/contracts/libs/ownership/Ownable.sol new file mode 100644 index 0000000..0d971e8 --- /dev/null +++ b/contracts/libs/ownership/Ownable.sol @@ -0,0 +1,77 @@ +pragma solidity ^0.5.0; + +import "../GSN/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. + * + * 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. + */ + constructor () 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(isOwner(), "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Returns true if the caller is the current owner. + */ + function isOwner() public view returns (bool) { + return _msgSender() == _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 { + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + */ + function _transferOwnership(address newOwner) internal { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + emit OwnershipTransferred(_owner, newOwner); + _owner = newOwner; + } +} diff --git a/contracts/libs/token/ERC20/ERC20.sol b/contracts/libs/token/ERC20/ERC20.sol new file mode 100644 index 0000000..a3a0535 --- /dev/null +++ b/contracts/libs/token/ERC20/ERC20.sol @@ -0,0 +1,231 @@ +pragma solidity ^0.5.0; + +import "./../../GSN/Context.sol"; +import "./IERC20.sol"; +import "./../../math/SafeMath.sol"; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20Mintable}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of ERC20 applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + * Refer from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol + */ +contract ERC20 is Context, IERC20 { + using SafeMath for uint256; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowances; + + uint256 private _totalSupply; + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for `sender`'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer(address sender, address recipient, uint256 amount) internal { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal { + require(account != address(0), "ERC20: mint to the zero address"); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal { + require(account != address(0), "ERC20: burn from the zero address"); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. + * + * This is internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal { + 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); + } + + /** + * @dev Destroys `amount` tokens from `account`.`amount` is then deducted + * from the caller's allowance. + * + * See {_burn} and {_approve}. + */ + function _burnFrom(address account, uint256 amount) internal { + _burn(account, amount); + _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); + } +} diff --git a/contracts/libs/token/ERC20/ERC20Detailed.sol b/contracts/libs/token/ERC20/ERC20Detailed.sol new file mode 100644 index 0000000..ed20649 --- /dev/null +++ b/contracts/libs/token/ERC20/ERC20Detailed.sol @@ -0,0 +1,55 @@ +pragma solidity ^0.5.0; + +import "./IERC20.sol"; + +/** + * @dev Optional functions from the ERC20 standard. + * Refer from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20Detailed.sol + */ +contract ERC20Detailed is IERC20 { + string private _name; + string private _symbol; + uint8 private _decimals; + + /** + * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of + * these values are immutable: they can only be set once during + * construction. + */ + constructor (string memory name, string memory symbol, uint8 decimals) public { + _name = name; + _symbol = symbol; + _decimals = decimals; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } +} diff --git a/contracts/libs/token/ERC20/ERC20Extended.sol b/contracts/libs/token/ERC20/ERC20Extended.sol new file mode 100644 index 0000000..fac040e --- /dev/null +++ b/contracts/libs/token/ERC20/ERC20Extended.sol @@ -0,0 +1,65 @@ +pragma solidity ^0.5.0; + +import "./ERC20Detailed.sol"; +import "./../../GSN/Context.sol"; +import "./ERC20.sol"; +import "./../../../core/cross_chain_manager/interface/IEthCrossChainManagerProxy.sol"; + +contract ERC20Extended is Context, ERC20, ERC20Detailed { + + address public managerProxyContract; // here managerContract should only be set as the ETH cross chain managing contract address + address public operator; // operator should be the address who deploys this contract, and responsible for 'setManager' and 'bindContractAddrWithChainId' + mapping(uint64 => bytes) public bondAssetHashes; + + event BindAssetHash(uint64 chainId, bytes contractAddr); + event SetManagerProxyEvent(address managerContract); + + + modifier onlyManagerContract() { + IEthCrossChainManagerProxy ieccmp = IEthCrossChainManagerProxy(managerProxyContract); + require(_msgSender() == ieccmp.getEthCrossChainManager(), "msgSender is not EthCrossChainManagerContract"); + _; + } + + modifier onlyOperator() { + require(_msgSender() == operator) ; + _; + } + + /* @notice Mint amount of tokens to the account + * @param account The account which will receive the minted tokens + * @param amount The amount of tokens to be minted + */ + function mint(address account, uint256 amount) public onlyManagerContract returns (bool) { + _mint(account, amount); + return true; + } + + /* @notice Burn amount of tokens from the msg.sender's balance + * @param amount The amount of tokens to be burnt + */ + function burn(uint256 amount) public returns (bool) { + _burn(_msgSender(), amount); + return true; + } + + /* @notice Set the ETH cross chain contract as the manager such that the ETH cross chain contract + * will be able to mint tokens to the designated account after a certain amount of tokens + * are locked in the source chain + * @param ethCrossChainContractAddr The ETH cross chain management contract address + */ + function setManagerProxy(address ethCrossChainManagerProxyAddr) onlyOperator public { + managerProxyContract = ethCrossChainManagerProxyAddr; + emit SetManagerProxyEvent(managerProxyContract); + } + + /* @notice Bind the target chain with the target chain id + * @param chainId The target chain id + * @param contractAddr The specific contract address in bytes format in the target chain + */ + function bindAssetHash(uint64 chainId, bytes memory contractAddr) onlyOperator public { + require(chainId != 0, "chainId illegal!"); + bondAssetHashes[chainId] = contractAddr; + emit BindAssetHash(chainId, contractAddr); + } +} \ No newline at end of file diff --git a/contracts/libs/token/ERC20/IERC20.sol b/contracts/libs/token/ERC20/IERC20.sol new file mode 100644 index 0000000..bf5245f --- /dev/null +++ b/contracts/libs/token/ERC20/IERC20.sol @@ -0,0 +1,76 @@ +pragma solidity ^0.5.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. Does not include + * the optional functions; to access them see {ERC20Detailed}. + */ +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); +} diff --git a/contracts/libs/token/ERC20/SafeERC20.sol b/contracts/libs/token/ERC20/SafeERC20.sol new file mode 100644 index 0000000..238e246 --- /dev/null +++ b/contracts/libs/token/ERC20/SafeERC20.sol @@ -0,0 +1,74 @@ +pragma solidity ^0.5.0; + +import "./IERC20.sol"; +import "../../math/SafeMath.sol"; +import "../../utils/Utils.sol"; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + + function safeTransfer(IERC20 token, address to, uint256 value) internal { + callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + function safeApprove(IERC20 token, address spender, uint256 value) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + // solhint-disable-next-line max-line-length + require((value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).add(value); + callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).sub(value); + callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. + + // A Solidity high level call has three parts: + // 1. The target address is checked to verify it contains contract code + // 2. The call itself is made, and success asserted + // 3. The return value is decoded, which in turn checks the size of the returned data. + // solhint-disable-next-line max-line-length + require(Utils.isContract(address(token)), "SafeERC20: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = address(token).call(data); + require(success, "SafeERC20: low-level call failed"); + + if (returndata.length > 0) { // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} \ No newline at end of file diff --git a/contracts/libs/utils/Encoder.sol b/contracts/libs/utils/Encoder.sol new file mode 100644 index 0000000..224da89 --- /dev/null +++ b/contracts/libs/utils/Encoder.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; + +contract AbiEncoder { + function encodeWhiteList(address _contract, bytes[] memory _methods) public pure returns(bytes memory) { + return abi.encode(_contract,_methods); + } +} \ No newline at end of file diff --git a/contracts/libs/utils/ReentrancyGuard.sol b/contracts/libs/utils/ReentrancyGuard.sol new file mode 100644 index 0000000..f977bec --- /dev/null +++ b/contracts/libs/utils/ReentrancyGuard.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.5.0; + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + * + * _Since v2.5.0:_ this module is now much more gas efficient, given net gas + * metering changes introduced in the Istanbul hardfork. + */ +contract ReentrancyGuard { + bool private _notEntered; + + constructor () internal { + // Storing an initial non-zero value makes deployment a bit more + // expensive, but in exchange the refund on every call to nonReentrant + // will be lower in amount. Since refunds are capped to a percetange of + // the total transaction's gas, it is best to keep them low in cases + // like this one, to increase the likelihood of the full refund coming + // into effect. + _notEntered = true; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and make it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + // On the first call to nonReentrant, _notEntered will be true + require(_notEntered, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _notEntered = false; + + _; + + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _notEntered = true; + } +} \ No newline at end of file diff --git a/contracts/libs/utils/Utils.sol b/contracts/libs/utils/Utils.sol new file mode 100644 index 0000000..16367c2 --- /dev/null +++ b/contracts/libs/utils/Utils.sol @@ -0,0 +1,311 @@ +pragma solidity ^0.5.0; + + +library Utils { + + /* @notice Convert the bytes array to bytes32 type, the bytes array length must be 32 + * @param _bs Source bytes array + * @return bytes32 + */ + function bytesToBytes32(bytes memory _bs) internal pure returns (bytes32 value) { + require(_bs.length == 32, "bytes length is not 32."); + assembly { + // load 32 bytes from memory starting from position _bs + 0x20 since the first 0x20 bytes stores _bs length + value := mload(add(_bs, 0x20)) + } + } + + /* @notice Convert bytes to uint256 + * @param _b Source bytes should have length of 32 + * @return uint256 + */ + function bytesToUint256(bytes memory _bs) internal pure returns (uint256 value) { + require(_bs.length == 32, "bytes length is not 32."); + assembly { + // load 32 bytes from memory starting from position _bs + 32 + value := mload(add(_bs, 0x20)) + } + require(value <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range"); + } + + /* @notice Convert uint256 to bytes + * @param _b uint256 that needs to be converted + * @return bytes + */ + function uint256ToBytes(uint256 _value) internal pure returns (bytes memory bs) { + require(_value <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range"); + assembly { + // Get a location of some free memory and store it in result as + // Solidity does for memory variables. + bs := mload(0x40) + // Put 0x20 at the first word, the length of bytes for uint256 value + mstore(bs, 0x20) + //In the next word, put value in bytes format to the next 32 bytes + mstore(add(bs, 0x20), _value) + // Update the free-memory pointer by padding our last write location to 32 bytes + mstore(0x40, add(bs, 0x40)) + } + } + + /* @notice Convert bytes to address + * @param _bs Source bytes: bytes length must be 20 + * @return Converted address from source bytes + */ + function bytesToAddress(bytes memory _bs) internal pure returns (address addr) + { + require(_bs.length == 20, "bytes length does not match address"); + assembly { + // for _bs, first word store _bs.length, second word store _bs.value + // load 32 bytes from mem[_bs+20], convert it into Uint160, meaning we take last 20 bytes as addr (address). + addr := mload(add(_bs, 0x14)) + } + + } + + /* @notice Convert address to bytes + * @param _addr Address need to be converted + * @return Converted bytes from address + */ + function addressToBytes(address _addr) internal pure returns (bytes memory bs){ + assembly { + // Get a location of some free memory and store it in result as + // Solidity does for memory variables. + bs := mload(0x40) + // Put 20 (address byte length) at the first word, the length of bytes for uint256 value + mstore(bs, 0x14) + // logical shift left _a by 12 bytes, change _a from right-aligned to left-aligned + mstore(add(bs, 0x20), shl(96, _addr)) + // Update the free-memory pointer by padding our last write location to 32 bytes + mstore(0x40, add(bs, 0x40)) + } + } + + /* @notice Do hash leaf as the multi-chain does + * @param _data Data in bytes format + * @return Hashed value in bytes32 format + */ + function hashLeaf(bytes memory _data) internal pure returns (bytes32 result) { + result = sha256(abi.encodePacked(byte(0x0), _data)); + } + + /* @notice Do hash children as the multi-chain does + * @param _l Left node + * @param _r Right node + * @return Hashed value in bytes32 format + */ + function hashChildren(bytes32 _l, bytes32 _r) internal pure returns (bytes32 result) { + result = sha256(abi.encodePacked(bytes1(0x01), _l, _r)); + } + + /* @notice Compare if two bytes are equal, which are in storage and memory, seperately + Refer from https://github.com/summa-tx/bitcoin-spv/blob/master/solidity/contracts/BytesLib.sol#L368 + * @param _preBytes The bytes stored in storage + * @param _postBytes The bytes stored in memory + * @return Bool type indicating if they are equal + */ + function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { + bool success = true; + + assembly { + // we know _preBytes_offset is 0 + let fslot := sload(_preBytes_slot) + // Arrays of 31 bytes or less have an even value in their slot, + // while longer arrays have an odd value. The actual length is + // the slot divided by two for odd values, and the lowest order + // byte divided by two for even values. + // If the slot is even, bitwise and the slot with 255 and divide by + // two to get the length. If the slot is odd, bitwise and the slot + // with -1 and divide by two. + let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) + let mlength := mload(_postBytes) + + // if lengths don't match the arrays are not equal + switch eq(slength, mlength) + case 1 { + // fslot can contain both the length and contents of the array + // if slength < 32 bytes so let's prepare for that + // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage + // slength != 0 + if iszero(iszero(slength)) { + switch lt(slength, 32) + case 1 { + // blank the last byte which is the length + fslot := mul(div(fslot, 0x100), 0x100) + + if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { + // unsuccess: + success := 0 + } + } + default { + // cb is a circuit breaker in the for loop since there's + // no said feature for inline assembly loops + // cb = 1 - don't breaker + // cb = 0 - break + let cb := 1 + + // get the keccak hash to get the contents of the array + mstore(0x0, _preBytes_slot) + let sc := keccak256(0x0, 0x20) + + let mc := add(_postBytes, 0x20) + let end := add(mc, mlength) + + // the next line is the loop condition: + // while(uint(mc < end) + cb == 2) + for {} eq(add(lt(mc, end), cb), 2) { + sc := add(sc, 1) + mc := add(mc, 0x20) + } { + if iszero(eq(sload(sc), mload(mc))) { + // unsuccess: + success := 0 + cb := 0 + } + } + } + } + } + default { + // unsuccess: + success := 0 + } + } + + return success; + } + + /* @notice Slice the _bytes from _start index till the result has length of _length + Refer from https://github.com/summa-tx/bitcoin-spv/blob/master/solidity/contracts/BytesLib.sol#L246 + * @param _bytes The original bytes needs to be sliced + * @param _start The index of _bytes for the start of sliced bytes + * @param _length The index of _bytes for the end of sliced bytes + * @return The sliced bytes + */ + function slice( + bytes memory _bytes, + uint _start, + uint _length + ) + internal + pure + returns (bytes memory) + { + require(_bytes.length >= (_start + _length)); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + // lengthmod <= _length % 32 + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; + } + /* @notice Check if the elements number of _signers within _keepers array is no less than _m + * @param _keepers The array consists of serveral address + * @param _signers Some specific addresses to be looked into + * @param _m The number requirement paramter + * @return True means containment, false meansdo do not contain. + */ + function containMAddresses(address[] memory _keepers, address[] memory _signers, uint _m) internal pure returns (bool){ + uint m = 0; + for(uint i = 0; i < _signers.length; i++){ + for (uint j = 0; j < _keepers.length; j++) { + if (_signers[i] == _keepers[j]) { + m++; + // delete _keepers[j]; + _keepers[j] = 0x7777777777777777777777777777777777777777; + } + } + } + return m >= _m; + } + + /* @notice TODO + * @param key + * @return + */ + function compressMCPubKey(bytes memory key) internal pure returns (bytes memory newkey) { + require(key.length >= 67, "key lenggh is too short"); + newkey = slice(key, 0, 35); + if (uint8(key[66]) % 2 == 0){ + newkey[2] = byte(0x02); + } else { + newkey[2] = byte(0x03); + } + return newkey; + } + + /** + * @dev Returns true if `account` is a contract. + * Refer from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L18 + * + * This test is non-exhaustive, and there may be false-negatives: during the + * execution of a contract's constructor, its address will be reported as + * not containing a contract. + * + * IMPORTANT: It is unsafe to assume that an address for which this + * function returns false is an externally-owned account (EOA) and not a + * contract. + */ + function isContract(address account) internal view returns (bool) { + // This method relies in extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + // According to EIP-1052, 0x0 is the value returned for not-yet created accounts + // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned + // for accounts without code, i.e. `keccak256('')` + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + // solhint-disable-next-line no-inline-assembly + assembly { codehash := extcodehash(account) } + return (codehash != 0x0 && codehash != accountHash); + } +} \ No newline at end of file diff --git a/importingAsset.md b/importingAsset.md new file mode 100644 index 0000000..67c05f5 --- /dev/null +++ b/importingAsset.md @@ -0,0 +1,103 @@ +