Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
4353631 | 85 days ago | Contract Creation | 0 S |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
WeirollWallet
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
No with 5000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import { VM } from "../lib/enso-weiroll/contracts/VM.sol"; import { Clone } from "../lib/clones-with-immutable-args/src/Clone.sol"; import { IERC1271 } from "./interfaces/IERC1271.sol"; import { ECDSA } from "../lib/solady/src/utils/ECDSA.sol"; /// @title WeirollWallet /// @author Jack Corddry, Shivaansh Kapoor, CopyPaste /// @notice WeirollWallet implementation contract. /// @notice Implements a simple smart contract wallet that can execute Weiroll VM commands contract WeirollWallet is IERC1271, Clone, VM { // Returned to indicate a valid ERC1271 signature bytes4 internal constant ERC1271_MAGIC_VALUE = 0x1626ba7e; // bytes4(keccak256("isValidSignature(bytes32,bytes)") // Returned to indicate an invalid ERC1271 signature bytes4 internal constant INVALID_SIGNATURE = 0x00000000; /// @notice Let the Weiroll Wallet receive ether directly if needed receive() external payable { } /// @notice Also allow a fallback with no logic if erroneous data is provided fallback() external payable { } /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ // Emit when owner executes an arbitrary script (not a market script) event WeirollWalletExecutedManually(); error NotOwner(); error NotRecipeMarketHub(); error WalletLocked(); error WalletNotForfeitable(); error OfferUnfilled(); error RawExecutionFailed(); /// @notice Only the owner of the contract can call the function modifier onlyOwner() { if (msg.sender != owner()) { revert NotOwner(); } _; } /// @notice Only the recipeMarketHub contract can call the function modifier onlyRecipeMarketHub() { if (msg.sender != recipeMarketHub()) { revert NotRecipeMarketHub(); } _; } /// @notice The wallet can be locked modifier notLocked() { if (!forfeited && lockedUntil() > block.timestamp) { revert WalletLocked(); } _; } /*////////////////////////////////////////////////////////////// STATE VARIABLES //////////////////////////////////////////////////////////////*/ /// @dev Whether or not this offer has been executed bool public executed; /// @dev Whether or not the wallet has been forfeited bool public forfeited; /// @notice Forfeit all rewards to get control of the wallet back function forfeit() public onlyRecipeMarketHub { if (!isForfeitable() || block.timestamp >= lockedUntil()) { // Can't forfeit if: // 1. Wallet not created through a forfeitable market // 2. Lock time has passed and claim window has started revert WalletNotForfeitable(); } forfeited = true; } /// @notice The address of the offer creator (owner) function owner() public pure returns (address) { return _getArgAddress(0); } /// @notice The address of the RecipeMarketHub contract function recipeMarketHub() public pure returns (address) { return _getArgAddress(20); } /// @notice The amount of tokens deposited into this wallet from the recipeMarketHub function amount() public pure returns (uint256) { return _getArgUint256(40); } /// @notice The timestamp after which the wallet may be interacted with function lockedUntil() public pure returns (uint256) { return _getArgUint256(72); } /// @notice Returns whether or not the wallet is forfeitable function isForfeitable() public pure returns (bool) { return _getArgUint8(104) != 0; } /// @notice Returns the hash of the market associated with this weiroll wallet function marketHash() public pure returns (bytes32) { return bytes32(_getArgUint256(105)); } /*////////////////////////////////////////////////////////////// EXECUTION LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Execute the Weiroll VM with the given commands. /// @param commands The commands to be executed by the Weiroll VM. function executeWeiroll(bytes32[] calldata commands, bytes[] calldata state) public payable onlyRecipeMarketHub returns (bytes[] memory) { executed = true; // Execute the Weiroll VM. return _execute(commands, state); } /// @notice Execute the Weiroll VM with the given commands. /// @param commands The commands to be executed by the Weiroll VM. function manualExecuteWeiroll(bytes32[] calldata commands, bytes[] calldata state) public payable onlyOwner notLocked returns (bytes[] memory) { // Prevent people from approving w/e then rugging during vesting if (!executed) revert OfferUnfilled(); emit WeirollWalletExecutedManually(); // Execute the Weiroll VM. return _execute(commands, state); } /// @notice Execute a generic call to another contract. /// @param to The address to call /// @param value The ether value of the execution /// @param data The data to pass along with the call function execute(address to, uint256 value, bytes memory data) public payable onlyOwner notLocked returns (bytes memory) { // Prevent people from approving w/e then rugging during vesting if (!executed) revert OfferUnfilled(); // Execute the call. (bool success, bytes memory result) = to.call{ value: value }(data); if (!success) revert RawExecutionFailed(); emit WeirollWalletExecutedManually(); return result; } /// @notice Check if signature is valid for this contract /// @dev Signature is valid if the signer is the owner of this wallet /// @param digest Hash of the message to validate the signature against /// @param signature Signature produced for the provided digest function isValidSignature(bytes32 digest, bytes calldata signature) external view returns (bytes4) { // Modify digest to include the chainId and address of this wallet to prevent replay attacks bytes32 walletSpecificDigest = keccak256(abi.encode(digest, block.chainid, address(this))); // Check if signature was produced by owner of this wallet // Don't revert on failure. Simply return INVALID_SIGNATURE. if (ECDSA.tryRecover(walletSpecificDigest, signature) == owner()) return ERC1271_MAGIC_VALUE; else return INVALID_SIGNATURE; } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.16; import "./CommandBuilder.sol"; abstract contract VM { using CommandBuilder for bytes[]; uint256 constant FLAG_CT_DELEGATECALL = 0x00; // Delegate call not currently supported uint256 constant FLAG_CT_CALL = 0x01; uint256 constant FLAG_CT_STATICCALL = 0x02; uint256 constant FLAG_CT_VALUECALL = 0x03; uint256 constant FLAG_CT_MASK = 0x03; uint256 constant FLAG_DATA = 0x20; uint256 constant FLAG_EXTENDED_COMMAND = 0x40; uint256 constant FLAG_TUPLE_RETURN = 0x80; uint256 constant SHORT_COMMAND_FILL = 0x000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; error ExecutionFailed( uint256 command_index, address target, string message ); function _execute(bytes32[] calldata commands, bytes[] memory state) internal returns (bytes[] memory) { bytes32 command; uint256 flags; bytes32 indices; bool success; bytes memory outData; uint256 commandsLength = commands.length; uint256 indicesLength; for (uint256 i; i < commandsLength; i = _uncheckedIncrement(i)) { command = commands[i]; flags = uint256(uint8(bytes1(command << 32))); if (flags & FLAG_EXTENDED_COMMAND != 0) { i = _uncheckedIncrement(i); indices = commands[i]; indicesLength = 32; } else { indices = bytes32(uint256(command << 40) | SHORT_COMMAND_FILL); indicesLength = 6; } if (flags & FLAG_CT_MASK == FLAG_CT_CALL) { (success, outData) = address(uint160(uint256(command))).call( // target // inputs flags & FLAG_DATA == 0 ? state.buildInputs( bytes4(command), // selector indices, indicesLength ) : state[ uint8(bytes1(indices)) & CommandBuilder.IDX_VALUE_MASK ] ); } else if (flags & FLAG_CT_MASK == FLAG_CT_STATICCALL) { (success, outData) = address(uint160(uint256(command))) // target .staticcall( // inputs flags & FLAG_DATA == 0 ? state.buildInputs( bytes4(command), // selector indices, indicesLength ) : state[ uint8(bytes1(indices)) & CommandBuilder.IDX_VALUE_MASK ] ); } else if (flags & FLAG_CT_MASK == FLAG_CT_VALUECALL) { bytes memory v = state[ uint8(bytes1(indices)) & CommandBuilder.IDX_VALUE_MASK ]; require(v.length == 32, "Value must be 32 bytes"); uint256 callEth = uint256(bytes32(v)); (success, outData) = address(uint160(uint256(command))).call{ // target value: callEth }( // inputs flags & FLAG_DATA == 0 ? state.buildInputs( bytes4(command), // selector indices << 8, // skip value input indicesLength - 1 // max indices length reduced by value input ) : state[ uint8(bytes1(indices << 8)) & // first byte after value input CommandBuilder.IDX_VALUE_MASK ] ); } else { revert("Invalid calltype"); } if (!success) { string memory message = "Unknown"; if (outData.length > 68) { // This might be an error message, parse the outData // Estimate the bytes length of the possible error message uint256 estimatedLength = _estimateBytesLength(outData, 68); // Remove selector. First 32 bytes should be a pointer that indicates the start of data in memory assembly { outData := add(outData, 4) } uint256 pointer = uint256(bytes32(outData)); if (pointer == 32) { // Remove pointer. If it is a string, the next 32 bytes will hold the size assembly { outData := add(outData, 32) } uint256 size = uint256(bytes32(outData)); // If the size variable is the same as the estimated bytes length, we can be fairly certain // this is a dynamic string, so convert the bytes to a string and emit the message. While an // error function with 3 static parameters is capable of producing a similar output, there is // low risk of a contract unintentionally emitting a message. if (size == estimatedLength) { // Remove size. The remaining data should be the string content assembly { outData := add(outData, 32) } message = string(outData); } } } revert ExecutionFailed({ command_index: flags & FLAG_EXTENDED_COMMAND == 0 ? i : i - 1, target: address(uint160(uint256(command))), message: message }); } if (flags & FLAG_TUPLE_RETURN != 0) { state.writeTuple(bytes1(command << 88), outData); } else { state = state.writeOutputs(bytes1(command << 88), outData); } } return state; } function _estimateBytesLength(bytes memory data, uint256 pos) internal pure returns (uint256 estimate) { uint256 length = data.length; estimate = length - pos; // Assume length equals alloted space for (uint256 i = pos; i < length; ) { if (data[i] == 0) { // Zero bytes found, adjust estimated length estimate = i - pos; break; } unchecked { ++i; } } } function _uncheckedIncrement(uint256 i) private pure returns (uint256) { unchecked { ++i; } return i; } }
// SPDX-License-Identifier: BSD pragma solidity ^0.8.4; /// @title Clone /// @author zefram.eth /// @notice Provides helper functions for reading immutable args from calldata contract Clone { /// @notice Reads an immutable arg with type address /// @param argOffset The offset of the arg in the packed data /// @return arg The arg value function _getArgAddress(uint256 argOffset) internal pure returns (address arg) { uint256 offset = _getImmutableArgsOffset(); assembly { arg := shr(0x60, calldataload(add(offset, argOffset))) } } /// @notice Reads an immutable arg with type uint256 /// @param argOffset The offset of the arg in the packed data /// @return arg The arg value function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) { uint256 offset = _getImmutableArgsOffset(); // solhint-disable-next-line no-inline-assembly assembly { arg := calldataload(add(offset, argOffset)) } } /// @notice Reads an immutable arg with type uint64 /// @param argOffset The offset of the arg in the packed data /// @return arg The arg value function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) { uint256 offset = _getImmutableArgsOffset(); // solhint-disable-next-line no-inline-assembly assembly { arg := shr(0xc0, calldataload(add(offset, argOffset))) } } /// @notice Reads an immutable arg with type uint8 /// @param argOffset The offset of the arg in the packed data /// @return arg The arg value function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) { uint256 offset = _getImmutableArgsOffset(); // solhint-disable-next-line no-inline-assembly assembly { arg := shr(0xf8, calldataload(add(offset, argOffset))) } } /// @return offset The offset of the packed immutable args in calldata function _getImmutableArgsOffset() internal pure returns (uint256 offset) { // solhint-disable-next-line no-inline-assembly assembly { offset := sub( calldatasize(), add(shr(240, calldataload(sub(calldatasize(), 2))), 2) ) } } }
/// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title IERC1271 /// @notice Interface defined by EIP-1271 /// @dev Interface for verifying contract account signatures interface IERC1271 { /// @notice Returns whether the provided signature is valid for the provided data /// @dev Returns 0x1626ba7e (magic value) when function passes. /// @param digest Hash of the message to validate the signature against /// @param signature Signature produced for the provided digest function isValidSignature(bytes32 digest, bytes memory signature) external view returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Gas optimized ECDSA wrapper. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) /// /// @dev Note: /// - The recovery functions use the ecrecover precompile (0x1). /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure. /// This is for more safety by default. /// Use the `tryRecover` variants if you need to get the zero address back /// upon recovery failure instead. /// - As of Solady version 0.0.134, all `bytes signature` variants accept both /// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. /// See: https://eips.ethereum.org/EIPS/eip-2098 /// This is for calldata efficiency on smart accounts prevalent on L2s. /// /// WARNING! Do NOT use signatures as unique identifiers: /// - Use a nonce in the digest to prevent replay attacks on the same contract. /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. /// EIP-712 also enables readable signing of typed data for better user safety. /// This implementation does NOT check if a signature is non-malleable. library ECDSA { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The signature is invalid. error InvalidSignature(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RECOVERY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. for {} 1 {} { mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. if eq(mload(signature), 64) { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(mload(signature), 65) { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. break } result := 0 break } result := mload( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) for {} 1 {} { if eq(signature.length, 64) { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(signature.length, 65) { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. break } result := 0 break } result := mload( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. result := mload( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) result := mload( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* TRY-RECOVER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // WARNING! // These functions will NOT revert upon recovery failure. // Instead, they will return the zero address upon recovery failure. // It is critical that the returned address is NEVER compared against // a zero address (e.g. an uninitialized address variable). /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. for {} 1 {} { mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. if eq(mload(signature), 64) { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(mload(signature), 65) { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. break } result := 0 break } pop( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) for {} 1 {} { if eq(signature.length, 64) { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(signature.length, 65) { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. break } result := 0 break } pop( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. pop( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) pop( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HASHING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an Ethereum Signed Message, created from a `hash`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) /// JSON-RPC method as part of EIP-191. function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, hash) // Store into scratch space for keccak256. mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. } } /// @dev Returns an Ethereum Signed Message, created from `s`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) /// JSON-RPC method as part of EIP-191. /// Note: Supports lengths of `s` up to 999999 bytes. function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let sLength := mload(s) let o := 0x20 mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. mstore(0x00, 0x00) // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. for { let temp := sLength } 1 {} { o := sub(o, 1) mstore8(o, add(48, mod(temp, 10))) temp := div(temp, 10) if iszero(temp) { break } } let n := sub(0x3a, o) // Header length: `26 + 32 - o`. // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) mstore(s, sLength) // Restore the length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes. function emptySignature() internal pure returns (bytes calldata signature) { /// @solidity memory-safe-assembly assembly { signature.length := 0 } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.16; library CommandBuilder { uint256 constant IDX_VARIABLE_LENGTH = 0x80; uint256 constant IDX_VALUE_MASK = 0x7f; uint256 constant IDX_END_OF_ARGS = 0xff; uint256 constant IDX_USE_STATE = 0xfe; uint256 constant IDX_ARRAY_START = 0xfd; uint256 constant IDX_TUPLE_START = 0xfc; uint256 constant IDX_DYNAMIC_END = 0xfb; function buildInputs( bytes[] memory state, bytes4 selector, bytes32 indices, uint256 indicesLength ) internal view returns (bytes memory ret) { uint256 idx; // The current command index uint256 offsetIdx; // The index of the current free offset uint256 count; // Number of bytes in whole ABI encoded message uint256 free; // Pointer to first free byte in tail part of message uint256[] memory dynamicLengths = new uint256[](10); // Optionally store the length of all dynamic types (a command cannot fit more than 10 dynamic types) bytes memory stateData; // Optionally encode the current state if the call requires it // Determine the length of the encoded data for (uint256 i; i < indicesLength; ) { idx = uint8(indices[i]); if (idx == IDX_END_OF_ARGS) { indicesLength = i; break; } if (idx & IDX_VARIABLE_LENGTH != 0) { if (idx == IDX_USE_STATE) { if (stateData.length == 0) { stateData = abi.encode(state); } unchecked { count += stateData.length; } } else { (dynamicLengths, offsetIdx, count, i) = setupDynamicType( state, indices, dynamicLengths, idx, offsetIdx, count, i ); } } else { count = setupStaticVariable(state, count, idx); } unchecked { free += 32; ++i; } } // Encode it ret = new bytes(count + 4); assembly { mstore(add(ret, 32), selector) } offsetIdx = 0; // Use count to track current memory slot assembly { count := add(ret, 36) } for (uint256 i; i < indicesLength; ) { idx = uint8(indices[i]); if (idx & IDX_VARIABLE_LENGTH != 0) { if (idx == IDX_USE_STATE) { assembly { mstore(count, free) } memcpy(stateData, 32, ret, free + 4, stateData.length - 32); unchecked { free += stateData.length - 32; } } else if (idx == IDX_ARRAY_START) { // Start of dynamic type, put pointer in current slot assembly { mstore(count, free) } (offsetIdx, free, i, ) = encodeDynamicArray( ret, state, indices, dynamicLengths, offsetIdx, free, i ); } else if (idx == IDX_TUPLE_START) { // Start of dynamic type, put pointer in current slot assembly { mstore(count, free) } (offsetIdx, free, i, ) = encodeDynamicTuple( ret, state, indices, dynamicLengths, offsetIdx, free, i ); } else { // Variable length data uint256 argLen = state[idx & IDX_VALUE_MASK].length; // Put a pointer in the current slot and write the data to first free slot assembly { mstore(count, free) } memcpy( state[idx & IDX_VALUE_MASK], 0, ret, free + 4, argLen ); unchecked { free += argLen; } } } else { // Fixed length data (length previously checked to be 32 bytes) bytes memory stateVar = state[idx & IDX_VALUE_MASK]; // Write the data to current slot assembly { mstore(count, mload(add(stateVar, 32))) } } unchecked { count += 32; ++i; } } } function setupStaticVariable( bytes[] memory state, uint256 count, uint256 idx ) internal pure returns (uint256 newCount) { require( state[idx & IDX_VALUE_MASK].length == 32, "Static state variables must be 32 bytes" ); unchecked { newCount = count + 32; } } function setupDynamicVariable( bytes[] memory state, uint256 count, uint256 idx ) internal pure returns (uint256 newCount) { bytes memory arg = state[idx & IDX_VALUE_MASK]; // Validate the length of the data in state is a multiple of 32 uint256 argLen = arg.length; require( argLen != 0 && argLen % 32 == 0, "Dynamic state variables must be a multiple of 32 bytes" ); // Add the length of the value, rounded up to the next word boundary, plus space for pointer unchecked { newCount = count + argLen + 32; } } function setupDynamicType( bytes[] memory state, bytes32 indices, uint256[] memory dynamicLengths, uint256 idx, uint256 offsetIdx, uint256 count, uint256 index ) internal view returns ( uint256[] memory newDynamicLengths, uint256 newOffsetIdx, uint256 newCount, uint256 newIndex ) { if (idx == IDX_ARRAY_START) { (newDynamicLengths, newOffsetIdx, newCount, newIndex) = setupDynamicArray( state, indices, dynamicLengths, offsetIdx, count, index ); } else if (idx == IDX_TUPLE_START) { (newDynamicLengths, newOffsetIdx, newCount, newIndex) = setupDynamicTuple( state, indices, dynamicLengths, offsetIdx, count, index ); } else { newDynamicLengths = dynamicLengths; newOffsetIdx = offsetIdx; newIndex = index; newCount = setupDynamicVariable(state, count, idx); } } function setupDynamicArray( bytes[] memory state, bytes32 indices, uint256[] memory dynamicLengths, uint256 offsetIdx, uint256 count, uint256 index ) internal view returns ( uint256[] memory newDynamicLengths, uint256 newOffsetIdx, uint256 newCount, uint256 newIndex ) { // Current idx is IDX_ARRAY_START, next idx will contain the array length unchecked { newIndex = index + 1; newCount = count + 32; } uint256 idx = uint8(indices[newIndex]); require( state[idx & IDX_VALUE_MASK].length == 32, "Array length must be 32 bytes" ); (newDynamicLengths, newOffsetIdx, newCount, newIndex) = setupDynamicTuple( state, indices, dynamicLengths, offsetIdx, newCount, newIndex ); } function setupDynamicTuple( bytes[] memory state, bytes32 indices, uint256[] memory dynamicLengths, uint256 offsetIdx, uint256 count, uint256 index ) internal view returns ( uint256[] memory newDynamicLengths, uint256 newOffsetIdx, uint256 newCount, uint256 newIndex ) { uint256 idx; uint256 offset; newDynamicLengths = dynamicLengths; // Progress to first index of the data and progress the next offset idx unchecked { newIndex = index + 1; newOffsetIdx = offsetIdx + 1; newCount = count + 32; } while (newIndex < 32) { idx = uint8(indices[newIndex]); if (idx & IDX_VARIABLE_LENGTH != 0) { if (idx == IDX_DYNAMIC_END) { newDynamicLengths[offsetIdx] = offset; // explicit return saves gas ¯\_(ツ)_/¯ return (newDynamicLengths, newOffsetIdx, newCount, newIndex); } else { require(idx != IDX_USE_STATE, "Cannot use state from inside dynamic type"); (newDynamicLengths, newOffsetIdx, newCount, newIndex) = setupDynamicType( state, indices, newDynamicLengths, idx, newOffsetIdx, newCount, newIndex ); } } else { newCount = setupStaticVariable(state, newCount, idx); } unchecked { offset += 32; ++newIndex; } } revert("Dynamic type was not properly closed"); } function encodeDynamicArray( bytes memory ret, bytes[] memory state, bytes32 indices, uint256[] memory dynamicLengths, uint256 offsetIdx, uint256 currentSlot, uint256 index ) internal view returns ( uint256 newOffsetIdx, uint256 newSlot, uint256 newIndex, uint256 length ) { // Progress to array length metadata unchecked { newIndex = index + 1; newSlot = currentSlot + 32; } // Encode array length uint256 idx = uint8(indices[newIndex]); // Array length value previously checked to be 32 bytes bytes memory stateVar = state[idx & IDX_VALUE_MASK]; assembly { mstore(add(add(ret, 36), currentSlot), mload(add(stateVar, 32))) } (newOffsetIdx, newSlot, newIndex, length) = encodeDynamicTuple( ret, state, indices, dynamicLengths, offsetIdx, newSlot, newIndex ); unchecked { length += 32; // Increase length to account for array length metadata } } function encodeDynamicTuple( bytes memory ret, bytes[] memory state, bytes32 indices, uint256[] memory dynamicLengths, uint256 offsetIdx, uint256 currentSlot, uint256 index ) internal view returns ( uint256 newOffsetIdx, uint256 newSlot, uint256 newIndex, uint256 length ) { uint256 idx; uint256 argLen; uint256 freePointer = dynamicLengths[offsetIdx]; // The pointer to the next free slot unchecked { newSlot = currentSlot + freePointer; // Update the next slot newOffsetIdx = offsetIdx + 1; // Progress to next offsetIdx newIndex = index + 1; // Progress to first index of the data } // Shift currentSlot to correct location in memory assembly { currentSlot := add(add(ret, 36), currentSlot) } while (newIndex < 32) { idx = uint8(indices[newIndex]); if (idx & IDX_VARIABLE_LENGTH != 0) { if (idx == IDX_DYNAMIC_END) { break; } else if (idx == IDX_ARRAY_START) { // Start of dynamic type, put pointer in current slot assembly { mstore(currentSlot, freePointer) } (newOffsetIdx, newSlot, newIndex, argLen) = encodeDynamicArray( ret, state, indices, dynamicLengths, newOffsetIdx, newSlot, newIndex ); unchecked { freePointer += argLen; length += (argLen + 32); // data + pointer } } else if (idx == IDX_TUPLE_START) { // Start of dynamic type, put pointer in current slot assembly { mstore(currentSlot, freePointer) } (newOffsetIdx, newSlot, newIndex, argLen) = encodeDynamicTuple( ret, state, indices, dynamicLengths, newOffsetIdx, newSlot, newIndex ); unchecked { freePointer += argLen; length += (argLen + 32); // data + pointer } } else { // Variable length data argLen = state[idx & IDX_VALUE_MASK].length; // Start of dynamic type, put pointer in current slot assembly { mstore(currentSlot, freePointer) } memcpy( state[idx & IDX_VALUE_MASK], 0, ret, newSlot + 4, argLen ); unchecked { newSlot += argLen; freePointer += argLen; length += (argLen + 32); // data + pointer } } } else { // Fixed length data (length previously checked to be 32 bytes) bytes memory stateVar = state[idx & IDX_VALUE_MASK]; // Write to first free slot assembly { mstore(currentSlot, mload(add(stateVar, 32))) } unchecked { length += 32; } } unchecked { currentSlot += 32; ++newIndex; } } } function writeOutputs( bytes[] memory state, bytes1 index, bytes memory output ) internal pure returns (bytes[] memory) { uint256 idx = uint8(index); if (idx == IDX_END_OF_ARGS) return state; if (idx & IDX_VARIABLE_LENGTH != 0) { if (idx == IDX_USE_STATE) { state = abi.decode(output, (bytes[])); } else { require(idx & IDX_VALUE_MASK < state.length, "Index out-of-bounds"); // Check the first field is 0x20 (because we have only a single return value) uint256 argPtr; assembly { argPtr := mload(add(output, 32)) } require( argPtr == 32, "Only one return value permitted (variable)" ); assembly { // Overwrite the first word of the return data with the length - 32 mstore(add(output, 32), sub(mload(output), 32)) // Insert a pointer to the return data, starting at the second word, into state mstore( add(add(state, 32), mul(and(idx, IDX_VALUE_MASK), 32)), add(output, 32) ) } } } else { require(idx & IDX_VALUE_MASK < state.length, "Index out-of-bounds"); // Single word require( output.length == 32, "Only one return value permitted (static)" ); state[idx & IDX_VALUE_MASK] = output; } return state; } function writeTuple( bytes[] memory state, bytes1 index, bytes memory output ) internal view { uint256 idx = uint8(index); if (idx == IDX_END_OF_ARGS) return; bytes memory entry = state[idx & IDX_VALUE_MASK] = new bytes(output.length + 32); memcpy(output, 0, entry, 32, output.length); assembly { let l := mload(output) mstore(add(entry, 32), l) } } function memcpy( bytes memory src, uint256 srcIdx, bytes memory dest, uint256 destIdx, uint256 len ) internal view { assembly { pop( staticcall( gas(), 4, add(add(src, 32), srcIdx), len, add(add(dest, 32), destIdx), len ) ) } } }
{ "remappings": [ "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "clones-with-immutable-args/=lib/clones-with-immutable-args/src/", "ds-test/=lib/solmate/lib/ds-test/src/", "enso-weiroll/=lib/enso-weiroll/contracts/", "erc4626-tests/=lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solady/=lib/solady/src/", "solmate/=lib/solmate/src/" ], "optimizer": { "enabled": false, "runs": 5000, "details": { "constantOptimizer": true, "yul": true, "yulDetails": { "stackAllocation": true } } }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": false }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"command_index","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"string","name":"message","type":"string"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotRecipeMarketHub","type":"error"},{"inputs":[],"name":"OfferUnfilled","type":"error"},{"inputs":[],"name":"RawExecutionFailed","type":"error"},{"inputs":[],"name":"WalletLocked","type":"error"},{"inputs":[],"name":"WalletNotForfeitable","type":"error"},{"anonymous":false,"inputs":[],"name":"WeirollWalletExecutedManually","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"amount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"commands","type":"bytes32[]"},{"internalType":"bytes[]","name":"state","type":"bytes[]"}],"name":"executeWeiroll","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"executed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forfeit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forfeited","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isForfeitable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"digest","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedUntil","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"commands","type":"bytes32[]"},{"internalType":"bytes[]","name":"state","type":"bytes[]"}],"name":"manualExecuteWeiroll","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"marketHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"recipeMarketHub","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60808060405234601557611deb908161001a8239f35b5f80fdfe60806040526004361015610010575b005b5f3560e01c80630e4ab719146100df5780631626ba7e146100da57806324b8b1e6146100d55780632e1cc2f6146100d057806331574546146100cb57806331a38c89146100c657806367a34844146100c15780636d710c72146100bc5780638da5cb5b146100b7578063aa8c217c146100b2578063b61d27f6146100ad578063ce0617ec146100a85763f3d86e4a0361000e57610894565b610866565b6106d5565b6105c0565b610592565b610566565b61053a565b610511565b6104de565b6104ad565b610375565b6102eb565b610223565b9181601f8401121561011d5782359167ffffffffffffffff8311610119576020808501948460051b01011161011557565b5f80fd5b5f80fd5b5f80fd5b604060031982011261017d5760043567ffffffffffffffff8111610179578161014c916004016100e4565b929092916024359067ffffffffffffffff82116101755761016f916004016100e4565b91909190565b5f80fd5b5f80fd5b5f80fd5b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b8383106101d857505050505090565b9091929394602080610214837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc086600196030187528951610181565b970193019301919392906101c9565b61022c36610121565b9290916014610239611862565b013560601c33036102955761027f610285936102919560017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff005f5416175f553691610983565b91610d77565b604051918291826101a6565b0390f35b7fb2717667000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190917fffffffff000000000000000000000000000000000000000000000000000000006020820193169052565b3461037157604060031936011261036d5760243567ffffffffffffffff811161036957366023820112156103655780600401359067ffffffffffffffff821161036157366024838301011161035d5761035991602461034d92016004356109f7565b604051918291826102bd565b0390f35b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b61037e36610121565b929091610389611862565b803560601c330361046c575f549060ff8260081c1615908161045d575b50610435576103b76103bd91610b1d565b1561050c565b61040d576103f76103fd93610409957f19f0b89b5c0f05620d67b659885ae073c29fa80357783988104f4daa4ecfd5115f80a13691610983565b91610d77565b604051918291826101a6565b0390f35b7f05fee1e2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fd550ed24000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050604842910135115f6103a6565b7f30cd7471000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff1690565b346104da575f6003193601126104d657602060146104c9611862565b013560601c604051908152f35b5f80fd5b5f80fd5b34610508575f60031936011261050457602060696104fa611862565b0135604051908152f35b5f80fd5b5f80fd5b151590565b34610536575f60031936011261053257602060ff5f54166040519015158152f35b5f80fd5b5f80fd5b34610562575f60031936011261055e57602060ff5f5460081c166040519015158152f35b5f80fd5b5f80fd5b3461058e575f60031936011261058a576020610580610b23565b6040519015158152f35b5f80fd5b5f80fd5b346105bc575f6003193601126105b85760206105ac611862565b3560601c604051908152f35b5f80fd5b5f80fd5b346105ea575f6003193601126105e657602060286105dc611862565b0135604051908152f35b5f80fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f601f19910116810190811067ffffffffffffffff82111761063e57604052565b6105ee565b67ffffffffffffffff811161066057601f19601f60209201160190565b6105ee565b92919261067182610643565b9161067f604051938461061b565b82948184528183011161069b578281602093845f960137010152565b5f80fd5b9080601f830112156106bd578160206106ba93359101610665565b90565b5f80fd5b9060206106d2928181520190610181565b90565b60606003193601126108625760043573ffffffffffffffffffffffffffffffffffffffff8116810361085e5760443567ffffffffffffffff811161085a5761072190369060040161069f565b9061072a611862565b803560601c3303610832575f549060ff8260081c16159081610823575b506107fb5760ff16156107d3575f82819260208251920190602435905af161076d610b37565b90156107ab576107a7906040519182917f19f0b89b5c0f05620d67b659885ae073c29fa80357783988104f4daa4ecfd5115f80a1826106c1565b0390f35b7f2cb48aa6000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f05fee1e2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fd550ed24000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050604842910135115f610747565b7f30cd7471000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f80fd5b5f80fd5b5f80fd5b34610890575f60031936011261088c5760206048610882611862565b0135604051908152f35b5f80fd5b5f80fd5b34610961575f60031936011261095d576108ac611862565b601481013560601c3303610935576108c2610b23565b15908115610926575b506108fe576101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff5f5416175f555f80f35b7fb44bb110000000000000000000000000000000000000000000000000000000005f5260045ffd5b6048915001354210155f6108cb565b7fb2717667000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f80fd5b5f80fd5b67ffffffffffffffff811161097e5760209060051b0190565b6105ee565b92919061098f81610965565b9361099d604051958661061b565b602085838152019160051b8101918383116109f35781905b8382106109c3575050505050565b813567ffffffffffffffff81116109ef576020916109e4878493870161069f565b8152019101906109b5565b5f80fd5b5f80fd5b90610a2c916040516020810191825246604082015230606082015260608152610a2160808261061b565b519020923691610665565b6001906040519260208201905f52516040526040815114610ade576041815114610ac15750506020604060805f805b5afa505f6060523d606018519060405273ffffffffffffffffffffffffffffffffffffffff610a90610a8b611155565b610494565b9116145f14610abd577f1626ba7e0000000000000000000000000000000000000000000000000000000090565b5f90565b60805f602093604084606082960151841a87520151606052610a5b565b60805f6020937f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604080950151601b8160ff1c01875216606052610a5b565b60ff1690565b6068610b2d611862565b013560f81c151590565b3d5f14610b64573d90610b4982610643565b91610b57604051938461061b565b8252815f60203d92013e5b565b606090610b62565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190811015610ba95760051b0190565b610b6c565b60201b90565b60281b90565b60081b90565b60581b90565b7fff000000000000000000000000000000000000000000000000000000000000001690565b60f81c90565b60609060208152601060208201527f496e76616c69642063616c6c747970650000000000000000000000000000000060408201520190565b8051821015610c3d5760209160051b010190565b610b6c565b15610c4957565b606460405162461bcd60e51b815260206004820152601660248201527f56616c7565206d757374206265203332206279746573000000000000000000006044820152fd5b602081519101519060208110610ca2575b5090565b5f199060200360031b1b165f610c9e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b905f198201918211610cee57565b610cb3565b90601f198201918211610d0257565b610cb3565b60405190610d1660408361061b565b600782527f556e6b6e6f776e000000000000000000000000000000000000000000000000006020830152565b73ffffffffffffffffffffffffffffffffffffffff610d74949360609383521660208201528160408201520190610181565b90565b905f5b818110610d875750505090565b610d92818385610b99565b3590610db5610db0610dab610da685610bae565b610bc6565b610beb565b610b1d565b9160408316159182155f1461112757610dcd90611164565b92610dd9848688610b99565b356020905b60038316600181145f14610f9757505f9190829160208516158314610f8057610e2a917fffffffff0000000000000000000000000000000000000000000000000000000087168c611204565b5b6020815191018273ffffffffffffffffffffffffffffffffffffffff87165af192610e54610b37565b935b849015610eba57505060801615155f14610e9457610e8f9291610e83610e7e610e8993610bc0565b610bc6565b87611803565b5b611164565b610d7a565b90610e8f9295610eae610ea9610eb494610bc0565b610bc6565b9061174a565b93610e8a565b610f27858773ffffffffffffffffffffffffffffffffffffffff948794610edf610d07565b936044815111610f39575b50505f14610f2b575b6040519485947fef3dcb2f000000000000000000000000000000000000000000000000000000008652169060048501610d42565b0390fd5b610f3490610ce0565b610ef3565b6020610f506004610f4984611465565b9301610c8d565b14610f5b575b610eea565b610f6760248301610c8d565b14610f73575b80610f56565b6044919350019186610f6d565b50607f610f919160f81c168a610c29565b51610e2b565b600281145f1461102657505f919082916020851615831461100f57610fdf917fffffffff0000000000000000000000000000000000000000000000000000000087168c611204565b5b60208151910173ffffffffffffffffffffffffffffffffffffffff86165afa92611008610b37565b935b610e56565b50607f6110209160f81c168a610c29565b51610fe0565b6003145f1461110a575f9181611059611046607f869560f81c168d610c29565b516110546020825114610c42565b610c8d565b91602086161584146110d55761107a6110746110a493610bba565b91610ce0565b907fffffffff0000000000000000000000000000000000000000000000000000000088168d611204565b905b6020825192019073ffffffffffffffffffffffffffffffffffffffff87165af1926110cf610b37565b9361100a565b50607f6110fc6110f76110f26110ed61110395610bba565b610bc6565b610beb565b610b1d565b168b610c29565b51906110a6565b60405162461bcd60e51b81528061112360048201610bf1565b0390fd5b9279ffffffffffffffffffffffffffffffffffffffffffffffffffff61114c83610bb4565b17600690610dde565b61115d611862565b3560601c90565b60010190565b67ffffffffffffffff600a116111bb576020600a60051b0190604051611190838261061b565b600a8152809267ffffffffffffffff600a116111b65780601f1991500190602036910137565b6105ee565b6105ee565b90600482018092116111ce57565b610cb3565b906111dd82610643565b6111ea604051918261061b565b828152601f196111fa8294610643565b0190602036910137565b919093925f915f925f9361121661116a565b906060925f905b868210611397575b5050611233611238916111c0565b6111d3565b9760208901525f9060248901935f965b86881061125a57505050505050505050565b6020881015611392578a89838a1a6080811615155f146113705760fe81145f146112ba575050601f19826112a96020936001958b52611298836111c0565b6112a28b51610cf3565b918b611a14565b87510101965b5b0197019694611248565b60fd819b96989b97949597939293145f146112f75750926112ea92878760019c9997948b99978560209d52611b85565b509891969096945b6112af565b60fc81145f1461132957509261131c92878760019c9997948b99978560209d52611a27565b509891969096945b6112f2565b93611369898293611358602096999e9b99607f6001999c9a169061134d8282610c29565b515197889552610c29565b5190611363856111c0565b91611a02565b0196611324565b60209250600193989150611388607f8492168d610c29565b51015181526112b0565b610b6c565b9094969160208610156114605787861a60ff811461144a576080811615155f146114355760fe81145f1461141557508451156113e5575b6020600191865101935b5b0195019096949661121d565b93506001602060405161140c816113fe8d8583016101a6565b03601f19810183528261061b565b959150506113ce565b906114299260019792602095968b8d611918565b969193909392946113d8565b6114446020916001938c611890565b936113d9565b5095505092611233611238919591969496611225565b610b6c565b9081517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8101818111611524579260445b8281106114a3575b505050565b815181101561151f577fff000000000000000000000000000000000000000000000000000000000000006020828401015116156114e257600101611496565b92935050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc810190811161151a57905f808061149e565b610cb3565b610b6c565b610cb3565b1561153057565b606460405162461bcd60e51b815260206004820152601360248201527f496e646578206f75742d6f662d626f756e6473000000000000000000000000006044820152fd5b1561157b57565b608460405162461bcd60e51b815260206004820152602860248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d69747465642060448201527f28737461746963290000000000000000000000000000000000000000000000006064820152fd5b156115ec57565b608460405162461bcd60e51b815260206004820152602a60248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d69747465642060448201527f287661726961626c6529000000000000000000000000000000000000000000006064820152fd5b6020818303126117465780519067ffffffffffffffff821161174257019080601f8301121561173e5781519161168b83610965565b92611699604051948561061b565b80845260208085019160051b8301019183831161173a5760208101915b8383106116c557505050505090565b825167ffffffffffffffff811161173657820185603f82011215611732576020810151916116f283610643565b6116ff604051918261061b565b8381528760208086860101011161172e575f602085819660408397018386015e830101528152019201916116b6565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b91908060f81c60ff81146117fd576080811615155f146117c35760fe81145f14611789575050611784915060208082518301019101611656565b5b5b90565b60209161179f610fe092607f8751911610611529565b601f19838501946117b2858751146115e5565b5101845260f31c1683010152611785565b6117f7929150607f16906117d984518310611529565b6117e66020825114611574565b6117f08285610c29565b5282610c29565b50611786565b50505090565b9060f81c60ff811461185d578251906020820180921161185857602092607f61182e611841946111d3565b92169161183b8383610c29565b52610c29565b51918051604084018184840160045afa5051910152565b610cb3565b505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8036013560f01c36030190565b6118a190607f602093941690610c29565b5151036118ae5760200190565b608460405162461bcd60e51b815260206004820152602760248201527f537461746963207374617465207661726961626c6573206d757374206265203360448201527f32206279746573000000000000000000000000000000000000000000000000006064820152fd5b90969594939260fd81145f1461193b5750956119349596611d65565b909192935b565b60fc81145f1461195b5750956119519596611ca5565b909192935b611939565b93929650607f611972929695969796941690610c29565b5151801515806119f7575b1561198d57602091010191611956565b608460405162461bcd60e51b815260206004820152603660248201527f44796e616d6963207374617465207661726961626c6573206d7573742062652060448201527f61206d756c7469706c65206f66203332206279746573000000000000000000006064820152fd5b50601f81161561197d565b916020809185930101920160045afa50565b91602060409185930101920160045afa50565b9193959692905f94611a398884610c29565b51936024600180878b019b019b0198820101915b60208910611a5e575b505050505050565b80891a6080811615155f14611b5c5760fb81145f14611a7d5750611a56565b60fd819c92959a9499969b93979c145f14611acf575090611aa592918b89528a858b89611b85565b9260209a93926001928c969480919d939d97929d9e01970101985b5b019301979291939490611a4d565b60fc81145f14611b0c575090611aec92918b89528a858b89611a27565b9260209a93926001928c969480919d939d97929d9e01970101985b611ac0565b6020898b8e6001959f9e979896611b50607f869716948d611b3f611b308888610c29565b51519788978897889552610c29565b5190611b4a856111c0565b91611a02565b019d0197010198611b07565b6020600192939796949981611b7988607f999e9983961690610c29565b5101518b520198611ac1565b939291909495600101946020861015611bd1576020611bbf9781611bae607f868b1a1685610c29565b510151602482890101520194611a27565b90602093929194919293909293940190565b610b6c565b15611bdd57565b608460405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207573652073746174652066726f6d20696e736964652064796e60448201527f616d6963207479706500000000000000000000000000000000000000000000006064820152fd5b60809060208152602460208201527f44796e616d6963207479706520776173206e6f742070726f7065726c7920636c60408201527f6f7365640000000000000000000000000000000000000000000000000000000060608201520190565b9395929491909460015f929301946020600183019801965b60208710611ce25760405162461bcd60e51b815280611cde60048201611c47565b0390fd5b80871a6080811615155f14611d4d5760fb81145f14611d0f57505050611d089083610c29565b5293929190565b611d2f9495611d2860fe839c949a969b959c1415611bd6565b888b611918565b91602093916001989399929998919894909899955b01930195611cbd565b611d5f60209160019399969a85611890565b98611d44565b9291909394600101936020851015611de6576020611d88607f83881a1686610c29565b515103611da2576020611d9c960193611ca5565b90919293565b606460405162461bcd60e51b815260206004820152601d60248201527f4172726179206c656e677468206d7573742062652033322062797465730000006044820152fd5b610b6c56
Deployed Bytecode
0x60806040526004361015610010575b005b5f3560e01c80630e4ab719146100df5780631626ba7e146100da57806324b8b1e6146100d55780632e1cc2f6146100d057806331574546146100cb57806331a38c89146100c657806367a34844146100c15780636d710c72146100bc5780638da5cb5b146100b7578063aa8c217c146100b2578063b61d27f6146100ad578063ce0617ec146100a85763f3d86e4a0361000e57610894565b610866565b6106d5565b6105c0565b610592565b610566565b61053a565b610511565b6104de565b6104ad565b610375565b6102eb565b610223565b9181601f8401121561011d5782359167ffffffffffffffff8311610119576020808501948460051b01011161011557565b5f80fd5b5f80fd5b5f80fd5b604060031982011261017d5760043567ffffffffffffffff8111610179578161014c916004016100e4565b929092916024359067ffffffffffffffff82116101755761016f916004016100e4565b91909190565b5f80fd5b5f80fd5b5f80fd5b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b8383106101d857505050505090565b9091929394602080610214837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc086600196030187528951610181565b970193019301919392906101c9565b61022c36610121565b9290916014610239611862565b013560601c33036102955761027f610285936102919560017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff005f5416175f553691610983565b91610d77565b604051918291826101a6565b0390f35b7fb2717667000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190917fffffffff000000000000000000000000000000000000000000000000000000006020820193169052565b3461037157604060031936011261036d5760243567ffffffffffffffff811161036957366023820112156103655780600401359067ffffffffffffffff821161036157366024838301011161035d5761035991602461034d92016004356109f7565b604051918291826102bd565b0390f35b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b61037e36610121565b929091610389611862565b803560601c330361046c575f549060ff8260081c1615908161045d575b50610435576103b76103bd91610b1d565b1561050c565b61040d576103f76103fd93610409957f19f0b89b5c0f05620d67b659885ae073c29fa80357783988104f4daa4ecfd5115f80a13691610983565b91610d77565b604051918291826101a6565b0390f35b7f05fee1e2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fd550ed24000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050604842910135115f6103a6565b7f30cd7471000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff1690565b346104da575f6003193601126104d657602060146104c9611862565b013560601c604051908152f35b5f80fd5b5f80fd5b34610508575f60031936011261050457602060696104fa611862565b0135604051908152f35b5f80fd5b5f80fd5b151590565b34610536575f60031936011261053257602060ff5f54166040519015158152f35b5f80fd5b5f80fd5b34610562575f60031936011261055e57602060ff5f5460081c166040519015158152f35b5f80fd5b5f80fd5b3461058e575f60031936011261058a576020610580610b23565b6040519015158152f35b5f80fd5b5f80fd5b346105bc575f6003193601126105b85760206105ac611862565b3560601c604051908152f35b5f80fd5b5f80fd5b346105ea575f6003193601126105e657602060286105dc611862565b0135604051908152f35b5f80fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f601f19910116810190811067ffffffffffffffff82111761063e57604052565b6105ee565b67ffffffffffffffff811161066057601f19601f60209201160190565b6105ee565b92919261067182610643565b9161067f604051938461061b565b82948184528183011161069b578281602093845f960137010152565b5f80fd5b9080601f830112156106bd578160206106ba93359101610665565b90565b5f80fd5b9060206106d2928181520190610181565b90565b60606003193601126108625760043573ffffffffffffffffffffffffffffffffffffffff8116810361085e5760443567ffffffffffffffff811161085a5761072190369060040161069f565b9061072a611862565b803560601c3303610832575f549060ff8260081c16159081610823575b506107fb5760ff16156107d3575f82819260208251920190602435905af161076d610b37565b90156107ab576107a7906040519182917f19f0b89b5c0f05620d67b659885ae073c29fa80357783988104f4daa4ecfd5115f80a1826106c1565b0390f35b7f2cb48aa6000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f05fee1e2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fd550ed24000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050604842910135115f610747565b7f30cd7471000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f80fd5b5f80fd5b5f80fd5b34610890575f60031936011261088c5760206048610882611862565b0135604051908152f35b5f80fd5b5f80fd5b34610961575f60031936011261095d576108ac611862565b601481013560601c3303610935576108c2610b23565b15908115610926575b506108fe576101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff5f5416175f555f80f35b7fb44bb110000000000000000000000000000000000000000000000000000000005f5260045ffd5b6048915001354210155f6108cb565b7fb2717667000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f80fd5b5f80fd5b67ffffffffffffffff811161097e5760209060051b0190565b6105ee565b92919061098f81610965565b9361099d604051958661061b565b602085838152019160051b8101918383116109f35781905b8382106109c3575050505050565b813567ffffffffffffffff81116109ef576020916109e4878493870161069f565b8152019101906109b5565b5f80fd5b5f80fd5b90610a2c916040516020810191825246604082015230606082015260608152610a2160808261061b565b519020923691610665565b6001906040519260208201905f52516040526040815114610ade576041815114610ac15750506020604060805f805b5afa505f6060523d606018519060405273ffffffffffffffffffffffffffffffffffffffff610a90610a8b611155565b610494565b9116145f14610abd577f1626ba7e0000000000000000000000000000000000000000000000000000000090565b5f90565b60805f602093604084606082960151841a87520151606052610a5b565b60805f6020937f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604080950151601b8160ff1c01875216606052610a5b565b60ff1690565b6068610b2d611862565b013560f81c151590565b3d5f14610b64573d90610b4982610643565b91610b57604051938461061b565b8252815f60203d92013e5b565b606090610b62565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190811015610ba95760051b0190565b610b6c565b60201b90565b60281b90565b60081b90565b60581b90565b7fff000000000000000000000000000000000000000000000000000000000000001690565b60f81c90565b60609060208152601060208201527f496e76616c69642063616c6c747970650000000000000000000000000000000060408201520190565b8051821015610c3d5760209160051b010190565b610b6c565b15610c4957565b606460405162461bcd60e51b815260206004820152601660248201527f56616c7565206d757374206265203332206279746573000000000000000000006044820152fd5b602081519101519060208110610ca2575b5090565b5f199060200360031b1b165f610c9e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b905f198201918211610cee57565b610cb3565b90601f198201918211610d0257565b610cb3565b60405190610d1660408361061b565b600782527f556e6b6e6f776e000000000000000000000000000000000000000000000000006020830152565b73ffffffffffffffffffffffffffffffffffffffff610d74949360609383521660208201528160408201520190610181565b90565b905f5b818110610d875750505090565b610d92818385610b99565b3590610db5610db0610dab610da685610bae565b610bc6565b610beb565b610b1d565b9160408316159182155f1461112757610dcd90611164565b92610dd9848688610b99565b356020905b60038316600181145f14610f9757505f9190829160208516158314610f8057610e2a917fffffffff0000000000000000000000000000000000000000000000000000000087168c611204565b5b6020815191018273ffffffffffffffffffffffffffffffffffffffff87165af192610e54610b37565b935b849015610eba57505060801615155f14610e9457610e8f9291610e83610e7e610e8993610bc0565b610bc6565b87611803565b5b611164565b610d7a565b90610e8f9295610eae610ea9610eb494610bc0565b610bc6565b9061174a565b93610e8a565b610f27858773ffffffffffffffffffffffffffffffffffffffff948794610edf610d07565b936044815111610f39575b50505f14610f2b575b6040519485947fef3dcb2f000000000000000000000000000000000000000000000000000000008652169060048501610d42565b0390fd5b610f3490610ce0565b610ef3565b6020610f506004610f4984611465565b9301610c8d565b14610f5b575b610eea565b610f6760248301610c8d565b14610f73575b80610f56565b6044919350019186610f6d565b50607f610f919160f81c168a610c29565b51610e2b565b600281145f1461102657505f919082916020851615831461100f57610fdf917fffffffff0000000000000000000000000000000000000000000000000000000087168c611204565b5b60208151910173ffffffffffffffffffffffffffffffffffffffff86165afa92611008610b37565b935b610e56565b50607f6110209160f81c168a610c29565b51610fe0565b6003145f1461110a575f9181611059611046607f869560f81c168d610c29565b516110546020825114610c42565b610c8d565b91602086161584146110d55761107a6110746110a493610bba565b91610ce0565b907fffffffff0000000000000000000000000000000000000000000000000000000088168d611204565b905b6020825192019073ffffffffffffffffffffffffffffffffffffffff87165af1926110cf610b37565b9361100a565b50607f6110fc6110f76110f26110ed61110395610bba565b610bc6565b610beb565b610b1d565b168b610c29565b51906110a6565b60405162461bcd60e51b81528061112360048201610bf1565b0390fd5b9279ffffffffffffffffffffffffffffffffffffffffffffffffffff61114c83610bb4565b17600690610dde565b61115d611862565b3560601c90565b60010190565b67ffffffffffffffff600a116111bb576020600a60051b0190604051611190838261061b565b600a8152809267ffffffffffffffff600a116111b65780601f1991500190602036910137565b6105ee565b6105ee565b90600482018092116111ce57565b610cb3565b906111dd82610643565b6111ea604051918261061b565b828152601f196111fa8294610643565b0190602036910137565b919093925f915f925f9361121661116a565b906060925f905b868210611397575b5050611233611238916111c0565b6111d3565b9760208901525f9060248901935f965b86881061125a57505050505050505050565b6020881015611392578a89838a1a6080811615155f146113705760fe81145f146112ba575050601f19826112a96020936001958b52611298836111c0565b6112a28b51610cf3565b918b611a14565b87510101965b5b0197019694611248565b60fd819b96989b97949597939293145f146112f75750926112ea92878760019c9997948b99978560209d52611b85565b509891969096945b6112af565b60fc81145f1461132957509261131c92878760019c9997948b99978560209d52611a27565b509891969096945b6112f2565b93611369898293611358602096999e9b99607f6001999c9a169061134d8282610c29565b515197889552610c29565b5190611363856111c0565b91611a02565b0196611324565b60209250600193989150611388607f8492168d610c29565b51015181526112b0565b610b6c565b9094969160208610156114605787861a60ff811461144a576080811615155f146114355760fe81145f1461141557508451156113e5575b6020600191865101935b5b0195019096949661121d565b93506001602060405161140c816113fe8d8583016101a6565b03601f19810183528261061b565b959150506113ce565b906114299260019792602095968b8d611918565b969193909392946113d8565b6114446020916001938c611890565b936113d9565b5095505092611233611238919591969496611225565b610b6c565b9081517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8101818111611524579260445b8281106114a3575b505050565b815181101561151f577fff000000000000000000000000000000000000000000000000000000000000006020828401015116156114e257600101611496565b92935050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc810190811161151a57905f808061149e565b610cb3565b610b6c565b610cb3565b1561153057565b606460405162461bcd60e51b815260206004820152601360248201527f496e646578206f75742d6f662d626f756e6473000000000000000000000000006044820152fd5b1561157b57565b608460405162461bcd60e51b815260206004820152602860248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d69747465642060448201527f28737461746963290000000000000000000000000000000000000000000000006064820152fd5b156115ec57565b608460405162461bcd60e51b815260206004820152602a60248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d69747465642060448201527f287661726961626c6529000000000000000000000000000000000000000000006064820152fd5b6020818303126117465780519067ffffffffffffffff821161174257019080601f8301121561173e5781519161168b83610965565b92611699604051948561061b565b80845260208085019160051b8301019183831161173a5760208101915b8383106116c557505050505090565b825167ffffffffffffffff811161173657820185603f82011215611732576020810151916116f283610643565b6116ff604051918261061b565b8381528760208086860101011161172e575f602085819660408397018386015e830101528152019201916116b6565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b91908060f81c60ff81146117fd576080811615155f146117c35760fe81145f14611789575050611784915060208082518301019101611656565b5b5b90565b60209161179f610fe092607f8751911610611529565b601f19838501946117b2858751146115e5565b5101845260f31c1683010152611785565b6117f7929150607f16906117d984518310611529565b6117e66020825114611574565b6117f08285610c29565b5282610c29565b50611786565b50505090565b9060f81c60ff811461185d578251906020820180921161185857602092607f61182e611841946111d3565b92169161183b8383610c29565b52610c29565b51918051604084018184840160045afa5051910152565b610cb3565b505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8036013560f01c36030190565b6118a190607f602093941690610c29565b5151036118ae5760200190565b608460405162461bcd60e51b815260206004820152602760248201527f537461746963207374617465207661726961626c6573206d757374206265203360448201527f32206279746573000000000000000000000000000000000000000000000000006064820152fd5b90969594939260fd81145f1461193b5750956119349596611d65565b909192935b565b60fc81145f1461195b5750956119519596611ca5565b909192935b611939565b93929650607f611972929695969796941690610c29565b5151801515806119f7575b1561198d57602091010191611956565b608460405162461bcd60e51b815260206004820152603660248201527f44796e616d6963207374617465207661726961626c6573206d7573742062652060448201527f61206d756c7469706c65206f66203332206279746573000000000000000000006064820152fd5b50601f81161561197d565b916020809185930101920160045afa50565b91602060409185930101920160045afa50565b9193959692905f94611a398884610c29565b51936024600180878b019b019b0198820101915b60208910611a5e575b505050505050565b80891a6080811615155f14611b5c5760fb81145f14611a7d5750611a56565b60fd819c92959a9499969b93979c145f14611acf575090611aa592918b89528a858b89611b85565b9260209a93926001928c969480919d939d97929d9e01970101985b5b019301979291939490611a4d565b60fc81145f14611b0c575090611aec92918b89528a858b89611a27565b9260209a93926001928c969480919d939d97929d9e01970101985b611ac0565b6020898b8e6001959f9e979896611b50607f869716948d611b3f611b308888610c29565b51519788978897889552610c29565b5190611b4a856111c0565b91611a02565b019d0197010198611b07565b6020600192939796949981611b7988607f999e9983961690610c29565b5101518b520198611ac1565b939291909495600101946020861015611bd1576020611bbf9781611bae607f868b1a1685610c29565b510151602482890101520194611a27565b90602093929194919293909293940190565b610b6c565b15611bdd57565b608460405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207573652073746174652066726f6d20696e736964652064796e60448201527f616d6963207479706500000000000000000000000000000000000000000000006064820152fd5b60809060208152602460208201527f44796e616d6963207479706520776173206e6f742070726f7065726c7920636c60408201527f6f7365640000000000000000000000000000000000000000000000000000000060608201520190565b9395929491909460015f929301946020600183019801965b60208710611ce25760405162461bcd60e51b815280611cde60048201611c47565b0390fd5b80871a6080811615155f14611d4d5760fb81145f14611d0f57505050611d089083610c29565b5293929190565b611d2f9495611d2860fe839c949a969b959c1415611bd6565b888b611918565b91602093916001989399929998919894909899955b01930195611cbd565b611d5f60209160019399969a85611890565b98611d44565b9291909394600101936020851015611de6576020611d88607f83881a1686610c29565b515103611da2576020611d9c960193611ca5565b90919293565b606460405162461bcd60e51b815260206004820152601d60248201527f4172726179206c656e677468206d7573742062652033322062797465730000006044820152fd5b610b6c56
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ 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.