More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Transfer | 11719459 | 1419 days ago | IN | 0 ETH | 0.00275624 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
10900642 | 1545 days ago | 0.26683024 ETH | ||||
10900642 | 1545 days ago | 0.26683024 ETH | ||||
9225590 | 1803 days ago | 0.001 ETH | ||||
9225590 | 1803 days ago | 0.001 ETH | ||||
9210096 | 1806 days ago | 0.03358888 ETH | ||||
9210096 | 1806 days ago | 0.03358888 ETH | ||||
9210033 | 1806 days ago | 0.01218586 ETH | ||||
9210033 | 1806 days ago | 0.01218586 ETH | ||||
9209526 | 1806 days ago | 0.01014606 ETH | ||||
9209526 | 1806 days ago | 0.01014606 ETH | ||||
9205597 | 1806 days ago | 0.00715469 ETH | ||||
9205597 | 1806 days ago | 0.00715469 ETH | ||||
9205563 | 1806 days ago | 0.01062331 ETH | ||||
9205563 | 1806 days ago | 0.01062331 ETH | ||||
9205550 | 1806 days ago | 0.00793001 ETH | ||||
9205550 | 1806 days ago | 0.00793001 ETH | ||||
9205533 | 1806 days ago | 0.01415672 ETH | ||||
9205533 | 1806 days ago | 0.01415672 ETH | ||||
9205517 | 1806 days ago | 0.02017341 ETH | ||||
9205517 | 1806 days ago | 0.02017341 ETH | ||||
9205505 | 1806 days ago | 0.01252464 ETH | ||||
9205505 | 1806 days ago | 0.01252464 ETH | ||||
9205499 | 1806 days ago | 0.01260684 ETH | ||||
9205499 | 1806 days ago | 0.01260684 ETH | ||||
9205436 | 1806 days ago | 0.01252462 ETH |
Loading...
Loading
Contract Name:
UniswapBridge
Compiler Version
v0.5.12+commit.7709ece9
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
constantinople EvmVersion, Apache-2.0 license
Contract Source Code (Solidity Standard Json-Input format)
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; import "../interfaces/IUniswapExchangeFactory.sol"; import "../interfaces/IUniswapExchange.sol"; import "../interfaces/IERC20Bridge.sol"; // solhint-disable space-after-comma // solhint-disable not-rely-on-time contract UniswapBridge is IERC20Bridge, IWallet { /* Mainnet addresses */ address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95; address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid // stack overflows. struct WithdrawToState { IUniswapExchange exchange; uint256 fromTokenBalance; IEtherToken weth; } // solhint-disable no-empty-blocks /// @dev Payable fallback to receive ETH from uniswap. function () external payable {} /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of /// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` /// token encoded in the bridge data. /// @param toTokenAddress The token to buy and transfer to `to`. /// @param to The recipient of the bought tokens. /// @param amount Minimum amount of `toTokenAddress` tokens to buy. /// @param bridgeData The abi-encoded "from" token address. /// @return success The magic bytes if successful. function bridgeTransferFrom( address toTokenAddress, address /* from */, address to, uint256 amount, bytes calldata bridgeData ) external returns (bytes4 success) { // State memory object to avoid stack overflows. WithdrawToState memory state; // Decode the bridge data to get the `fromTokenAddress`. (address fromTokenAddress) = abi.decode(bridgeData, (address)); // Just transfer the tokens if they're the same. if (fromTokenAddress == toTokenAddress) { LibERC20Token.transfer(fromTokenAddress, to, amount); return BRIDGE_SUCCESS; } // Get the exchange for the token pair. state.exchange = _getUniswapExchangeForTokenPair( fromTokenAddress, toTokenAddress ); // Get our balance of `fromTokenAddress` token. state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); // Get the weth contract. state.weth = getWethContract(); // Convert from WETH to a token. if (fromTokenAddress == address(state.weth)) { // Unwrap the WETH. state.weth.withdraw(state.fromTokenBalance); // Buy as much of `toTokenAddress` token with ETH as possible and // transfer it to `to`. state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)( // Minimum buy amount. amount, // Expires after this block. block.timestamp, // Recipient is `to`. to ); // Convert from a token to WETH. } else if (toTokenAddress == address(state.weth)) { // Grant the exchange an allowance. _grantExchangeAllowance(state.exchange, fromTokenAddress); // Buy as much ETH with `fromTokenAddress` token as possible. uint256 ethBought = state.exchange.tokenToEthSwapInput( // Sell all tokens we hold. state.fromTokenBalance, // Minimum buy amount. amount, // Expires after this block. block.timestamp ); // Wrap the ETH. state.weth.deposit.value(ethBought)(); // Transfer the WETH to `to`. IEtherToken(toTokenAddress).transfer(to, ethBought); // Convert from one token to another. } else { // Grant the exchange an allowance. _grantExchangeAllowance(state.exchange, fromTokenAddress); // Buy as much `toTokenAddress` token with `fromTokenAddress` token // and transfer it to `to`. state.exchange.tokenToTokenTransferInput( // Sell all tokens we hold. state.fromTokenBalance, // Minimum buy amount. amount, // No minimum intermediate ETH buy amount. 0, // Expires after this block. block.timestamp, // Recipient is `to`. to, // Convert to `toTokenAddress`. toTokenAddress ); } return BRIDGE_SUCCESS; } /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker /// and sign for itself in orders. Always succeeds. /// @return magicValue Success bytes, always. function isValidSignature( bytes32, bytes calldata ) external view returns (bytes4 magicValue) { return LEGACY_WALLET_MAGIC_VALUE; } /// @dev Overridable way to get the weth contract. /// @return token The WETH contract. function getWethContract() public view returns (IEtherToken token) { return IEtherToken(WETH_ADDRESS); } /// @dev Overridable way to get the uniswap exchange factory contract. /// @return factory The exchange factory contract. function getUniswapExchangeFactoryContract() public view returns (IUniswapExchangeFactory factory) { return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS); } /// @dev Grants an unlimited allowance to the exchange for its token /// on behalf of this contract. /// @param exchange The Uniswap token exchange. /// @param tokenAddress The token address for the exchange. function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress) private { LibERC20Token.approve(tokenAddress, address(exchange), uint256(-1)); } /// @dev Retrieves the uniswap exchange for a given token pair. /// In the case of a WETH-token exchange, this will be the non-WETH token. /// In th ecase of a token-token exchange, this will be the first token. /// @param fromTokenAddress The address of the token we are converting from. /// @param toTokenAddress The address of the token we are converting to. /// @return exchange The uniswap exchange. function _getUniswapExchangeForTokenPair( address fromTokenAddress, address toTokenAddress ) private view returns (IUniswapExchange exchange) { address exchangeTokenAddress = fromTokenAddress; // Whichever isn't WETH is the exchange token. if (fromTokenAddress == address(getWethContract())) { exchangeTokenAddress = toTokenAddress; } exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress); require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN"); return exchange; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "../archive/Ownable.sol"; import "../src/interfaces/IAssetProxy.sol"; import "../src/interfaces/IAssetProxyDispatcher.sol"; contract MixinAssetProxyDispatcher is Ownable, IAssetProxyDispatcher { // Mapping from Asset Proxy Id's to their respective Asset Proxy mapping (bytes4 => address) public assetProxies; /// @dev Registers an asset proxy to its asset proxy id. /// Once an asset proxy is registered, it cannot be unregistered. /// @param assetProxy Address of new asset proxy to register. function registerAssetProxy(address assetProxy) external onlyOwner { // Ensure that no asset proxy exists with current id. bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId(); address currentAssetProxy = assetProxies[assetProxyId]; require( currentAssetProxy == address(0), "ASSET_PROXY_ALREADY_EXISTS" ); // Add asset proxy and log registration. assetProxies[assetProxyId] = assetProxy; emit AssetProxyRegistered( assetProxyId, assetProxy ); } /// @dev Gets an asset proxy. /// @param assetProxyId Id of the asset proxy. /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. function getAssetProxy(bytes4 assetProxyId) external view returns (address) { return assetProxies[assetProxyId]; } /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws. /// @param assetData Byte array encoded for the asset. /// @param from Address to transfer token from. /// @param to Address to transfer token to. /// @param amount Amount of token to transfer. function _dispatchTransferFrom( bytes memory assetData, address from, address to, uint256 amount ) internal { // Do nothing if no amount should be transferred. if (amount > 0 && from != to) { // Ensure assetData length is valid require( assetData.length > 3, "LENGTH_GREATER_THAN_3_REQUIRED" ); // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons. bytes4 assetProxyId; assembly { assetProxyId := and(mload( add(assetData, 32)), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 ) } address assetProxy = assetProxies[assetProxyId]; // Ensure that assetProxy exists require( assetProxy != address(0), "ASSET_PROXY_DOES_NOT_EXIST" ); // We construct calldata for the `assetProxy.transferFrom` ABI. // The layout of this calldata is in the table below. // // | Area | Offset | Length | Contents | // | -------- |--------|---------|-------------------------------------------- | // | Header | 0 | 4 | function selector | // | Params | | 4 * 32 | function parameters: | // | | 4 | | 1. offset to assetData (*) | // | | 36 | | 2. from | // | | 68 | | 3. to | // | | 100 | | 4. amount | // | Data | | | assetData: | // | | 132 | 32 | assetData Length | // | | 164 | ** | assetData Contents | assembly { /////// Setup State /////// // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr). let cdStart := mload(64) // `dataAreaLength` is the total number of words needed to store `assetData` // As-per the ABI spec, this value is padded up to the nearest multiple of 32, // and includes 32-bytes for length. let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0) // `cdEnd` is the end of the calldata for `assetProxy.transferFrom`. let cdEnd := add(cdStart, add(132, dataAreaLength)) /////// Setup Header Area /////// // This area holds the 4-byte `transferFromSelector`. // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000) /////// Setup Params Area /////// // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes. // Notes: // 1. The offset to `assetData` is the length of the Params Area (128 bytes). // 2. A 20-byte mask is applied to addresses to zero-out the unused bytes. mstore(add(cdStart, 4), 128) mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) mstore(add(cdStart, 100), amount) /////// Setup Data Area /////// // This area holds `assetData`. let dataArea := add(cdStart, 132) // solhint-disable-next-line no-empty-blocks for {} lt(dataArea, cdEnd) {} { mstore(dataArea, mload(assetData)) dataArea := add(dataArea, 32) assetData := add(assetData, 32) } /////// Call `assetProxy.transferFrom` using the constructed calldata /////// let success := call( gas, // forward all gas assetProxy, // call address of asset proxy 0, // don't send any ETH cdStart, // pointer to start of input sub(cdEnd, cdStart), // length of input cdStart, // write output over input 512 // reserve 512 bytes for output ) if iszero(success) { revert(cdStart, returndatasize()) } } } } }
pragma solidity ^0.5.9; import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol"; contract Ownable is IOwnable { address public owner; constructor () public { owner = msg.sender; } modifier onlyOwner() { require( msg.sender == owner, "ONLY_CONTRACT_OWNER" ); _; } function transferOwnership(address newOwner) public onlyOwner { if (newOwner != address(0)) { owner = newOwner; } } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; contract IOwnable { /// @dev Emitted by Ownable when ownership is transferred. /// @param previousOwner The previous owner of the contract. /// @param newOwner The new owner of the contract. event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /// @dev Transfers ownership of the contract to a new address. /// @param newOwner The address that will become the owner. function transferOwnership(address newOwner) public; }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; contract IAssetProxy { /// @dev Transfers assets. Either succeeds or throws. /// @param assetData Byte array encoded for the respective asset proxy. /// @param from Address to transfer asset from. /// @param to Address to transfer asset to. /// @param amount Amount of asset to transfer. function transferFrom( bytes calldata assetData, address from, address to, uint256 amount ) external; /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() external pure returns (bytes4); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; contract IAssetProxyDispatcher { // Logs registration of new asset proxy event AssetProxyRegistered( bytes4 id, // Id of new registered AssetProxy. address assetProxy // Address of new registered AssetProxy. ); /// @dev Registers an asset proxy to its asset proxy id. /// Once an asset proxy is registered, it cannot be unregistered. /// @param assetProxy Address of new asset proxy to register. function registerAssetProxy(address assetProxy) external; /// @dev Gets an asset proxy. /// @param assetProxyId Id of the asset proxy. /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. function getAssetProxy(bytes4 assetProxyId) external view returns (address); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "../archive/Ownable.sol"; import "../src/interfaces/IAuthorizable.sol"; contract MixinAuthorizable is Ownable, IAuthorizable { /// @dev Only authorized addresses can invoke functions with this modifier. modifier onlyAuthorized { require( authorized[msg.sender], "SENDER_NOT_AUTHORIZED" ); _; } mapping (address => bool) public authorized; address[] public authorities; /// @dev Authorizes an address. /// @param target Address to authorize. function addAuthorizedAddress(address target) external onlyOwner { require( !authorized[target], "TARGET_ALREADY_AUTHORIZED" ); authorized[target] = true; authorities.push(target); emit AuthorizedAddressAdded(target, msg.sender); } /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. function removeAuthorizedAddress(address target) external onlyOwner { require( authorized[target], "TARGET_NOT_AUTHORIZED" ); delete authorized[target]; for (uint256 i = 0; i < authorities.length; i++) { if (authorities[i] == target) { authorities[i] = authorities[authorities.length - 1]; authorities.length -= 1; break; } } emit AuthorizedAddressRemoved(target, msg.sender); } /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. /// @param index Index of target in authorities array. function removeAuthorizedAddressAtIndex( address target, uint256 index ) external onlyOwner { require( authorized[target], "TARGET_NOT_AUTHORIZED" ); require( index < authorities.length, "INDEX_OUT_OF_BOUNDS" ); require( authorities[index] == target, "AUTHORIZED_ADDRESS_MISMATCH" ); delete authorized[target]; authorities[index] = authorities[authorities.length - 1]; authorities.length -= 1; emit AuthorizedAddressRemoved(target, msg.sender); } /// @dev Gets all authorized addresses. /// @return Array of authorized addresses. function getAuthorizedAddresses() external view returns (address[] memory) { return authorities; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol"; contract IAuthorizable is IOwnable { // Event logged when a new address is authorized. event AuthorizedAddressAdded( address indexed target, address indexed caller ); // Event logged when a currently authorized address is unauthorized. event AuthorizedAddressRemoved( address indexed target, address indexed caller ); /// @dev Authorizes an address. /// @param target Address to authorize. function addAuthorizedAddress(address target) external; /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. function removeAuthorizedAddress(address target) external; /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. /// @param index Index of target in authorities array. function removeAuthorizedAddressAtIndex( address target, uint256 index ) external; /// @dev Gets all authorized addresses. /// @return Array of authorized addresses. function getAuthorizedAddresses() external view returns (address[] memory); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; import "../archive/MixinAuthorizable.sol"; import "./interfaces/IAssetProxy.sol"; contract ERC1155Proxy is MixinAuthorizable, IAssetProxy { using LibBytes for bytes; using LibSafeMath for uint256; // Id of this proxy. bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)")); /// @dev Transfers batch of ERC1155 assets. Either succeeds or throws. /// @param assetData Byte array encoded with ERC1155 token address, array of ids, array of values, and callback data. /// @param from Address to transfer assets from. /// @param to Address to transfer assets to. /// @param amount Amount that will be multiplied with each element of `assetData.values` to scale the /// values that will be transferred. function transferFrom( bytes calldata assetData, address from, address to, uint256 amount ) external onlyAuthorized { // Decode params from `assetData` // solhint-disable indent ( address erc1155TokenAddress, uint256[] memory ids, uint256[] memory values, bytes memory data ) = abi.decode( assetData.sliceDestructive(4, assetData.length), (address, uint256[], uint256[], bytes) ); // solhint-enable indent // Scale values up by `amount` uint256 length = values.length; uint256[] memory scaledValues = new uint256[](length); for (uint256 i = 0; i != length; i++) { // We write the scaled values to an unused location in memory in order // to avoid copying over `ids` or `data`. This is possible if they are // identical to `values` and the offsets for each are pointing to the // same location in the ABI encoded calldata. scaledValues[i] = values[i].safeMul(amount); } // Execute `safeBatchTransferFrom` call // Either succeeds or throws IERC1155(erc1155TokenAddress).safeBatchTransferFrom( from, to, ids, scaledValues, data ); } /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() external pure returns (bytes4) { return PROXY_ID; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "./LibBytesRichErrors.sol"; import "./LibRichErrors.sol"; library LibBytes { using LibBytes for bytes; /// @dev Gets the memory address for a byte array. /// @param input Byte array to lookup. /// @return memoryAddress Memory address of byte array. This /// points to the header of the byte array which contains /// the length. function rawAddress(bytes memory input) internal pure returns (uint256 memoryAddress) { assembly { memoryAddress := input } return memoryAddress; } /// @dev Gets the memory address for the contents of a byte array. /// @param input Byte array to lookup. /// @return memoryAddress Memory address of the contents of the byte array. function contentAddress(bytes memory input) internal pure returns (uint256 memoryAddress) { assembly { memoryAddress := add(input, 32) } return memoryAddress; } /// @dev Copies `length` bytes from memory location `source` to `dest`. /// @param dest memory address to copy bytes to. /// @param source memory address to copy bytes from. /// @param length number of bytes to copy. function memCopy( uint256 dest, uint256 source, uint256 length ) internal pure { if (length < 32) { // Handle a partial word by reading destination and masking // off the bits we are interested in. // This correctly handles overlap, zero lengths and source == dest assembly { let mask := sub(exp(256, sub(32, length)), 1) let s := and(mload(source), not(mask)) let d := and(mload(dest), mask) mstore(dest, or(s, d)) } } else { // Skip the O(length) loop when source == dest. if (source == dest) { return; } // For large copies we copy whole words at a time. The final // word is aligned to the end of the range (instead of after the // previous) to handle partial words. So a copy will look like this: // // #### // #### // #### // #### // // We handle overlap in the source and destination range by // changing the copying direction. This prevents us from // overwriting parts of source that we still need to copy. // // This correctly handles source == dest // if (source > dest) { assembly { // We subtract 32 from `sEnd` and `dEnd` because it // is easier to compare with in the loop, and these // are also the addresses we need for copying the // last bytes. length := sub(length, 32) let sEnd := add(source, length) let dEnd := add(dest, length) // Remember the last 32 bytes of source // This needs to be done here and not after the loop // because we may have overwritten the last bytes in // source already due to overlap. let last := mload(sEnd) // Copy whole words front to back // Note: the first check is always true, // this could have been a do-while loop. // solhint-disable-next-line no-empty-blocks for {} lt(source, sEnd) {} { mstore(dest, mload(source)) source := add(source, 32) dest := add(dest, 32) } // Write the last 32 bytes mstore(dEnd, last) } } else { assembly { // We subtract 32 from `sEnd` and `dEnd` because those // are the starting points when copying a word at the end. length := sub(length, 32) let sEnd := add(source, length) let dEnd := add(dest, length) // Remember the first 32 bytes of source // This needs to be done here and not after the loop // because we may have overwritten the first bytes in // source already due to overlap. let first := mload(source) // Copy whole words back to front // We use a signed comparisson here to allow dEnd to become // negative (happens when source and dest < 32). Valid // addresses in local memory will never be larger than // 2**255, so they can be safely re-interpreted as signed. // Note: the first check is always true, // this could have been a do-while loop. // solhint-disable-next-line no-empty-blocks for {} slt(dest, dEnd) {} { mstore(dEnd, mload(sEnd)) sEnd := sub(sEnd, 32) dEnd := sub(dEnd, 32) } // Write the first 32 bytes mstore(dest, first) } } } } /// @dev Returns a slices from a byte array. /// @param b The byte array to take a slice from. /// @param from The starting index for the slice (inclusive). /// @param to The final index for the slice (exclusive). /// @return result The slice containing bytes at indices [from, to) function slice( bytes memory b, uint256 from, uint256 to ) internal pure returns (bytes memory result) { // Ensure that the from and to positions are valid positions for a slice within // the byte array that is being used. if (from > to) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired, from, to )); } if (to > b.length) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired, to, b.length )); } // Create a new bytes structure and copy contents result = new bytes(to - from); memCopy( result.contentAddress(), b.contentAddress() + from, result.length ); return result; } /// @dev Returns a slice from a byte array without preserving the input. /// @param b The byte array to take a slice from. Will be destroyed in the process. /// @param from The starting index for the slice (inclusive). /// @param to The final index for the slice (exclusive). /// @return result The slice containing bytes at indices [from, to) /// @dev When `from == 0`, the original array will match the slice. In other cases its state will be corrupted. function sliceDestructive( bytes memory b, uint256 from, uint256 to ) internal pure returns (bytes memory result) { // Ensure that the from and to positions are valid positions for a slice within // the byte array that is being used. if (from > to) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired, from, to )); } if (to > b.length) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired, to, b.length )); } // Create a new bytes structure around [from, to) in-place. assembly { result := add(b, from) mstore(result, sub(to, from)) } return result; } /// @dev Pops the last byte off of a byte array by modifying its length. /// @param b Byte array that will be modified. /// @return The byte that was popped off. function popLastByte(bytes memory b) internal pure returns (bytes1 result) { if (b.length == 0) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired, b.length, 0 )); } // Store last byte. result = b[b.length - 1]; assembly { // Decrement length of byte array. let newLen := sub(mload(b), 1) mstore(b, newLen) } return result; } /// @dev Tests equality of two byte arrays. /// @param lhs First byte array to compare. /// @param rhs Second byte array to compare. /// @return True if arrays are the same. False otherwise. function equals( bytes memory lhs, bytes memory rhs ) internal pure returns (bool equal) { // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare. // We early exit on unequal lengths, but keccak would also correctly // handle this. return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs); } /// @dev Reads an address from a position in a byte array. /// @param b Byte array containing an address. /// @param index Index in byte array of address. /// @return address from byte array. function readAddress( bytes memory b, uint256 index ) internal pure returns (address result) { if (b.length < index + 20) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired, b.length, index + 20 // 20 is length of address )); } // Add offset to index: // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index) index += 20; // Read address from array memory assembly { // 1. Add index to address of bytes array // 2. Load 32-byte word from memory // 3. Apply 20-byte mask to obtain address result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff) } return result; } /// @dev Writes an address into a specific position in a byte array. /// @param b Byte array to insert address into. /// @param index Index in byte array of address. /// @param input Address to put into byte array. function writeAddress( bytes memory b, uint256 index, address input ) internal pure { if (b.length < index + 20) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired, b.length, index + 20 // 20 is length of address )); } // Add offset to index: // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index) index += 20; // Store address into array memory assembly { // The address occupies 20 bytes and mstore stores 32 bytes. // First fetch the 32-byte word where we'll be storing the address, then // apply a mask so we have only the bytes in the word that the address will not occupy. // Then combine these bytes with the address and store the 32 bytes back to memory with mstore. // 1. Add index to address of bytes array // 2. Load 32-byte word from memory // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address let neighbors := and( mload(add(b, index)), 0xffffffffffffffffffffffff0000000000000000000000000000000000000000 ) // Make sure input address is clean. // (Solidity does not guarantee this) input := and(input, 0xffffffffffffffffffffffffffffffffffffffff) // Store the neighbors and address into memory mstore(add(b, index), xor(input, neighbors)) } } /// @dev Reads a bytes32 value from a position in a byte array. /// @param b Byte array containing a bytes32 value. /// @param index Index in byte array of bytes32 value. /// @return bytes32 value from byte array. function readBytes32( bytes memory b, uint256 index ) internal pure returns (bytes32 result) { if (b.length < index + 32) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired, b.length, index + 32 )); } // Arrays are prefixed by a 256 bit length parameter index += 32; // Read the bytes32 from array memory assembly { result := mload(add(b, index)) } return result; } /// @dev Writes a bytes32 into a specific position in a byte array. /// @param b Byte array to insert <input> into. /// @param index Index in byte array of <input>. /// @param input bytes32 to put into byte array. function writeBytes32( bytes memory b, uint256 index, bytes32 input ) internal pure { if (b.length < index + 32) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired, b.length, index + 32 )); } // Arrays are prefixed by a 256 bit length parameter index += 32; // Read the bytes32 from array memory assembly { mstore(add(b, index), input) } } /// @dev Reads a uint256 value from a position in a byte array. /// @param b Byte array containing a uint256 value. /// @param index Index in byte array of uint256 value. /// @return uint256 value from byte array. function readUint256( bytes memory b, uint256 index ) internal pure returns (uint256 result) { result = uint256(readBytes32(b, index)); return result; } /// @dev Writes a uint256 into a specific position in a byte array. /// @param b Byte array to insert <input> into. /// @param index Index in byte array of <input>. /// @param input uint256 to put into byte array. function writeUint256( bytes memory b, uint256 index, uint256 input ) internal pure { writeBytes32(b, index, bytes32(input)); } /// @dev Reads an unpadded bytes4 value from a position in a byte array. /// @param b Byte array containing a bytes4 value. /// @param index Index in byte array of bytes4 value. /// @return bytes4 value from byte array. function readBytes4( bytes memory b, uint256 index ) internal pure returns (bytes4 result) { if (b.length < index + 4) { LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError( LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired, b.length, index + 4 )); } // Arrays are prefixed by a 32 byte length field index += 32; // Read the bytes4 from array memory assembly { result := mload(add(b, index)) // Solidity does not require us to clean the trailing bytes. // We do it anyway result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) } return result; } /// @dev Writes a new length to a byte array. /// Decreasing length will lead to removing the corresponding lower order bytes from the byte array. /// Increasing length may lead to appending adjacent in-memory bytes to the end of the byte array. /// @param b Bytes array to write new length to. /// @param length New length of byte array. function writeLength(bytes memory b, uint256 length) internal pure { assembly { mstore(b, length) } } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; library LibBytesRichErrors { enum InvalidByteOperationErrorCodes { FromLessThanOrEqualsToRequired, ToLessThanOrEqualsLengthRequired, LengthGreaterThanZeroRequired, LengthGreaterThanOrEqualsFourRequired, LengthGreaterThanOrEqualsTwentyRequired, LengthGreaterThanOrEqualsThirtyTwoRequired, LengthGreaterThanOrEqualsNestedBytesLengthRequired, DestinationLengthGreaterThanOrEqualSourceLengthRequired } // bytes4(keccak256("InvalidByteOperationError(uint8,uint256,uint256)")) bytes4 internal constant INVALID_BYTE_OPERATION_ERROR_SELECTOR = 0x28006595; // solhint-disable func-name-mixedcase function InvalidByteOperationError( InvalidByteOperationErrorCodes errorCode, uint256 offset, uint256 required ) internal pure returns (bytes memory) { return abi.encodeWithSelector( INVALID_BYTE_OPERATION_ERROR_SELECTOR, errorCode, offset, required ); } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; library LibRichErrors { // bytes4(keccak256("Error(string)")) bytes4 internal constant STANDARD_ERROR_SELECTOR = 0x08c379a0; // solhint-disable func-name-mixedcase /// @dev ABI encode a standard, string revert error payload. /// This is the same payload that would be included by a `revert(string)` /// solidity statement. It has the function signature `Error(string)`. /// @param message The error string. /// @return The ABI encoded error. function StandardError( string memory message ) internal pure returns (bytes memory) { return abi.encodeWithSelector( STANDARD_ERROR_SELECTOR, bytes(message) ); } // solhint-enable func-name-mixedcase /// @dev Reverts an encoded rich revert reason `errorData`. /// @param errorData ABI encoded error data. function rrevert(bytes memory errorData) internal pure { assembly { revert(add(errorData, 0x20), mload(errorData)) } } }
pragma solidity ^0.5.9; import "./LibRichErrors.sol"; import "./LibSafeMathRichErrors.sol"; library LibSafeMath { function safeMul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; if (c / a != b) { LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError( LibSafeMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW, a, b )); } return c; } function safeDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError( LibSafeMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO, a, b )); } uint256 c = a / b; return c; } function safeSub(uint256 a, uint256 b) internal pure returns (uint256) { if (b > a) { LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError( LibSafeMathRichErrors.BinOpErrorCodes.SUBTRACTION_UNDERFLOW, a, b )); } return a - b; } function safeAdd(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; if (c < a) { LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError( LibSafeMathRichErrors.BinOpErrorCodes.ADDITION_OVERFLOW, a, b )); } return c; } function max256(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } function min256(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } }
pragma solidity ^0.5.9; library LibSafeMathRichErrors { // bytes4(keccak256("Uint256BinOpError(uint8,uint256,uint256)")) bytes4 internal constant UINT256_BINOP_ERROR_SELECTOR = 0xe946c1bb; // bytes4(keccak256("Uint256DowncastError(uint8,uint256)")) bytes4 internal constant UINT256_DOWNCAST_ERROR_SELECTOR = 0xc996af7b; enum BinOpErrorCodes { ADDITION_OVERFLOW, MULTIPLICATION_OVERFLOW, SUBTRACTION_UNDERFLOW, DIVISION_BY_ZERO } enum DowncastErrorCodes { VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT32, VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64, VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96 } // solhint-disable func-name-mixedcase function Uint256BinOpError( BinOpErrorCodes errorCode, uint256 a, uint256 b ) internal pure returns (bytes memory) { return abi.encodeWithSelector( UINT256_BINOP_ERROR_SELECTOR, errorCode, a, b ); } function Uint256DowncastError( DowncastErrorCodes errorCode, uint256 a ) internal pure returns (bytes memory) { return abi.encodeWithSelector( UINT256_DOWNCAST_ERROR_SELECTOR, errorCode, a ); } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; /// @title ERC-1155 Multi Token Standard /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md /// Note: The ERC-165 identifier for this interface is 0xd9b67a26. interface IERC1155 { /// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, /// including zero value transfers as well as minting or burning. /// Operator will always be msg.sender. /// Either event from address `0x0` signifies a minting operation. /// An event to address `0x0` signifies a burning or melting operation. /// The total value transferred from address 0x0 minus the total value transferred to 0x0 may /// be used by clients and exchanges to be added to the "circulating supply" for a given token ID. /// To define a token ID with no initial balance, the contract SHOULD emit the TransferSingle event /// from `0x0` to `0x0`, with the token creator as `_operator`. event TransferSingle( address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value ); /// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, /// including zero value transfers as well as minting or burning. ///Operator will always be msg.sender. /// Either event from address `0x0` signifies a minting operation. /// An event to address `0x0` signifies a burning or melting operation. /// The total value transferred from address 0x0 minus the total value transferred to 0x0 may /// be used by clients and exchanges to be added to the "circulating supply" for a given token ID. /// To define multiple token IDs with no initial balance, this SHOULD emit the TransferBatch event /// from `0x0` to `0x0`, with the token creator as `_operator`. event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /// @dev MUST emit when an approval is updated. event ApprovalForAll( address indexed owner, address indexed operator, bool approved ); /// @dev MUST emit when the URI is updated for a token ID. /// URIs are defined in RFC 3986. /// The URI MUST point a JSON file that conforms to the "ERC-1155 Metadata JSON Schema". event URI( string value, uint256 indexed id ); /// @notice Transfers value amount of an _id from the _from address to the _to address specified. /// @dev MUST emit TransferSingle event on success. /// Caller must be approved to manage the _from account's tokens (see isApprovedForAll). /// MUST throw if `_to` is the zero address. /// MUST throw if balance of sender for token `_id` is lower than the `_value` sent. /// MUST throw on any other error. /// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). /// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value /// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`. /// @param from Source address /// @param to Target address /// @param id ID of the token type /// @param value Transfer amount /// @param data Additional data with no specified format, sent in call to `_to` function safeTransferFrom( address from, address to, uint256 id, uint256 value, bytes calldata data ) external; /// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call). /// @dev MUST emit TransferBatch event on success. /// Caller must be approved to manage the _from account's tokens (see isApprovedForAll). /// MUST throw if `_to` is the zero address. /// MUST throw if length of `_ids` is not the same as length of `_values`. /// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent. /// MUST throw on any other error. /// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). /// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value /// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`. /// @param from Source addresses /// @param to Target addresses /// @param ids IDs of each token type /// @param values Transfer amounts per token type /// @param data Additional data with no specified format, sent in call to `_to` function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external; /// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens. /// @dev MUST emit the ApprovalForAll event on success. /// @param operator Address to add to the set of authorized operators /// @param approved True if the operator is approved, false to revoke approval function setApprovalForAll(address operator, bool approved) external; /// @notice Queries the approval status of an operator for a given owner. /// @param owner The owner of the Tokens /// @param operator Address of authorized operator /// @return True if the operator is approved, false if not function isApprovedForAll(address owner, address operator) external view returns (bool); /// @notice Get the balance of an account's Tokens. /// @param owner The address of the token holder /// @param id ID of the Token /// @return The _owner's balance of the Token type requested function balanceOf(address owner, uint256 id) external view returns (uint256); /// @notice Get the balance of multiple account/token pairs /// @param owners The addresses of the token holders /// @param ids ID of the Tokens /// @return The _owner's balance of the Token types requested function balanceOfBatch( address[] calldata owners, uint256[] calldata ids ) external view returns (uint256[] memory balances_); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "@0x/contracts-utils/contracts/src/Authorizable.sol"; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "./interfaces/IAssetProxy.sol"; import "./interfaces/IERC20Bridge.sol"; contract ERC20BridgeProxy is IAssetProxy, Authorizable { using LibBytes for bytes; using LibSafeMath for uint256; // @dev Id of this proxy. Also the result of a successful bridge call. // bytes4(keccak256("ERC20Bridge(address,address,bytes)")) bytes4 constant private PROXY_ID = 0xdc1600f3; /// @dev Calls a bridge contract to transfer `amount` of ERC20 from `from` /// to `to`. Asserts that the balance of `to` has increased by `amount`. /// @param assetData Abi-encoded data for this asset proxy encoded as: /// abi.encodeWithSelector( /// bytes4 PROXY_ID, /// address tokenAddress, /// address bridgeAddress, /// bytes bridgeData /// ) /// @param from Address to transfer asset from. /// @param to Address to transfer asset to. /// @param amount Amount of asset to transfer. function transferFrom( bytes calldata assetData, address from, address to, uint256 amount ) external onlyAuthorized { // Extract asset data fields. ( address tokenAddress, address bridgeAddress, bytes memory bridgeData ) = abi.decode( assetData.sliceDestructive(4, assetData.length), (address, address, bytes) ); // Remember the balance of `to` before calling the bridge. uint256 balanceBefore = balanceOf(tokenAddress, to); // Call the bridge, who should transfer `amount` of `tokenAddress` to // `to`. bytes4 success = IERC20Bridge(bridgeAddress).bridgeTransferFrom( tokenAddress, from, to, amount, bridgeData ); // Bridge must return the proxy ID to indicate success. require(success == PROXY_ID, "BRIDGE_FAILED"); // Ensure that the balance of `to` has increased by at least `amount`. require( balanceBefore.safeAdd(amount) <= balanceOf(tokenAddress, to), "BRIDGE_UNDERPAY" ); } /// @dev Gets the proxy id associated with this asset proxy. /// @return proxyId The proxy id. function getProxyId() external pure returns (bytes4 proxyId) { return PROXY_ID; } /// @dev Retrieves the balance of `owner` for this asset. /// @return balance The balance of the ERC20 token being transferred by this /// asset proxy. function balanceOf(bytes calldata assetData, address owner) external view returns (uint256 balance) { (address tokenAddress) = abi.decode( assetData.sliceDestructive(4, assetData.length), (address) ); return balanceOf(tokenAddress, owner); } /// @dev Retrieves the balance of `owner` given an ERC20 address. /// @return balance The balance of the ERC20 token for `owner`. function balanceOf(address tokenAddress, address owner) private view returns (uint256 balance) { return IERC20Token(tokenAddress).balanceOf(owner); } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "./interfaces/IAuthorizable.sol"; import "./LibAuthorizableRichErrors.sol"; import "./LibRichErrors.sol"; import "./Ownable.sol"; // solhint-disable no-empty-blocks contract Authorizable is Ownable, IAuthorizable { /// @dev Only authorized addresses can invoke functions with this modifier. modifier onlyAuthorized { _assertSenderIsAuthorized(); _; } mapping (address => bool) public authorized; address[] public authorities; /// @dev Initializes the `owner` address. constructor() public Ownable() {} /// @dev Authorizes an address. /// @param target Address to authorize. function addAuthorizedAddress(address target) external onlyOwner { _addAuthorizedAddress(target); } /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. function removeAuthorizedAddress(address target) external onlyOwner { if (!authorized[target]) { LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target)); } for (uint256 i = 0; i < authorities.length; i++) { if (authorities[i] == target) { _removeAuthorizedAddressAtIndex(target, i); break; } } } /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. /// @param index Index of target in authorities array. function removeAuthorizedAddressAtIndex( address target, uint256 index ) external onlyOwner { _removeAuthorizedAddressAtIndex(target, index); } /// @dev Gets all authorized addresses. /// @return Array of authorized addresses. function getAuthorizedAddresses() external view returns (address[] memory) { return authorities; } /// @dev Reverts if msg.sender is not authorized. function _assertSenderIsAuthorized() internal view { if (!authorized[msg.sender]) { LibRichErrors.rrevert(LibAuthorizableRichErrors.SenderNotAuthorizedError(msg.sender)); } } /// @dev Authorizes an address. /// @param target Address to authorize. function _addAuthorizedAddress(address target) internal { // Ensure that the target is not the zero address. if (target == address(0)) { LibRichErrors.rrevert(LibAuthorizableRichErrors.ZeroCantBeAuthorizedError()); } // Ensure that the target is not already authorized. if (authorized[target]) { LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetAlreadyAuthorizedError(target)); } authorized[target] = true; authorities.push(target); emit AuthorizedAddressAdded(target, msg.sender); } /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. /// @param index Index of target in authorities array. function _removeAuthorizedAddressAtIndex( address target, uint256 index ) internal { if (!authorized[target]) { LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target)); } if (index >= authorities.length) { LibRichErrors.rrevert(LibAuthorizableRichErrors.IndexOutOfBoundsError( index, authorities.length )); } if (authorities[index] != target) { LibRichErrors.rrevert(LibAuthorizableRichErrors.AuthorizedAddressMismatchError( authorities[index], target )); } delete authorized[target]; authorities[index] = authorities[authorities.length - 1]; authorities.length -= 1; emit AuthorizedAddressRemoved(target, msg.sender); } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "./IOwnable.sol"; contract IAuthorizable is IOwnable { // Event logged when a new address is authorized. event AuthorizedAddressAdded( address indexed target, address indexed caller ); // Event logged when a currently authorized address is unauthorized. event AuthorizedAddressRemoved( address indexed target, address indexed caller ); /// @dev Authorizes an address. /// @param target Address to authorize. function addAuthorizedAddress(address target) external; /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. function removeAuthorizedAddress(address target) external; /// @dev Removes authorizion of an address. /// @param target Address to remove authorization from. /// @param index Index of target in authorities array. function removeAuthorizedAddressAtIndex( address target, uint256 index ) external; /// @dev Gets all authorized addresses. /// @return Array of authorized addresses. function getAuthorizedAddresses() external view returns (address[] memory); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; library LibAuthorizableRichErrors { // bytes4(keccak256("AuthorizedAddressMismatchError(address,address)")) bytes4 internal constant AUTHORIZED_ADDRESS_MISMATCH_ERROR_SELECTOR = 0x140a84db; // bytes4(keccak256("IndexOutOfBoundsError(uint256,uint256)")) bytes4 internal constant INDEX_OUT_OF_BOUNDS_ERROR_SELECTOR = 0xe9f83771; // bytes4(keccak256("SenderNotAuthorizedError(address)")) bytes4 internal constant SENDER_NOT_AUTHORIZED_ERROR_SELECTOR = 0xb65a25b9; // bytes4(keccak256("TargetAlreadyAuthorizedError(address)")) bytes4 internal constant TARGET_ALREADY_AUTHORIZED_ERROR_SELECTOR = 0xde16f1a0; // bytes4(keccak256("TargetNotAuthorizedError(address)")) bytes4 internal constant TARGET_NOT_AUTHORIZED_ERROR_SELECTOR = 0xeb5108a2; // bytes4(keccak256("ZeroCantBeAuthorizedError()")) bytes internal constant ZERO_CANT_BE_AUTHORIZED_ERROR_BYTES = hex"57654fe4"; // solhint-disable func-name-mixedcase function AuthorizedAddressMismatchError( address authorized, address target ) internal pure returns (bytes memory) { return abi.encodeWithSelector( AUTHORIZED_ADDRESS_MISMATCH_ERROR_SELECTOR, authorized, target ); } function IndexOutOfBoundsError( uint256 index, uint256 length ) internal pure returns (bytes memory) { return abi.encodeWithSelector( INDEX_OUT_OF_BOUNDS_ERROR_SELECTOR, index, length ); } function SenderNotAuthorizedError(address sender) internal pure returns (bytes memory) { return abi.encodeWithSelector( SENDER_NOT_AUTHORIZED_ERROR_SELECTOR, sender ); } function TargetAlreadyAuthorizedError(address target) internal pure returns (bytes memory) { return abi.encodeWithSelector( TARGET_ALREADY_AUTHORIZED_ERROR_SELECTOR, target ); } function TargetNotAuthorizedError(address target) internal pure returns (bytes memory) { return abi.encodeWithSelector( TARGET_NOT_AUTHORIZED_ERROR_SELECTOR, target ); } function ZeroCantBeAuthorizedError() internal pure returns (bytes memory) { return ZERO_CANT_BE_AUTHORIZED_ERROR_BYTES; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "./interfaces/IOwnable.sol"; import "./LibOwnableRichErrors.sol"; import "./LibRichErrors.sol"; contract Ownable is IOwnable { address public owner; constructor () public { owner = msg.sender; } modifier onlyOwner() { _assertSenderIsOwner(); _; } function transferOwnership(address newOwner) public onlyOwner { if (newOwner == address(0)) { LibRichErrors.rrevert(LibOwnableRichErrors.TransferOwnerToZeroError()); } else { owner = newOwner; emit OwnershipTransferred(msg.sender, newOwner); } } function _assertSenderIsOwner() internal view { if (msg.sender != owner) { LibRichErrors.rrevert(LibOwnableRichErrors.OnlyOwnerError( msg.sender, owner )); } } }
pragma solidity ^0.5.9; library LibOwnableRichErrors { // bytes4(keccak256("OnlyOwnerError(address,address)")) bytes4 internal constant ONLY_OWNER_ERROR_SELECTOR = 0x1de45ad1; // bytes4(keccak256("TransferOwnerToZeroError()")) bytes internal constant TRANSFER_OWNER_TO_ZERO_ERROR_BYTES = hex"e69edc3e"; // solhint-disable func-name-mixedcase function OnlyOwnerError( address sender, address owner ) internal pure returns (bytes memory) { return abi.encodeWithSelector( ONLY_OWNER_ERROR_SELECTOR, sender, owner ); } function TransferOwnerToZeroError() internal pure returns (bytes memory) { return TRANSFER_OWNER_TO_ZERO_ERROR_BYTES; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; contract IERC20Token { // solhint-disable no-simple-event-func-name event Transfer( address indexed _from, address indexed _to, uint256 _value ); event Approval( address indexed _owner, address indexed _spender, uint256 _value ); /// @dev send `value` token to `to` from `msg.sender` /// @param _to The address of the recipient /// @param _value The amount of token to be transferred /// @return True if transfer was successful function transfer(address _to, uint256 _value) external returns (bool); /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` /// @param _from The address of the sender /// @param _to The address of the recipient /// @param _value The amount of token to be transferred /// @return True if transfer was successful function transferFrom( address _from, address _to, uint256 _value ) external returns (bool); /// @dev `msg.sender` approves `_spender` to spend `_value` tokens /// @param _spender The address of the account able to transfer the tokens /// @param _value The amount of wei to be approved for transfer /// @return Always true if the call has enough gas to complete execution function approve(address _spender, uint256 _value) external returns (bool); /// @dev Query total supply of token /// @return Total supply of token function totalSupply() external view returns (uint256); /// @param _owner The address from which the balance will be retrieved /// @return Balance of owner function balanceOf(address _owner) external view returns (uint256); /// @param _owner The address of the account owning tokens /// @param _spender The address of the account able to transfer the tokens /// @return Amount of remaining tokens allowed to spent function allowance(address _owner, address _spender) external view returns (uint256); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; contract IERC20Bridge { // @dev Result of a successful bridge call. bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3; /// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`. /// @param tokenAddress The address of the ERC20 token to transfer. /// @param from Address to transfer asset from. /// @param to Address to transfer asset to. /// @param amount Amount of asset to transfer. /// @param bridgeData Arbitrary asset data needed by the bridge contract. /// @return success The magic bytes `0x37708e9b` if successful. function bridgeTransferFrom( address tokenAddress, address from, address to, uint256 amount, bytes calldata bridgeData ) external returns (bytes4 success); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "../archive/MixinAuthorizable.sol"; contract ERC20Proxy is MixinAuthorizable { // Id of this proxy. bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)")); // solhint-disable-next-line payable-fallback function () external { assembly { // The first 4 bytes of calldata holds the function selector let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) // `transferFrom` will be called with the following parameters: // assetData Encoded byte array. // from Address to transfer asset from. // to Address to transfer asset to. // amount Amount of asset to transfer. // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { // To lookup a value in a mapping, we load from the storage location keccak256(k, p), // where k is the key left padded to 32 bytes and p is the storage slot let start := mload(64) mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) mstore(add(start, 32), authorized_slot) // Revert if authorized[msg.sender] == false if iszero(sload(keccak256(start, 64))) { // Revert with `Error("SENDER_NOT_AUTHORIZED")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) mstore(96, 0) revert(0, 100) } // `transferFrom`. // The function is marked `external`, so no abi decodeding is done for // us. Instead, we expect the `calldata` memory to contain the // following: // // | Area | Offset | Length | Contents | // |----------|--------|---------|-------------------------------------| // | Header | 0 | 4 | function selector | // | Params | | 4 * 32 | function parameters: | // | | 4 | | 1. offset to assetData (*) | // | | 36 | | 2. from | // | | 68 | | 3. to | // | | 100 | | 4. amount | // | Data | | | assetData: | // | | 132 | 32 | assetData Length | // | | 164 | ** | assetData Contents | // // (*): offset is computed from start of function parameters, so offset // by an additional 4 bytes in the calldata. // // (**): see table below to compute length of assetData Contents // // WARNING: The ABIv2 specification allows additional padding between // the Params and Data section. This will result in a larger // offset to assetData. // Asset data itself is encoded as follows: // // | Area | Offset | Length | Contents | // |----------|--------|---------|-------------------------------------| // | Header | 0 | 4 | function selector | // | Params | | 1 * 32 | function parameters: | // | | 4 | 12 + 20 | 1. token address | // We construct calldata for the `token.transferFrom` ABI. // The layout of this calldata is in the table below. // // | Area | Offset | Length | Contents | // |----------|--------|---------|-------------------------------------| // | Header | 0 | 4 | function selector | // | Params | | 3 * 32 | function parameters: | // | | 4 | | 1. from | // | | 36 | | 2. to | // | | 68 | | 3. amount | /////// Read token address from calldata /////// // * The token address is stored in `assetData`. // // * The "offset to assetData" is stored at offset 4 in the calldata (table 1). // [assetDataOffsetFromParams = calldataload(4)] // // * Notes that the "offset to assetData" is relative to the "Params" area of calldata; // add 4 bytes to account for the length of the "Header" area (table 1). // [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4] // // * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2). // [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36] let token := calldataload(add(calldataload(4), 40)) /////// Setup Header Area /////// // This area holds the 4-byte `transferFrom` selector. // Any trailing data in transferFromSelector will be // overwritten in the next `mstore` call. mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) /////// Setup Params Area /////// // We copy the fields `from`, `to` and `amount` in bulk // from our own calldata to the new calldata. calldatacopy(4, 36, 96) /////// Call `token.transferFrom` using the calldata /////// let success := call( gas, // forward all gas token, // call address of token contract 0, // don't send any ETH 0, // pointer to start of input 100, // length of input 0, // write output over input 32 // output size should be 32 bytes ) /////// Check return data. /////// // If there is no return data, we assume the token incorrectly // does not return a bool. In this case we expect it to revert // on failure, which was handled above. // If the token does return data, we require that it is a single // nonzero 32 bytes value. // So the transfer succeeded if the call succeeded and either // returned nothing, or returned a non-zero 32 byte value. success := and(success, or( iszero(returndatasize), and( eq(returndatasize, 32), gt(mload(0), 0) ) )) if success { return(0, 0) } // Revert with `Error("TRANSFER_FAILED")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) mstore(96, 0) revert(0, 100) } // Revert if undefined function is called revert(0, 0) } } /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() external pure returns (bytes4) { return PROXY_ID; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "../archive/MixinAuthorizable.sol"; contract ERC721Proxy is MixinAuthorizable { // Id of this proxy. bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)")); // solhint-disable-next-line payable-fallback function () external { assembly { // The first 4 bytes of calldata holds the function selector let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) // `transferFrom` will be called with the following parameters: // assetData Encoded byte array. // from Address to transfer asset from. // to Address to transfer asset to. // amount Amount of asset to transfer. // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { // To lookup a value in a mapping, we load from the storage location keccak256(k, p), // where k is the key left padded to 32 bytes and p is the storage slot let start := mload(64) mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) mstore(add(start, 32), authorized_slot) // Revert if authorized[msg.sender] == false if iszero(sload(keccak256(start, 64))) { // Revert with `Error("SENDER_NOT_AUTHORIZED")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) mstore(96, 0) revert(0, 100) } // `transferFrom`. // The function is marked `external`, so no abi decodeding is done for // us. Instead, we expect the `calldata` memory to contain the // following: // // | Area | Offset | Length | Contents | // |----------|--------|---------|-------------------------------------| // | Header | 0 | 4 | function selector | // | Params | | 4 * 32 | function parameters: | // | | 4 | | 1. offset to assetData (*) | // | | 36 | | 2. from | // | | 68 | | 3. to | // | | 100 | | 4. amount | // | Data | | | assetData: | // | | 132 | 32 | assetData Length | // | | 164 | ** | assetData Contents | // // (*): offset is computed from start of function parameters, so offset // by an additional 4 bytes in the calldata. // // (**): see table below to compute length of assetData Contents // // WARNING: The ABIv2 specification allows additional padding between // the Params and Data section. This will result in a larger // offset to assetData. // Asset data itself is encoded as follows: // // | Area | Offset | Length | Contents | // |----------|--------|---------|-------------------------------------| // | Header | 0 | 4 | function selector | // | Params | | 2 * 32 | function parameters: | // | | 4 | 12 + 20 | 1. token address | // | | 36 | | 2. tokenId | // We construct calldata for the `token.transferFrom` ABI. // The layout of this calldata is in the table below. // // | Area | Offset | Length | Contents | // |----------|--------|---------|-------------------------------------| // | Header | 0 | 4 | function selector | // | Params | | 3 * 32 | function parameters: | // | | 4 | | 1. from | // | | 36 | | 2. to | // | | 68 | | 3. tokenId | // There exists only 1 of each token. // require(amount == 1, "INVALID_AMOUNT") if sub(calldataload(100), 1) { // Revert with `Error("INVALID_AMOUNT")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000) mstore(96, 0) revert(0, 100) } /////// Setup Header Area /////// // This area holds the 4-byte `transferFrom` selector. // Any trailing data in transferFromSelector will be // overwritten in the next `mstore` call. mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) /////// Setup Params Area /////// // We copy the fields `from` and `to` in bulk // from our own calldata to the new calldata. calldatacopy(4, 36, 64) // Copy `tokenId` field from our own calldata to the new calldata. let assetDataOffset := calldataload(4) calldatacopy(68, add(assetDataOffset, 72), 32) /////// Call `token.transferFrom` using the calldata /////// let token := calldataload(add(assetDataOffset, 40)) let success := call( gas, // forward all gas token, // call address of token contract 0, // don't send any ETH 0, // pointer to start of input 100, // length of input 0, // write output to null 0 // output size is 0 bytes ) if success { return(0, 0) } // Revert with `Error("TRANSFER_FAILED")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) mstore(96, 0) revert(0, 100) } // Revert if undefined function is called revert(0, 0) } } /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() external pure returns (bytes4) { return PROXY_ID; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "../archive/MixinAssetProxyDispatcher.sol"; import "../archive/MixinAuthorizable.sol"; contract MultiAssetProxy is MixinAssetProxyDispatcher, MixinAuthorizable { // Id of this proxy. bytes4 constant internal PROXY_ID = bytes4(keccak256("MultiAsset(uint256[],bytes[])")); // solhint-disable-next-line payable-fallback function () external { // NOTE: The below assembly assumes that clients do some input validation and that the input is properly encoded according to the AbiV2 specification. // It is technically possible for inputs with very large lengths and offsets to cause overflows. However, this would make the calldata prohibitively // expensive and we therefore do not check for overflows in these scenarios. assembly { // The first 4 bytes of calldata holds the function selector let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) // `transferFrom` will be called with the following parameters: // assetData Encoded byte array. // from Address to transfer asset from. // to Address to transfer asset to. // amount Amount of asset to transfer. // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { // To lookup a value in a mapping, we load from the storage location keccak256(k, p), // where k is the key left padded to 32 bytes and p is the storage slot mstore(0, caller) mstore(32, authorized_slot) // Revert if authorized[msg.sender] == false if iszero(sload(keccak256(0, 64))) { // Revert with `Error("SENDER_NOT_AUTHORIZED")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) mstore(96, 0) revert(0, 100) } // `transferFrom`. // The function is marked `external`, so no abi decoding is done for // us. Instead, we expect the `calldata` memory to contain the // following: // // | Area | Offset | Length | Contents | // |----------|--------|---------|-------------------------------------| // | Header | 0 | 4 | function selector | // | Params | | 4 * 32 | function parameters: | // | | 4 | | 1. offset to assetData (*) | // | | 36 | | 2. from | // | | 68 | | 3. to | // | | 100 | | 4. amount | // | Data | | | assetData: | // | | 132 | 32 | assetData Length | // | | 164 | ** | assetData Contents | // // (*): offset is computed from start of function parameters, so offset // by an additional 4 bytes in the calldata. // // (**): see table below to compute length of assetData Contents // // WARNING: The ABIv2 specification allows additional padding between // the Params and Data section. This will result in a larger // offset to assetData. // Load offset to `assetData` let assetDataOffset := add(calldataload(4), 4) // Load length in bytes of `assetData` let assetDataLength := calldataload(assetDataOffset) // Asset data itself is encoded as follows: // // | Area | Offset | Length | Contents | // |----------|-------------|---------|-------------------------------------| // | Header | 0 | 4 | assetProxyId | // | Params | | 2 * 32 | function parameters: | // | | 4 | | 1. offset to amounts (*) | // | | 36 | | 2. offset to nestedAssetData (*) | // | Data | | | amounts: | // | | 68 | 32 | amounts Length | // | | 100 | a | amounts Contents | // | | | | nestedAssetData: | // | | 100 + a | 32 | nestedAssetData Length | // | | 132 + a | b | nestedAssetData Contents (offsets) | // | | 132 + a + b | | nestedAssetData[0, ..., len] | // Assert that the length of asset data: // 1. Must be at least 68 bytes (see table above) // 2. Must be a multiple of 32 (excluding the 4-byte selector) if or(lt(assetDataLength, 68), mod(sub(assetDataLength, 4), 32)) { // Revert with `Error("INVALID_ASSET_DATA_LENGTH")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000) mstore(96, 0) revert(0, 100) } // End of asset data in calldata // assetDataOffset // + 32 (assetData len) let assetDataEnd := add(assetDataOffset, add(assetDataLength, 32)) if gt(assetDataEnd, calldatasize()) { // Revert with `Error("INVALID_ASSET_DATA_END")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000) mstore(96, 0) revert(0, 100) } // In order to find the offset to `amounts`, we must add: // assetDataOffset // + 32 (assetData len) // + 4 (assetProxyId) let amountsOffset := calldataload(add(assetDataOffset, 36)) // In order to find the offset to `nestedAssetData`, we must add: // assetDataOffset // + 32 (assetData len) // + 4 (assetProxyId) // + 32 (amounts offset) let nestedAssetDataOffset := calldataload(add(assetDataOffset, 68)) // In order to find the start of the `amounts` contents, we must add: // assetDataOffset // + 32 (assetData len) // + 4 (assetProxyId) // + amountsOffset // + 32 (amounts len) let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 68)) // Load number of elements in `amounts` let amountsLen := calldataload(sub(amountsContentsStart, 32)) // In order to find the start of the `nestedAssetData` contents, we must add: // assetDataOffset // + 32 (assetData len) // + 4 (assetProxyId) // + nestedAssetDataOffset // + 32 (nestedAssetData len) let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 68)) // Load number of elements in `nestedAssetData` let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32)) // Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData` if sub(amountsLen, nestedAssetDataLen) { // Revert with `Error("LENGTH_MISMATCH")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000000f4c454e4754485f4d49534d4154434800000000000000000000000000) mstore(96, 0) revert(0, 100) } // Copy `transferFrom` selector, offset to `assetData`, `from`, and `to` from calldata to memory calldatacopy( 0, // memory can safely be overwritten from beginning 0, // start of calldata 100 // length of selector (4) and 3 params (32 * 3) ) // Overwrite existing offset to `assetData` with our own mstore(4, 128) // Load `amount` let amount := calldataload(100) // Calculate number of bytes in `amounts` contents let amountsByteLen := mul(amountsLen, 32) // Initialize `assetProxyId` and `assetProxy` to 0 let assetProxyId := 0 let assetProxy := 0 // Loop through `amounts` and `nestedAssetData`, calling `transferFrom` for each respective element for {let i := 0} lt(i, amountsByteLen) {i := add(i, 32)} { // Calculate the total amount let amountsElement := calldataload(add(amountsContentsStart, i)) let totalAmount := mul(amountsElement, amount) // Revert if `amount` != 0 and multiplication resulted in an overflow if iszero(or( iszero(amount), eq(div(totalAmount, amount), amountsElement) )) { // Revert with `Error("UINT256_OVERFLOW")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000) mstore(96, 0) revert(0, 100) } // Write `totalAmount` to memory mstore(100, totalAmount) // Load offset to `nestedAssetData[i]` let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i)) // In order to find the start of the `nestedAssetData[i]` contents, we must add: // assetDataOffset // + 32 (assetData len) // + 4 (assetProxyId) // + nestedAssetDataOffset // + 32 (nestedAssetData len) // + nestedAssetDataElementOffset // + 32 (nestedAssetDataElement len) let nestedAssetDataElementContentsStart := add( assetDataOffset, add( nestedAssetDataOffset, add(nestedAssetDataElementOffset, 100) ) ) // Load length of `nestedAssetData[i]` let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32) let nestedAssetDataElementLen := calldataload(nestedAssetDataElementLenStart) // Revert if the `nestedAssetData` does not contain a 4 byte `assetProxyId` if lt(nestedAssetDataElementLen, 4) { // Revert with `Error("LENGTH_GREATER_THAN_3_REQUIRED")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000001e4c454e4754485f475245415445525f5448414e5f335f524551554952) mstore(96, 0x4544000000000000000000000000000000000000000000000000000000000000) revert(0, 100) } // Load AssetProxy id let currentAssetProxyId := and( calldataload(nestedAssetDataElementContentsStart), 0xffffffff00000000000000000000000000000000000000000000000000000000 ) // Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId` // We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0 if sub(currentAssetProxyId, assetProxyId) { // Update `assetProxyId` assetProxyId := currentAssetProxyId // To lookup a value in a mapping, we load from the storage location keccak256(k, p), // where k is the key left padded to 32 bytes and p is the storage slot mstore(132, assetProxyId) mstore(164, assetProxies_slot) assetProxy := sload(keccak256(132, 64)) } // Revert if AssetProxy with given id does not exist if iszero(assetProxy) { // Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) mstore(64, 0x0000001a41535345545f50524f58595f444f45535f4e4f545f45584953540000) mstore(96, 0) revert(0, 100) } // Copy `nestedAssetData[i]` from calldata to memory calldatacopy( 132, // memory slot after `amounts[i]` nestedAssetDataElementLenStart, // location of `nestedAssetData[i]` in calldata add(nestedAssetDataElementLen, 32) // `nestedAssetData[i].length` plus 32 byte length ) // call `assetProxy.transferFrom` let success := call( gas, // forward all gas assetProxy, // call address of asset proxy 0, // don't send any ETH 0, // pointer to start of input add(164, nestedAssetDataElementLen), // length of input 0, // write output over memory that won't be reused 0 // don't copy output to memory ) // Revert with reason given by AssetProxy if `transferFrom` call failed if iszero(success) { returndatacopy( 0, // copy to memory at 0 0, // copy from return data at 0 returndatasize() // copy all return data ) revert(0, returndatasize()) } } // Return if no `transferFrom` calls reverted return(0, 0) } // Revert if undefined function is called revert(0, 0) } } /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() external pure returns (bytes4) { return PROXY_ID; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; // solhint-disable no-unused-vars contract StaticCallProxy { using LibBytes for bytes; // Id of this proxy. bytes4 constant internal PROXY_ID = bytes4(keccak256("StaticCall(address,bytes,bytes32)")); /// @dev Makes a staticcall to a target address and verifies that the data returned matches the expected return data. /// @param assetData Byte array encoded with staticCallTarget, staticCallData, and expectedCallResultHash /// @param from This value is ignored. /// @param to This value is ignored. /// @param amount This value is ignored. function transferFrom( bytes calldata assetData, address from, address to, uint256 amount ) external view { // Decode params from `assetData` ( address staticCallTarget, bytes memory staticCallData, bytes32 expectedReturnDataHash ) = abi.decode( assetData.sliceDestructive(4, assetData.length), (address, bytes, bytes32) ); // Execute staticcall (bool success, bytes memory returnData) = staticCallTarget.staticcall(staticCallData); // Revert with returned data if staticcall is unsuccessful if (!success) { assembly { revert(add(returnData, 32), mload(returnData)) } } // Revert if hash of return data is not as expected bytes32 returnDataHash = keccak256(returnData); require( expectedReturnDataHash == returnDataHash, "UNEXPECTED_STATIC_CALL_RESULT" ); } /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() external pure returns (bytes4) { return PROXY_ID; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; import "../interfaces/IERC20Bridge.sol"; import "../interfaces/IEth2Dai.sol"; // solhint-disable space-after-comma contract Eth2DaiBridge is IERC20Bridge, IWallet { /* Mainnet addresses */ address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e; /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of /// `toTokenAddress` tokens by selling the entirety of the opposing asset /// (DAI or WETH) to the Eth2Dai contract, then transfers the bought /// tokens to `to`. /// @param toTokenAddress The token to give to `to` (either DAI or WETH). /// @param to The recipient of the bought tokens. /// @param amount Minimum amount of `toTokenAddress` tokens to buy. /// @param bridgeData The abi-encoeded "from" token address. /// @return success The magic bytes if successful. function bridgeTransferFrom( address toTokenAddress, address /* from */, address to, uint256 amount, bytes calldata bridgeData ) external returns (bytes4 success) { // Decode the bridge data to get the `fromTokenAddress`. (address fromTokenAddress) = abi.decode(bridgeData, (address)); IEth2Dai exchange = _getEth2DaiContract(); // Grant an allowance to the exchange to spend `fromTokenAddress` token. LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1)); // Try to sell all of this contract's `fromTokenAddress` token balance. uint256 boughtAmount = _getEth2DaiContract().sellAllAmount( address(fromTokenAddress), IERC20Token(fromTokenAddress).balanceOf(address(this)), toTokenAddress, amount ); // Transfer the converted `toToken`s to `to`. LibERC20Token.transfer(toTokenAddress, to, boughtAmount); return BRIDGE_SUCCESS; } /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker /// and sign for itself in orders. Always succeeds. /// @return magicValue Magic success bytes, always. function isValidSignature( bytes32, bytes calldata ) external view returns (bytes4 magicValue) { return LEGACY_WALLET_MAGIC_VALUE; } /// @dev Overridable way to get the eth2dai contract. /// @return exchange The Eth2Dai exchange contract. function _getEth2DaiContract() internal view returns (IEth2Dai exchange) { return IEth2Dai(ETH2DAI_ADDRESS); } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "../src/interfaces/IERC20Token.sol"; library LibERC20Token { /// @dev Calls `IERC20Token(token).approve()`. /// Reverts if `false` is returned or if the return /// data length is nonzero and not 32 bytes. /// @param token The address of the token contract. /// @param spender The address that receives an allowance. /// @param allowance The allowance to set. function approve( address token, address spender, uint256 allowance ) internal { bytes memory callData = abi.encodeWithSelector( IERC20Token(0).approve.selector, spender, allowance ); _callWithOptionalBooleanResult(token, callData); } /// @dev Calls `IERC20Token(token).transfer()`. /// Reverts if `false` is returned or if the return /// data length is nonzero and not 32 bytes. /// @param token The address of the token contract. /// @param to The address that receives the tokens /// @param amount Number of tokens to transfer. function transfer( address token, address to, uint256 amount ) internal { bytes memory callData = abi.encodeWithSelector( IERC20Token(0).transfer.selector, to, amount ); _callWithOptionalBooleanResult(token, callData); } /// @dev Calls `IERC20Token(token).transferFrom()`. /// Reverts if `false` is returned or if the return /// data length is nonzero and not 32 bytes. /// @param token The address of the token contract. /// @param from The owner of the tokens. /// @param to The address that receives the tokens /// @param amount Number of tokens to transfer. function transferFrom( address token, address from, address to, uint256 amount ) internal { bytes memory callData = abi.encodeWithSelector( IERC20Token(0).transferFrom.selector, from, to, amount ); _callWithOptionalBooleanResult(token, callData); } /// @dev Executes a call on address `target` with calldata `callData` /// and asserts that either nothing was returned or a single boolean /// was returned equal to `true`. /// @param target The call target. /// @param callData The abi-encoded call data. function _callWithOptionalBooleanResult( address target, bytes memory callData ) private { (bool didSucceed, bytes memory resultData) = target.call(callData); if (didSucceed) { if (resultData.length == 0) { return; } if (resultData.length == 32) { uint256 result = LibBytes.readUint256(resultData, 0); if (result == 1) { return; } } } LibRichErrors.rrevert(resultData); } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; contract IWallet { bytes4 internal constant LEGACY_WALLET_MAGIC_VALUE = 0xb0671381; /// @dev Validates a hash with the `Wallet` signature type. /// @param hash Message hash that is signed. /// @param signature Proof of signing. /// @return magicValue `bytes4(0xb0671381)` if the signature check succeeds. function isValidSignature( bytes32 hash, bytes calldata signature ) external view returns (bytes4 magicValue); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; interface IEth2Dai { /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token. /// @param fromToken The token being sold. /// @param sellAmount The amount of `fromToken` token being sold. /// @param toToken The token being bought. /// @param minFillAmount Minimum amount of `toToken` token to buy. /// @return fillAmount Amount of `toToken` bought. function sellAllAmount( address fromToken, uint256 sellAmount, address toToken, uint256 minFillAmount ) external returns (uint256 fillAmount); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "./IERC20Token.sol"; contract IEtherToken is IERC20Token { function deposit() public payable; function withdraw(uint256 amount) public; }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "./IUniswapExchange.sol"; interface IUniswapExchangeFactory { /// @dev Get the exchange for a token. /// @param tokenAddress The address of the token contract. function getExchange(address tokenAddress) external view returns (IUniswapExchange); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; interface IUniswapExchange { /// @dev Buys at least `minTokensBought` tokens with ETH and transfer them /// to `recipient`. /// @param minTokensBought The minimum number of tokens to buy. /// @param deadline Time when this order expires. /// @param recipient Who to transfer the tokens to. /// @return tokensBought Amount of tokens bought. function ethToTokenTransferInput( uint256 minTokensBought, uint256 deadline, address recipient ) external payable returns (uint256 tokensBought); /// @dev Buys at least `minEthBought` ETH with tokens. /// @param tokensSold Amount of tokens to sell. /// @param minEthBought The minimum amount of ETH to buy. /// @param deadline Time when this order expires. /// @return ethBought Amount of tokens bought. function tokenToEthSwapInput( uint256 tokensSold, uint256 minEthBought, uint256 deadline ) external returns (uint256 ethBought); /// @dev Buys at least `minTokensBought` tokens with the exchange token /// and transfer them to `recipient`. /// @param minTokensBought The minimum number of tokens to buy. /// @param minEthBought The minimum amount of intermediate ETH to buy. /// @param deadline Time when this order expires. /// @param recipient Who to transfer the tokens to. /// @param toTokenAddress The token being bought. /// @return tokensBought Amount of tokens bought. function tokenToTokenTransferInput( uint256 tokensSold, uint256 minTokensBought, uint256 minEthBought, uint256 deadline, address recipient, address toTokenAddress ) external returns (uint256 tokensBought); /// @dev Retrieves the token that is associated with this exchange. /// @return tokenAddress The token address. function toTokenAddress() external view returns (address tokenAddress); }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // solhint-disable pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; // @dev Interface of the asset proxy's assetData. // The asset proxies take an ABI encoded `bytes assetData` as argument. // This argument is ABI encoded as one of the methods of this interface. interface IAssetData { /// @dev Function signature for encoding ERC20 assetData. /// @param tokenAddress Address of ERC20Token contract. function ERC20Token(address tokenAddress) external; /// @dev Function signature for encoding ERC721 assetData. /// @param tokenAddress Address of ERC721 token contract. /// @param tokenId Id of ERC721 token to be transferred. function ERC721Token( address tokenAddress, uint256 tokenId ) external; /// @dev Function signature for encoding ERC1155 assetData. /// @param tokenAddress Address of ERC1155 token contract. /// @param tokenIds Array of ids of tokens to be transferred. /// @param values Array of values that correspond to each token id to be transferred. /// Note that each value will be multiplied by the amount being filled in the order before transferring. /// @param callbackData Extra data to be passed to receiver's `onERC1155Received` callback function. function ERC1155Assets( address tokenAddress, uint256[] calldata tokenIds, uint256[] calldata values, bytes calldata callbackData ) external; /// @dev Function signature for encoding MultiAsset assetData. /// @param values Array of amounts that correspond to each asset to be transferred. /// Note that each value will be multiplied by the amount being filled in the order before transferring. /// @param nestedAssetData Array of assetData fields that will be be dispatched to their correspnding AssetProxy contract. function MultiAsset( uint256[] calldata values, bytes[] calldata nestedAssetData ) external; /// @dev Function signature for encoding StaticCall assetData. /// @param staticCallTargetAddress Address that will execute the staticcall. /// @param staticCallData Data that will be executed via staticcall on the staticCallTargetAddress. /// @param expectedReturnDataHash Keccak-256 hash of the expected staticcall return data. function StaticCall( address staticCallTargetAddress, bytes calldata staticCallData, bytes32 expectedReturnDataHash ) external; /// @dev Function signature for encoding ERC20Bridge assetData. /// @param tokenAddress Address of token to transfer. /// @param bridgeAddress Address of the bridge contract. /// @param bridgeData Arbitrary data to be passed to the bridge contract. function ERC20Bridge( address tokenAddress, address bridgeAddress, bytes calldata bridgeData ) external; }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "../src/interfaces/IERC20Bridge.sol"; /// @dev Test bridge token contract TestERC20BridgeToken { mapping (address => uint256) private _balances; function addBalance(address owner, int256 amount) external { setBalance(owner, uint256(int256(balanceOf(owner)) + amount)); } function setBalance(address owner, uint256 balance) public { _balances[owner] = balance; } function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } } /// @dev Test bridge contract. contract TestERC20Bridge is IERC20Bridge { TestERC20BridgeToken public testToken; event BridgeWithdrawTo( address tokenAddress, address from, address to, uint256 amount, bytes bridgeData ); constructor() public { testToken = new TestERC20BridgeToken(); } function setTestTokenBalance(address owner, uint256 balance) external { testToken.setBalance(owner, balance); } function bridgeTransferFrom( address tokenAddress, address from, address to, uint256 amount, bytes calldata bridgeData ) external returns (bytes4) { emit BridgeWithdrawTo( tokenAddress, from, to, amount, bridgeData ); // Unpack the bridgeData. ( int256 transferAmount, bytes memory revertData, bytes memory returnData ) = abi.decode(bridgeData, (int256, bytes, bytes)); // If `revertData` is set, revert. if (revertData.length != 0) { assembly { revert(add(revertData, 0x20), mload(revertData)) } } // Increase `to`'s balance by `transferAmount`. TestERC20BridgeToken(tokenAddress).addBalance(to, transferAmount); // Return `returnData`. assembly { return(add(returnData, 0x20), mload(returnData)) } } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "../src/bridges/Eth2DaiBridge.sol"; import "../src/interfaces/IEth2Dai.sol"; // solhint-disable no-simple-event-func-name contract TestEvents { event TokenTransfer( address token, address from, address to, uint256 amount ); event TokenApprove( address token, address spender, uint256 allowance ); function raiseTokenTransfer( address from, address to, uint256 amount ) external { emit TokenTransfer( msg.sender, from, to, amount ); } function raiseTokenApprove(address spender, uint256 allowance) external { emit TokenApprove(msg.sender, spender, allowance); } } /// @dev A minimalist ERC20 token. contract TestToken { mapping (address => uint256) public balances; string private _nextTransferRevertReason; bytes private _nextTransferReturnData; /// @dev Just calls `raiseTokenTransfer()` on the caller. function transfer(address to, uint256 amount) external returns (bool) { TestEvents(msg.sender).raiseTokenTransfer(msg.sender, to, amount); if (bytes(_nextTransferRevertReason).length != 0) { revert(_nextTransferRevertReason); } bytes memory returnData = _nextTransferReturnData; assembly { return(add(returnData, 0x20), mload(returnData)) } } /// @dev Set the balance for `owner`. function setBalance(address owner, uint256 balance) external { balances[owner] = balance; } /// @dev Set the behavior of the `transfer()` call. function setTransferBehavior( string calldata revertReason, bytes calldata returnData ) external { _nextTransferRevertReason = revertReason; _nextTransferReturnData = returnData; } /// @dev Just calls `raiseTokenApprove()` on the caller. function approve(address spender, uint256 allowance) external returns (bool) { TestEvents(msg.sender).raiseTokenApprove(spender, allowance); return true; } /// @dev Retrieve the balance for `owner`. function balanceOf(address owner) external view returns (uint256) { return balances[owner]; } } /// @dev Eth2DaiBridge overridden to mock tokens and /// implement IEth2Dai. contract TestEth2DaiBridge is TestEvents, IEth2Dai, Eth2DaiBridge { event SellAllAmount( address sellToken, uint256 sellTokenAmount, address buyToken, uint256 minimumFillAmount ); mapping (address => TestToken) public testTokens; string private _nextRevertReason; uint256 private _nextFillAmount; /// @dev Create a token and set this contract's balance. function createToken(uint256 balance) external returns (address tokenAddress) { TestToken token = new TestToken(); testTokens[address(token)] = token; token.setBalance(address(this), balance); return address(token); } /// @dev Set the behavior for `IEth2Dai.sellAllAmount()`. function setFillBehavior(string calldata revertReason, uint256 fillAmount) external { _nextRevertReason = revertReason; _nextFillAmount = fillAmount; } /// @dev Set the behavior of a token's `transfer()`. function setTransferBehavior( address tokenAddress, string calldata revertReason, bytes calldata returnData ) external { testTokens[tokenAddress].setTransferBehavior(revertReason, returnData); } /// @dev Implementation of `IEth2Dai.sellAllAmount()` function sellAllAmount( address sellTokenAddress, uint256 sellTokenAmount, address buyTokenAddress, uint256 minimumFillAmount ) external returns (uint256 fillAmount) { emit SellAllAmount( sellTokenAddress, sellTokenAmount, buyTokenAddress, minimumFillAmount ); if (bytes(_nextRevertReason).length != 0) { revert(_nextRevertReason); } return _nextFillAmount; } // @dev This contract will double as the Eth2Dai contract. function _getEth2DaiContract() internal view returns (IEth2Dai) { return IEth2Dai(address(this)); } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; contract TestStaticCallTarget { using LibBytes for bytes; uint256 internal _state; function updateState() external { _state++; } function assertEvenNumber(uint256 target) external pure { require( target % 2 == 0, "TARGET_NOT_EVEN" ); } function isOddNumber(uint256 target) external pure returns (bool isOdd) { isOdd = target % 2 == 1; return isOdd; } function noInputFunction() external pure { assert(msg.data.length == 4 && msg.data.readBytes4(0) == bytes4(keccak256("noInputFunction()"))); } function dynamicInputFunction(bytes calldata a) external pure { bytes memory abiEncodedData = abi.encodeWithSignature("dynamicInputFunction(bytes)", a); assert(msg.data.equals(abiEncodedData)); } function returnComplexType(uint256 a, uint256 b) external view returns (bytes memory result) { result = abi.encodePacked( address(this), a, b ); return result; } }
/* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "../src/bridges/UniswapBridge.sol"; import "../src/interfaces/IUniswapExchangeFactory.sol"; import "../src/interfaces/IUniswapExchange.sol"; // solhint-disable no-simple-event-func-name contract TestEventsRaiser { event TokenTransfer( address token, address from, address to, uint256 amount ); event TokenApprove( address spender, uint256 allowance ); event WethDeposit( uint256 amount ); event WethWithdraw( uint256 amount ); event EthToTokenTransferInput( address exchange, uint256 minTokensBought, uint256 deadline, address recipient ); event TokenToEthSwapInput( address exchange, uint256 tokensSold, uint256 minEthBought, uint256 deadline ); event TokenToTokenTransferInput( address exchange, uint256 tokensSold, uint256 minTokensBought, uint256 minEthBought, uint256 deadline, address recipient, address toTokenAddress ); function raiseEthToTokenTransferInput( uint256 minTokensBought, uint256 deadline, address recipient ) external { emit EthToTokenTransferInput( msg.sender, minTokensBought, deadline, recipient ); } function raiseTokenToEthSwapInput( uint256 tokensSold, uint256 minEthBought, uint256 deadline ) external { emit TokenToEthSwapInput( msg.sender, tokensSold, minEthBought, deadline ); } function raiseTokenToTokenTransferInput( uint256 tokensSold, uint256 minTokensBought, uint256 minEthBought, uint256 deadline, address recipient, address toTokenAddress ) external { emit TokenToTokenTransferInput( msg.sender, tokensSold, minTokensBought, minEthBought, deadline, recipient, toTokenAddress ); } function raiseTokenTransfer( address from, address to, uint256 amount ) external { emit TokenTransfer( msg.sender, from, to, amount ); } function raiseTokenApprove(address spender, uint256 allowance) external { emit TokenApprove(spender, allowance); } function raiseWethDeposit(uint256 amount) external { emit WethDeposit(amount); } function raiseWethWithdraw(uint256 amount) external { emit WethWithdraw(amount); } } /// @dev A minimalist ERC20/WETH token. contract TestToken { using LibSafeMath for uint256; mapping (address => uint256) public balances; string private _nextRevertReason; /// @dev Set the balance for `owner`. function setBalance(address owner) external payable { balances[owner] = msg.value; } /// @dev Set the revert reason for `transfer()`, /// `deposit()`, and `withdraw()`. function setRevertReason(string calldata reason) external { _nextRevertReason = reason; } /// @dev Just calls `raiseTokenTransfer()` on the caller. function transfer(address to, uint256 amount) external returns (bool) { _revertIfReasonExists(); TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount); return true; } /// @dev Just calls `raiseTokenApprove()` on the caller. function approve(address spender, uint256 allowance) external returns (bool) { TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance); return true; } /// @dev `IWETH.deposit()` that increases balances and calls /// `raiseWethDeposit()` on the caller. function deposit() external payable { _revertIfReasonExists(); balances[msg.sender] += balances[msg.sender].safeAdd(msg.value); TestEventsRaiser(msg.sender).raiseWethDeposit(msg.value); } /// @dev `IWETH.withdraw()` that just reduces balances and calls /// `raiseWethWithdraw()` on the caller. function withdraw(uint256 amount) external { _revertIfReasonExists(); balances[msg.sender] = balances[msg.sender].safeSub(amount); msg.sender.transfer(amount); TestEventsRaiser(msg.sender).raiseWethWithdraw(amount); } /// @dev Retrieve the balance for `owner`. function balanceOf(address owner) external view returns (uint256) { return balances[owner]; } function _revertIfReasonExists() private view { if (bytes(_nextRevertReason).length != 0) { revert(_nextRevertReason); } } } contract TestExchange is IUniswapExchange { address public tokenAddress; string private _nextRevertReason; constructor(address _tokenAddress) public { tokenAddress = _tokenAddress; } function setFillBehavior( string calldata revertReason ) external payable { _nextRevertReason = revertReason; } function ethToTokenTransferInput( uint256 minTokensBought, uint256 deadline, address recipient ) external payable returns (uint256 tokensBought) { TestEventsRaiser(msg.sender).raiseEthToTokenTransferInput( minTokensBought, deadline, recipient ); _revertIfReasonExists(); return address(this).balance; } function tokenToEthSwapInput( uint256 tokensSold, uint256 minEthBought, uint256 deadline ) external returns (uint256 ethBought) { TestEventsRaiser(msg.sender).raiseTokenToEthSwapInput( tokensSold, minEthBought, deadline ); _revertIfReasonExists(); uint256 fillAmount = address(this).balance; msg.sender.transfer(fillAmount); return fillAmount; } function tokenToTokenTransferInput( uint256 tokensSold, uint256 minTokensBought, uint256 minEthBought, uint256 deadline, address recipient, address toTokenAddress ) external returns (uint256 tokensBought) { TestEventsRaiser(msg.sender).raiseTokenToTokenTransferInput( tokensSold, minTokensBought, minEthBought, deadline, recipient, toTokenAddress ); _revertIfReasonExists(); return address(this).balance; } function toTokenAddress() external view returns (address _tokenAddress) { return tokenAddress; } function _revertIfReasonExists() private view { if (bytes(_nextRevertReason).length != 0) { revert(_nextRevertReason); } } } /// @dev UniswapBridge overridden to mock tokens and implement IUniswapExchangeFactory. contract TestUniswapBridge is IUniswapExchangeFactory, TestEventsRaiser, UniswapBridge { TestToken public wethToken; // Token address to TestToken instance. mapping (address => TestToken) private _testTokens; // Token address to TestExchange instance. mapping (address => TestExchange) private _testExchanges; constructor() public { wethToken = new TestToken(); _testTokens[address(wethToken)] = wethToken; } /// @dev Sets the balance of this contract for an existing token. /// The wei attached will be the balance. function setTokenBalance(address tokenAddress) external payable { TestToken token = _testTokens[tokenAddress]; token.deposit.value(msg.value)(); } /// @dev Sets the revert reason for an existing token. function setTokenRevertReason(address tokenAddress, string calldata revertReason) external { TestToken token = _testTokens[tokenAddress]; token.setRevertReason(revertReason); } /// @dev Create a token and exchange (if they don't exist) for a new token /// and sets the exchange revert and fill behavior. The wei attached /// will be the fill amount for the exchange. /// @param tokenAddress The token address. If zero, one will be created. /// @param revertReason The revert reason for exchange operations. function createTokenAndExchange( address tokenAddress, string calldata revertReason ) external payable returns (TestToken token, TestExchange exchange) { token = TestToken(tokenAddress); if (tokenAddress == address(0)) { token = new TestToken(); } _testTokens[address(token)] = token; exchange = _testExchanges[address(token)]; if (address(exchange) == address(0)) { _testExchanges[address(token)] = exchange = new TestExchange(address(token)); } exchange.setFillBehavior.value(msg.value)(revertReason); return (token, exchange); } /// @dev `IUniswapExchangeFactory.getExchange` function getExchange(address tokenAddress) external view returns (IUniswapExchange) { return IUniswapExchange(_testExchanges[tokenAddress]); } // @dev Use `wethToken`. function getWethContract() public view returns (IEtherToken) { return IEtherToken(address(wethToken)); } // @dev This contract will double as the Uniswap contract. function getUniswapExchangeFactoryContract() public view returns (IUniswapExchangeFactory) { return IUniswapExchangeFactory(address(this)); } }
{ "optimizer": { "enabled": true, "runs": 1000000, "details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true } }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "evmVersion": "constantinople", "remappings": [ "@0x/contracts-utils=/Users/amir/github/0xproject/0x-monorepo/contracts/asset-proxy/node_modules/@0x/contracts-utils", "@0x/contracts-erc1155=/Users/amir/github/0xproject/0x-monorepo/contracts/asset-proxy/node_modules/@0x/contracts-erc1155", "@0x/contracts-erc20=/Users/amir/github/0xproject/0x-monorepo/contracts/asset-proxy/node_modules/@0x/contracts-erc20", "@0x/contracts-exchange-libs=/Users/amir/github/0xproject/0x-monorepo/node_modules/@0x/contracts-exchange-libs" ] }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":false,"inputs":[{"internalType":"address","name":"toTokenAddress","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"bridgeData","type":"bytes"}],"name":"bridgeTransferFrom","outputs":[{"internalType":"bytes4","name":"success","type":"bytes4"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getUniswapExchangeFactoryContract","outputs":[{"internalType":"contract IUniswapExchangeFactory","name":"factory","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getWethContract","outputs":[{"internalType":"contract IEtherToken","name":"token","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"magicValue","type":"bytes4"}],"payable":false,"stateMutability":"view","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50610e42806100206000396000f3fe60806040526004361061003f5760003560e01c80631626ba7e146100415780633c277fc514610077578063c2df82e614610099578063fc7e20c6146100b9575b005b34801561004d57600080fd5b5061006161005c366004610bca565b6100ce565b60405161006e9190610cc8565b60405180910390f35b34801561008357600080fd5b5061008c6100f6565b60405161006e9190610c81565b3480156100a557600080fd5b506100616100b4366004610b27565b61010e565b3480156100c557600080fd5b5061008c610687565b7fb0671381000000000000000000000000000000000000000000000000000000009392505050565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000610118610aa4565b600061012684860186610b0b565b90508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156101925761016781888861069f565b507fdc1600f300000000000000000000000000000000000000000000000000000000915061067d9050565b61019c818a610766565b73ffffffffffffffffffffffffffffffffffffffff90811683526040517f70a08231000000000000000000000000000000000000000000000000000000008152908216906370a08231906101f4903090600401610c81565b60206040518083038186803b15801561020c57600080fd5b505afa158015610220573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506102449190810190610c30565b60208301526102516100f6565b73ffffffffffffffffffffffffffffffffffffffff9081166040840181905290821614156103a357816040015173ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d83602001516040518263ffffffff1660e01b81526004016102ba9190610d4e565b600060405180830381600087803b1580156102d457600080fd5b505af11580156102e8573d6000803e3d6000fd5b5050835160208501516040517fad65d76d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909216935063ad65d76d92509061034b908a9042908d90600401610d57565b6020604051808303818588803b15801561036457600080fd5b505af1158015610378573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061039d9190810190610c30565b50610657565b816040015173ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156105ab5781516103e79082610896565b815160208301516040517f95e3c50b00000000000000000000000000000000000000000000000000000000815260009273ffffffffffffffffffffffffffffffffffffffff16916395e3c50b9161044591908b904290600401610dc4565b602060405180830381600087803b15801561045f57600080fd5b505af1158015610473573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104979190810190610c30565b9050826040015173ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156104e557600080fd5b505af11580156104f9573d6000803e3d6000fd5b50506040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8e16935063a9059cbb925061055291508b908590600401610ca2565b602060405180830381600087803b15801561056c57600080fd5b505af1158015610580573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105a49190810190610baa565b5050610657565b81516105b79082610896565b816000015173ffffffffffffffffffffffffffffffffffffffff1663f552d91b8360200151886000428c8f6040518763ffffffff1660e01b815260040161060396959493929190610d83565b602060405180830381600087803b15801561061d57600080fd5b505af1158015610631573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106559190810190610c30565b505b507fdc1600f3000000000000000000000000000000000000000000000000000000009150505b9695505050505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6040516060907fa9059cbb00000000000000000000000000000000000000000000000000000000906106d79085908590602401610ca2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152905061076084826108c5565b50505050565b6000826107716100f6565b73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614156107a75750815b6107af610687565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62826040518263ffffffff1660e01b81526004016107e79190610c81565b60206040518083038186803b1580156107ff57600080fd5b505afa158015610813573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108379190810190610c14565b915073ffffffffffffffffffffffffffffffffffffffff821661088f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161088690610d17565b60405180910390fd5b5092915050565b6108c181837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61097d565b5050565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516108ee9190610c48565b6000604051808303816000865af19150503d806000811461092b576040519150601f19603f3d011682016040523d82523d6000602084013e610930565b606091505b509150915081156109745780516109485750506108c1565b80516020141561097457600061095f8260006109b5565b90508060011415610972575050506108c1565b505b610760816109c8565b6040516060907f095ea7b300000000000000000000000000000000000000000000000000000000906106d79085908590602401610ca2565b60006109c183836109d0565b9392505050565b805160208201fd5b600081602001835110156109f6576109f66109f160058551856020016109ff565b6109c8565b50016020015190565b6060632800659560e01b848484604051602401610a1e93929190610cf5565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b604080516060810182526000808252602082018190529181019190915290565b60008083601f840112610ad5578182fd5b50813567ffffffffffffffff811115610aec578182fd5b602083019150836020828501011115610b0457600080fd5b9250929050565b600060208284031215610b1c578081fd5b81356109c181610dda565b60008060008060008060a08789031215610b3f578182fd5b8635610b4a81610dda565b95506020870135610b5a81610dda565b94506040870135610b6a81610dda565b935060608701359250608087013567ffffffffffffffff811115610b8c578283fd5b610b9889828a01610ac4565b979a9699509497509295939492505050565b600060208284031215610bbb578081fd5b815180151581146109c1578182fd5b600080600060408486031215610bde578283fd5b83359250602084013567ffffffffffffffff811115610bfb578283fd5b610c0786828701610ac4565b9497909650939450505050565b600060208284031215610c25578081fd5b81516109c181610dda565b600060208284031215610c41578081fd5b5051919050565b60008251815b81811015610c685760208186018101518583015201610c4e565b81811115610c765782828501525b509190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6060810160088510610d0357fe5b938152602081019290925260409091015290565b6020808252601d908201527f4e4f5f554e49535741505f45584348414e47455f464f525f544f4b454e000000604082015260600190565b90815260200190565b928352602083019190915273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b95865260208601949094526040850192909252606084015273ffffffffffffffffffffffffffffffffffffffff90811660808401521660a082015260c00190565b9283526020830191909152604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff81168114610dfc57600080fd5b5056fea365627a7a72315820b36daa42e8d314c3085f196c752ce75ea20d5c8baf1e5c9a3eccfc61a5163bc56c6578706572696d656e74616cf564736f6c634300050c0040
Deployed Bytecode
0x60806040526004361061003f5760003560e01c80631626ba7e146100415780633c277fc514610077578063c2df82e614610099578063fc7e20c6146100b9575b005b34801561004d57600080fd5b5061006161005c366004610bca565b6100ce565b60405161006e9190610cc8565b60405180910390f35b34801561008357600080fd5b5061008c6100f6565b60405161006e9190610c81565b3480156100a557600080fd5b506100616100b4366004610b27565b61010e565b3480156100c557600080fd5b5061008c610687565b7fb0671381000000000000000000000000000000000000000000000000000000009392505050565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000610118610aa4565b600061012684860186610b0b565b90508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156101925761016781888861069f565b507fdc1600f300000000000000000000000000000000000000000000000000000000915061067d9050565b61019c818a610766565b73ffffffffffffffffffffffffffffffffffffffff90811683526040517f70a08231000000000000000000000000000000000000000000000000000000008152908216906370a08231906101f4903090600401610c81565b60206040518083038186803b15801561020c57600080fd5b505afa158015610220573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506102449190810190610c30565b60208301526102516100f6565b73ffffffffffffffffffffffffffffffffffffffff9081166040840181905290821614156103a357816040015173ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d83602001516040518263ffffffff1660e01b81526004016102ba9190610d4e565b600060405180830381600087803b1580156102d457600080fd5b505af11580156102e8573d6000803e3d6000fd5b5050835160208501516040517fad65d76d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909216935063ad65d76d92509061034b908a9042908d90600401610d57565b6020604051808303818588803b15801561036457600080fd5b505af1158015610378573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061039d9190810190610c30565b50610657565b816040015173ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156105ab5781516103e79082610896565b815160208301516040517f95e3c50b00000000000000000000000000000000000000000000000000000000815260009273ffffffffffffffffffffffffffffffffffffffff16916395e3c50b9161044591908b904290600401610dc4565b602060405180830381600087803b15801561045f57600080fd5b505af1158015610473573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104979190810190610c30565b9050826040015173ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156104e557600080fd5b505af11580156104f9573d6000803e3d6000fd5b50506040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8e16935063a9059cbb925061055291508b908590600401610ca2565b602060405180830381600087803b15801561056c57600080fd5b505af1158015610580573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105a49190810190610baa565b5050610657565b81516105b79082610896565b816000015173ffffffffffffffffffffffffffffffffffffffff1663f552d91b8360200151886000428c8f6040518763ffffffff1660e01b815260040161060396959493929190610d83565b602060405180830381600087803b15801561061d57600080fd5b505af1158015610631573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106559190810190610c30565b505b507fdc1600f3000000000000000000000000000000000000000000000000000000009150505b9695505050505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6040516060907fa9059cbb00000000000000000000000000000000000000000000000000000000906106d79085908590602401610ca2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152905061076084826108c5565b50505050565b6000826107716100f6565b73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614156107a75750815b6107af610687565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62826040518263ffffffff1660e01b81526004016107e79190610c81565b60206040518083038186803b1580156107ff57600080fd5b505afa158015610813573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108379190810190610c14565b915073ffffffffffffffffffffffffffffffffffffffff821661088f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161088690610d17565b60405180910390fd5b5092915050565b6108c181837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61097d565b5050565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516108ee9190610c48565b6000604051808303816000865af19150503d806000811461092b576040519150601f19603f3d011682016040523d82523d6000602084013e610930565b606091505b509150915081156109745780516109485750506108c1565b80516020141561097457600061095f8260006109b5565b90508060011415610972575050506108c1565b505b610760816109c8565b6040516060907f095ea7b300000000000000000000000000000000000000000000000000000000906106d79085908590602401610ca2565b60006109c183836109d0565b9392505050565b805160208201fd5b600081602001835110156109f6576109f66109f160058551856020016109ff565b6109c8565b50016020015190565b6060632800659560e01b848484604051602401610a1e93929190610cf5565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b604080516060810182526000808252602082018190529181019190915290565b60008083601f840112610ad5578182fd5b50813567ffffffffffffffff811115610aec578182fd5b602083019150836020828501011115610b0457600080fd5b9250929050565b600060208284031215610b1c578081fd5b81356109c181610dda565b60008060008060008060a08789031215610b3f578182fd5b8635610b4a81610dda565b95506020870135610b5a81610dda565b94506040870135610b6a81610dda565b935060608701359250608087013567ffffffffffffffff811115610b8c578283fd5b610b9889828a01610ac4565b979a9699509497509295939492505050565b600060208284031215610bbb578081fd5b815180151581146109c1578182fd5b600080600060408486031215610bde578283fd5b83359250602084013567ffffffffffffffff811115610bfb578283fd5b610c0786828701610ac4565b9497909650939450505050565b600060208284031215610c25578081fd5b81516109c181610dda565b600060208284031215610c41578081fd5b5051919050565b60008251815b81811015610c685760208186018101518583015201610c4e565b81811115610c765782828501525b509190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6060810160088510610d0357fe5b938152602081019290925260409091015290565b6020808252601d908201527f4e4f5f554e49535741505f45584348414e47455f464f525f544f4b454e000000604082015260600190565b90815260200190565b928352602083019190915273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b95865260208601949094526040850192909252606084015273ffffffffffffffffffffffffffffffffffffffff90811660808401521660a082015260c00190565b9283526020830191909152604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff81168114610dfc57600080fd5b5056fea365627a7a72315820b36daa42e8d314c3085f196c752ce75ea20d5c8baf1e5c9a3eccfc61a5163bc56c6578706572696d656e74616cf564736f6c634300050c0040
Deployed Bytecode Sourcemap
1120:6993:10:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5832:192;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5832:192:10;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;6126:144;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6126:144:10;;;:::i;:::-;;;;;;;;2316:3316;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2316:3316:10;;;;;;;;:::i;6406:208::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6406:208:10;;;:::i;5832:192::-;5992:25;5832:192;;;;;:::o;6126:144::-;1358:42;6126:144;:::o;2316:3316::-;2523:14;2610:28;;:::i;:::-;2714:24;2742:33;;;;2753:10;2742:33;;;2713:62;;2867:14;2847:34;;:16;:34;;;2843:152;;;2897:52;2920:16;2938:2;2942:6;2897:22;:52::i;:::-;-1:-1:-1;2970:14:10;;-1:-1:-1;2963:21:10;;-1:-1:-1;2963:21:10;2843:152;3070:99;3115:16;3145:14;3070:31;:99::i;:::-;3053:116;;;;;;3260:54;;;;;:39;;;;;;:54;;3308:4;;3260:54;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3260:54:10;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3260:54:10;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;3260:54:10;;;;;;;;;3235:22;;;:79;3371:17;:15;:17::i;:::-;3358:30;;;;:10;;;:30;;;3444:39;;;;3440:2155;;;3531:5;:10;;;:19;;;3551:5;:22;;;3531:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3531:43:10;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;3702:14:10;;3747:22;;;;3702:281;;;;;:38;;;;;-1:-1:-1;3702:38:10;;-1:-1:-1;3747:22:10;3702:281;;3827:6;;3896:15;;3967:2;;3702:281;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3702:281:10;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3702:281:10;;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;3702:281:10;;;;;;;;;;3440:2155;;;4072:5;:10;;;4046:37;;:14;:37;;;4042:1553;;;4171:14;;4147:57;;4187:16;4147:23;:57::i;:::-;4312:14;;4408:22;;;;4312:273;;;;;4292:17;;4312:34;;;;;:273;;4408:22;4487:6;;4556:15;;4312:273;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4312:273:10;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4312:273:10;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;4312:273:10;;;;;;;;;4292:293;;4628:5;:10;;;:18;;;4653:9;4628:37;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4628:37:10;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;4721:51:10;;;;;:36;;;;-1:-1:-1;4721:36:10;;-1:-1:-1;4721:51:10;;-1:-1:-1;4758:2:10;;4762:9;;4721:51;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4721:51:10;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4721:51:10;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;4721:51:10;;;;;;;;;;4042:1553;;;;4922:14;;4898:57;;4938:16;4898:23;:57::i;:::-;5089:5;:14;;;:40;;;5191:5;:22;;;5270:6;5353:1;5417:15;5488:2;5556:14;5089:495;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5089:495:10;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;5089:495:10;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;5089:495:10;;;;;;;;;;4042:1553;-1:-1:-1;5611:14:10;;-1:-1:-1;;2316:3316:10;;;;;;;;;:::o;6406:208::-;1270:42;6406:208;:::o;1805:324:24:-;1951:114;;1927:21;;1987:32;;1951:114;;2033:2;;2049:6;;1951:114;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;1951:114:24;;;49:4:-1;25:18;;61:17;;1951:114:24;182:15:-1;1951:114:24;;;;179:29:-1;;;;160:49;;;1951:114:24;-1:-1:-1;2075:47:24;2106:5;1951:114;2075:30;:47::i;:::-;1805:324;;;;:::o;7481:630:10:-;7640:25;7712:16;7825:17;:15;:17::i;:::-;7797:46;;:16;:46;;;7793:114;;;-1:-1:-1;7882:14:10;7793:114;7927:35;:33;:35::i;:::-;:47;;;7975:20;7927:69;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;7927:69:10;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;7927:69:10;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;7927:69:10;;;;;;;;;7916:80;-1:-1:-1;8014:31:10;;;8006:73;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7481:630:10;;;;:::o;6850:186::-;6962:67;6984:12;7006:8;7025:2;6962:21;:67::i;:::-;6850:186;;:::o;3177:571:24:-;3309:15;3326:23;3353:6;:11;;3365:8;3353:21;;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;3308:66:24;;;;3388:10;3384:315;;;3418:17;;3414:67;;3460:7;;;;3414:67;3498:10;:17;3519:2;3498:23;3494:195;;;3541:14;3558:35;3579:10;3591:1;3558:20;:35::i;:::-;3541:52;;3615:6;3625:1;3615:11;3611:64;;;3650:7;;;;;3611:64;3494:195;;3708:33;3730:10;3708:21;:33::i;1131:338::-;1284:121;;1260:21;;1320:31;;1284:121;;1365:7;;1386:9;;1284:121;;;;15929:220:29;16050:14;16097:21;16109:1;16112:5;16097:11;:21::i;:::-;16089:30;15929:220;-1:-1:-1;;;15929:220:29:o;1511:170:32:-;1654:9;1648:16;1641:4;1630:9;1626:20;1619:46;14133:679:29;14254:14;14299:5;14307:2;14299:10;14288:1;:8;:21;14284:297;;;14325:245;14347:222;14409:92;14519:1;:8;14545:5;14553:2;14545:10;14347:44;:222::i;:::-;14325:21;:245::i;:::-;-1:-1:-1;14759:13:29;14661:2;14759:13;14753:20;;14133:679::o;1292:378:30:-;1480:12;1232:10;1551:37;;1602:9;1625:6;1645:8;1515:148;;;;;;;;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;1515:148:30;;;49:4:-1;25:18;;61:17;;1515:148:30;182:15:-1;1515:148:30;;;;179:29:-1;;;;160:49;;;1515:148:30;-1:-1:-1;1292:378:30;;;;;:::o;1120:6993:10:-;;;;;;;;;-1:-1:-1;1120:6993:10;;;;;;;;;;;;;;;;;:::o;581:335:-1:-;;;695:3;688:4;680:6;676:17;672:27;662:2;;-1:-1;;703:12;662:2;-1:-1;733:20;;773:18;762:30;;759:2;;;-1:-1;;795:12;759:2;839:4;831:6;827:17;815:29;;889:3;839:4;870:16;831:6;856:31;;853:40;850:2;;;906:1;;896:12;850:2;655:261;;;;;;1393:257;;1505:2;1493:9;1484:7;1480:23;1476:32;1473:2;;;-1:-1;;1511:12;1473:2;230:6;217:20;242:41;277:5;242:41;;1657:867;;;;;;;1848:3;1836:9;1827:7;1823:23;1819:33;1816:2;;;-1:-1;;1855:12;1816:2;85:6;72:20;97:33;124:5;97:33;;;1907:63;-1:-1;2007:2;2046:22;;72:20;97:33;72:20;97:33;;;2015:63;-1:-1;2115:2;2154:22;;72:20;97:33;72:20;97:33;;;2123:63;-1:-1;2223:2;2262:22;;1182:20;;-1:-1;2359:3;2344:19;;2331:33;2384:18;2373:30;;2370:2;;;-1:-1;;2406:12;2370:2;2444:64;2500:7;2491:6;2480:9;2476:22;2444:64;;;1810:714;;;;-1:-1;1810:714;;-1:-1;1810:714;;2434:74;;1810:714;-1:-1;;;1810:714;2531:257;;2643:2;2631:9;2622:7;2618:23;2614:32;2611:2;;;-1:-1;;2649:12;2611:2;376:6;370:13;14052:5;11201:13;11194:21;14030:5;14027:32;14017:2;;-1:-1;;14063:12;2795:490;;;;2935:2;2923:9;2914:7;2910:23;2906:32;2903:2;;;-1:-1;;2941:12;2903:2;510:6;497:20;2993:63;;3121:2;3110:9;3106:18;3093:32;3145:18;3137:6;3134:30;3131:2;;;-1:-1;;3167:12;3131:2;3205:64;3261:7;3252:6;3241:9;3237:22;3205:64;;;2897:388;;3195:74;;-1:-1;3195:74;;-1:-1;;;;2897:388;3292:313;;3432:2;3420:9;3411:7;3407:23;3403:32;3400:2;;;-1:-1;;3438:12;3400:2;1033:6;1027:13;1045:58;1097:5;1045:58;;3612:263;;3727:2;3715:9;3706:7;3702:23;3698:32;3695:2;;;-1:-1;;3733:12;3695:2;-1:-1;1330:13;;3689:186;-1:-1;3689:186;5842:262;;4428:5;10565:12;-1:-1;13370:101;13384:6;13381:1;13378:13;13370:101;;;4572:4;13451:11;;;;;13445:18;13432:11;;;13425:39;13399:10;13370:101;;;13486:6;13483:1;13480:13;13477:2;;;-1:-1;13542:6;13537:3;13533:16;13526:27;13477:2;-1:-1;4603:16;;;;;5967:137;-1:-1;;5967:137;6111:213;11836:42;11825:54;;;;4102:37;;6229:2;6214:18;;6200:124;6567:324;11836:42;11825:54;;;;4102:37;;6877:2;6862:18;;5793:37;6713:2;6698:18;;6684:207;6898:209;11378:66;11367:78;;;;4220:36;;7014:2;6999:18;;6985:122;7658:501;7865:2;7850:18;;13678:1;13668:12;;13658:2;;13684:9;13658:2;5105:83;;;8062:2;8047:18;;5793:37;;;;8145:2;8130:18;;;5793:37;7836:323;;8166:407;8357:2;8371:47;;;5574:2;8342:18;;;10863:19;5610:66;10903:14;;;5590:87;5696:12;;;8328:245;8580:213;5793:37;;;8698:2;8683:18;;8669:124;8800:435;5793:37;;;9138:2;9123:18;;5793:37;;;;11836:42;11825:54;9221:2;9206:18;;4102:37;8974:2;8959:18;;8945:290;9242:787;5793:37;;;9673:2;9658:18;;5793:37;;;;9764:2;9749:18;;5279:58;;;;9847:2;9832:18;;5793:37;11836:42;11825:54;;;9930:3;9915:19;;4102:37;11825:54;10014:3;9999:19;;4102:37;9508:3;9493:19;;9479:550;10036:435;5793:37;;;10374:2;10359:18;;5793:37;;;;10457:2;10442:18;;5793:37;10210:2;10195:18;;10181:290;13707:117;11836:42;13794:5;11825:54;13769:5;13766:35;13756:2;;13815:1;;13805:12;13756:2;13750:74;
Swarm Source
bzzr://b36daa42e8d314c3085f196c752ce75ea20d5c8baf1e5c9a3eccfc61a5163bc5
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
BSC | 100.00% | $7.85 | 1 | $7.85 |
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.