Transaction Hash:
Block:
13991619 at Jan-12-2022 03:54:00 PM +UTC
Transaction Fee:
0.025176006834799067 ETH
$64.14
Gas Used:
146,341 Gas / 172.036591487 Gwei
Emitted Events:
40 |
EthVault.0xa866edf1861d39a9973d7892f3869e1bb4b76bc00d216872c1dd2bc547f2da2e( 0xa866edf1861d39a9973d7892f3869e1bb4b76bc00d216872c1dd2bc547f2da2e, 00000000000000000000000000000000000000000000000000000000000000e0, 0000000000000000000000000000000000000000000000000000000000000120, 0000000000000000000000000000000000000000000000000000000000000160, 00000000000000000000000000000000000000000000000000000000000001a0, 00000000000000000000000000000000000000000000000000000000000001e0, 0000000000000000000000000000000000000000000000000000000000000240, 00000000000000000000000000000000000000000000000000000000000002c0, 0000000000000000000000000000000000000000000000000000000000000006, 4b4c4159544e0000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000014, 0edd98bfcf021468d305fff872b9bb9f6406e1d4000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000014, 0777eff009d0770eff7bafc1c0ba7da9ab0c8a95000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000014, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000002, 50f408f4b0fb17bf4f5143de4bd95802410d00422f008e9deef06fb101a0f060, 7625e8dd92c4c4382992618aacc5e59ee25027430563ca8d1f81428fa7b76ec4, 0000000000000000000000000000000000000000000000000000000000000003, 00000000000000000000000000000000000000000000000017e0b6f9c1333500, 0000000000000000000000000000000000000000000000000000000000000012, 00000000000000000000000000000000000000000000000000000000000105e1, 0000000000000000000000000000000000000000000000000000000000000000 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x0777efF0...9aB0C8A95 | 0.028356923147187467 Eth | 1.748933164607187467 Eth | 1.72057624146 | ||
0x1Bf68A9d...293cb489a | (Orbit Chain: ETH Vault) | 38,715.092744106025615325 Eth | 38,713.372167864565615325 Eth | 1.72057624146 | |
0x829BD824...93333A830
Miner
| (F2Pool Old) | 3,036.524587486825848261 Eth | 3,036.525026509825848261 Eth | 0.000439023 | |
0xdc5432C5...d2B0Bc522 |
5.682431327450945056 Eth
Nonce: 37836
|
5.657255320616145989 Eth
Nonce: 37837
| 0.025176006834799067 |
Execution Trace
EthVault.2ac5ab1b( )
EthVaultImpl.withdraw( )
-
Null: 0x000...002.1bf68a9d( )
-
Null: 0x000...002.1bf68a9d( )
-
Null: 0x000...002.b5680a55( )
-
Null: 0x000...002.1bf68a9d( )
-
Null: 0x000...002.b5680a55( )
-
Null: 0x000...001.bac14b09( )
-
Null: 0x000...001.bac14b09( )
-
Null: 0x000...001.bac14b09( )
-
Null: 0x000...001.bac14b09( )
-
Null: 0x000...001.bac14b09( )
-
Null: 0x000...001.bac14b09( )
- ETH 1.72057624146
0x0777eff009d0770eff7bafc1c0ba7da9ab0c8a95.CALL( )
-
withdraw[EthVaultImpl (ln:456)]
getChainId[EthVaultImpl (ln:470)]
sha256[EthVaultImpl (ln:245)]
getChainId[EthVaultImpl (ln:472)]
sha256[EthVaultImpl (ln:245)]
sha256[EthVaultImpl (ln:474)]
getChainId[EthVaultImpl (ln:475)]
sha256[EthVaultImpl (ln:245)]
sha256[EthVaultImpl (ln:478)]
_validate[EthVaultImpl (ln:483)]
ecrecover[EthVaultImpl (ln:556)]
withdraw[EthVaultImpl (ln:489)]
_transferToken[EthVaultImpl (ln:492)]
value[EthVaultImpl (ln:580)]
safeTransfer[EthVaultImpl (ln:584)]
isContract[EthVaultImpl (ln:495)]
callReceiver[EthVaultImpl (ln:496)]
encodeWithSignature[LibCallBridgeReceiver (ln:142)]
encodeWithSignature[LibCallBridgeReceiver (ln:144)]
gas[LibCallBridgeReceiver (ln:147)]
call[LibCallBridgeReceiver (ln:149)]
decode[LibCallBridgeReceiver (ln:155)]
BridgeReceiverResult[EthVaultImpl (ln:497)]
Withdraw[EthVaultImpl (ln:500)]
File 1 of 2: EthVault
File 2 of 2: EthVaultImpl
{"EthVault.impl.sol":{"content":"pragma solidity ^0.5.0;\n\nimport \"./SafeMath.sol\";\nimport \"./EthVault.sol\";\n\ninterface IERC20 {\n function transfer(address to, uint256 value) external returns (bool);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function transferFrom(address from, address to, uint256 value) external returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address who) external view returns (uint256);\n\n function allowance(address owner, address spender) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\ncontract TIERC20 {\n function transfer(address to, uint value) public;\n function transferFrom(address from, address to, uint value) public;\n\n function balanceOf(address who) public view returns (uint);\n function allowance(address owner, address spender) public view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\ncontract EthVaultImpl is EthVault, SafeMath{\n event Deposit(string fromChain, string toChain, address fromAddr, bytes toAddr, address token, uint8 decimal, uint amount, uint depositId, uint block);\n event Withdraw(address hubContract, string fromChain, string toChain, bytes fromAddr, bytes toAddr, bytes token, bytes32[] bytes32s, uint[] uints);\n\n modifier onlyActivated {\n require(isActivated);\n _;\n }\n\n constructor(address[] memory _owner) public EthVault(_owner, _owner.length, address(0), address(0)) {\n }\n\n function getVersion() public pure returns(string memory){\n return \"1028\";\n }\n\n function changeActivate(bool activate) public onlyWallet {\n isActivated = activate;\n }\n\n function setTetherAddress(address tether) public onlyWallet {\n tetherAddress = tether;\n }\n\n function getChainId(string memory _chain) public view returns(bytes32){\n return sha256(abi.encodePacked(address(this), _chain));\n }\n\n function setValidChain(string memory _chain, bool valid) public onlyWallet {\n isValidChain[getChainId(_chain)] = valid;\n }\n\n function deposit(string memory toChain, bytes memory toAddr) payable public onlyActivated {\n require(isValidChain[getChainId(toChain)]);\n require(msg.value \u003e 0);\n\n depositCount = depositCount + 1;\n emit Deposit(chain, toChain, msg.sender, toAddr, address(0), 18, msg.value, depositCount, block.number);\n }\n\n function depositToken(address token, string memory toChain, bytes memory toAddr, uint amount) public onlyActivated{\n require(isValidChain[getChainId(toChain)]);\n require(token != address(0));\n require(amount \u003e 0);\n\n uint8 decimal = 0;\n if(token == tetherAddress){\n TIERC20(token).transferFrom(msg.sender, address(this), amount);\n decimal = TIERC20(token).decimals();\n }else{\n if(!IERC20(token).transferFrom(msg.sender, address(this), amount)) revert();\n decimal = IERC20(token).decimals();\n }\n \n require(decimal \u003e 0);\n\n depositCount = depositCount + 1;\n emit Deposit(chain, toChain, msg.sender, toAddr, token, decimal, amount, depositCount, block.number);\n }\n\n // Fix Data Info\n ///@param bytes32s [0]:govId, [1]:txHash\n ///@param uints [0]:amount, [1]:decimals\n function withdraw(\n address hubContract,\n string memory fromChain,\n bytes memory fromAddr,\n bytes memory toAddr,\n bytes memory token,\n bytes32[] memory bytes32s,\n uint[] memory uints,\n uint8[] memory v,\n bytes32[] memory r,\n bytes32[] memory s\n ) public onlyActivated {\n require(bytes32s.length \u003e= 1);\n require(bytes32s[0] == sha256(abi.encodePacked(hubContract, chain, address(this))));\n require(uints.length \u003e= 2);\n require(isValidChain[getChainId(fromChain)]);\n\n bytes32 whash = sha256(abi.encodePacked(hubContract, fromChain, chain, fromAddr, toAddr, token, bytes32s, uints));\n\n require(!isUsedWithdrawal[whash]);\n isUsedWithdrawal[whash] = true;\n\n uint validatorCount = _validate(whash, v, r, s);\n require(validatorCount \u003e= required);\n\n address payable _toAddr = bytesToAddress(toAddr);\n address tokenAddress = bytesToAddress(token);\n if(tokenAddress == address(0)){\n if(!_toAddr.send(uints[0])) revert();\n }else{\n if(tokenAddress == tetherAddress){\n TIERC20(tokenAddress).transfer(_toAddr, uints[0]);\n }\n else{\n if(!IERC20(tokenAddress).transfer(_toAddr, uints[0])) revert();\n }\n }\n\n emit Withdraw(hubContract, fromChain, chain, fromAddr, toAddr, token, bytes32s, uints);\n }\n\n function _validate(bytes32 whash, uint8[] memory v, bytes32[] memory r, bytes32[] memory s) private view returns(uint){\n uint validatorCount = 0;\n address[] memory vaList = new address[](owners.length);\n\n uint i=0;\n uint j=0;\n\n for(i; i\u003cv.length; i++){\n address va = ecrecover(whash,v[i],r[i],s[i]);\n if(isOwner[va]){\n for(j=0; j\u003cvalidatorCount; j++){\n require(vaList[j] != va);\n }\n\n vaList[validatorCount] = va;\n validatorCount += 1;\n }\n }\n\n return validatorCount;\n }\n\n function bytesToAddress(bytes memory bys) private pure returns (address payable addr) {\n assembly {\n addr := mload(add(bys,20))\n }\n }\n\n function () payable external{\n }\n}\n"},"EthVault.sol":{"content":"pragma solidity ^0.5.0;\n\nimport \"./MultiSigWallet.sol\";\n\ncontract EthVault is MultiSigWallet{\n string public constant chain = \"ETH\";\n\n bool public isActivated = true;\n\n address payable public implementation;\n address public tetherAddress;\n\n uint public depositCount = 0;\n\n mapping(bytes32 =\u003e bool) public isUsedWithdrawal;\n\n mapping(bytes32 =\u003e address) public tokenAddr;\n mapping(address =\u003e bytes32) public tokenSummaries;\n\n mapping(bytes32 =\u003e bool) public isValidChain;\n\n constructor(address[] memory _owners, uint _required, address payable _implementation, address _tetherAddress) MultiSigWallet(_owners, _required) public {\n implementation = _implementation;\n tetherAddress = _tetherAddress;\n\n // klaytn valid chain default setting\n isValidChain[sha256(abi.encodePacked(address(this), \"KLAYTN\"))] = true;\n }\n\n function _setImplementation(address payable _newImp) public onlyWallet {\n require(implementation != _newImp);\n implementation = _newImp;\n\n }\n\n function () payable external {\n address impl = implementation;\n require(impl != address(0));\n assembly {\n let ptr := mload(0x40)\n calldatacopy(ptr, 0, calldatasize)\n let result := delegatecall(gas, impl, ptr, calldatasize, 0, 0)\n let size := returndatasize\n returndatacopy(ptr, 0, size)\n\n switch result\n case 0 { revert(ptr, size) }\n default { return(ptr, size) }\n }\n }\n}\n"},"MultiSigWallet.sol":{"content":"pragma solidity ^0.5.0;\n\n/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.\n/// @author Stefan George - \[email protected]\u003e\ncontract MultiSigWallet {\n\n uint constant public MAX_OWNER_COUNT = 50;\n\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n mapping (uint =\u003e Transaction) public transactions;\n mapping (uint =\u003e mapping (address =\u003e bool)) public confirmations;\n mapping (address =\u003e bool) public isOwner;\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n struct Transaction {\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n modifier onlyWallet() {\n if (msg.sender != address(this))\n revert(\"Unauthorized.\");\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n if (isOwner[owner])\n revert(\"Unauthorized.\");\n _;\n }\n\n modifier ownerExists(address owner) {\n if (!isOwner[owner])\n revert(\"Unauthorized.\");\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n if (transactions[transactionId].destination == address(0))\n revert(\"Existed transaction id.\");\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n if (!confirmations[transactionId][owner])\n revert(\"Not confirmed transaction.\");\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n if (confirmations[transactionId][owner])\n revert(\"Confirmed transaction.\");\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n if (transactions[transactionId].executed)\n revert(\"Executed transaction.\");\n _;\n }\n\n modifier notNull(address _address) {\n if (_address == address(0))\n revert(\"Address is null\");\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n if ( ownerCount \u003e MAX_OWNER_COUNT\n || _required \u003e ownerCount\n || _required == 0\n || ownerCount == 0)\n revert(\"Invalid requirement\");\n _;\n }\n\n /// @dev Fallback function allows to deposit ether.\n function()\n external\n payable\n {\n if (msg.value \u003e 0)\n emit Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n constructor(address[] memory _owners, uint _required)\n public\n validRequirement(_owners.length, _required)\n {\n for (uint i=0; i\u003c_owners.length; i++) {\n if (isOwner[_owners[i]] || _owners[i] == address(0))\n revert(\"Invalid owner\");\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n }\n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(address owner)\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n emit OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner)\n public\n onlyWallet\n ownerExists(owner)\n {\n isOwner[owner] = false;\n for (uint i=0; i\u003cowners.length - 1; i++)\n if (owners[i] == owner) {\n owners[i] = owners[owners.length - 1];\n break;\n }\n owners.length -= 1;\n if (required \u003e owners.length)\n changeRequirement(owners.length);\n emit OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param owner Address of new owner.\n function replaceOwner(address owner, address newOwner)\n public\n onlyWallet\n ownerExists(owner)\n ownerDoesNotExist(newOwner)\n {\n for (uint i=0; i\u003cowners.length; i++)\n if (owners[i] == owner) {\n owners[i] = newOwner;\n break;\n }\n isOwner[owner] = false;\n isOwner[newOwner] = true;\n emit OwnerRemoval(owner);\n emit OwnerAddition(newOwner);\n }\n\n /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.\n /// @param _required Number of required confirmations.\n function changeRequirement(uint _required)\n public\n onlyWallet\n validRequirement(owners.length, _required)\n {\n required = _required;\n emit RequirementChange(_required);\n }\n\n /// @dev Allows an owner to submit and confirm a transaction.\n /// @param destination Transaction target address.\n /// @param value Transaction ether value.\n /// @param data Transaction data payload.\n /// @return Returns transaction ID.\n function submitTransaction(address destination, uint value, bytes memory data)\n public\n returns (uint transactionId)\n {\n transactionId = addTransaction(destination, value, data);\n confirmTransaction(transactionId);\n }\n\n /// @dev Allows an owner to confirm a transaction.\n /// @param transactionId Transaction ID.\n function confirmTransaction(uint transactionId)\n public\n ownerExists(msg.sender)\n transactionExists(transactionId)\n notConfirmed(transactionId, msg.sender)\n {\n confirmations[transactionId][msg.sender] = true;\n emit Confirmation(msg.sender, transactionId);\n executeTransaction(transactionId);\n }\n\n /// @dev Allows an owner to revoke a confirmation for a transaction.\n /// @param transactionId Transaction ID.\n function revokeConfirmation(uint transactionId)\n public\n ownerExists(msg.sender)\n confirmed(transactionId, msg.sender)\n notExecuted(transactionId)\n {\n confirmations[transactionId][msg.sender] = false;\n emit Revocation(msg.sender, transactionId);\n }\n\n /// @dev Allows anyone to execute a confirmed transaction.\n /// @param transactionId Transaction ID.\n function executeTransaction(uint transactionId)\n public\n notExecuted(transactionId)\n {\n if (isConfirmed(transactionId)) {\n Transaction storage txn = transactions[transactionId];\n txn.executed = true;\n (bool result, ) = txn.destination.call.value(txn.value)(txn.data);\n if (result)\n emit Execution(transactionId);\n else {\n emit ExecutionFailure(transactionId);\n txn.executed = false;\n }\n }\n }\n\n /// @dev Returns the confirmation status of a transaction.\n /// @param transactionId Transaction ID.\n /// @return Confirmation status.\n function isConfirmed(uint transactionId)\n public\n view\n returns (bool)\n {\n uint count = 0;\n for (uint i=0; i\u003cowners.length; i++) {\n if (confirmations[transactionId][owners[i]])\n count += 1;\n if (count == required)\n return true;\n }\n }\n\n /*\n * Internal functions\n */\n /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.\n /// @param destination Transaction target address.\n /// @param value Transaction ether value.\n /// @param data Transaction data payload.\n /// @return Returns transaction ID.\n function addTransaction(address destination, uint value, bytes memory data)\n public\n notNull(destination)\n returns (uint transactionId)\n {\n transactionId = transactionCount;\n transactions[transactionId] = Transaction({\n destination: destination,\n value: value,\n data: data,\n executed: false\n });\n transactionCount += 1;\n emit Submission(transactionId);\n }\n\n /*\n * Web3 call functions\n */\n /// @dev Returns number of confirmations of a transaction.\n /// @param transactionId Transaction ID.\n /// @return Number of confirmations.\n function getConfirmationCount(uint transactionId)\n public\n view\n returns (uint count)\n {\n for (uint i=0; i\u003cowners.length; i++)\n if (confirmations[transactionId][owners[i]])\n count += 1;\n }\n\n /// @dev Returns total number of transactions after filers are applied.\n /// @param pending Include pending transactions.\n /// @param executed Include executed transactions.\n /// @return Total number of transactions after filters are applied.\n function getTransactionCount(bool pending, bool executed)\n public\n view\n returns (uint count)\n {\n for (uint i=0; i\u003ctransactionCount; i++)\n if ( pending \u0026\u0026 !transactions[i].executed\n || executed \u0026\u0026 transactions[i].executed)\n count += 1;\n }\n\n /// @dev Returns list of owners.\n /// @return List of owner addresses.\n function getOwners()\n public\n view\n returns (address[] memory)\n {\n return owners;\n }\n\n /// @dev Returns array with owner addresses, which confirmed transaction.\n /// @param transactionId Transaction ID.\n /// @return Returns array of owner addresses.\n function getConfirmations(uint transactionId)\n public\n view\n returns (address[] memory _confirmations)\n {\n address[] memory confirmationsTemp = new address[](owners.length);\n uint count = 0;\n uint i;\n for (i=0; i\u003cowners.length; i++)\n if (confirmations[transactionId][owners[i]]) {\n confirmationsTemp[count] = owners[i];\n count += 1;\n }\n _confirmations = new address[](count);\n for (i=0; i\u003ccount; i++)\n _confirmations[i] = confirmationsTemp[i];\n }\n\n /// @dev Returns list of transaction IDs in defined range.\n /// @param from Index start position of transaction array.\n /// @param to Index end position of transaction array.\n /// @param pending Include pending transactions.\n /// @param executed Include executed transactions.\n /// @return Returns array of transaction IDs.\n function getTransactionIds(uint from, uint to, bool pending, bool executed)\n public\n view\n returns (uint[] memory _transactionIds)\n {\n uint[] memory transactionIdsTemp = new uint[](transactionCount);\n uint count = 0;\n uint i;\n for (i=0; i\u003ctransactionCount; i++)\n if ( pending \u0026\u0026 !transactions[i].executed\n || executed \u0026\u0026 transactions[i].executed)\n {\n transactionIdsTemp[count] = i;\n count += 1;\n }\n _transactionIds = new uint[](to - from);\n for (i=from; i\u003cto; i++)\n _transactionIds[i - from] = transactionIdsTemp[i];\n }\n}\n"},"SafeMath.sol":{"content":"pragma solidity ^0.5.0;\n\ncontract SafeMath {\n function safeMul(uint a, uint b) internal pure returns(uint) {\n uint c = a * b;\n assertion(a == 0 || c / a == b);\n return c;\n }\n\n function safeSub(uint a, uint b) internal pure returns(uint) {\n assertion(b \u003c= a);\n return a - b;\n }\n\n function safeAdd(uint a, uint b) internal pure returns(uint) {\n uint c = a + b;\n assertion(c \u003e= a \u0026\u0026 c \u003e= b);\n return c;\n }\n\n function safeDiv(uint a, uint b) internal pure returns(uint) {\n require(b != 0, \u0027Divide by zero\u0027);\n\n return a / b;\n }\n\n function safeCeil(uint a, uint b) internal pure returns (uint) {\n require(b \u003e 0);\n\n uint v = a / b;\n\n if(v * b == a) return v;\n\n return v + 1; // b cannot be 1, so v \u003c= a / 2\n }\n\n function assertion(bool flag) internal pure {\n if (!flag) revert(\u0027Assertion fail.\u0027);\n }\n}\n"}}
File 2 of 2: EthVaultImpl
pragma solidity 0.5.0; library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } library Address { function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } } interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); function decimals() external view returns (uint8); } library SafeERC20 { using SafeMath for uint256; using Address for address; 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 { 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, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function callOptionalReturn(IERC20 token, bytes memory data) private { require(address(token).isContract(), "SafeERC20: call to non-contract"); (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } interface IERC721 { function balanceOf(address owner) external view returns (uint256 balance); function ownerOf(uint256 tokenId) external view returns (address owner); function safeTransferFrom(address from, address to, uint256 tokenId) external; function transferFrom(address from, address to, uint256 tokenId) external; function approve(address to, uint256 tokenId) external; function getApproved(uint256 tokenId) external view returns (address operator); function setApprovalForAll(address operator, bool _approved) external; function isApprovedForAll(address owner, address operator) external view returns (bool); function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; } interface IFarm { function deposit(uint amount) external; function withdrawAll() external; function withdraw(address toAddr, uint amount) external; } interface OrbitBridgeReceiver { function onTokenBridgeReceived(address _token, uint256 _value, bytes calldata _data) external returns(uint); function onNFTBridgeReceived(address _token, uint256 _tokenId, bytes calldata _data) external returns(uint); } library LibCallBridgeReceiver { function callReceiver(bool isFungible, uint gasLimitForBridgeReceiver, address tokenAddress, uint256 _int, bytes memory data, address toAddr) internal returns (bool){ bool result; bytes memory callbytes; bytes memory returnbytes; if (isFungible) { callbytes = abi.encodeWithSignature("onTokenBridgeReceived(address,uint256,bytes)", tokenAddress, _int, data); } else { callbytes = abi.encodeWithSignature("onNFTBridgeReceived(address,uint256,bytes)", tokenAddress, _int, data); } if (gasLimitForBridgeReceiver > 0) { (result, returnbytes) = toAddr.call.gas(gasLimitForBridgeReceiver)(callbytes); } else { (result, returnbytes) = toAddr.call(callbytes); } if(!result){ return false; } else { (uint flag) = abi.decode(returnbytes, (uint)); return flag > 0; } } } contract EthVaultStorage { ///////////////////////////////////////////////////////////////////////// // MultiSigWallet.sol uint constant public MAX_OWNER_COUNT = 50; mapping (uint => Transaction) public transactions; mapping (uint => mapping (address => bool)) public confirmations; mapping (address => bool) public isOwner; address[] public owners; uint public required; uint public transactionCount; struct Transaction { address destination; uint value; bytes data; bool executed; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // EthVault.sol string public constant chain = "ETH"; bool public isActivated = true; address payable public implementation; address public tetherAddress; uint public depositCount = 0; mapping(bytes32 => bool) public isUsedWithdrawal; mapping(bytes32 => address) public tokenAddr; mapping(address => bytes32) public tokenSummaries; mapping(bytes32 => bool) public isValidChain; ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // EthVault.impl.sol uint public bridgingFee = 0; address payable public feeGovernance; mapping(address => bool) public silentTokenList; mapping(address => address payable) public farms; uint public taxRate; // 0.01% interval address public taxReceiver; uint public gasLimitForBridgeReceiver; address public policyAdmin; mapping(bytes32 => uint256) public chainFee; mapping(bytes32 => uint256) public chainFeeWithData; mapping(bytes32 => uint256) public chainUintsLength; mapping(bytes32 => uint256) public chainAddressLength; ///////////////////////////////////////////////////////////////////////// } contract EthVaultImpl is EthVaultStorage { using SafeERC20 for IERC20; using SafeMath for uint; event Deposit(string toChain, address fromAddr, bytes toAddr, address token, uint8 decimal, uint amount, uint depositId, bytes data); event DepositNFT(string toChain, address fromAddr, bytes toAddr, address token, uint tokenId, uint amount, uint depositId, bytes data); event Withdraw(string fromChain, bytes fromAddr, bytes toAddr, bytes token, bytes32[] bytes32s, uint[] uints, bytes data); event WithdrawNFT(string fromChain, bytes fromAddr, bytes toAddr, bytes token, bytes32[] bytes32s, uint[] uints, bytes data); event BridgeReceiverResult(bool success, bytes fromAddress, address tokenAddress, bytes data); modifier onlyActivated { require(isActivated); _; } modifier onlyWallet { require(msg.sender == address(this)); _; } modifier onlyPolicyAdmin { require(msg.sender == policyAdmin); _; } constructor() public payable { } function getVersion() public pure returns(string memory){ return "EthVault20210817A"; } function getChainId(string memory _chain) public view returns(bytes32){ return sha256(abi.encodePacked(address(this), _chain)); } function setValidChain(string memory _chain, bool valid, uint fromAddrLen, uint uintsLen) public onlyWallet { bytes32 chainId = getChainId(_chain); require(chainId != getChainId(chain)); isValidChain[chainId] = valid; if(valid){ chainAddressLength[chainId] = fromAddrLen; chainUintsLength[chainId] = uintsLen; } else{ chainAddressLength[chainId] = 0; chainUintsLength[chainId] = 0; } } function setTaxParams(uint _taxRate, address _taxReceiver) public onlyWallet { require(_taxRate < 10000); require(_taxReceiver != address(0)); taxRate = _taxRate; taxReceiver = _taxReceiver; } function setPolicyAdmin(address _policyAdmin) public onlyWallet { require(_policyAdmin != address(0)); policyAdmin = _policyAdmin; } function changeActivate(bool activate) public onlyPolicyAdmin { isActivated = activate; } function setSilentToken(address token, bool v) public onlyPolicyAdmin { require(token != address(0)); silentTokenList[token] = v; } function setFeeGovernance(address payable _feeGovernance) public onlyWallet { require(_feeGovernance != address(0)); feeGovernance = _feeGovernance; } function setChainFee(string memory chainSymbol, uint256 _fee, uint256 _feeWithData) public onlyPolicyAdmin { bytes32 chainId = getChainId(chainSymbol); require(isValidChain[chainId]); chainFee[chainId] = _fee; chainFeeWithData[chainId] = _feeWithData; } function setGasLimitForBridgeReceiver(uint256 _gasLimitForBridgeReceiver) public onlyPolicyAdmin { gasLimitForBridgeReceiver = _gasLimitForBridgeReceiver; } function addFarm(address token, address payable proxy) public onlyWallet { require(farms[token] == address(0)); uint amount; if(token == address(0)){ amount = address(this).balance; } else{ amount = IERC20(token).balanceOf(address(this)); } _transferToken(token, proxy, amount); IFarm(proxy).deposit(amount); farms[token] = proxy; } function removeFarm(address token, address payable newProxy) public onlyWallet { require(farms[token] != address(0)); IFarm(farms[token]).withdrawAll(); if(newProxy != address(0)){ uint amount; if(token == address(0)){ amount = address(this).balance; } else{ amount = IERC20(token).balanceOf(address(this)); } _transferToken(token, newProxy, amount); IFarm(newProxy).deposit(amount); } farms[token] = newProxy; } function deposit(string memory toChain, bytes memory toAddr) payable public { uint256 fee = chainFee[getChainId(toChain)]; if(fee != 0){ require(msg.value > fee); _transferToken(address(0), feeGovernance, fee); } _depositToken(address(0), toChain, toAddr, (msg.value).sub(fee), ""); } function deposit(string memory toChain, bytes memory toAddr, bytes memory data) payable public { require(data.length != 0); uint256 fee = chainFeeWithData[getChainId(toChain)]; if(fee != 0){ require(msg.value > fee); _transferToken(address(0), feeGovernance, fee); } _depositToken(address(0), toChain, toAddr, (msg.value).sub(fee), data); } function depositToken(address token, string memory toChain, bytes memory toAddr, uint amount) public payable { require(token != address(0)); uint256 fee = chainFee[getChainId(toChain)]; if(fee != 0){ require(msg.value >= fee); _transferToken(address(0), feeGovernance, msg.value); } _depositToken(token, toChain, toAddr, amount, ""); } function depositToken(address token, string memory toChain, bytes memory toAddr, uint amount, bytes memory data) public payable { require(token != address(0)); require(data.length != 0); uint256 fee = chainFeeWithData[getChainId(toChain)]; if(fee != 0){ require(msg.value >= fee); _transferToken(address(0), feeGovernance, msg.value); } _depositToken(token, toChain, toAddr, amount, data); } function _depositToken(address token, string memory toChain, bytes memory toAddr, uint amount, bytes memory data) private onlyActivated { require(isValidChain[getChainId(toChain)]); require(amount != 0); require(!silentTokenList[token]); uint8 decimal; if(token == address(0)){ decimal = 18; } else{ IERC20(token).safeTransferFrom(msg.sender, address(this), amount); decimal = IERC20(token).decimals(); } require(decimal > 0); address payable farm = farms[token]; if(farm != address(0)){ _transferToken(token, farm, amount); IFarm(farm).deposit(amount); } if(taxRate > 0 && taxReceiver != address(0)){ uint tax = _payTax(token, amount, decimal); amount = amount.sub(tax); } depositCount = depositCount + 1; emit Deposit(toChain, msg.sender, toAddr, token, decimal, amount, depositCount, data); } function depositNFT(address token, string memory toChain, bytes memory toAddr, uint tokenId) public payable { uint256 fee = chainFee[getChainId(toChain)]; if(fee != 0){ require(msg.value >= fee); _transferToken(address(0), feeGovernance, msg.value); } _depositNFT(token, toChain, toAddr, tokenId, ""); } function depositNFT(address token, string memory toChain, bytes memory toAddr, uint tokenId, bytes memory data) public payable { require(data.length != 0); uint256 fee = chainFeeWithData[getChainId(toChain)]; if(fee != 0){ require(msg.value >= fee); _transferToken(address(0), feeGovernance, msg.value); } _depositNFT(token, toChain, toAddr, tokenId, data); } function _depositNFT(address token, string memory toChain, bytes memory toAddr, uint tokenId, bytes memory data) private onlyActivated { require(isValidChain[getChainId(toChain)]); require(token != address(0)); require(IERC721(token).ownerOf(tokenId) == msg.sender); require(!silentTokenList[token]); IERC721(token).transferFrom(msg.sender, address(this), tokenId); require(IERC721(token).ownerOf(tokenId) == address(this)); depositCount = depositCount + 1; emit DepositNFT(toChain, msg.sender, toAddr, token, tokenId, 1, depositCount, data); } // Fix Data Info ///@param bytes32s [0]:govId, [1]:txHash ///@param uints [0]:amount, [1]:decimal function withdraw( address hubContract, string memory fromChain, bytes memory fromAddr, address payable toAddr, address token, bytes32[] memory bytes32s, uint[] memory uints, bytes memory data, uint8[] memory v, bytes32[] memory r, bytes32[] memory s ) public onlyActivated { require(bytes32s.length == 2); require(uints.length == chainUintsLength[getChainId(fromChain)]); require(uints[1] <= 100); require(fromAddr.length == chainAddressLength[getChainId(fromChain)]); require(bytes32s[0] == sha256(abi.encodePacked(hubContract, chain, address(this)))); require(isValidChain[getChainId(fromChain)]); { bytes32 whash = sha256(abi.encodePacked(hubContract, fromChain, chain, fromAddr, toAddr, token, bytes32s, uints, data)); require(!isUsedWithdrawal[whash]); isUsedWithdrawal[whash] = true; uint validatorCount = _validate(whash, v, r, s); require(validatorCount >= required); } if(farms[token] != address(0)){ IFarm(farms[token]).withdraw(toAddr, uints[0]); } else{ _transferToken(token, toAddr, uints[0]); } if(isContract(toAddr) && data.length != 0){ bool result = LibCallBridgeReceiver.callReceiver(true, gasLimitForBridgeReceiver, token, uints[0], data, toAddr); emit BridgeReceiverResult(result, fromAddr, token, data); } emit Withdraw(fromChain, fromAddr, abi.encodePacked(toAddr), abi.encodePacked(token), bytes32s, uints, data); } // Fix Data Info ///@param bytes32s [0]:govId, [1]:txHash ///@param uints [0]:amount, [1]:tokenId function withdrawNFT( address hubContract, string memory fromChain, bytes memory fromAddr, address payable toAddr, address token, bytes32[] memory bytes32s, uint[] memory uints, bytes memory data, uint8[] memory v, bytes32[] memory r, bytes32[] memory s ) public onlyActivated { require(bytes32s.length == 2); require(uints.length == chainUintsLength[getChainId(fromChain)]); require(fromAddr.length == chainAddressLength[getChainId(fromChain)]); require(bytes32s[0] == sha256(abi.encodePacked(hubContract, chain, address(this)))); require(isValidChain[getChainId(fromChain)]); { bytes32 whash = sha256(abi.encodePacked("NFT", hubContract, fromChain, chain, fromAddr, toAddr, token, bytes32s, uints, data)); require(!isUsedWithdrawal[whash]); isUsedWithdrawal[whash] = true; uint validatorCount = _validate(whash, v, r, s); require(validatorCount >= required); } require(IERC721(token).ownerOf(uints[1]) == address(this)); IERC721(token).transferFrom(address(this), toAddr, uints[1]); require(IERC721(token).ownerOf(uints[1]) == toAddr); if(isContract(toAddr) && data.length != 0){ bool result = LibCallBridgeReceiver.callReceiver(false, gasLimitForBridgeReceiver, token, uints[1], data, toAddr); emit BridgeReceiverResult(result, fromAddr, token, data); } emit WithdrawNFT(fromChain, fromAddr, abi.encodePacked(toAddr), abi.encodePacked(token), bytes32s, uints, data); } function _validate(bytes32 whash, uint8[] memory v, bytes32[] memory r, bytes32[] memory s) private view returns(uint){ uint validatorCount = 0; address[] memory vaList = new address[](owners.length); uint i=0; uint j=0; for(i; i<v.length; i++){ address va = ecrecover(whash,v[i],r[i],s[i]); if(isOwner[va]){ for(j=0; j<validatorCount; j++){ require(vaList[j] != va); } vaList[validatorCount] = va; validatorCount += 1; } } return validatorCount; } function _payTax(address token, uint amount, uint8 decimal) private returns (uint tax) { tax = amount.mul(taxRate).div(10000); if(tax > 0){ depositCount = depositCount + 1; emit Deposit("ORBIT", msg.sender, abi.encodePacked(taxReceiver), token, decimal, tax, depositCount, ""); } } function _transferToken(address token, address payable destination, uint amount) private { if(token == address(0)){ (bool transfered,) = destination.call.value(amount)(""); require(transfered); } else{ IERC20(token).safeTransfer(destination, amount); } } function isContract(address _addr) private view returns (bool){ uint32 size; assembly { size := extcodesize(_addr) } return (size > 0); } function bytesToAddress(bytes memory bys) public pure returns (address payable addr) { assembly { addr := mload(add(bys,20)) } } function () payable external{ } }