Contract
0x007FE70dc9797C4198528aE43d8195ffF82Bdc95
15
Contract Overview
Balance:
0 ETH
Token:
My Name Tag:
Not Available
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
BondFixedExpiryTeller
Compiler Version
v0.8.15+commit.e14f2714
Optimization Enabled:
Yes with 100000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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: BSD pragma solidity ^0.8.4; /// @title ClonesWithImmutableArgs /// @author wighawag, zefram.eth /// @notice Enables creating clone contracts with immutable args library ClonesWithImmutableArgs { error CreateFail(); /// @notice Creates a clone proxy of the implementation contract, with immutable args /// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length /// @param implementation The implementation contract to clone /// @param data Encoded immutable args /// @return instance The address of the created clone function clone(address implementation, bytes memory data) internal returns (address instance) { // unrealistic for memory ptr or data length to exceed 256 bits unchecked { uint256 extraLength = data.length + 2; // +2 bytes for telling how much data there is appended to the call uint256 creationSize = 0x43 + extraLength; uint256 runSize = creationSize - 11; uint256 dataPtr; uint256 ptr; // solhint-disable-next-line no-inline-assembly assembly { ptr := mload(0x40) // ------------------------------------------------------------------------------------------------------------- // CREATION (11 bytes) // ------------------------------------------------------------------------------------------------------------- // 3d | RETURNDATASIZE | 0 | – // 61 runtime | PUSH2 runtime (r) | r 0 | – mstore( ptr, 0x3d61000000000000000000000000000000000000000000000000000000000000 ) mstore(add(ptr, 0x02), shl(240, runSize)) // size of the contract running bytecode (16 bits) // creation size = 0b // 80 | DUP1 | r r 0 | – // 60 creation | PUSH1 creation (c) | c r r 0 | – // 3d | RETURNDATASIZE | 0 c r r 0 | – // 39 | CODECOPY | r 0 | [0-2d]: runtime code // 81 | DUP2 | 0 c 0 | [0-2d]: runtime code // f3 | RETURN | 0 | [0-2d]: runtime code mstore( add(ptr, 0x04), 0x80600b3d3981f300000000000000000000000000000000000000000000000000 ) // ------------------------------------------------------------------------------------------------------------- // RUNTIME // ------------------------------------------------------------------------------------------------------------- // 36 | CALLDATASIZE | cds | – // 3d | RETURNDATASIZE | 0 cds | – // 3d | RETURNDATASIZE | 0 0 cds | – // 37 | CALLDATACOPY | – | [0, cds] = calldata // 61 | PUSH2 extra | extra | [0, cds] = calldata mstore( add(ptr, 0x0b), 0x363d3d3761000000000000000000000000000000000000000000000000000000 ) mstore(add(ptr, 0x10), shl(240, extraLength)) // 60 0x38 | PUSH1 0x38 | 0x38 extra | [0, cds] = calldata // 0x38 (56) is runtime size - data // 36 | CALLDATASIZE | cds 0x38 extra | [0, cds] = calldata // 39 | CODECOPY | _ | [0, cds] = calldata // 3d | RETURNDATASIZE | 0 | [0, cds] = calldata // 3d | RETURNDATASIZE | 0 0 | [0, cds] = calldata // 3d | RETURNDATASIZE | 0 0 0 | [0, cds] = calldata // 36 | CALLDATASIZE | cds 0 0 0 | [0, cds] = calldata // 61 extra | PUSH2 extra | extra cds 0 0 0 | [0, cds] = calldata mstore( add(ptr, 0x12), 0x603836393d3d3d36610000000000000000000000000000000000000000000000 ) mstore(add(ptr, 0x1b), shl(240, extraLength)) // 01 | ADD | cds+extra 0 0 0 | [0, cds] = calldata // 3d | RETURNDATASIZE | 0 cds 0 0 0 | [0, cds] = calldata // 73 addr | PUSH20 0x123… | addr 0 cds 0 0 0 | [0, cds] = calldata mstore( add(ptr, 0x1d), 0x013d730000000000000000000000000000000000000000000000000000000000 ) mstore(add(ptr, 0x20), shl(0x60, implementation)) // 5a | GAS | gas addr 0 cds 0 0 0 | [0, cds] = calldata // f4 | DELEGATECALL | success 0 | [0, cds] = calldata // 3d | RETURNDATASIZE | rds success 0 | [0, cds] = calldata // 82 | DUP3 | 0 rds success 0 | [0, cds] = calldata // 80 | DUP1 | 0 0 rds success 0 | [0, cds] = calldata // 3e | RETURNDATACOPY | success 0 | [0, rds] = return data (there might be some irrelevant leftovers in memory [rds, cds] when rds < cds) // 90 | SWAP1 | 0 success | [0, rds] = return data // 3d | RETURNDATASIZE | rds 0 success | [0, rds] = return data // 91 | SWAP2 | success 0 rds | [0, rds] = return data // 60 0x36 | PUSH1 0x36 | 0x36 sucess 0 rds | [0, rds] = return data // 57 | JUMPI | 0 rds | [0, rds] = return data // fd | REVERT | – | [0, rds] = return data // 5b | JUMPDEST | 0 rds | [0, rds] = return data // f3 | RETURN | – | [0, rds] = return data mstore( add(ptr, 0x34), 0x5af43d82803e903d91603657fd5bf30000000000000000000000000000000000 ) } // ------------------------------------------------------------------------------------------------------------- // APPENDED DATA (Accessible from extcodecopy) // (but also send as appended data to the delegatecall) // ------------------------------------------------------------------------------------------------------------- extraLength -= 2; uint256 counter = extraLength; uint256 copyPtr = ptr + 0x43; // solhint-disable-next-line no-inline-assembly assembly { dataPtr := add(data, 32) } for (; counter >= 32; counter -= 32) { // solhint-disable-next-line no-inline-assembly assembly { mstore(copyPtr, mload(dataPtr)) } copyPtr += 32; dataPtr += 32; } uint256 mask = ~(256**(32 - counter) - 1); // solhint-disable-next-line no-inline-assembly assembly { mstore(copyPtr, and(mload(dataPtr), mask)) } copyPtr += counter; // solhint-disable-next-line no-inline-assembly assembly { mstore(copyPtr, shl(240, extraLength)) } // solhint-disable-next-line no-inline-assembly assembly { instance := create(0, ptr, creationSize) } if (instance == address(0)) { revert CreateFail(); } } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol) /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) abstract contract Auth { event OwnerUpdated(address indexed user, address indexed newOwner); event AuthorityUpdated(address indexed user, Authority indexed newAuthority); address public owner; Authority public authority; constructor(address _owner, Authority _authority) { owner = _owner; authority = _authority; emit OwnerUpdated(msg.sender, _owner); emit AuthorityUpdated(msg.sender, _authority); } modifier requiresAuth() { require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED"); _; } function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) { Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas. // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be // aware that this makes protected functions uncallable even to the owner if the authority is out of order. return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner; } function setAuthority(Authority newAuthority) public virtual { // We check if the caller is the owner first because we want to ensure they can // always swap out the authority even if it's reverting or using up a lot of gas. require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig)); authority = newAuthority; emit AuthorityUpdated(msg.sender, newAuthority); } function setOwner(address newOwner) public virtual requiresAuth { owner = newOwner; emit OwnerUpdated(msg.sender, newOwner); } } /// @notice A generic interface for a contract which provides authorization data to an Auth instance. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol) /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) interface Authority { function canCall( address user, address target, bytes4 functionSig ) external view returns (bool); }
// 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/Rari-Capital/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 //////////////////////////////////////////////////////////////*/ bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 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 { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) ) ); address recoveredAddress = ecrecover(digest, 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 Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.15; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ClonesWithImmutableArgs} from "clones/ClonesWithImmutableArgs.sol"; import {BondBaseTeller, IBondAggregator, Authority} from "./bases/BondBaseTeller.sol"; import {IBondFixedExpiryTeller} from "./interfaces/IBondFixedExpiryTeller.sol"; import {ERC20BondToken} from "./ERC20BondToken.sol"; import {TransferHelper} from "./lib/TransferHelper.sol"; import {FullMath} from "./lib/FullMath.sol"; /// @title Bond Fixed Expiry Teller /// @notice Bond Fixed Expiry Teller Contract /// @dev Bond Protocol is a permissionless system to create Olympus-style bond markets /// for any token pair. The markets do not require maintenance and will manage /// bond prices based on activity. Bond issuers create BondMarkets that pay out /// a Payout Token in exchange for deposited Quote Tokens. Users can purchase /// future-dated Payout Tokens with Quote Tokens at the current market price and /// receive Bond Tokens to represent their position while their bond vests. /// Once the Bond Tokens vest, they can redeem it for the Quote Tokens. /// @dev The Bond Fixed Expiry Teller is an implementation of the /// Bond Base Teller contract specific to handling user bond transactions /// and tokenizing bond markets where all purchases vest at the same timestamp /// as ERC20 tokens. Vesting timestamps are rounded to the nearest day to avoid /// duplicate tokens with the same name/symbol. /// /// @author Oighty, Zeus, Potted Meat, indigo contract BondFixedExpiryTeller is BondBaseTeller, IBondFixedExpiryTeller { using TransferHelper for ERC20; using FullMath for uint256; using ClonesWithImmutableArgs for address; /* ========== EVENTS ========== */ event ERC20BondTokenCreated( ERC20BondToken bondToken, ERC20 indexed underlying, uint48 indexed expiry ); /* ========== STATE VARIABLES ========== */ /// @notice ERC20 bond tokens (unique to a underlying and expiry) mapping(ERC20 => mapping(uint48 => ERC20BondToken)) public bondTokens; /// @notice ERC20BondToken reference implementation (deployed on creation to clone from) ERC20BondToken public immutable bondTokenImplementation; /* ========== CONSTRUCTOR ========== */ constructor( address protocol_, IBondAggregator aggregator_, address guardian_, Authority authority_ ) BondBaseTeller(protocol_, aggregator_, guardian_, authority_) { bondTokenImplementation = new ERC20BondToken(); } /* ========== PURCHASE ========== */ /// @notice Handle payout to recipient /// @param recipient_ Address to receive payout /// @param payout_ Amount of payoutToken to be paid /// @param underlying_ Token to be paid out /// @param vesting_ Timestamp when the payout will vest /// @return expiry Timestamp when the payout will vest function _handlePayout( address recipient_, uint256 payout_, ERC20 underlying_, uint48 vesting_ ) internal override returns (uint48 expiry) { // If there is no vesting time, the deposit is treated as an instant swap. // otherwise, deposit info is stored and payout is available at a future timestamp. // instant swap is denoted by expiry == 0. // // bonds mature with a cliff at a set timestamp // prior to the expiry timestamp, no payout tokens are accessible to the user // after the expiry timestamp, the entire payout can be redeemed // // fixed-expiry bonds mature at a set timestamp // i.e. expiry = day 10. when alice deposits on day 1, her term // is 9 days. when bob deposits on day 2, his term is 8 days. if (vesting_ > uint48(block.timestamp)) { expiry = vesting_; // Fixed-expiry bonds mint ERC-20 tokens bondTokens[underlying_][expiry].mint(recipient_, payout_); } else { // If no expiry, then transfer payout directly to user underlying_.safeTransfer(recipient_, payout_); } } /* ========== DEPOSIT/MINT ========== */ /// @inheritdoc IBondFixedExpiryTeller function create( ERC20 underlying_, uint48 expiry_, uint256 amount_ ) external override nonReentrant returns (ERC20BondToken, uint256) { // Expiry is rounded to the nearest day at 0000 UTC (in seconds) since bond tokens // are only unique to a day, not a specific timestamp. uint48 expiry = uint48(expiry_ / 1 days) * 1 days; // Revert if expiry is in the past if (uint256(expiry) < block.timestamp) revert Teller_InvalidParams(); ERC20BondToken bondToken = bondTokens[underlying_][expiry]; // Revert if no token exists, must call deploy first if (bondToken == ERC20BondToken(address(0x00))) revert Teller_TokenDoesNotExist(underlying_, expiry); // Transfer in underlying // Check that amount received is not less than amount expected // Handles edge cases like fee-on-transfer tokens (which are not supported) uint256 oldBalance = underlying_.balanceOf(address(this)); underlying_.safeTransferFrom(msg.sender, address(this), amount_); if (underlying_.balanceOf(address(this)) < oldBalance + amount_) revert Teller_UnsupportedToken(); // If fee is greater than the create discount, then calculate the fee and store it // Otherwise, fee is zero. if (protocolFee > createFeeDiscount) { // Calculate fee amount uint256 feeAmount = amount_.mulDiv(protocolFee - createFeeDiscount, FEE_DECIMALS); rewards[_protocol][underlying_] += feeAmount; // Mint new bond tokens bondToken.mint(msg.sender, amount_ - feeAmount); return (bondToken, amount_ - feeAmount); } else { // Mint new bond tokens bondToken.mint(msg.sender, amount_); return (bondToken, amount_); } } /* ========== REDEEM ========== */ /// @inheritdoc IBondFixedExpiryTeller function redeem(ERC20BondToken token_, uint256 amount_) external override nonReentrant { // Validate token is issued by this teller ERC20 underlying = token_.underlying(); uint48 expiry = token_.expiry(); if (token_ != bondTokens[underlying][expiry]) revert Teller_UnsupportedToken(); // Validate token expiry has passed if (uint48(block.timestamp) < expiry) revert Teller_TokenNotMatured(expiry); // Burn bond token and transfer underlying token_.burn(msg.sender, amount_); underlying.safeTransfer(msg.sender, amount_); } /* ========== TOKENIZATION ========== */ /// @inheritdoc IBondFixedExpiryTeller function deploy(ERC20 underlying_, uint48 expiry_) external override nonReentrant returns (ERC20BondToken) { // Expiry is rounded to the nearest day at 0000 UTC (in seconds) since bond tokens // are only unique to a day, not a specific timestamp. uint48 expiry = uint48(expiry_ / 1 days) * 1 days; // Revert if expiry is in the past if (uint256(expiry) < block.timestamp) revert Teller_InvalidParams(); // Create bond token if one doesn't already exist ERC20BondToken bondToken = bondTokens[underlying_][expiry]; if (bondToken == ERC20BondToken(address(0))) { (string memory name, string memory symbol) = _getNameAndSymbol(underlying_, expiry); bytes memory tokenData = abi.encodePacked( bytes32(bytes(name)), bytes32(bytes(symbol)), uint8(underlying_.decimals()), underlying_, uint256(expiry), address(this) ); bondToken = ERC20BondToken(address(bondTokenImplementation).clone(tokenData)); bondTokens[underlying_][expiry] = bondToken; emit ERC20BondTokenCreated(bondToken, underlying_, expiry); } return bondToken; } /// @inheritdoc IBondFixedExpiryTeller function getBondTokenForMarket(uint256 id_) external view override returns (ERC20BondToken) { // Check that the id is for a market served by this teller if (address(_aggregator.getTeller(id_)) != address(this)) revert Teller_InvalidParams(); // Get the underlying and expiry for the market (, , ERC20 underlying, , uint48 expiry, ) = _aggregator .getAuctioneer(id_) .getMarketInfoForPurchase(id_); return bondTokens[underlying][expiry]; } /// @inheritdoc IBondFixedExpiryTeller function getBondToken(ERC20 underlying_, uint48 expiry_) external view override returns (ERC20BondToken) { // Expiry is rounded to the nearest day at 0000 UTC (in seconds) since bond tokens // are only unique to a day, not a specific timestamp. uint48 expiry = uint48(expiry_ / 1 days) * 1 days; ERC20BondToken bondToken = bondTokens[underlying_][expiry]; // Revert if token does not exist if (address(bondToken) == address(0)) revert Teller_TokenDoesNotExist(underlying_, expiry); return bondToken; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.15; import {ERC20} from "solmate/tokens/ERC20.sol"; import {CloneERC20} from "./lib/CloneERC20.sol"; /// @title ERC20 Bond Token /// @notice ERC20 Bond Token Contract /// @dev Bond Protocol is a permissionless system to create Olympus-style bond markets /// for any token pair. The markets do not require maintenance and will manage /// bond prices based on activity. Bond issuers create BondMarkets that pay out /// a Payout Token in exchange for deposited Quote Tokens. Users can purchase /// future-dated Payout Tokens with Quote Tokens at the current market price and /// receive Bond Tokens to represent their position while their bond vests. /// Once the Bond Tokens vest, they can redeem it for the Quote Tokens. /// /// @dev The ERC20 Bond Token contract is issued by a Fixed Expiry Teller to /// represent bond positions until they vest. Bond tokens can be redeemed for // the underlying token 1:1 at or after expiry. /// /// @dev This contract uses Clones (https://github.com/wighawag/clones-with-immutable-args) /// to save gas on deployment and is based on VestedERC20 (https://github.com/ZeframLou/vested-erc20) /// /// @author Oighty, Zeus, Potted Meat, indigo contract ERC20BondToken is CloneERC20 { /* ========== ERRORS ========== */ error BondToken_OnlyTeller(); /* ========== IMMUTABLE PARAMETERS ========== */ /// @notice The token to be redeemed when the bond vests /// @return _underlying The address of the underlying token function underlying() external pure returns (ERC20 _underlying) { return ERC20(_getArgAddress(0x41)); } /// @notice Timestamp at which the BondToken can be redeemed for the underlying /// @return _expiry The vest start timestamp function expiry() external pure returns (uint48 _expiry) { return uint48(_getArgUint256(0x55)); } /// @notice Address of the Teller that created the token function teller() public pure returns (address _teller) { return _getArgAddress(0x75); } /* ========== MINT/BURN ========== */ function mint(address to, uint256 amount) external { if (msg.sender != teller()) revert BondToken_OnlyTeller(); _mint(to, amount); } function burn(address from, uint256 amount) external { if (msg.sender != teller()) revert BondToken_OnlyTeller(); _burn(from, amount); } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.15; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol"; import {Auth, Authority} from "solmate/auth/Auth.sol"; import {IBondTeller} from "../interfaces/IBondTeller.sol"; import {IBondCallback} from "../interfaces/IBondCallback.sol"; import {IBondAggregator} from "../interfaces/IBondAggregator.sol"; import {IBondAuctioneer} from "../interfaces/IBondAuctioneer.sol"; import {TransferHelper} from "../lib/TransferHelper.sol"; import {FullMath} from "../lib/FullMath.sol"; /// @title Bond Teller /// @notice Bond Teller Base Contract /// @dev Bond Protocol is a permissionless system to create Olympus-style bond markets /// for any token pair. The markets do not require maintenance and will manage /// bond prices based on activity. Bond issuers create BondMarkets that pay out /// a Payout Token in exchange for deposited Quote Tokens. Users can purchase /// future-dated Payout Tokens with Quote Tokens at the current market price and /// receive Bond Tokens to represent their position while their bond vests. /// Once the Bond Tokens vest, they can redeem it for the Quote Tokens. /// /// @dev The Teller contract handles all interactions with end users and manages tokens /// issued to represent bond positions. Users purchase bonds by depositing Quote Tokens /// and receive a Bond Token (token type is implementation-specific) that represents /// their payout and the designated expiry. Once a bond vests, Investors can redeem their /// Bond Tokens for the underlying Payout Token. A Teller requires one or more Auctioneer /// contracts to be deployed to provide markets for users to purchase bonds from. /// /// @author Oighty, Zeus, Potted Meat, indigo abstract contract BondBaseTeller is IBondTeller, Auth, ReentrancyGuard { using TransferHelper for ERC20; using FullMath for uint256; /* ========== ERRORS ========== */ error Teller_InvalidCallback(); error Teller_TokenNotMatured(uint48 maturesOn); error Teller_NotAuthorized(); error Teller_TokenDoesNotExist(ERC20 underlying, uint48 expiry); error Teller_UnsupportedToken(); error Teller_InvalidParams(); /* ========== EVENTS ========== */ event Bonded(uint256 indexed id, address indexed referrer, uint256 amount, uint256 payout); /* ========== STATE VARIABLES ========== */ /// @notice Fee paid to a front end operator in basis points (3 decimals). Set by the referrer, must be less than or equal to 5% (5e3). /// @dev There are some situations where the fees may round down to zero if quantity of baseToken /// is < 1e5 wei (can happen with big price differences on small decimal tokens). This is purely /// a theoretical edge case, as the bond amount would not be practical. mapping(address => uint48) public referrerFees; /// @notice Fee paid to protocol in basis points (3 decimal places). uint48 public protocolFee; /// @notice 'Create' function fee discount in basis points (3 decimal places). Amount standard fee is reduced by for partners who just want to use the 'create' function to issue bond tokens. uint48 public createFeeDiscount; uint48 public constant FEE_DECIMALS = 1e5; // one percent equals 1000. /// @notice Fees earned by an address, by token mapping(address => mapping(ERC20 => uint256)) public rewards; // Address the protocol receives fees at address internal immutable _protocol; // BondAggregator contract with utility functions IBondAggregator internal immutable _aggregator; constructor( address protocol_, IBondAggregator aggregator_, address guardian_, Authority authority_ ) Auth(guardian_, authority_) { _protocol = protocol_; _aggregator = aggregator_; // Explicitly setting these values to zero to document protocolFee = 0; createFeeDiscount = 0; } /// @inheritdoc IBondTeller function setReferrerFee(uint48 fee_) external override nonReentrant { if (fee_ > 5e3) revert Teller_InvalidParams(); referrerFees[msg.sender] = fee_; } /// @inheritdoc IBondTeller function setProtocolFee(uint48 fee_) external override requiresAuth { if (fee_ > 5e3) revert Teller_InvalidParams(); protocolFee = fee_; } /// @inheritdoc IBondTeller function setCreateFeeDiscount(uint48 discount_) external override requiresAuth { if (discount_ > protocolFee) revert Teller_InvalidParams(); createFeeDiscount = discount_; } /// @inheritdoc IBondTeller function claimFees(ERC20[] memory tokens_, address to_) external override nonReentrant { uint256 len = tokens_.length; for (uint256 i; i < len; ++i) { ERC20 token = tokens_[i]; uint256 send = rewards[msg.sender][token]; if (send != 0) { rewards[msg.sender][token] = 0; token.safeTransfer(to_, send); } } } /// @inheritdoc IBondTeller function getFee(address referrer_) external view returns (uint48) { return protocolFee + referrerFees[referrer_]; } /* ========== USER FUNCTIONS ========== */ /// @inheritdoc IBondTeller function purchase( address recipient_, address referrer_, uint256 id_, uint256 amount_, uint256 minAmountOut_ ) external virtual nonReentrant returns (uint256, uint48) { ERC20 payoutToken; ERC20 quoteToken; uint48 vesting; uint256 payout; // Calculate fees for purchase // 1. Calculate referrer fee // 2. Calculate protocol fee as the total expected fee amount minus the referrer fee // to avoid issues with rounding from separate fee calculations uint256 toReferrer = amount_.mulDiv(referrerFees[referrer_], FEE_DECIMALS); uint256 toProtocol = amount_.mulDiv(protocolFee + referrerFees[referrer_], FEE_DECIMALS) - toReferrer; { IBondAuctioneer auctioneer = _aggregator.getAuctioneer(id_); address owner; (owner, , payoutToken, quoteToken, vesting, ) = auctioneer.getMarketInfoForPurchase( id_ ); // Auctioneer handles bond pricing, capacity, and duration uint256 amountLessFee = amount_ - toReferrer - toProtocol; payout = auctioneer.purchaseBond(id_, amountLessFee, minAmountOut_); } // Allocate fees to protocol and referrer rewards[referrer_][quoteToken] += toReferrer; rewards[_protocol][quoteToken] += toProtocol; // Transfer quote tokens from sender and ensure enough payout tokens are available _handleTransfers(id_, amount_, payout, toReferrer + toProtocol); // Handle payout to user (either transfer tokens if instant swap or issue bond token) uint48 expiry = _handlePayout(recipient_, payout, payoutToken, vesting); emit Bonded(id_, referrer_, amount_, payout); return (payout, expiry); } /// @notice Handles transfer of funds from user and market owner/callback function _handleTransfers( uint256 id_, uint256 amount_, uint256 payout_, uint256 feePaid_ ) internal { // Get info from auctioneer (address owner, address callbackAddr, ERC20 payoutToken, ERC20 quoteToken, , ) = _aggregator .getAuctioneer(id_) .getMarketInfoForPurchase(id_); // Calculate amount net of fees uint256 amountLessFee = amount_ - feePaid_; // Have to transfer to teller first since fee is in quote token // Check balance before and after to ensure full amount received, revert if not // Handles edge cases like fee-on-transfer tokens (which are not supported) uint256 quoteBalance = quoteToken.balanceOf(address(this)); quoteToken.safeTransferFrom(msg.sender, address(this), amount_); if (quoteToken.balanceOf(address(this)) < quoteBalance + amount_) revert Teller_UnsupportedToken(); // If callback address supplied, transfer tokens from teller to callback, then execute callback function, // and ensure proper amount of tokens transferred in. if (callbackAddr != address(0)) { // Send quote token to callback (transferred in first to allow use during callback) quoteToken.safeTransfer(callbackAddr, amountLessFee); // Call the callback function to receive payout tokens for payout uint256 payoutBalance = payoutToken.balanceOf(address(this)); IBondCallback(callbackAddr).callback(id_, amountLessFee, payout_); // Check to ensure that the callback sent the requested amount of payout tokens back to the teller if (payoutToken.balanceOf(address(this)) < (payoutBalance + payout_)) revert Teller_InvalidCallback(); } else { // If no callback is provided, transfer tokens from market owner to this contract // for payout. // Check balance before and after to ensure full amount received, revert if not // Handles edge cases like fee-on-transfer tokens (which are not supported) uint256 payoutBalance = payoutToken.balanceOf(address(this)); payoutToken.safeTransferFrom(owner, address(this), payout_); if (payoutToken.balanceOf(address(this)) < (payoutBalance + payout_)) revert Teller_UnsupportedToken(); quoteToken.safeTransfer(owner, amountLessFee); } } /// @notice Handle payout to recipient /// @dev Implementation-agnostic. Must be implemented in contracts that /// extend this base since it is called by purchase. /// @param recipient_ Address to receive payout /// @param payout_ Amount of payoutToken to be paid /// @param underlying_ Token to be paid out /// @param vesting_ Time parameter for when the payout is available, could be a /// timestamp or duration depending on the implementation /// @return expiry Timestamp when the payout will vest function _handlePayout( address recipient_, uint256 payout_, ERC20 underlying_, uint48 vesting_ ) internal virtual returns (uint48 expiry); /// @notice Derive name and symbol of token for market /// @param underlying_ Underlying token to be paid out when the Bond Token vests /// @param expiry_ Timestamp that the Bond Token vests at /// @return name Bond token name, format is "Token YYYY-MM-DD" /// @return symbol Bond token symbol, format is "TKN-YYYYMMDD" function _getNameAndSymbol(ERC20 underlying_, uint256 expiry_) internal view returns (string memory name, string memory symbol) { // Convert a number of days into a human-readable date, courtesy of BokkyPooBah. // Source: https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary/blob/master/contracts/BokkyPooBahsDateTimeLibrary.sol uint256 year; uint256 month; uint256 day; { int256 __days = int256(expiry_ / 1 days); int256 num1 = __days + 68569 + 2440588; // 2440588 = OFFSET19700101 int256 num2 = (4 * num1) / 146097; num1 = num1 - (146097 * num2 + 3) / 4; int256 _year = (4000 * (num1 + 1)) / 1461001; num1 = num1 - (1461 * _year) / 4 + 31; int256 _month = (80 * num1) / 2447; int256 _day = num1 - (2447 * _month) / 80; num1 = _month / 11; _month = _month + 2 - 12 * num1; _year = 100 * (num2 - 49) + _year + num1; year = uint256(_year); month = uint256(_month); day = uint256(_day); } string memory yearStr = _uint2str(year % 10000); string memory monthStr = month < 10 ? string(abi.encodePacked("0", _uint2str(month))) : _uint2str(month); string memory dayStr = day < 10 ? string(abi.encodePacked("0", _uint2str(day))) : _uint2str(day); // Construct name/symbol strings. name = string( abi.encodePacked(underlying_.name(), " ", yearStr, "-", monthStr, "-", dayStr) ); symbol = string(abi.encodePacked(underlying_.symbol(), "-", yearStr, monthStr, dayStr)); } // Some fancy math to convert a uint into a string, courtesy of Provable Things. // Updated to work with solc 0.8.0. // https://github.com/provable-things/ethereum-api/blob/master/provableAPI_0.6.sol function _uint2str(uint256 _i) internal pure returns (string memory) { if (_i == 0) { return "0"; } uint256 j = _i; uint256 len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint256 k = len; while (_i != 0) { k = k - 1; uint8 temp = (48 + uint8(_i - (_i / 10) * 10)); bytes1 b1 = bytes1(temp); bstr[k] = b1; _i /= 10; } return string(bstr); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.0; import {ERC20} from "solmate/tokens/ERC20.sol"; import {IBondAuctioneer} from "../interfaces/IBondAuctioneer.sol"; import {IBondTeller} from "../interfaces/IBondTeller.sol"; interface IBondAggregator { /// @notice Register a auctioneer with the aggregator /// @notice Only Guardian /// @param auctioneer_ Address of the Auctioneer to register /// @dev A auctioneer must be registered with an aggregator to create markets function registerAuctioneer(IBondAuctioneer auctioneer_) external; /// @notice Register a new market with the aggregator /// @notice Only registered depositories /// @param payoutToken_ Token to be paid out by the market /// @param quoteToken_ Token to be accepted by the market /// @param marketId ID of the market being created function registerMarket(ERC20 payoutToken_, ERC20 quoteToken_) external returns (uint256 marketId); /// @notice Get the auctioneer for the provided market ID /// @param id_ ID of Market function getAuctioneer(uint256 id_) external view returns (IBondAuctioneer); /// @notice Calculate current market price of payout token in quote tokens /// @dev Accounts for debt and control variable decay since last deposit (vs _marketPrice()) /// @param id_ ID of market /// @return Price for market (see the specific auctioneer for units) // // if price is below minimum price, minimum price is returned // this is enforced on deposits by manipulating total debt (see _decay()) function marketPrice(uint256 id_) external view returns (uint256); /// @notice Scale value to use when converting between quote token and payout token amounts with marketPrice() /// @param id_ ID of market /// @return Scaling factor for market in configured decimals function marketScale(uint256 id_) external view returns (uint256); /// @notice Payout due for amount of quote tokens /// @dev Accounts for debt and control variable decay so it is up to date /// @param amount_ Amount of quote tokens to spend /// @param id_ ID of market /// @param referrer_ Address of referrer, used to get fees to calculate accurate payout amount. /// Inputting the zero address will take into account just the protocol fee. /// @return amount of payout tokens to be paid function payoutFor( uint256 amount_, uint256 id_, address referrer_ ) external view returns (uint256); /// @notice Returns maximum amount of quote token accepted by the market /// @param id_ ID of market /// @param referrer_ Address of referrer, used to get fees to calculate accurate payout amount. /// Inputting the zero address will take into account just the protocol fee. function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256); /// @notice Does market send payout immediately /// @param id_ Market ID to search for function isInstantSwap(uint256 id_) external view returns (bool); /// @notice Is a given market accepting deposits /// @param id_ ID of market function isLive(uint256 id_) external view returns (bool); /// @notice Returns array of active market IDs within a range /// @dev Should be used if length exceeds max to query entire array function liveMarketsBetween(uint256 firstIndex_, uint256 lastIndex_) external view returns (uint256[] memory); /// @notice Returns an array of all active market IDs for a given quote token /// @param token_ Address of token to query by /// @param isPayout_ If true, search by payout token, else search for quote token function liveMarketsFor(address token_, bool isPayout_) external view returns (uint256[] memory); /// @notice Returns an array of all active market IDs for a given owner /// @param owner_ Address of owner to query by /// @param firstIndex_ Market ID to start at /// @param lastIndex_ Market ID to end at (non-inclusive) function liveMarketsBy( address owner_, uint256 firstIndex_, uint256 lastIndex_ ) external view returns (uint256[] memory); /// @notice Returns an array of all active market IDs for a given payout and quote token /// @param payout_ Address of payout token /// @param quote_ Address of quote token function marketsFor(address payout_, address quote_) external view returns (uint256[] memory); /// @notice Returns the market ID with the highest current payoutToken payout for depositing quoteToken /// @param payout_ Address of payout token /// @param quote_ Address of quote token /// @param amountIn_ Amount of quote tokens to deposit /// @param minAmountOut_ Minimum amount of payout tokens to receive as payout /// @param maxExpiry_ Latest acceptable vesting timestamp for bond /// Inputting the zero address will take into account just the protocol fee. function findMarketFor( address payout_, address quote_, uint256 amountIn_, uint256 minAmountOut_, uint256 maxExpiry_ ) external view returns (uint256 id); /// @notice Returns the Teller that services the market ID function getTeller(uint256 id_) external view returns (IBondTeller); /// @notice Returns current capacity of a market function currentCapacity(uint256 id_) external view returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.0; import {ERC20} from "solmate/tokens/ERC20.sol"; import {IBondTeller} from "../interfaces/IBondTeller.sol"; import {IBondAggregator} from "../interfaces/IBondAggregator.sol"; interface IBondAuctioneer { /// @notice Creates a new bond market /// @param params_ Configuration data needed for market creation, encoded in a bytes array /// @dev See specific auctioneer implementations for details on encoding the parameters. /// @return id ID of new bond market function createMarket(bytes memory params_) external returns (uint256); /// @notice Disable existing bond market /// @notice Must be market owner /// @param id_ ID of market to close function closeMarket(uint256 id_) external; /// @notice Exchange quote tokens for a bond in a specified market /// @notice Must be teller /// @param id_ ID of the Market the bond is being purchased from /// @param amount_ Amount to deposit in exchange for bond (after fee has been deducted) /// @param minAmountOut_ Minimum acceptable amount of bond to receive. Prevents frontrunning /// @return payout Amount of payout token to be received from the bond function purchaseBond( uint256 id_, uint256 amount_, uint256 minAmountOut_ ) external returns (uint256 payout); /// @notice Set market intervals to different values than the defaults /// @notice Must be market owner /// @dev Changing the intervals could cause markets to behave in unexpected way /// tuneInterval should be greater than tuneAdjustmentDelay /// @param id_ Market ID /// @param intervals_ Array of intervals (3) /// 1. Tune interval - Frequency of tuning /// 2. Tune adjustment delay - Time to implement downward tuning adjustments /// 3. Debt decay interval - Interval over which debt should decay completely function setIntervals(uint256 id_, uint32[3] calldata intervals_) external; /// @notice Designate a new owner of a market /// @notice Must be market owner /// @dev Doesn't change permissions until newOwner calls pullOwnership /// @param id_ Market ID /// @param newOwner_ New address to give ownership to function pushOwnership(uint256 id_, address newOwner_) external; /// @notice Accept ownership of a market /// @notice Must be market newOwner /// @dev The existing owner must call pushOwnership prior to the newOwner calling this function /// @param id_ Market ID function pullOwnership(uint256 id_) external; /// @notice Set the auctioneer defaults /// @notice Must be policy /// @param defaults_ Array of default values /// 1. Tune interval - amount of time between tuning adjustments /// 2. Tune adjustment delay - amount of time to apply downward tuning adjustments /// 3. Minimum debt decay interval - minimum amount of time to let debt decay to zero /// 4. Minimum deposit interval - minimum amount of time to wait between deposits /// 5. Minimum market duration - minimum amount of time a market can be created for /// 6. Minimum debt buffer - the minimum amount of debt over the initial debt to trigger a market shutdown /// @dev The defaults set here are important to avoid edge cases in market behavior, e.g. a very short market reacts doesn't tune well /// @dev Only applies to new markets that are created after the change function setDefaults(uint32[6] memory defaults_) external; /// @notice Change the status of the auctioneer to allow creation of new markets /// @dev Setting to false and allowing active markets to end will sunset the auctioneer /// @param status_ Allow market creation (true) : Disallow market creation (false) function setAllowNewMarkets(bool status_) external; /// @notice Change whether a market creator is allowed to use a callback address in their markets or not /// @notice Must be guardian /// @dev Callback is believed to be safe, but a whitelist is implemented to prevent abuse /// @param creator_ Address of market creator /// @param status_ Allow callback (true) : Disallow callback (false) function setCallbackAuthStatus(address creator_, bool status_) external; /* ========== VIEW FUNCTIONS ========== */ /// @notice Provides information for the Teller to execute purchases on a Market /// @param id_ Market ID /// @return owner Address of the market owner (tokens transferred from this address if no callback) /// @return callbackAddr Address of the callback contract to get tokens for payouts /// @return payoutToken Payout Token (token paid out) for the Market /// @return quoteToken Quote Token (token received) for the Market /// @return vesting Timestamp or duration for vesting, implementation-dependent /// @return maxPayout Maximum amount of payout tokens you can purchase in one transaction function getMarketInfoForPurchase(uint256 id_) external view returns ( address owner, address callbackAddr, ERC20 payoutToken, ERC20 quoteToken, uint48 vesting, uint256 maxPayout ); /// @notice Calculate current market price of payout token in quote tokens /// @param id_ ID of market /// @return Price for market in configured decimals // // if price is below minimum price, minimum price is returned function marketPrice(uint256 id_) external view returns (uint256); /// @notice Scale value to use when converting between quote token and payout token amounts with marketPrice() /// @param id_ ID of market /// @return Scaling factor for market in configured decimals function marketScale(uint256 id_) external view returns (uint256); /// @notice Payout due for amount of quote tokens /// @dev Accounts for debt and control variable decay so it is up to date /// @param amount_ Amount of quote tokens to spend /// @param id_ ID of market /// @param referrer_ Address of referrer, used to get fees to calculate accurate payout amount. /// Inputting the zero address will take into account just the protocol fee. /// @return amount of payout tokens to be paid function payoutFor( uint256 amount_, uint256 id_, address referrer_ ) external view returns (uint256); /// @notice Returns maximum amount of quote token accepted by the market /// @param id_ ID of market /// @param referrer_ Address of referrer, used to get fees to calculate accurate payout amount. /// Inputting the zero address will take into account just the protocol fee. function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256); /// @notice Does market send payout immediately /// @param id_ Market ID to search for function isInstantSwap(uint256 id_) external view returns (bool); /// @notice Is a given market accepting deposits /// @param id_ ID of market function isLive(uint256 id_) external view returns (bool); /// @notice Returns the address of the market owner /// @param id_ ID of market function ownerOf(uint256 id_) external view returns (address); /// @notice Returns the Teller that services the Auctioneer function getTeller() external view returns (IBondTeller); /// @notice Returns the Aggregator that services the Auctioneer function getAggregator() external view returns (IBondAggregator); /// @notice Returns current capacity of a market function currentCapacity(uint256 id_) external view returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.0; import {ERC20} from "solmate/tokens/ERC20.sol"; interface IBondCallback { /// @notice Send payout tokens to Teller while allowing market owners to perform custom logic on received or paid out tokens /// @notice Market ID on Teller must be whitelisted /// @param id_ ID of the market /// @param inputAmount_ Amount of quote tokens bonded to the market /// @param outputAmount_ Amount of payout tokens to be paid out to the market /// @dev Must transfer the output amount of payout tokens back to the Teller /// @dev Should check that the quote tokens have been transferred to the contract in the _callback function function callback( uint256 id_, uint256 inputAmount_, uint256 outputAmount_ ) external; /// @notice Returns the number of quote tokens received and payout tokens paid out for a market /// @param id_ ID of the market /// @return in_ Amount of quote tokens bonded to the market /// @return out_ Amount of payout tokens paid out to the market function amountsForMarket(uint256 id_) external view returns (uint256 in_, uint256 out_); /// @notice Whitelist a teller and market ID combination /// @notice Must be callback owner /// @param teller_ Address of the Teller contract which serves the market /// @param id_ ID of the market function whitelist(address teller_, uint256 id_) external; /// @notice Remove a market ID on a teller from the whitelist /// @dev Shutdown function in case there's an issue with the teller /// @param teller_ Address of the Teller contract which serves the market /// @param id_ ID of the market to remove from whitelist function blacklist(address teller_, uint256 id_) external; /// @notice Withdraw tokens from the callback and update balances /// @notice Only callback owner /// @param to_ Address of the recipient /// @param token_ Address of the token to withdraw /// @param amount_ Amount of tokens to withdraw function withdraw( address to_, ERC20 token_, uint256 amount_ ) external; /// @notice Deposit tokens to the callback and update balances /// @notice Only callback owner /// @param token_ Address of the token to deposit /// @param amount_ Amount of tokens to deposit function deposit(ERC20 token_, uint256 amount_) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.0; import {ERC20BondToken} from "../ERC20BondToken.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; interface IBondFixedExpiryTeller { /// @notice Redeem a fixed-expiry bond token for the underlying token (bond token must have matured) /// @param token_ Token to redeem /// @param amount_ Amount to redeem function redeem(ERC20BondToken token_, uint256 amount_) external; /// @notice Deposit an ERC20 token and mint a future-dated ERC20 bond token /// @param underlying_ ERC20 token redeemable when the bond token vests /// @param expiry_ Timestamp at which the bond token can be redeemed for the underlying token /// @param amount_ Amount of underlying tokens to deposit /// @return Address of the ERC20 bond token received /// @return Amount of the ERC20 bond token received function create( ERC20 underlying_, uint48 expiry_, uint256 amount_ ) external returns (ERC20BondToken, uint256); /// @notice Deploy a new ERC20 bond token for an (underlying, expiry) pair and return its address /// @dev ERC20 used for fixed-expiry /// @dev If a bond token exists for the (underlying, expiry) pair, it returns that address /// @param underlying_ ERC20 token redeemable when the bond token vests /// @param expiry_ Timestamp at which the bond token can be redeemed for the underlying token /// @return Address of the ERC20 bond token being created function deploy(ERC20 underlying_, uint48 expiry_) external returns (ERC20BondToken); /// @notice Get the ERC20BondToken contract corresponding to a market /// @param id_ ID of the market /// @return ERC20BondToken contract address function getBondTokenForMarket(uint256 id_) external view returns (ERC20BondToken); /// @notice Get the ERC20BondToken contract corresponding to an (underlying, expiry) pair, reverts if no token exists /// @param underlying_ ERC20 token redeemable when the bond token vests /// @param expiry_ Timestamp at which the bond token can be redeemed for the underlying token (this is rounded to the nearest day) /// @return ERC20BondToken contract address function getBondToken(ERC20 underlying_, uint48 expiry_) external view returns (ERC20BondToken); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.0; import {ERC20} from "solmate/tokens/ERC20.sol"; interface IBondTeller { /// @notice Exchange quote tokens for a bond in a specified market /// @param recipient_ Address of recipient of bond. Allows deposits for other addresses /// @param referrer_ Address of referrer who will receive referral fee. For frontends to fill. /// Direct calls can use the zero address for no referrer fee. /// @param id_ ID of the Market the bond is being purchased from /// @param amount_ Amount to deposit in exchange for bond /// @param minAmountOut_ Minimum acceptable amount of bond to receive. Prevents frontrunning /// @return Amount of payout token to be received from the bond /// @return Timestamp at which the bond token can be redeemed for the underlying token function purchase( address recipient_, address referrer_, uint256 id_, uint256 amount_, uint256 minAmountOut_ ) external returns (uint256, uint48); /// @notice Get current fee charged by the teller based on the combined protocol and referrer fee /// @param referrer_ Address of the referrer /// @return Fee in basis points (3 decimal places) function getFee(address referrer_) external view returns (uint48); /// @notice Set protocol fee /// @notice Must be guardian /// @param fee_ Protocol fee in basis points (3 decimal places) function setProtocolFee(uint48 fee_) external; /// @notice Set the discount for creating bond tokens from the base protocol fee /// @dev The discount is subtracted from the protocol fee to determine the fee /// when using create() to mint bond tokens without using an Auctioneer /// @param discount_ Create Fee Discount in basis points (3 decimal places) function setCreateFeeDiscount(uint48 discount_) external; /// @notice Set your fee as a referrer to the protocol /// @notice Fee is set for sending address /// @param fee_ Referrer fee in basis points (3 decimal places) function setReferrerFee(uint48 fee_) external; /// @notice Claim fees accrued by sender in the input tokens and sends them to the provided address /// @param tokens_ Array of tokens to claim fees for /// @param to_ Address to send fees to function claimFees(ERC20[] memory tokens_, address to_) external; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {Clone} from "clones/Clone.sol"; /// @notice Modern and gas efficient ERC20 implementation. /// @author Solmate (https://github.com/Rari-Capital/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 CloneERC20 is Clone { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*/////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*/////////////////////////////////////////////////////////////// METADATA //////////////////////////////////////////////////////////////*/ function name() external pure returns (string memory) { return string(abi.encodePacked(_getArgUint256(0))); } function symbol() external pure returns (string memory) { return string(abi.encodePacked(_getArgUint256(0x20))); } function decimals() external pure returns (uint8) { return _getArgUint8(0x40); } /*/////////////////////////////////////////////////////////////// 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 increaseAllowance(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] += amount; emit Approval(msg.sender, spender, allowance[msg.sender][spender]); return true; } function decreaseAllowance(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] -= amount; emit Approval(msg.sender, spender, allowance[msg.sender][spender]); 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; } /*/////////////////////////////////////////////////////////////// INTERNAL 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); } function _getImmutableVariablesOffset() internal pure returns (uint256 offset) { assembly { offset := sub(calldatasize(), add(shr(240, calldataload(sub(calldatasize(), 2))), 2)) } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; /// @title Contains 512-bit math functions /// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision /// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits library FullMath { /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv function mulDiv( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = a * b // Compute the product mod 2**256 and mod 2**256 - 1 // then use the Chinese Remainder Theorem to reconstruct // the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2**256 + prod0 uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(a, b, not(0)) prod0 := mul(a, b) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { require(denominator > 0); assembly { result := div(prod0, denominator) } return result; } // Make sure the result is less than 2**256. // Also prevents denominator == 0 require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0] // Compute remainder using mulmod uint256 remainder; assembly { remainder := mulmod(a, b, denominator) } // Subtract 256 bit number from 512 bit number assembly { prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator // Compute largest power of two divisor of denominator. // Always >= 1. uint256 twos = (type(uint256).max - denominator + 1) & denominator; // Divide denominator by power of two assembly { denominator := div(denominator, twos) } // Divide [prod1 prod0] by the factors of two assembly { prod0 := div(prod0, twos) } // Shift in bits from prod1 into prod0. For this we need // to flip `twos` such that it is 2**256 / twos. // If twos is zero, then it becomes one assembly { twos := add(div(sub(0, twos), twos), 1) } prod0 |= prod1 * twos; // Invert denominator mod 2**256 // Now that denominator is an odd number, it has an inverse // modulo 2**256 such that denominator * inv = 1 mod 2**256. // Compute the inverse by starting with a seed that is correct // correct for four bits. That is, denominator * inv = 1 mod 2**4 uint256 inv = (3 * denominator) ^ 2; // Now use Newton-Raphson iteration to improve the precision. // Thanks to Hensel's lifting lemma, this also works in modular // arithmetic, doubling the correct bits in each step. inv *= 2 - denominator * inv; // inverse mod 2**8 inv *= 2 - denominator * inv; // inverse mod 2**16 inv *= 2 - denominator * inv; // inverse mod 2**32 inv *= 2 - denominator * inv; // inverse mod 2**64 inv *= 2 - denominator * inv; // inverse mod 2**128 inv *= 2 - denominator * inv; // inverse mod 2**256 // Because the division is now exact we can divide by multiplying // with the modular inverse of denominator. This will give us the // correct result modulo 2**256. Since the precoditions guarantee // that the outcome is less than 2**256, this is the final result. // We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inv; return result; } } /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result function mulDivUp( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { result = mulDiv(a, b, denominator); unchecked { if (mulmod(a, b, denominator) > 0) { require(result < type(uint256).max); result++; } } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "solmate/tokens/ERC20.sol"; /// @notice Safe ERC20 and ETH transfer library that safely handles missing return values. /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/libraries/TransferHelper.sol) /// @author Taken from Solmate. library TransferHelper { function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { (bool success, bytes memory data) = address(token).call( abi.encodeWithSelector(ERC20.transferFrom.selector, from, to, amount) ); require( success && (data.length == 0 || abi.decode(data, (bool))) && address(token).code.length > 0, "TRANSFER_FROM_FAILED" ); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { (bool success, bytes memory data) = address(token).call( abi.encodeWithSelector(ERC20.transfer.selector, to, amount) ); require( success && (data.length == 0 || abi.decode(data, (bool))) && address(token).code.length > 0, "TRANSFER_FAILED" ); } // function safeApprove( // ERC20 token, // address to, // uint256 amount // ) internal { // (bool success, bytes memory data) = address(token).call( // abi.encodeWithSelector(ERC20.approve.selector, to, amount) // ); // require(success && (data.length == 0 || abi.decode(data, (bool))), "APPROVE_FAILED"); // } // function safeTransferETH(address to, uint256 amount) internal { // (bool success, ) = to.call{value: amount}(new bytes(0)); // require(success, "ETH_TRANSFER_FAILED"); // } }
{ "remappings": [ "clones-with-immutable-args/=lib/clones-with-immutable-args/src/", "clones/=lib/clones-with-immutable-args/src/", "ds-test/=lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "hardhat/=node_modules/hardhat/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "solidity-code-metrics/=node_modules/solidity-code-metrics/", "solmate/=lib/solmate/src/", "weird-erc20/=lib/solmate/lib/weird-erc20/src/" ], "optimizer": { "enabled": true, "runs": 100000 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
[{"inputs":[{"internalType":"address","name":"protocol_","type":"address"},{"internalType":"contract IBondAggregator","name":"aggregator_","type":"address"},{"internalType":"address","name":"guardian_","type":"address"},{"internalType":"contract Authority","name":"authority_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CreateFail","type":"error"},{"inputs":[],"name":"Teller_InvalidCallback","type":"error"},{"inputs":[],"name":"Teller_InvalidParams","type":"error"},{"inputs":[],"name":"Teller_NotAuthorized","type":"error"},{"inputs":[{"internalType":"contract ERC20","name":"underlying","type":"address"},{"internalType":"uint48","name":"expiry","type":"uint48"}],"name":"Teller_TokenDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint48","name":"maturesOn","type":"uint48"}],"name":"Teller_TokenNotMatured","type":"error"},{"inputs":[],"name":"Teller_UnsupportedToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"}],"name":"Bonded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ERC20BondToken","name":"bondToken","type":"address"},{"indexed":true,"internalType":"contract ERC20","name":"underlying","type":"address"},{"indexed":true,"internalType":"uint48","name":"expiry","type":"uint48"}],"name":"ERC20BondTokenCreated","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"},{"inputs":[],"name":"FEE_DECIMALS","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondTokenImplementation","outputs":[{"internalType":"contract ERC20BondToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"","type":"address"},{"internalType":"uint48","name":"","type":"uint48"}],"name":"bondTokens","outputs":[{"internalType":"contract ERC20BondToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20[]","name":"tokens_","type":"address[]"},{"internalType":"address","name":"to_","type":"address"}],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"underlying_","type":"address"},{"internalType":"uint48","name":"expiry_","type":"uint48"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"create","outputs":[{"internalType":"contract ERC20BondToken","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"createFeeDiscount","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"underlying_","type":"address"},{"internalType":"uint48","name":"expiry_","type":"uint48"}],"name":"deploy","outputs":[{"internalType":"contract ERC20BondToken","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"underlying_","type":"address"},{"internalType":"uint48","name":"expiry_","type":"uint48"}],"name":"getBondToken","outputs":[{"internalType":"contract ERC20BondToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"getBondTokenForMarket","outputs":[{"internalType":"contract ERC20BondToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"referrer_","type":"address"}],"name":"getFee","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"address","name":"referrer_","type":"address"},{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"minAmountOut_","type":"uint256"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20BondToken","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"referrerFees","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"contract ERC20","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"discount_","type":"uint48"}],"name":"setCreateFeeDiscount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"fee_","type":"uint48"}],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"fee_","type":"uint48"}],"name":"setReferrerFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60e060405260016002553480156200001657600080fd5b5060405162004a7b38038062004a7b833981016040819052620000399162000164565b600080546001600160a01b03199081166001600160a01b0385811691821784556001805490931690851617909155604051869286928692869284928492909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a36040516001600160a01b0382169033907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a35050506001600160a01b03928316608052501660a052600480546001600160601b031916905560405162000108906200013d565b604051809103906000f08015801562000125573d6000803e3d6000fd5b506001600160a01b031660c05250620001cc92505050565b610af48062003f8783390190565b6001600160a01b03811681146200016157600080fd5b50565b600080600080608085870312156200017b57600080fd5b845162000188816200014b565b60208601519094506200019b816200014b565b6040860151909350620001ae816200014b565b6060860151909250620001c1816200014b565b939692955090935050565b60805160a05160c051613d686200021f600039600081816102120152611a72015260008181610bb901528181611c4f01528181611d5001526121fc015260008181610e1d01526115f50152613d686000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c8063b0e21e8a116100d8578063c23135dd1161008c578063e70b9e2711610066578063e70b9e27146103eb578063fa922c2614610424578063fcdc31261461043757600080fd5b8063c23135dd146103a6578063c6e38a4b146103ce578063ccf288c6146103e157600080fd5b8063b7314ef9116100bd578063b7314ef914610334578063b88c914814610373578063bf7e214f1461038657600080fd5b8063b0e21e8a1461030f578063b1e555291461032157600080fd5b80636c436dd51161013a5780637a9e5e4b116101145780637a9e5e4b146102c957806381690927146102dc5780638da5cb5b146102ef57600080fd5b80636c436dd51461020d5780636de5b4cd14610259578063709ea8651461028857600080fd5b806313af40351161016b57806313af4035146101d45780631e9a6950146101e75780632f6fa94e146101fa57600080fd5b806306692a37146101875780630d4dfc21146101bf575b600080fd5b6004546101a3906601000000000000900465ffffffffffff1681565b60405165ffffffffffff90911681526020015b60405180910390f35b6101d26101cd366004613249565b61044a565b005b6101d26101e2366004613298565b61054e565b6101d26101f53660046132b5565b610652565b6101d261020836600461335f565b610929565b6102347f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101b6565b61026c610267366004613423565b610a51565b6040805192835265ffffffffffff9091166020830152016101b6565b610234610296366004613474565b600660209081526000928352604080842090915290825290205473ffffffffffffffffffffffffffffffffffffffff1681565b6101d26102d7366004613298565b610f08565b6101d26102ea366004613249565b611065565b6000546102349073ffffffffffffffffffffffffffffffffffffffff1681565b6004546101a39065ffffffffffff1681565b6101d261032f366004613249565b611184565b6103476103423660046134ad565b611295565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152016101b6565b6101a3610381366004613298565b6117ae565b6001546102349073ffffffffffffffffffffffffffffffffffffffff1681565b6101a36103b4366004613298565b60036020526000908152604090205465ffffffffffff1681565b6102346103dc366004613474565b6117f2565b6101a3620186a081565b6104166103f93660046134ee565b600560209081526000928352604080842090915290825290205481565b6040519081526020016101b6565b610234610432366004613474565b611b46565b61023461044536600461351c565b611c06565b6002546001146104bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6002805561138865ffffffffffff82161115610503576040517fe94b8f2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260036020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff929092169190911790556001600255565b61057c336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e7f565b6105e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104b2565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b6002546001146106be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016104b2565b6002808190555060008273ffffffffffffffffffffffffffffffffffffffff16636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610712573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107369190613535565b905060008373ffffffffffffffffffffffffffffffffffffffff1663e184c9be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610785573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a99190613552565b73ffffffffffffffffffffffffffffffffffffffff808416600090815260066020908152604080832065ffffffffffff86168452909152902054919250858116911614610822576040517fd354452400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8065ffffffffffff164265ffffffffffff161015610876576040517f4fb1e97300000000000000000000000000000000000000000000000000000000815265ffffffffffff821660048201526024016104b2565b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201526024810184905273ffffffffffffffffffffffffffffffffffffffff851690639dc29fac90604401600060405180830381600087803b1580156108e457600080fd5b505af11580156108f8573d6000803e3d6000fd5b5061091e9250505073ffffffffffffffffffffffffffffffffffffffff83163385611f8f565b505060016002555050565b600254600114610995576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016104b2565b60028055815160005b8181101561091e5760008482815181106109ba576109ba61356f565b60209081029190910181015133600090815260058352604080822073ffffffffffffffffffffffffffffffffffffffff8416835290935291909120549091508015610a3e5733600090815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff86168085529252822091909155610a3e908683611f8f565b505080610a4a906135cd565b905061099e565b600080600254600114610ac0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016104b2565b6002805573ffffffffffffffffffffffffffffffffffffffff86166000908152600360205260408120548190819081908190610b09908a9065ffffffffffff16620186a0612122565b73ffffffffffffffffffffffffffffffffffffffff8c1660009081526003602052604081205460045492935090918391610b6591610b529165ffffffffffff9081169116613605565b8c9065ffffffffffff16620186a0612122565b610b6f919061362f565b6040517f52047942000000000000000000000000000000000000000000000000000000008152600481018d905290915060009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690635204794290602401602060405180830381865afa158015610c00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c249190613535565b6040517facc5570c000000000000000000000000000000000000000000000000000000008152600481018e905290915060009073ffffffffffffffffffffffffffffffffffffffff83169063acc5570c9060240160c060405180830381865afa158015610c95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb99190613646565b50919b50995097509091506000905083610cd3868f61362f565b610cdd919061362f565b90508273ffffffffffffffffffffffffffffffffffffffff1663e007fa978f838f6040518463ffffffff1660e01b8152600401610d2d939291909283526020830191909152604082015260600190565b6020604051808303816000875af1158015610d4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7091906136c3565b955050505081600560008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610e0191906136dc565b909155505073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600090815260056020908152604080832093891683529290529081208054839290610e6a9084906136dc565b90915550610e8590508b8b85610e8085876136dc565b6121f4565b6000610e938e85898861289c565b90508c73ffffffffffffffffffffffffffffffffffffffff168c7f3ae5efadaccde413bdfcb3a4626babecd844c502ee4c2c9b340f28cd9d5527328d87604051610ee7929190918252602082015260400190565b60405180910390a36001600255929d929c50919a5050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480610feb57506001546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201526000357fffffffff0000000000000000000000000000000000000000000000000000000016604482015273ffffffffffffffffffffffffffffffffffffffff9091169063b700961390606401602060405180830381865afa158015610fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610feb91906136f4565b610ff457600080fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b611093336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e7f565b6110f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104b2565b60045465ffffffffffff9081169082161115611141576040517fe94b8f2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805465ffffffffffff9092166601000000000000027fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff909216919091179055565b6111b2336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e7f565b611218576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104b2565b6113888165ffffffffffff16111561125c576040517fe94b8f2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff92909216919091179055565b600080600254600114611304576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016104b2565b6002805560006113176201518086613745565b611324906201518061376a565b9050428165ffffffffffff161015611368576040517fe94b8f2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff808716600090815260066020908152604080832065ffffffffffff861684529091529020541680611400576040517fcb66c9db00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8816600482015265ffffffffffff831660248201526044016104b2565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8916906370a0823190602401602060405180830381865afa15801561146d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149191906136c3565b90506114b573ffffffffffffffffffffffffffffffffffffffff891633308961298f565b6114bf86826136dc565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8a16906370a0823190602401602060405180830381865afa158015611529573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154d91906136c3565b1015611585576040517fd354452400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045465ffffffffffff66010000000000008204811691161115611710576004546000906115de906115cb9065ffffffffffff6601000000000000820481169116613798565b889065ffffffffffff16620186a0612122565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166000908152600560209081526040808320938e168352929052908120805492935083929091906116479084906136dc565b909155505073ffffffffffffffffffffffffffffffffffffffff83166340c10f1933611673848b61362f565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401600060405180830381600087803b1580156116de57600080fd5b505af11580156116f2573d6000803e3d6000fd5b50505050828188611703919061362f565b955095505050505061179f565b6040517f40c10f190000000000000000000000000000000000000000000000000000000081523360048201526024810187905273ffffffffffffffffffffffffffffffffffffffff8316906340c10f1990604401600060405180830381600087803b15801561177e57600080fd5b505af1158015611792573d6000803e3d6000fd5b5093965087955050505050505b60016002559094909350915050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600360205260408120546004546117ec9165ffffffffffff9081169116613605565b92915050565b6000600254600114611860576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016104b2565b6002805560006118736201518084613745565b611880906201518061376a565b9050428165ffffffffffff1610156118c4576040517fe94b8f2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff808516600090815260066020908152604080832065ffffffffffff861684529091529020541680611b3957600080611919878565ffffffffffff16612b2b565b91509150600082611929906137bf565b611932836137bf565b8973ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561197d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a19190613804565b60408051602081019490945283019190915260f81b7fff000000000000000000000000000000000000000000000000000000000000001660608083019190915289811b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116606184015265ffffffffffff881660758401523090911b16609582015260a901604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529050611a9873ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001682612ecc565b73ffffffffffffffffffffffffffffffffffffffff898116600081815260066020908152604080832065ffffffffffff8c168085529083529281902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169587169586179055519384529397509290917f4fd9a46575749d9ddf290fadaa5729fc640790e2b6360df8cc8af35e418dcec0910160405180910390a35050505b6001600255949350505050565b600080611b566201518084613745565b611b63906201518061376a565b73ffffffffffffffffffffffffffffffffffffffff808616600090815260066020908152604080832065ffffffffffff861684529091529020549192501680611bfe576040517fcb66c9db00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616600482015265ffffffffffff831660248201526044016104b2565b949350505050565b6040517f78b5a87a00000000000000000000000000000000000000000000000000000000815260048101829052600090309073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906378b5a87a90602401602060405180830381865afa158015611c96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cba9190613535565b73ffffffffffffffffffffffffffffffffffffffff1614611d07576040517fe94b8f2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5204794200000000000000000000000000000000000000000000000000000000815260048101839052600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690635204794290602401602060405180830381865afa158015611d97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dbb9190613535565b73ffffffffffffffffffffffffffffffffffffffff1663acc5570c856040518263ffffffff1660e01b8152600401611df591815260200190565b60c060405180830381865afa158015611e12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e369190613646565b5073ffffffffffffffffffffffffffffffffffffffff928316600090815260066020908152604080832065ffffffffffff90941683529290522054909116979650505050505050565b60015460009073ffffffffffffffffffffffffffffffffffffffff168015801590611f6357506040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301523060248301527fffffffff000000000000000000000000000000000000000000000000000000008516604483015282169063b700961390606401602060405180830381865afa158015611f3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6391906136f4565b80611bfe575060005473ffffffffffffffffffffffffffffffffffffffff858116911614949350505050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000928392908716916120269190613857565b6000604051808303816000865af19150503d8060008114612063576040519150601f19603f3d011682016040523d82523d6000602084013e612068565b606091505b509150915081801561209257508051158061209257508080602001905181019061209291906136f4565b80156120b5575060008573ffffffffffffffffffffffffffffffffffffffff163b115b61211b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016104b2565b5050505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003612179576000841161216e57600080fd5b5082900490506121ed565b80841161218557600080fd5b600084868809851960019081018716968790049682860381900495909211909303600082900391909104909201919091029190911760038402600290811880860282030280860282030280860282030280860282030280860282030280860290910302029150505b9392505050565b6000806000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166352047942896040518263ffffffff1660e01b815260040161225591815260200190565b602060405180830381865afa158015612272573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122969190613535565b73ffffffffffffffffffffffffffffffffffffffff1663acc5570c896040518263ffffffff1660e01b81526004016122d091815260200190565b60c060405180830381865afa1580156122ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123119190613646565b5050935093509350935060008588612329919061362f565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa158015612399573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bd91906136c3565b90506123e173ffffffffffffffffffffffffffffffffffffffff841633308c61298f565b6123eb89826136dc565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8516906370a0823190602401602060405180830381865afa158015612455573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247991906136c3565b10156124b1576040517fd354452400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516156126e8576124ee73ffffffffffffffffffffffffffffffffffffffff84168684611f8f565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa15801561255b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257f91906136c3565b6040517f4b9de77c000000000000000000000000000000000000000000000000000000008152600481018d905260248101859052604481018b905290915073ffffffffffffffffffffffffffffffffffffffff871690634b9de77c90606401600060405180830381600087803b1580156125f857600080fd5b505af115801561260c573d6000803e3d6000fd5b50505050888161261c91906136dc565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015612686573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126aa91906136c3565b10156126e2576040517fe2a575ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50612890565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa158015612755573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061277991906136c3565b905061279d73ffffffffffffffffffffffffffffffffffffffff861688308c61298f565b6127a789826136dc565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015612811573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283591906136c3565b101561286d576040517fd354452400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61288e73ffffffffffffffffffffffffffffffffffffffff85168885611f8f565b505b50505050505050505050565b60004265ffffffffffff168265ffffffffffff16111561296e575073ffffffffffffffffffffffffffffffffffffffff828116600090815260066020908152604080832065ffffffffffff86168452909152908190205490517f40c10f19000000000000000000000000000000000000000000000000000000008152868316600482015260248101869052839291909116906340c10f1990604401600060405180830381600087803b15801561295157600080fd5b505af1158015612965573d6000803e3d6000fd5b50505050611bfe565b611bfe73ffffffffffffffffffffffffffffffffffffffff84168686611f8f565b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790529151600092839290881691612a2e9190613857565b6000604051808303816000865af19150503d8060008114612a6b576040519150601f19603f3d011682016040523d82523d6000602084013e612a70565b606091505b5091509150818015612a9a575080511580612a9a575080806020019051810190612a9a91906136f4565b8015612abd575060008673ffffffffffffffffffffffffffffffffffffffff163b115b612b23576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c454400000000000000000000000060448201526064016104b2565b505050505050565b6060806000808080612b406201518088613873565b90506000612b518262010bd9613887565b612b5e9062253d8c613887565b9050600062023ab1612b718360046138fb565b612b7b91906139b7565b90506004612b8c8262023ab16138fb565b612b97906003613887565b612ba191906139b7565b612bab9083613a1f565b9150600062164b09612bbe846001613887565b612bca90610fa06138fb565b612bd491906139b7565b90506004612be4826105b56138fb565b612bee91906139b7565b612bf89084613a1f565b612c0390601f613887565b9250600061098f612c158560506138fb565b612c1f91906139b7565b905060006050612c318361098f6138fb565b612c3b91906139b7565b612c459086613a1f565b9050612c52600b836139b7565b9450612c5f85600c6138fb565b612c6a836002613887565b612c749190613a1f565b91508483612c83603187613a1f565b612c8e9060646138fb565b612c989190613887565b612ca29190613887565b9850909650945060009350612cc79250612cc29150612710905086613a93565b6130d2565b90506000600a8410612ce157612cdc846130d2565b612d0a565b612cea846130d2565b604051602001612cfa9190613aa7565b6040516020818303038152906040525b90506000600a8410612d2457612d1f846130d2565b612d4d565b612d2d846130d2565b604051602001612d3d9190613aa7565b6040516020818303038152906040525b90508973ffffffffffffffffffffffffffffffffffffffff166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa158015612d9a573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612de09190810190613aec565b838383604051602001612df69493929190613b95565b60405160208183030381529060405297508973ffffffffffffffffffffffffffffffffffffffff166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015612e52573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612e989190810190613aec565b838383604051602001612eae9493929190613c4a565b60405160208183030381529060405296505050505050509250929050565b80516040517f3d610000000000000000000000000000000000000000000000000000000000008152603a820160f081811b6002848101919091527f80600b3d3981f30000000000000000000000000000000000000000000000000060048501527f363d3d3761000000000000000000000000000000000000000000000000000000600b8501528401901b601083018190527f603836393d3d3d366100000000000000000000000000000000000000000000006012840152601b8301527f013d730000000000000000000000000000000000000000000000000000000000601d830152606085901b6020808401919091527f5af43d82803e903d91603657fd5bf3000000000000000000000000000000000060348401526000939260458401929186019084604382015b6020821061303257835181526020938401937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019101612ff5565b83517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60208490036101000a0119908116825260f088901b91830191825286846000f0985073ffffffffffffffffffffffffffffffffffffffff89166130c4576040517febfef18800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505092915050565b60608160000361311557505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b811561313f5780613129816135cd565b91506131389050600a83613873565b9150613119565b60008167ffffffffffffffff81111561315a5761315a6132e1565b6040519080825280601f01601f191660200182016040528015613184576020820181803683370190505b509050815b85156132295761319a60018261362f565b905060006131a9600a88613873565b6131b490600a613cd0565b6131be908861362f565b6131c9906030613d0d565b905060008160f81b9050808484815181106131e6576131e661356f565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350613220600a89613873565b97505050613189565b50949350505050565b65ffffffffffff8116811461324657600080fd5b50565b60006020828403121561325b57600080fd5b81356121ed81613232565b73ffffffffffffffffffffffffffffffffffffffff8116811461324657600080fd5b803561329381613266565b919050565b6000602082840312156132aa57600080fd5b81356121ed81613266565b600080604083850312156132c857600080fd5b82356132d381613266565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613357576133576132e1565b604052919050565b6000806040838503121561337257600080fd5b823567ffffffffffffffff8082111561338a57600080fd5b818501915085601f83011261339e57600080fd5b81356020828211156133b2576133b26132e1565b8160051b92506133c3818401613310565b82815292840181019281810190898511156133dd57600080fd5b948201945b8486101561340757853593506133f784613266565b83825294820194908201906133e2565b96506134169050878201613288565b9450505050509250929050565b600080600080600060a0868803121561343b57600080fd5b853561344681613266565b9450602086013561345681613266565b94979496505050506040830135926060810135926080909101359150565b6000806040838503121561348757600080fd5b823561349281613266565b915060208301356134a281613232565b809150509250929050565b6000806000606084860312156134c257600080fd5b83356134cd81613266565b925060208401356134dd81613232565b929592945050506040919091013590565b6000806040838503121561350157600080fd5b823561350c81613266565b915060208301356134a281613266565b60006020828403121561352e57600080fd5b5035919050565b60006020828403121561354757600080fd5b81516121ed81613266565b60006020828403121561356457600080fd5b81516121ed81613232565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036135fe576135fe61359e565b5060010190565b600065ffffffffffff8083168185168083038211156136265761362661359e565b01949350505050565b6000828210156136415761364161359e565b500390565b60008060008060008060c0878903121561365f57600080fd5b865161366a81613266565b602088015190965061367b81613266565b604088015190955061368c81613266565b606088015190945061369d81613266565b60808801519093506136ae81613232565b8092505060a087015190509295509295509295565b6000602082840312156136d557600080fd5b5051919050565b600082198211156136ef576136ef61359e565b500190565b60006020828403121561370657600080fd5b815180151581146121ed57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600065ffffffffffff8084168061375e5761375e613716565b92169190910492915050565b600065ffffffffffff8083168185168183048111821515161561378f5761378f61359e565b02949350505050565b600065ffffffffffff838116908316818110156137b7576137b761359e565b039392505050565b805160208083015191908110156137fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8160200360031b1b821691505b50919050565b60006020828403121561381657600080fd5b815160ff811681146121ed57600080fd5b60005b8381101561384257818101518382015260200161382a565b83811115613851576000848401525b50505050565b60008251613869818460208701613827565b9190910192915050565b60008261388257613882613716565b500490565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038413811516156138c1576138c161359e565b827f80000000000000000000000000000000000000000000000000000000000000000384128116156138f5576138f561359e565b50500190565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561393c5761393c61359e565b7f800000000000000000000000000000000000000000000000000000000000000060008712868205881281841616156139775761397761359e565b600087129250878205871284841616156139935761399361359e565b878505871281841616156139a9576139a961359e565b505050929093029392505050565b6000826139c6576139c6613716565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615613a1a57613a1a61359e565b500590565b6000808312837f800000000000000000000000000000000000000000000000000000000000000001831281151615613a5957613a5961359e565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018313811615613a8d57613a8d61359e565b50500390565b600082613aa257613aa2613716565b500690565b7f3000000000000000000000000000000000000000000000000000000000000000815260008251613adf816001850160208701613827565b9190910160010192915050565b600060208284031215613afe57600080fd5b815167ffffffffffffffff80821115613b1657600080fd5b818401915084601f830112613b2a57600080fd5b815181811115613b3c57613b3c6132e1565b613b6d60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613310565b9150808252856020828501011115613b8457600080fd5b613229816020840160208601613827565b60008551613ba7818460208a01613827565b7f20000000000000000000000000000000000000000000000000000000000000009083019081528551613be1816001840160208a01613827565b8082019150507f2d000000000000000000000000000000000000000000000000000000000000008060018301528551613c21816002850160208a01613827565b60029201918201528351613c3c816003840160208801613827565b016003019695505050505050565b60008551613c5c818460208a01613827565b7f2d000000000000000000000000000000000000000000000000000000000000009083019081528551613c96816001840160208a01613827565b8551910190613cac816001840160208901613827565b8451910190613cc2816001840160208801613827565b016001019695505050505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613d0857613d0861359e565b500290565b600060ff821660ff84168060ff03821115613d2a57613d2a61359e565b01939250505056fea2646970667358221220b5b0d844086dde2ae9d0ee7c92e2968b7ffcade16d6b7f35fd4d723eab98647b64736f6c634300080f0033608060405234801561001057600080fd5b50610ad4806100206000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80636f307dc311610097578063a457c2d711610066578063a457c2d714610222578063a9059cbb14610235578063dd62ed3e14610248578063e184c9be1461027357600080fd5b80636f307dc3146101df57806370a08231146101e757806395d89b41146102075780639dc29fac1461020f57600080fd5b8063313ce567116100d3578063313ce56714610170578063395093511461018a57806340c10f191461019d57806357edab4e146101b257600080fd5b806306fdde0314610105578063095ea7b31461012357806318160ddd1461014657806323b872dd1461015d575b600080fd5b61010d610292565b60405161011a91906108e9565b60405180910390f35b610136610131366004610985565b6102c4565b604051901515815260200161011a565b61014f60005481565b60405190815260200161011a565b61013661016b3660046109af565b61033d565b610178610481565b60405160ff909116815260200161011a565b610136610198366004610985565b610492565b6101b06101ab366004610985565b61053d565b005b6101ba6105b7565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161011a565b6101ba6105c3565b61014f6101f53660046109eb565b60016020526000908152604090205481565b61010d6105cf565b6101b061021d366004610985565b6105db565b610136610230366004610985565b610651565b610136610243366004610985565b610693565b61014f610256366004610a0d565b600260209081526000928352604080842090915290825290205481565b61027b610718565b60405165ffffffffffff909116815260200161011a565b606061029e6000610720565b6040516020016102b091815260200190565b604051602081830303815290604052905090565b33600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061032c9086815260200190565b60405180910390a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146103d15761039f8382610a6f565b73ffffffffffffffffffffffffffffffffffffffff861660009081526002602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526001602052604081208054859290610406908490610a6f565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260016020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061046e9087815260200190565b60405180910390a3506001949350505050565b600061048d6040610760565b905090565b33600090815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff861684529091528120805483919083906104d4908490610a86565b909155505033600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8816808552908352928190205490519081529192917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910161032c565b6105456105b7565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146105a9576040517f5451629900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105b382826107a3565b5050565b600061048d607561081b565b600061048d604161081b565b606061029e6020610720565b6105e36105b7565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610647576040517f5451629900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105b3828261085e565b33600090815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff861684529091528120805483919083906104d4908490610a6f565b336000908152600160205260408120805483919083906106b4908490610a6f565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260016020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061032c9086815260200190565b600061048d60555b6000806107557ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe368181013560f01c90030190565b929092013592915050565b6000806107957ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe368181013560f01c90030190565b929092013560f81c92915050565b806000808282546107b49190610a86565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600160209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b6000806108507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe368181013560f01c90030190565b929092013560601c92915050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604081208054839290610893908490610a6f565b909155505060008054829003815560405182815273ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200161080f565b600060208083528351808285015260005b81811015610916578581018301518582016040015282016108fa565b81811115610928576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461098057600080fd5b919050565b6000806040838503121561099857600080fd5b6109a18361095c565b946020939093013593505050565b6000806000606084860312156109c457600080fd5b6109cd8461095c565b92506109db6020850161095c565b9150604084013590509250925092565b6000602082840312156109fd57600080fd5b610a068261095c565b9392505050565b60008060408385031215610a2057600080fd5b610a298361095c565b9150610a376020840161095c565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015610a8157610a81610a40565b500390565b60008219821115610a9957610a99610a40565b50019056fea26469706673582212207056477b95d254084e602829034ff91761b7876d88d1b27c93982a33799874ea64736f6c634300080f0033000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a66a2a13415db3613c1a4dd1c942a285902d1000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a0f48a4e3d74ab4234adf9ea9eb32f87b4b14
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a66a2a13415db3613c1a4dd1c942a285902d1000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a0f48a4e3d74ab4234adf9ea9eb32f87b4b14
-----Decoded View---------------
Arg [0] : protocol_ (address): 0x007bd11fca0daaeadd455b51826f9a015f2f0969
Arg [1] : aggregator_ (address): 0x007a66a2a13415db3613c1a4dd1c942a285902d1
Arg [2] : guardian_ (address): 0x007bd11fca0daaeadd455b51826f9a015f2f0969
Arg [3] : authority_ (address): 0x007a0f48a4e3d74ab4234adf9ea9eb32f87b4b14
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969
Arg [1] : 000000000000000000000000007a66a2a13415db3613c1a4dd1c942a285902d1
Arg [2] : 000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969
Arg [3] : 000000000000000000000000007a0f48a4e3d74ab4234adf9ea9eb32f87b4b14
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|