More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 24 from a total of 24 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Approve | 20321632 | 163 days ago | IN | 0 ETH | 0.00024715 | ||||
Approve | 19733701 | 245 days ago | IN | 0 ETH | 0.00133187 | ||||
Approve | 18934821 | 357 days ago | IN | 0 ETH | 0.00186079 | ||||
Approve | 18362251 | 438 days ago | IN | 0 ETH | 0.00019376 | ||||
Approve | 17710938 | 529 days ago | IN | 0 ETH | 0.00076155 | ||||
Approve | 17710874 | 529 days ago | IN | 0 ETH | 0.0008019 | ||||
Approve | 17320419 | 584 days ago | IN | 0 ETH | 0.00236165 | ||||
Approve | 17307058 | 586 days ago | IN | 0 ETH | 0.00129677 | ||||
Approve | 17236035 | 596 days ago | IN | 0 ETH | 0.00433562 | ||||
Approve | 17136707 | 610 days ago | IN | 0 ETH | 0.00150505 | ||||
Approve | 17103073 | 614 days ago | IN | 0 ETH | 0.00175624 | ||||
Approve | 17102822 | 614 days ago | IN | 0 ETH | 0.00151997 | ||||
Approve | 17097225 | 615 days ago | IN | 0 ETH | 0.00203973 | ||||
Approve | 17095801 | 615 days ago | IN | 0 ETH | 0.00195036 | ||||
Approve | 17093226 | 616 days ago | IN | 0 ETH | 0.00216372 | ||||
Approve | 17092349 | 616 days ago | IN | 0 ETH | 0.00246289 | ||||
Transfer | 17092255 | 616 days ago | IN | 0 ETH | 0.00210893 | ||||
Approve | 17092215 | 616 days ago | IN | 0 ETH | 0.00176496 | ||||
Approve | 17092212 | 616 days ago | IN | 0 ETH | 0.0009031 | ||||
Approve | 17092204 | 616 days ago | IN | 0 ETH | 0.00197015 | ||||
Approve | 17089561 | 616 days ago | IN | 0 ETH | 0.00326481 | ||||
Approve | 17089288 | 616 days ago | IN | 0 ETH | 0.00257884 | ||||
Approve | 17087243 | 617 days ago | IN | 0 ETH | 0.00207295 | ||||
Approve | 17081948 | 617 days ago | IN | 0 ETH | 0.0035265 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
16241114 | 735 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xCd0E5871...b45BfE2cC The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
StakeableEulerERC4626
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.13; import {ERC20} from "solmate/tokens/ERC20.sol"; import {Owned} from "solmate/auth/Owned.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {IEulerEToken} from "../external/IEulerEToken.sol"; import {IRewardsDistribution} from "../external/IRewardsDistribution.sol"; import {IStakingRewards} from "../external/IStakingRewards.sol"; import {EulerERC4626} from "../EulerERC4626.sol"; /// @title StakeableEulerERC4626 /// @author Sam Bugs /// @notice A ERC4626 wrapper for Euler Finance, that can handle staking contract StakeableEulerERC4626 is EulerERC4626, Owned { /// ----------------------------------------------------------------------- /// Errors /// ----------------------------------------------------------------------- /// @notice Thrown when trying to assign an invalid rewards contract error StakeableEulerERC4626__InvalidRewardContract(); /// ----------------------------------------------------------------------- /// Libraries usage /// ----------------------------------------------------------------------- using SafeTransferLib for ERC20; /// ----------------------------------------------------------------------- /// Immutable params /// ----------------------------------------------------------------------- /// @notice The rewards distribution address IRewardsDistribution public immutable rewardsDistribution; /// ----------------------------------------------------------------------- /// Mutable params /// ----------------------------------------------------------------------- /// @notice The staking rewards address IStakingRewards public stakingRewards; /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor(ERC20 asset_, address euler_, IEulerEToken eToken_, IRewardsDistribution rewardsDistribution_, address owner_) EulerERC4626(asset_, euler_, eToken_) Owned(owner_) { rewardsDistribution = rewardsDistribution_; } /// ----------------------------------------------------------------------- /// ERC4626 overrides /// ----------------------------------------------------------------------- function totalAssets() public view virtual override returns (uint256) { uint256 eTokenStakedBalance = _getStakedBalance(); if (eTokenStakedBalance > 0) { // We add all eToken balance (staked and non-staked) and then convert to underlying // We do this to prevent rounding differences if we converted to underlying and then added the results uint256 eTokenBalanceInContract = eToken.balanceOf(address(this)); return eToken.convertBalanceToUnderlying(eTokenStakedBalance + eTokenBalanceInContract); } return eToken.balanceOfUnderlying(address(this)); } function beforeWithdraw(uint256 assets, uint256 shares) internal virtual override { uint256 underlyingBalanceInContract = eToken.balanceOfUnderlying(address(this)); if (underlyingBalanceInContract < assets) { // Need to unstake to meet the demand uint256 neededUnderlying = assets - underlyingBalanceInContract; uint256 neededEToken = eToken.convertUnderlyingToBalance(neededUnderlying); // We also withdraw 5% of remaining staked eTokens so that we can avoid unstaking again in the next withdraw uint256 eTokenStakedBalance = _getStakedBalance(); uint256 remaining = eTokenStakedBalance - neededEToken; uint256 withdrawAttempt = neededEToken + FixedPointMathLib.mulDivUp(remaining, 5, 100); // We make sure we don't try to withdraw more than available uint256 toWithdraw = eTokenStakedBalance < withdrawAttempt ? eTokenStakedBalance : withdrawAttempt; stakingRewards.withdraw(toWithdraw); } super.beforeWithdraw(assets, shares); } /// ----------------------------------------------------------------------- /// Staking functions /// ----------------------------------------------------------------------- /// @notice Returns how much was earned during staking function reward() public view returns (address rewardsToken, uint256 earned) { return _calculateReward(stakingRewards); } /// @notice Allows owner to set or update a new staking contract. Will claim rewards from previous staking if available function updateStakingAddress(uint256 rewardIndex, address recipient) external onlyOwner { _stopStaking(recipient); IRewardsDistribution.DistributionData memory data = rewardsDistribution.distributions(rewardIndex); IStakingRewards stakingRewards_ = IStakingRewards(data.destination); if (stakingRewards_.stakingToken() != address(eToken)) revert StakeableEulerERC4626__InvalidRewardContract(); stakingRewards = stakingRewards_; ERC20(address(eToken)).safeApprove(address(stakingRewards_), type(uint256).max); } /// @notice Allows owner to claim rewards and stop staking all together function stopStaking(address recipient) external onlyOwner { _stopStaking(recipient); stakingRewards = IStakingRewards(address(0)); } /// @notice Allows owner to stake a certain amount of tokens function stake(uint256 amount) external onlyOwner { stakingRewards.stake(amount); } /// @notice Allows owner to unstake a certain amount of tokens function unstake(uint256 amount) external onlyOwner { stakingRewards.withdraw(amount); } /// @notice Allows owner to claim all staking rewards function claimReward(address recipient) public onlyOwner returns (address rewardsToken, uint256 earned) { (rewardsToken, earned) = reward(); stakingRewards.getReward(); _transferRewardToken(rewardsToken, earned, recipient); } /// ----------------------------------------------------------------------- /// Internal functions /// ----------------------------------------------------------------------- function _getStakedBalance() internal view returns (uint256 eTokenStakedBalance) { IStakingRewards stakingRewards_ = stakingRewards; if (address(stakingRewards_) != address(0)) { return stakingRewards_.balanceOf(address(this)); } } function _calculateReward(IStakingRewards stakingRewards_) public view returns (address rewardToken, uint256 earned) { if (address(stakingRewards_) != address(0)) { rewardToken = stakingRewards_.rewardsToken(); earned = stakingRewards_.earned(address(this)); } } function _stopStaking(address recipient) internal { IStakingRewards stakingRewards_ = stakingRewards; if (address(stakingRewards_) != address(0)) { ERC20(address(eToken)).safeApprove(address(stakingRewards_), 0); (address rewardToken, uint256 earned) = _calculateReward(stakingRewards_); stakingRewards_.exit(); _transferRewardToken(rewardToken, earned, recipient); } } function _transferRewardToken(address rewardToken, uint256 amount, address recipient) internal { if (amount > 0) { ERC20(rewardToken).safeTransfer(recipient, amount); } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Simple single owner authorization mixin. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) abstract contract Owned { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event OwnerUpdated(address indexed user, address indexed newOwner); /*////////////////////////////////////////////////////////////// OWNERSHIP STORAGE //////////////////////////////////////////////////////////////*/ address public owner; modifier onlyOwner() virtual { require(msg.sender == owner, "UNAUTHORIZED"); _; } /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _owner) { owner = _owner; emit OwnerUpdated(address(0), _owner); } /*////////////////////////////////////////////////////////////// OWNERSHIP LOGIC //////////////////////////////////////////////////////////////*/ function setOwner(address newOwner) public virtual onlyOwner { owner = newOwner; emit OwnerUpdated(msg.sender, newOwner); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // Divide z by the denominator. z := div(z, denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // First, divide z - 1 by the denominator and add 1. // We allow z - 1 to underflow if z is 0, because we multiply the // end result by 0 if z is zero, ensuring we return 0 if z is zero. z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.13; /// @notice Tokenised representation of assets interface IEulerEToken { // @notice Retrieve the current allowance /// @param holder Xor with the desired sub-account ID (if applicable) /// @param spender Trusted address function allowance(address holder, address spender) external view returns (uint); /// @notice Address of underlying asset function underlyingAsset() external view returns (address); /// @notice Balance of a particular account, in underlying units (increases as interest is earned) function balanceOfUnderlying(address account) external view returns (uint256); /// @notice Balance of a particular account function balanceOf(address account) external view returns (uint256); /// @notice Transfer underlying tokens from sender to the Euler pool, and increase account's eTokens /// @param subAccountId 0 for primary, 1-255 for a sub-account /// @param amount In underlying units (use max uint256 for full underlying token balance) function deposit(uint256 subAccountId, uint256 amount) external; /// @notice Transfer underlying tokens from Euler pool to sender, and decrease account's eTokens /// @param subAccountId 0 for primary, 1-255 for a sub-account /// @param amount In underlying units (use max uint256 for full pool balance) function withdraw(uint256 subAccountId, uint256 amount) external; /// @notice Convert an eToken balance to an underlying amount, taking into account current exchange rate /// @param balance eToken balance, in internal book-keeping units (18 decimals) /// @return Amount in underlying units, (same decimals as underlying token) function convertBalanceToUnderlying(uint balance) external view returns (uint); /// @notice Convert an underlying amount to an eToken balance, taking into account current exchange rate /// @param underlyingAmount Amount in underlying units (same decimals as underlying token) /// @return eToken balance, in internal book-keeping units (18 decimals) function convertUnderlyingToBalance(uint underlyingAmount) external view returns (uint); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // Original contract can be found under the following link: // https://github.com/Synthetixio/synthetix/blob/master/contracts/interfaces/IRewardsDistribution.sol interface IRewardsDistribution { // Structs struct DistributionData { address destination; uint amount; } // Views function distributions(uint256 index) external view returns (DistributionData memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // Original contract can be found under the following link: // https://github.com/Synthetixio/synthetix/blob/master/contracts/interfaces/IStakingRewards.sol interface IStakingRewards { // Views function rewardsToken() external view returns (address); function stakingToken() external view returns (address); function balanceOf(address account) external view returns (uint256); function earned(address account) external view returns (uint256); // Mutative function exit() external; function getReward() external; function stake(uint256 amount) external; function withdraw(uint256 amount) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.13; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ERC4626} from "solmate/mixins/ERC4626.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {IEulerEToken} from "./external/IEulerEToken.sol"; /// @title EulerERC4626 /// @author zefram.eth /// @notice ERC4626 wrapper for Euler Finance contract EulerERC4626 is ERC4626 { /// ----------------------------------------------------------------------- /// Libraries usage /// ----------------------------------------------------------------------- using SafeTransferLib for ERC20; /// ----------------------------------------------------------------------- /// Immutable params /// ----------------------------------------------------------------------- /// @notice The Euler main contract address /// @dev Target of ERC20 approval when depositing address public immutable euler; /// @notice The Euler eToken contract IEulerEToken public immutable eToken; /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor(ERC20 asset_, address euler_, IEulerEToken eToken_) ERC4626(asset_, _vaultName(asset_), _vaultSymbol(asset_)) { euler = euler_; eToken = eToken_; } /// ----------------------------------------------------------------------- /// ERC4626 overrides /// ----------------------------------------------------------------------- function totalAssets() public view virtual override returns (uint256) { return eToken.balanceOfUnderlying(address(this)); } function beforeWithdraw(uint256 assets, uint256 /*shares*/ ) internal virtual override { /// ----------------------------------------------------------------------- /// Withdraw assets from Euler /// ----------------------------------------------------------------------- eToken.withdraw(0, assets); } function afterDeposit(uint256 assets, uint256 /*shares*/ ) internal virtual override { /// ----------------------------------------------------------------------- /// Deposit assets into Euler /// ----------------------------------------------------------------------- // approve to euler asset.safeApprove(address(euler), assets); // deposit into eToken eToken.deposit(0, assets); } function maxWithdraw(address owner) public view override returns (uint256) { uint256 cash = asset.balanceOf(euler); uint256 assetsBalance = convertToAssets(balanceOf[owner]); return cash < assetsBalance ? cash : assetsBalance; } function maxRedeem(address owner) public view override returns (uint256) { uint256 cash = asset.balanceOf(euler); uint256 cashInShares = convertToShares(cash); uint256 shareBalance = balanceOf[owner]; return cashInShares < shareBalance ? cashInShares : shareBalance; } /// ----------------------------------------------------------------------- /// ERC20 metadata generation /// ----------------------------------------------------------------------- function _vaultName(ERC20 asset_) internal view virtual returns (string memory vaultName) { vaultName = string.concat("ERC4626-Wrapped Euler ", asset_.symbol()); } function _vaultSymbol(ERC20 asset_) internal view virtual returns (string memory vaultSymbol) { vaultSymbol = string.concat("we", asset_.symbol()); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; /// @notice Minimal ERC4626 tokenized Vault implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol) abstract contract ERC4626 is ERC20 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /*////////////////////////////////////////////////////////////// IMMUTABLES //////////////////////////////////////////////////////////////*/ ERC20 public immutable asset; constructor( ERC20 _asset, string memory _name, string memory _symbol ) ERC20(_name, _symbol, _asset.decimals()) { asset = _asset; } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LOGIC //////////////////////////////////////////////////////////////*/ function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function withdraw( uint256 assets, address receiver, address owner ) public virtual returns (uint256 shares) { shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } function redeem( uint256 shares, address receiver, address owner ) public virtual returns (uint256 assets) { if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } // Check for rounding error since we round down in previewRedeem. require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } /*////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ function totalAssets() public view virtual returns (uint256); function convertToShares(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); } function convertToAssets(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); } function previewDeposit(uint256 assets) public view virtual returns (uint256) { return convertToShares(assets); } function previewMint(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); } function previewWithdraw(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); } function previewRedeem(uint256 shares) public view virtual returns (uint256) { return convertToAssets(shares); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } function maxWithdraw(address owner) public view virtual returns (uint256) { return convertToAssets(balanceOf[owner]); } function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf[owner]; } /*////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/ function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} function afterDeposit(uint256 assets, uint256 shares) internal virtual {} }
{ "remappings": [ "create3-factory/=lib/create3-factory/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "solmate/=lib/solmate/src/", "src/=src/", "script/=script/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract ERC20","name":"asset_","type":"address"},{"internalType":"address","name":"euler_","type":"address"},{"internalType":"contract IEulerEToken","name":"eToken_","type":"address"},{"internalType":"contract IRewardsDistribution","name":"rewardsDistribution_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"StakeableEulerERC4626__InvalidRewardContract","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IStakingRewards","name":"stakingRewards_","type":"address"}],"name":"_calculateReward","outputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"earned","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"claimReward","outputs":[{"internalType":"address","name":"rewardsToken","type":"address"},{"internalType":"uint256","name":"earned","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eToken","outputs":[{"internalType":"contract IEulerEToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"euler","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reward","outputs":[{"internalType":"address","name":"rewardsToken","type":"address"},{"internalType":"uint256","name":"earned","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDistribution","outputs":[{"internalType":"contract IRewardsDistribution","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingRewards","outputs":[{"internalType":"contract IStakingRewards","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"stopStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rewardIndex","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"updateStakingAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102d35760003560e01c80637c6967dc11610186578063ba087652116100e3578063d505accf11610097578063dd62ed3e11610071578063dd62ed3e146106a3578063e0bd6775146106ce578063ef8b30f7146106e157600080fd5b8063d505accf14610656578063d905777e14610669578063db661b711461067c57600080fd5b8063c6e6f592116100c8578063c6e6f5921461061d578063ce96cb7714610630578063d279c1911461064357600080fd5b8063ba0876521461060a578063c63d75b61461047d57600080fd5b806395d89b411161013a578063a9059cbb1161011f578063a9059cbb146105d1578063b3d7f6b9146105e4578063b460af94146105f757600080fd5b806395d89b41146105b6578063a694fc3a146105be57600080fd5b80638da5cb5b1161016b5780638da5cb5b1461055c57806394bf804d1461057c578063953d9cf11461058f57600080fd5b80637c6967dc146105295780637ecebe001461053c57600080fd5b8063313ce56711610234578063497f285d116101e857806364b87a70116101cd57806364b87a70146104d65780636e553f65146104f657806370a082311461050957600080fd5b8063497f285d146104b05780634cdad506146104c357600080fd5b806338d52e0f1161021957806338d52e0f1461040a5780633fc6df6e14610456578063402d267d1461047d57600080fd5b8063313ce567146103c95780633644e5151461040257600080fd5b806313af40351161028b578063228cb73311610270578063228cb7331461036f57806323b872dd146103a35780632e17de78146103b657600080fd5b806313af40351461035157806318160ddd1461036657600080fd5b806307a2d13a116102bc57806307a2d13a14610308578063095ea7b31461031b5780630a28a4771461033e57600080fd5b806301e1d114146102d857806306fdde03146102f3575b600080fd5b6102e06106f4565b6040519081526020015b60405180910390f35b6102fb61091b565b6040516102ea91906129ca565b6102e0610316366004612a36565b6109a9565b61032e610329366004612a74565b6109d6565b60405190151581526020016102ea565b6102e061034c366004612a36565b610a50565b61036461035f366004612aa0565b610a70565b005b6102e060025481565b610377610b67565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152016102ea565b61032e6103b1366004612abd565b610b96565b6103646103c4366004612a36565b610cda565b6103f07f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff90911681526020016102ea565b6102e0610de3565b6104317f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102ea565b6104317f000000000000000000000000a9839d52e964d0ed0d6d546c27d2248fac610c4381565b6102e061048b366004612aa0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b6103776104be366004612aa0565b610e3e565b6102e06104d1366004612a36565b610f63565b6007546104319073ffffffffffffffffffffffffffffffffffffffff1681565b6102e0610504366004612afe565b610f6e565b6102e0610517366004612aa0565b60036020526000908152604090205481565b610364610537366004612aa0565b61108e565b6102e061054a366004612aa0565b60056020526000908152604090205481565b6006546104319073ffffffffffffffffffffffffffffffffffffffff1681565b6102e061058a366004612afe565b611143565b6104317f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d81565b6102fb6111f9565b6103646105cc366004612a36565b611206565b61032e6105df366004612a74565b6112de565b6102e06105f2366004612a36565b611363565b6102e0610605366004612b2e565b611382565b6102e0610618366004612b2e565b6114ef565b6102e061062b366004612a36565b6116c6565b6102e061063e366004612aa0565b6116e6565b610377610651366004612aa0565b611807565b610364610664366004612b70565b611928565b6102e0610677366004612aa0565b611c47565b6104317f00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d381565b6102e06106b1366004612be7565b600460209081526000928352604080842090915290825290205481565b6103646106dc366004612afe565b611d6a565b6102e06106ef366004612a36565b61203b565b6000806106ff612046565b90508015610866576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d73ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b89190612c15565b905073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d1663010ad6d16108008385612c5d565b6040518263ffffffff1660e01b815260040161081e91815260200190565b602060405180830381865afa15801561083b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085f9190612c15565b9250505090565b6040517f3af9e6690000000000000000000000000000000000000000000000000000000081523060048201527f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d73ffffffffffffffffffffffffffffffffffffffff1690633af9e669906024015b602060405180830381865afa1580156108f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109159190612c15565b91505090565b6000805461092890612c70565b80601f016020809104026020016040519081016040528092919081815260200182805461095490612c70565b80156109a15780601f10610976576101008083540402835291602001916109a1565b820191906000526020600020905b81548152906001019060200180831161098457829003601f168201915b505050505081565b60025460009080156109cd576109c86109c06106f4565b8490836120be565b6109cf565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610a3e9086815260200190565b60405180910390a35060015b92915050565b60025460009080156109cd576109c881610a686106f4565b8591906120dd565b60065473ffffffffffffffffffffffffffffffffffffffff163314610af6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a350565b6007546000908190610b8e9073ffffffffffffffffffffffffffffffffffffffff16610e3e565b915091509091565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610c2a57610bf88382612cc3565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526003602052604081208054859290610c5f908490612cc3565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610cc79087815260200190565b60405180910390a3506001949350505050565b60065473ffffffffffffffffffffffffffffffffffffffff163314610d5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b6007546040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d906024015b600060405180830381600087803b158015610dc857600080fd5b505af1158015610ddc573d6000803e3d6000fd5b5050505050565b60007f00000000000000000000000000000000000000000000000000000000000000014614610e1957610e1461210b565b905090565b507f393a75e7db882e133095dca0a506500963cab6cb9c8570928f7f41451e6ef9dc90565b60008073ffffffffffffffffffffffffffffffffffffffff831615610f5e578273ffffffffffffffffffffffffffffffffffffffff1663d1af0c7d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ea8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ecc9190612cd6565b6040517e8cc26200000000000000000000000000000000000000000000000000000000815230600482015290925073ffffffffffffffffffffffffffffffffffffffff841690628cc26290602401602060405180830381865afa158015610f37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5b9190612c15565b90505b915091565b6000610a4a826109a9565b6000610f798361203b565b905080600003610fe5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401610aed565b61102773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2163330866121a5565b6110318282612264565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610a4a83826122dd565b60065473ffffffffffffffffffffffffffffffffffffffff16331461110f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611118816123ea565b50600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600061114e83611363565b905061119273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2163330846121a5565b61119c8284612264565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610a4a81846122dd565b6001805461092890612c70565b60065473ffffffffffffffffffffffffffffffffffffffff163314611287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b6007546040517fa694fc3a0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff9091169063a694fc3a90602401610dae565b336000908152600360205260408120805483919083906112ff908490612cc3565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610a3e9086815260200190565b60025460009080156109cd576109c861137a6106f4565b8490836120dd565b600061138d84610a50565b90503373ffffffffffffffffffffffffffffffffffffffff8316146114425773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146114405761140e8282612cc3565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b61144c84826124ca565b6114568282612729565b604080518581526020810183905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46109cf73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21684866127b7565b60003373ffffffffffffffffffffffffffffffffffffffff8316146115a45773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146115a2576115708582612cc3565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6115ad84610f63565b905080600003611619576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152606401610aed565b61162381856124ca565b61162d8285612729565b604080518281526020810186905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46109cf73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21684836127b7565b60025460009080156109cd576109c8816116de6106f4565b8591906120be565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d38116600483015260009182917f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa158015611797573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bb9190612c15565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260036020526040812054919250906117ee906109a9565b90508082106117fd57806117ff565b815b949350505050565b600654600090819073ffffffffffffffffffffffffffffffffffffffff16331461188d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611895610b67565b600754604080517f3d18b912000000000000000000000000000000000000000000000000000000008152905193955091935073ffffffffffffffffffffffffffffffffffffffff1691633d18b9129160048082019260009290919082900301818387803b15801561190557600080fd5b505af1158015611919573d6000803e3d6000fd5b50505050610f5e828285612870565b42841015611992576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610aed565b6000600161199e610de3565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015611af0573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611b6b57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611bd1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610aed565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d38116600483015260009182917f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa158015611cf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d1c9190612c15565b90506000611d29826116c6565b73ffffffffffffffffffffffffffffffffffffffff8516600090815260036020526040902054909150808210611d5f5780611d61565b815b95945050505050565b60065473ffffffffffffffffffffffffffffffffffffffff163314611deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611df4816123ea565b6040517f4487d3df000000000000000000000000000000000000000000000000000000008152600481018390526000907f000000000000000000000000a9839d52e964d0ed0d6d546c27d2248fac610c4373ffffffffffffffffffffffffffffffffffffffff1690634487d3df906024016040805180830381865afa158015611e81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea59190612cf3565b90506000816000015190507f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff166372f702f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f569190612cd6565b73ffffffffffffffffffffffffffffffffffffffff1614611fa3576040517f63eea51600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691909117909155612035907f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d16827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612897565b50505050565b6000610a4a826116c6565b60075460009073ffffffffffffffffffffffffffffffffffffffff1680156120ba576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8216906370a08231906024016108d4565b5090565b8282028115158415858304851417166120d657600080fd5b0492915050565b8282028115158415858304851417166120f557600080fd5b6001826001830304018115150290509392505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161213d9190612d72565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080610ddc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610aed565b80600260008282546122769190612c5d565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b61233e73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2167f00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d384612897565b6040517fe2bbb15800000000000000000000000000000000000000000000000000000000815260006004820152602481018390527f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d73ffffffffffffffffffffffffffffffffffffffff169063e2bbb158906044015b600060405180830381600087803b1580156123ce57600080fd5b505af11580156123e2573d6000803e3d6000fd5b505050505050565b60075473ffffffffffffffffffffffffffffffffffffffff1680156124c65761244b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d16826000612897565b60008061245783610e3e565b915091508273ffffffffffffffffffffffffffffffffffffffff1663e9fad8ee6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156124a357600080fd5b505af11580156124b7573d6000803e3d6000fd5b50505050612035828286612870565b5050565b6040517f3af9e6690000000000000000000000000000000000000000000000000000000081523060048201526000907f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d73ffffffffffffffffffffffffffffffffffffffff1690633af9e66990602401602060405180830381865afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b9190612c15565b90508281101561271a5760006125918285612cc3565b6040517f52eac8af0000000000000000000000000000000000000000000000000000000081526004810182905290915060009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d16906352eac8af90602401602060405180830381865afa158015612622573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126469190612c15565b90506000612652612046565b905060006126608383612cc3565b9050600061267182600560646120dd565b61267b9085612c5d565b9050600081841061268c578161268e565b835b6007546040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810183905291925073ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d90602401600060405180830381600087803b1580156126fb57600080fd5b505af115801561270f573d6000803e3d6000fd5b505050505050505050505b6127248383612950565b505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600360205260408120805483929061275e908490612cc3565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020016122d1565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080612035576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610aed565b81156127245761272473ffffffffffffffffffffffffffffffffffffffff841682846127b7565b60006040517f095ea7b3000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080612035576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610aed565b6040517f441a3e7000000000000000000000000000000000000000000000000000000000815260006004820152602481018390527f0000000000000000000000001b808f49add4b8c6b5117d9681cf7312fcf0dc1d73ffffffffffffffffffffffffffffffffffffffff169063441a3e70906044016123b4565b600060208083528351808285015260005b818110156129f7578581018301518582016040015282016129db565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b600060208284031215612a4857600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114612a7157600080fd5b50565b60008060408385031215612a8757600080fd5b8235612a9281612a4f565b946020939093013593505050565b600060208284031215612ab257600080fd5b81356109cf81612a4f565b600080600060608486031215612ad257600080fd5b8335612add81612a4f565b92506020840135612aed81612a4f565b929592945050506040919091013590565b60008060408385031215612b1157600080fd5b823591506020830135612b2381612a4f565b809150509250929050565b600080600060608486031215612b4357600080fd5b833592506020840135612b5581612a4f565b91506040840135612b6581612a4f565b809150509250925092565b600080600080600080600060e0888a031215612b8b57600080fd5b8735612b9681612a4f565b96506020880135612ba681612a4f565b95506040880135945060608801359350608088013560ff81168114612bca57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215612bfa57600080fd5b8235612c0581612a4f565b91506020830135612b2381612a4f565b600060208284031215612c2757600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610a4a57610a4a612c2e565b600181811c90821680612c8457607f821691505b602082108103612cbd577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b81810381811115610a4a57610a4a612c2e565b600060208284031215612ce857600080fd5b81516109cf81612a4f565b600060408284031215612d0557600080fd5b6040516040810181811067ffffffffffffffff82111715612d4f577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528251612d5d81612a4f565b81526020928301519281019290925250919050565b600080835481600182811c915080831680612d8e57607f831692505b60208084108203612dc6577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015612dda5760018114612e0d57612e3a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650612e3a565b60008a81526020902060005b86811015612e325781548b820152908501908301612e19565b505084890196505b50949897505050505050505056fea26469706673582212205b2ec9951b3f843925558c2e058c3dd16626e97babb9bf6e691e03b57940817164736f6c63430008100033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.