Overview
ETH Balance
0 ETH
Eth Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60808060 | 21040393 | 497 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
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 "src/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
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
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.