Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 1,185 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Deploy Org | 15925746 | 1204 days ago | IN | 0 ETH | 0.00351115 | ||||
| Deploy Fund | 15922611 | 1204 days ago | IN | 0 ETH | 0.00566945 | ||||
| Deploy Fund | 15922574 | 1204 days ago | IN | 0 ETH | 0.00433694 | ||||
| Deploy Fund | 15898857 | 1208 days ago | IN | 0 ETH | 0.00470627 | ||||
| Deploy Fund | 15898855 | 1208 days ago | IN | 0 ETH | 0.00466447 | ||||
| Deploy Org | 15898630 | 1208 days ago | IN | 0 ETH | 0.00559306 | ||||
| Deploy Org | 15883334 | 1210 days ago | IN | 0 ETH | 0.00298234 | ||||
| Deploy Org | 15877277 | 1211 days ago | IN | 0 ETH | 0.00300265 | ||||
| Deploy Org | 15870772 | 1212 days ago | IN | 0 ETH | 0.00545172 | ||||
| Deploy Org | 15870721 | 1212 days ago | IN | 0 ETH | 0.00643614 | ||||
| Deploy Org | 15870709 | 1212 days ago | IN | 0 ETH | 0.0052264 | ||||
| Deploy Org | 15870651 | 1212 days ago | IN | 0 ETH | 0.00777853 | ||||
| Deploy Org | 15868051 | 1212 days ago | IN | 0 ETH | 0.00493424 | ||||
| Deploy Org | 15857951 | 1213 days ago | IN | 0 ETH | 0.00195036 | ||||
| Deploy Org | 15842166 | 1216 days ago | IN | 0 ETH | 0.00318524 | ||||
| Deploy Fund | 15836402 | 1216 days ago | IN | 0 ETH | 0.00270602 | ||||
| Deploy Org | 15833937 | 1217 days ago | IN | 0 ETH | 0.00530503 | ||||
| Deploy Fund | 15832778 | 1217 days ago | IN | 0 ETH | 0.00765728 | ||||
| Deploy Org | 15829014 | 1217 days ago | IN | 0 ETH | 0.00295679 | ||||
| Deploy Org | 15825232 | 1218 days ago | IN | 0 ETH | 0.00321557 | ||||
| Deploy Fund | 15821308 | 1219 days ago | IN | 0 ETH | 0.00698145 | ||||
| Deploy Fund | 15819689 | 1219 days ago | IN | 0 ETH | 0.01196412 | ||||
| Deploy Org | 15817959 | 1219 days ago | IN | 0 ETH | 0.00311852 | ||||
| Deploy Org | 15806484 | 1221 days ago | IN | 0 ETH | 0.0033626 | ||||
| Deploy Fund | 15794542 | 1222 days ago | IN | 0 ETH | 0.00448156 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x602d8060 | 15925746 | 1204 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15922611 | 1204 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15922574 | 1204 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15898857 | 1208 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15898855 | 1208 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15898630 | 1208 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15883334 | 1210 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15877277 | 1211 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15870772 | 1212 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15870721 | 1212 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15870709 | 1212 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15870651 | 1212 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15868051 | 1212 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15857951 | 1213 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15842166 | 1216 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15836402 | 1216 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15833937 | 1217 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15832778 | 1217 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15829014 | 1217 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15825232 | 1218 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15821308 | 1219 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15819689 | 1219 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15817959 | 1219 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15806484 | 1221 days ago | Contract Creation | 0 ETH | |||
| 0x602d8060 | 15794542 | 1222 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
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:
OrgFundFactory
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 9999999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;
import { Clones } from "openzeppelin-contracts/contracts/proxy/Clones.sol";
import { ERC20 } from "solmate/tokens/ERC20.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { Registry } from "./Registry.sol";
import { EntityFactory } from "./EntityFactory.sol";
import { Entity } from "./Entity.sol";
import { Org } from "./Org.sol";
import { Fund } from "./Fund.sol";
import { ISwapWrapper } from "./interfaces/ISwapWrapper.sol";
/**
* @notice This contract is the factory for both the Org and Fund objects.
*/
contract OrgFundFactory is EntityFactory {
using SafeTransferLib for ERC20;
/// @notice Placeholder address used in swapping method to denote usage of ETH instead of a token.
address public constant ETH_PLACEHOLDER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev The concrete Org used for minimal proxy deployment.
Org public immutable orgImplementation;
/// @dev The concrete Fund used for minimal proxy deployment.
Fund public immutable fundImplementation;
/// @notice Base Token address is the stable coin used throughout the system.
ERC20 public immutable baseToken;
/**
* @param _registry The Registry this factory will configure Entities to interact with. This factory must be
* approved on this Registry for it to work properly.
*/
constructor(Registry _registry) EntityFactory(_registry) {
orgImplementation = new Org();
orgImplementation.initialize(_registry, bytes32("IMPL")); // necessary?
fundImplementation = new Fund();
fundImplementation.initialize(_registry, address(0));
baseToken = _registry.baseToken();
}
/**
* @notice Deploys a Fund.
* @param _manager The address of the Fund's manager.
* @param _salt A 32-byte value used to create the contract at a deterministic address.
* @return _fund The deployed Fund.
*/
function deployFund(address _manager, bytes32 _salt) public returns (Fund _fund) {
_fund = Fund(Clones.cloneDeterministic(address(fundImplementation), keccak256(bytes.concat(bytes20(_manager), _salt))));
_fund.initialize(registry, _manager);
registry.setEntityActive(_fund);
emit EntityDeployed(address(_fund), _fund.entityType(), _manager);
}
/**
* @notice Deploys a Fund then pulls base token from the sender and donates to it.
* @param _manager The address of the Fund's manager.
* @param _salt A 32-byte value used to create the contract at a deterministic address.
* @param _amount The amount of base token to donate.
* @return _fund The deployed Fund.
*/
function deployFundAndDonate(address _manager, bytes32 _salt, uint256 _amount) external returns (Fund _fund) {
_fund = deployFund(_manager, _salt);
_donate(_fund, _amount);
}
/**
* @notice Deploys a new Fund, then pulls a ETH or ERC20 tokens, swaps them to base tokens,
* and donates to the new Fund.
* @param _manager The address of the Fund's manager.
* @param _salt A 32-byte value used to create the contract at a deterministic address.
* @param _swapWrapper The swap wrapper to use for the donation. Must be whitelisted on the Registry.
* @param _tokenIn The address of the ERC20 token to swap and donate, or ETH_PLACEHOLDER if donating ETH.
* @param _amountIn The amount of tokens or ETH being swapped and donated.
* @param _data Additional call data required by the ISwapWrapper being used.
* @return _fund The deployed Fund.
*/
function deployFundSwapAndDonate(
address _manager,
bytes32 _salt,
ISwapWrapper _swapWrapper,
address _tokenIn,
uint256 _amountIn,
bytes calldata _data
) external payable returns (Fund _fund) {
_fund = deployFund(_manager, _salt);
_swapAndDonate(_fund, _swapWrapper, _tokenIn, _amountIn, _data);
}
/**
* @notice Deploys an Org.
* @param _orgId The Org's ID for tax purposes.
* @return _org The deployed Org.
*/
function deployOrg(bytes32 _orgId) public returns (Org _org) {
_org = Org(Clones.cloneDeterministic(address(orgImplementation), _orgId));
_org.initialize(registry, _orgId);
registry.setEntityActive(_org);
emit EntityDeployed(address(_org), _org.entityType(), _org.manager());
}
/**
* @notice Deploys an Org then pulls base token from the sender and donates to it.
* @param _orgId The Org's ID for tax purposes.
* @param _amount The amount of base token to donate.
* @return _org The deployed Org.
*/
function deployOrgAndDonate(bytes32 _orgId, uint256 _amount) external returns (Org _org) {
_org = deployOrg(_orgId);
_donate(_org, _amount);
}
/**
* @notice Deploys a new Org, then pulls a ETH or ERC20 tokens, swaps them to base tokens,
* and donates to the new Org.
* @param _orgId The Org's ID for tax purposes.
* @param _swapWrapper The swap wrapper to use for the donation. Must be whitelisted on the Registry.
* @param _tokenIn The address of the ERC20 token to swap and donate, or ETH_PLACEHOLDER if donating ETH.
* @param _amountIn The amount of tokens or ETH being swapped and donated.
* @param _data Additional call data required by the ISwapWrapper being used.
* @return _org The deployed Org.
*/
function deployOrgSwapAndDonate(
bytes32 _orgId,
ISwapWrapper _swapWrapper,
address _tokenIn,
uint256 _amountIn,
bytes calldata _data
) external payable returns (Org _org) {
_org = deployOrg(_orgId);
_swapAndDonate(_org, _swapWrapper, _tokenIn, _amountIn, _data);
}
/**
* @notice Calculates an Org contract's deployment address.
* @param _orgId Org's tax ID.
* @return The Org's deployment address.
* @dev This function is used off-chain by the automated tests to verify proper contract address deployment.
*/
function computeOrgAddress(bytes32 _orgId) external view returns (address) {
return Clones.predictDeterministicAddress(address(orgImplementation), _orgId, address(this));
}
/**
* @notice Calculates a Fund contract's deployment address.
* @param _manager The manager of the fund.
* @param _salt A 32-byte value used to create the contract at a deterministic address.
* @return The Fund's deployment address.
* @dev This function is used off-chain by the automated tests to verify proper contract address deployment.
*/
function computeFundAddress(address _manager, bytes32 _salt) external view returns (address) {
return Clones.predictDeterministicAddress(address(fundImplementation), keccak256(bytes.concat(bytes20(_manager), _salt)), address(this));
}
/// @dev Pulls base tokens from sender and donates them to the entity.
function _donate(Entity _entity, uint256 _amount) private {
// Send tokens directly to the entity, then reconcile its balance. Cheaper than doing a double transfer
// and calling `donate`.
baseToken.safeTransferFrom(msg.sender, address(_entity), _amount);
_entity.reconcileBalance();
}
/// @dev Pulls ERC20 tokens, or receives ETH, and swaps and donates them to the entity.
function _swapAndDonate(
Entity _entity,
ISwapWrapper _swapWrapper,
address _tokenIn,
uint256 _amountIn,
bytes calldata _data
) private {
if (_tokenIn != ETH_PLACEHOLDER) {
ERC20(_tokenIn).safeTransferFrom(msg.sender, address(this), _amountIn);
ERC20(_tokenIn).safeApprove(address(_entity), 0);
ERC20(_tokenIn).safeApprove(address(_entity), _amountIn);
}
_entity.swapAndDonate{value: msg.value}(_swapWrapper, _tokenIn, _amountIn, _data);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol)
pragma solidity ^0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*
* _Available since v3.4._
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000)
mstore(add(ptr, 0x13), shl(0x60, implementation))
mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create(0, ptr, 0x36)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000)
mstore(add(ptr, 0x13), shl(0x60, implementation))
mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create2(0, ptr, 0x36, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000)
mstore(add(ptr, 0x13), shl(0x60, implementation))
mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x37), shl(0x60, deployer))
mstore(add(ptr, 0x4b), salt)
mstore(add(ptr, 0x6b), keccak256(ptr, 0x36))
predicted := keccak256(add(ptr, 0x36), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(address implementation, bytes32 salt)
internal
view
returns (address predicted)
{
return predictDeterministicAddress(implementation, salt, address(this));
}
}// 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;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
/*///////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool callStatus;
assembly {
// Transfer the ETH and store if it succeeded or not.
callStatus := call(gas(), to, amount, 0, 0, 0, 0)
}
require(callStatus, "ETH_TRANSFER_FAILED");
}
/*///////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool callStatus;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 100 because the calldata length is 4 + 32 * 3.
callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool callStatus;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 68 because the calldata length is 4 + 32 * 2.
callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool callStatus;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 68 because the calldata length is 4 + 32 * 2.
callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
}
/*///////////////////////////////////////////////////////////////
INTERNAL HELPER LOGIC
//////////////////////////////////////////////////////////////*/
function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
assembly {
// Get how many bytes the call returned.
let returnDataSize := returndatasize()
// If the call reverted:
if iszero(callStatus) {
// Copy the revert message into memory.
returndatacopy(0, 0, returnDataSize)
// Revert with the same message.
revert(0, returnDataSize)
}
switch returnDataSize
case 32 {
// Copy the return data into memory.
returndatacopy(0, 0, returnDataSize)
// Set success to whether it returned true.
success := iszero(iszero(mload(0)))
}
case 0 {
// There was no return data.
success := 1
}
default {
// It returned some malformed input.
success := 0
}
}
}
}//SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;
import { Math } from "./lib/Math.sol";
import { ERC20 } from "solmate/tokens/ERC20.sol";
import { Auth, Authority } from "./lib/auth/Auth.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { RegistryAuth } from "./RegistryAuth.sol";
import { Entity } from "./Entity.sol";
import { ISwapWrapper } from "./interfaces/ISwapWrapper.sol";
import { Portfolio } from "./Portfolio.sol";
// --- Errors ---
error Unauthorized();
error UnsupportedSwapper();
/**
* @notice Registry entity - manages Factory and Entity state info.
*/
contract Registry is RegistryAuth {
// --- Storage ---
/// @notice Treasury address can receives fees.
address public treasury;
/// @notice Base Token address is the stable coin contract used throughout the system.
ERC20 public immutable baseToken;
/// @notice Mapping of approved factory contracts that are allowed to register new Entities.
mapping (address => bool) public isApprovedFactory;
/// @notice Mapping of active status of entities.
mapping (Entity => bool) public isActiveEntity;
/// @notice Maps entity type to donation fee percentage stored as a zoc, where type(uint32).max represents 0.
mapping (uint8 => uint32) defaultDonationFee;
/// @notice Maps specific entity receiver to donation fee percentage stored as a zoc.
mapping (Entity => uint32) donationFeeReceiverOverride;
/// @notice Maps entity type to payout fee percentage stored as a zoc, where type(uint32).max represents 0.
mapping (uint8 => uint32) defaultPayoutFee;
/// @notice Maps specific entity sender to payout fee percentage stored as a zoc.
mapping (Entity => uint32) payoutFeeOverride;
/// @notice Maps sender entity type to receiver entity type to fee percentage as a zoc.
mapping (uint8 => mapping(uint8 => uint32)) defaultTransferFee;
/// @notice Maps specific entity sender to receiver entity type to fee percentage as a zoc.
mapping (Entity => mapping(uint8 => uint32)) transferFeeSenderOverride;
/// @notice Maps sender entity type to specific entity receiver to fee percentage as a zoc.
mapping (uint8 => mapping(Entity => uint32)) transferFeeReceiverOverride;
/// @notice Maps swap wrappers to their enabled/disabled status.
mapping (ISwapWrapper => bool) public isSwapperSupported;
/// @notice Maps portfolios to their enabled/disabled status.
mapping (Portfolio => bool) public isActivePortfolio;
// --- Events ---
/// @notice The event emitted when a factory is approved (whitelisted) or has it's approval removed.
event FactoryApprovalSet(address indexed factory, bool isApproved);
/// @notice The event emitted when an entity is set active or inactive.
event EntityStatusSet(address indexed entity, bool isActive);
/// @notice The event emitted when a swap wrapper is set active or inactive.
event SwapWrapperStatusSet(address indexed swapWrapper, bool isSupported);
/// @notice The event emitted when a portfolio is set active or inactive.
event PortfolioStatusSet(address indexed portfolio, bool isActive);
/// @notice Emitted when a default donation fee is set for an entity type.
event DefaultDonationFeeSet(uint8 indexed entityType, uint32 fee);
/// @notice Emitted when a donation fee override is set for a specific receiving entity.
event DonationFeeReceiverOverrideSet(address indexed entity, uint32 fee);
/// @notice Emitted when a default payout fee is set for an entity type.
event DefaultPayoutFeeSet(uint8 indexed entityType, uint32 fee);
/// @notice Emitted when a payout fee override is set for a specific sender entity.
event PayoutFeeOverrideSet(address indexed entity, uint32 fee);
/// @notice Emitted when a default transfer fee is set for transfers between entity types.
event DefaultTransferFeeSet(uint8 indexed fromEntityType, uint8 indexed toEntityType, uint32 fee);
/// @notice Emitted when a transfer fee override is set for transfers from an entity to a specific entityType.
event TransferFeeSenderOverrideSet(address indexed fromEntity, uint8 indexed toEntityType, uint32 fee);
/// @notice Emitted when a transfer fee override is set for transfers from an entityType to an entity.
event TransferFeeReceiverOverrideSet(uint8 indexed fromEntityType, address indexed toEntity, uint32 fee);
/// @notice Emitted when the registry treasury contract is changed
event TreasuryChanged(address oldTreasury, address indexed newTreasury);
/**
* @notice Modifier for methods that require auth and that the manager cannot access.
* @dev Overridden from Auth.sol. Reason: use custom error.
*/
modifier requiresAuth override {
if(!isAuthorized(msg.sender, msg.sig)) revert Unauthorized();
_;
}
// --- Constructor ---
constructor(address _admin, address _treasury, ERC20 _baseToken) RegistryAuth(_admin, Authority(address(this))) {
treasury = _treasury;
emit TreasuryChanged(address(0), _treasury);
baseToken = _baseToken;
}
// --- Internal fns ---
/**
* @notice Fee parsing to convert the special "type(uint32).max" value to zero, and zero to the "max".
* @dev After converting, "type(uint32).max" will cause overflow/revert when used as a fee percentage multiplier and zero will mean no fee.
* @param _value The value to be converted.
* @return The parsed fee to use.
*/
function _parseFeeWithFlip(uint32 _value) private pure returns (uint32) {
if (_value == 0) {
return type(uint32).max;
} else if (_value == type(uint32).max) {
return 0;
} else {
return _value;
}
}
// --- External fns ---
/**
* @notice Sets a new Endaoment treasury address.
* @param _newTreasury The new treasury.
*/
function setTreasury(address _newTreasury) external requiresAuth {
emit TreasuryChanged(treasury, _newTreasury);
treasury = _newTreasury;
}
/**
* @notice Sets the approval state of a factory. Grants the factory permissions to set entity status.
* @param _factory The factory whose approval state is to be updated.
* @param _isApproved True if the factory should be approved, false otherwise.
*/
function setFactoryApproval(address _factory, bool _isApproved) external requiresAuth {
isApprovedFactory[_factory] = _isApproved;
emit FactoryApprovalSet(address(_factory), _isApproved);
}
/**
* @notice Sets the enable/disable state of an Entity.
* @param _entity The entity whose active state is to be updated.
* @param _isActive True if the entity should be active, false otherwise.
*/
function setEntityStatus(Entity _entity, bool _isActive) external requiresAuth {
isActiveEntity[_entity] = _isActive;
emit EntityStatusSet(address(_entity), _isActive);
}
/**
* @notice Sets Entity as active. This is a special method to be called only by approved factories.
* Other callers should use `setEntityStatus` instead.
* @param _entity The entity.
*/
function setEntityActive(Entity _entity) external {
if(!isApprovedFactory[msg.sender]) revert Unauthorized();
isActiveEntity[_entity] = true;
emit EntityStatusSet(address(_entity), true);
}
/**
* @notice Sets the enable/disable state of a Portfolio.
* @param _portfolio Portfolio.
* @param _isActive True if setting portfolio to active, false otherwise.
*/
function setPortfolioStatus(Portfolio _portfolio, bool _isActive) external requiresAuth {
isActivePortfolio[_portfolio] = _isActive;
emit PortfolioStatusSet(address(_portfolio), _isActive);
}
/**
* @notice Gets default donation fee pct (as a zoc) for an Entity.
* @param _entity The receiving entity of the donation for which the fee is being fetched.
* @return uint32 The default donation fee for the entity's type.
* @dev Makes use of _parseFeeWithFlip, so if no default exists, "max" will be returned.
*/
function getDonationFee(Entity _entity) external view returns (uint32) {
return _parseFeeWithFlip(defaultDonationFee[_entity.entityType()]);
}
/**
* @notice Gets lowest possible donation fee pct (as a zoc) for an Entity, among default and override.
* @param _entity The receiving entity of the donation for which the fee is being fetched.
* @return uint32 The minimum of the default donation fee and the receiver's fee override.
* @dev Makes use of _parseFeeWithFlip, so if no default or override exists, "max" will be returned.
*/
function getDonationFeeWithOverrides(Entity _entity) external view returns (uint32) {
uint32 _default = _parseFeeWithFlip(defaultDonationFee[_entity.entityType()]);
uint32 _receiverOverride = _parseFeeWithFlip(donationFeeReceiverOverride[_entity]);
return _receiverOverride < _default ? _receiverOverride : _default;
}
/**
* @notice Gets default payout fee pct (as a zoc) for an Entity.
* @param _entity The sender entity of the payout for which the fee is being fetched.
* @return uint32 The default payout fee for the entity's type.
* @dev Makes use of _parseFeeWithFlip, so if no default exists, "max" will be returned.
*/
function getPayoutFee(Entity _entity) external view returns (uint32) {
return _parseFeeWithFlip(defaultPayoutFee[_entity.entityType()]);
}
/**
* @notice Gets lowest possible payout fee pct (as a zoc) for an Entity, among default and override.
* @param _entity The sender entity of the payout for which the fee is being fetched.
* @return uint32 The minimum of the default payout fee and the sender's fee override.
* @dev Makes use of _parseFeeWithFlip, so if no default or override exists, "max" will be returned.
*/
function getPayoutFeeWithOverrides(Entity _entity) external view returns (uint32) {
uint32 _default = _parseFeeWithFlip(defaultPayoutFee[_entity.entityType()]);
uint32 _senderOverride = _parseFeeWithFlip(payoutFeeOverride[_entity]);
return _senderOverride < _default ? _senderOverride : _default;
}
/**
* @notice Gets default transfer fee pct (as a zoc) between sender & receiver Entities.
* @param _sender The sending entity of the transfer for which the fee is being fetched.
* @param _receiver The receiving entity of the transfer for which the fee is being fetched.
* @return uint32 The default transfer fee.
* @dev Makes use of _parseFeeWithFlip, so if no default exists, "type(uint32).max" will be returned.
*/
function getTransferFee(Entity _sender, Entity _receiver) external view returns (uint32) {
return _parseFeeWithFlip(defaultTransferFee[_sender.entityType()][_receiver.entityType()]);
}
/**
* @notice Gets lowest possible transfer fee pct (as a zoc) between sender & receiver Entities, among default and overrides.
* @param _sender The sending entity of the transfer for which the fee is being fetched.
* @param _receiver The receiving entity of the transfer for which the fee is being fetched.
* @return uint32 The minimum of the default transfer fee, and sender and receiver overrides.
* @dev Makes use of _parseFeeWithFlip, so if no default or overrides exist, "type(uint32).max" will be returned.
*/
function getTransferFeeWithOverrides(Entity _sender, Entity _receiver) external view returns (uint32) {
uint32 _default = _parseFeeWithFlip(defaultTransferFee[_sender.entityType()][_receiver.entityType()]);
uint32 _senderOverride = _parseFeeWithFlip(transferFeeSenderOverride[_sender][_receiver.entityType()]);
uint32 _receiverOverride = _parseFeeWithFlip(transferFeeReceiverOverride[_sender.entityType()][_receiver]);
uint32 _lowestFee = _default;
_lowestFee = _senderOverride < _lowestFee ? _senderOverride : _lowestFee;
_lowestFee = _receiverOverride < _lowestFee ? _receiverOverride : _lowestFee;
return _lowestFee;
}
/**
* @notice Sets the default donation fee for an entity type.
* @param _entityType Entity type.
* @param _fee The fee percentage to be set (a zoc).
*/
function setDefaultDonationFee(uint8 _entityType, uint32 _fee) external requiresAuth {
defaultDonationFee[_entityType] = _parseFeeWithFlip(_fee);
emit DefaultDonationFeeSet(_entityType, _fee);
}
/**
* @notice Sets the donation fee receiver override for a specific entity.
* @param _entity Entity.
* @param _fee The overriding fee (a zoc).
*/
function setDonationFeeReceiverOverride(Entity _entity, uint32 _fee) external requiresAuth {
donationFeeReceiverOverride[_entity] = _parseFeeWithFlip(_fee);
emit DonationFeeReceiverOverrideSet(address(_entity), _fee);
}
/**
* @notice Sets the default payout fee for an entity type.
* @param _entityType Entity type.
* @param _fee The fee percentage to be set (a zoc).
*/
function setDefaultPayoutFee(uint8 _entityType, uint32 _fee) external requiresAuth {
defaultPayoutFee[_entityType] = _parseFeeWithFlip(_fee);
emit DefaultPayoutFeeSet(_entityType, _fee);
}
/**
* @notice Sets the payout fee override for a specific entity.
* @param _entity Entity.
* @param _fee The overriding fee (a zoc).
*/
function setPayoutFeeOverride(Entity _entity, uint32 _fee) external requiresAuth {
payoutFeeOverride[_entity] = _parseFeeWithFlip(_fee);
emit PayoutFeeOverrideSet(address(_entity), _fee);
}
/**
* @notice Sets the default transfer fee for transfers from one specific entity type to another.
* @param _fromEntityType The entityType making the transfer.
* @param _toEntityType The receiving entityType.
* @param _fee The transfer fee percentage (a zoc).
*/
function setDefaultTransferFee(uint8 _fromEntityType, uint8 _toEntityType, uint32 _fee) external requiresAuth {
defaultTransferFee[_fromEntityType][_toEntityType] = _parseFeeWithFlip(_fee);
emit DefaultTransferFeeSet(_fromEntityType, _toEntityType, _fee);
}
/**
* @notice Sets the transfer fee override for transfers from one specific entity to entities of a given type.
* @param _fromEntity The entity making the transfer.
* @param _toEntityType The receiving entityType.
* @param _fee The overriding fee percentage (a zoc).
*/
function setTransferFeeSenderOverride(Entity _fromEntity, uint8 _toEntityType, uint32 _fee) external requiresAuth {
transferFeeSenderOverride[_fromEntity][_toEntityType] = _parseFeeWithFlip(_fee);
emit TransferFeeSenderOverrideSet(address(_fromEntity), _toEntityType, _fee);
}
/**
* @notice Sets the transfer fee override for transfers from entities of a given type to a specific entity.
* @param _fromEntityType The entityType making the transfer.
* @param _toEntity The receiving entity.
* @param _fee The overriding fee percentage (a zoc).
*/
function setTransferFeeReceiverOverride(uint8 _fromEntityType, Entity _toEntity, uint32 _fee) external requiresAuth {
transferFeeReceiverOverride[_fromEntityType][_toEntity] = _parseFeeWithFlip(_fee);
emit TransferFeeReceiverOverrideSet(_fromEntityType, address(_toEntity), _fee);
}
/**
* @notice Sets the enable/disable state of a SwapWrapper. System owners must ensure meticulous review of SwapWrappers before approving them.
* @param _swapWrapper A contract that implements ISwapWrapper.
* @param _supported `true` if supported, `false` if unsupported.
*/
function setSwapWrapperStatus(ISwapWrapper _swapWrapper, bool _supported) external requiresAuth {
isSwapperSupported[_swapWrapper] = _supported;
emit SwapWrapperStatusSet(address(_swapWrapper), _supported);
}
}//SPDX-License-Identifier: BSD 3-Clause
pragma solidity >=0.8.0;
import "./Registry.sol";
import "./Entity.sol";
/**
* @notice EntityFactory contract inherited by OrgFundFactory and future factories.
*/
abstract contract EntityFactory {
/// @notice _registry The registry to host the Entity.
Registry public immutable registry;
/// @notice Emitted when an Entity is deployed.
event EntityDeployed(address indexed entity, uint8 indexed entityType, address indexed entityManager);
/**
* @param _registry The registry to host the Entity.
*/
constructor(Registry _registry) {
registry = _registry;
}
}//SPDX-License-Identifier: BSD 3-Clause
pragma solidity >=0.8.0;
import "solmate/tokens/ERC20.sol";
import "solmate/utils/SafeTransferLib.sol";
import "./lib/ReentrancyGuard.sol";
import { Registry } from "./Registry.sol";
import { ISwapWrapper } from "./interfaces/ISwapWrapper.sol";
import { EndaomentAuth } from "./lib/auth/EndaomentAuth.sol";
import { Portfolio } from "./Portfolio.sol";
import { Math } from "./lib/Math.sol";
error EntityInactive();
error PortfolioInactive();
error InsufficientFunds();
error InvalidAction();
error BalanceMismatch();
error CallFailed(bytes response);
/**
* @notice Entity contract inherited by Org and Fund contracts (and all future kinds of Entities).
*/
abstract contract Entity is EndaomentAuth, ReentrancyGuard {
using Math for uint256;
using SafeTransferLib for ERC20;
/// @notice The base registry to which the entity is connected.
Registry public registry;
/// @notice The entity's manager.
address public manager;
// @notice The base token used for tracking the entity's fund balance.
ERC20 public baseToken;
/// @notice The current balance for the entity, denominated in the base token's units.
uint256 public balance;
/// @notice Placeholder address used in swapping method to denote usage of ETH instead of a token.
address public constant ETH_PLACEHOLDER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @notice Emitted when manager is set.
event EntityManagerSet(address indexed oldManager, address indexed newManager);
/// @notice Emitted when a donation is made.
event EntityDonationReceived(
address indexed from,
address indexed to,
address indexed tokenIn,
uint256 amountIn,
uint256 amountReceived,
uint256 amountFee
);
/// @notice Emitted when a payout is made from an entity.
event EntityValuePaidOut(address indexed from, address indexed to, uint256 amountSent, uint256 amountFee);
/// @notice Emitted when a transfer is made between entities.
event EntityValueTransferred(address indexed from, address indexed to, uint256 amountReceived, uint256 amountFee);
/// @notice Emitted when a base token reconciliation completes
event EntityBalanceReconciled(address indexed entity, uint256 amountReceived, uint256 amountFee);
/// @notice Emitted when a base token balance is used to correct the internal contract balance.
event EntityBalanceCorrected(address indexed entity, uint256 newBalance);
/// @notice Emitted when a portfolio deposit is made.
event EntityDeposit(address indexed portfolio, uint256 baseTokenDeposited, uint256 sharesReceived);
/// @notice Emitted when a portfolio share redemption is made.
event EntityRedeem(address indexed portfolio, uint256 sharesRedeemed, uint256 baseTokenReceived);
/**
* @notice Modifier for methods that require auth and that the manager can access.
* @dev Uses the same condition as `requiresAuth` but with added manager access.
*/
modifier requiresManager {
if(msg.sender != manager && !isAuthorized(msg.sender, msg.sig)) revert Unauthorized();
_;
}
/// @notice Each entity will implement this function to allow a caller to interrogate what kind of entity it is.
function entityType() public pure virtual returns (uint8);
/**
* @notice One time method to be called at deployment to configure the contract. Required so Entity
* contracts can be deployed as minimal proxies (clones).
* @param _registry The registry to host the Entity.
* @param _manager The address of the Entity's manager.
*/
function __initEntity(Registry _registry, address _manager) internal {
// Call to EndaomentAuth's initialize function ensures that this can't be called again
__initEndaomentAuth(_registry, bytes20(bytes.concat("entity", bytes1(entityType()))));
__initReentrancyGuard();
registry = _registry;
manager = _manager;
baseToken = _registry.baseToken();
}
/**
* @notice Set a new manager for this entity.
* @param _manager Address of new manager.
* @dev Callable by current manager or permissioned role.
*/
function setManager(address _manager) external virtual requiresManager {
emit EntityManagerSet(manager, _manager);
manager = _manager;
}
/**
* @notice Receives a donated amount of base tokens to be added to the entity's balance. Transfers default fee to treasury.
* @param _amount Amount donated in base token.
* @dev Reverts if the donation fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts if the token transfer fails.
*/
function donate(uint256 _amount) external virtual {
uint32 _feeMultiplier = registry.getDonationFee(this);
_donateWithFeeMultiplier(_amount, _feeMultiplier);
}
/**
* @notice Receives a donated amount of base tokens to be added to the entity's balance. Transfers default or overridden fee to treasury.
* @param _amount Amount donated in base token.
* @dev Reverts if the donation fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts if the token transfer fails.
*/
function donateWithOverrides(uint256 _amount) external virtual {
uint32 _feeMultiplier = registry.getDonationFeeWithOverrides(this);
_donateWithFeeMultiplier(_amount, _feeMultiplier);
}
/**
* @notice Receives a donated amount of base tokens to be added to the entity's balance.
* This method can be called by permissioned actors to make a donation with a manually specified fee.
* @param _amount Amount donated in base token.
* @param _feeOverride Fee percentage as zoc.
* @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts if the token transfer fails.
* @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
*/
function donateWithAdminOverrides(uint256 _amount, uint32 _feeOverride) requiresAuth external virtual {
_donateWithFeeMultiplier(_amount, _feeOverride);
}
/**
* @notice Receives a donated amount of base tokens to be added to the entity's balance. Transfers fee calculated by fee multiplier to treasury.
* @param _amount Amount donated in base token.
* @param _feeMultiplier Value indicating the percentage of the Endaoment donation fee to go to the Endaoment treasury.
* @dev Reverts if the donation fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts if the token transfer fails.
*/
function _donateWithFeeMultiplier(uint256 _amount, uint32 _feeMultiplier) internal virtual {
(uint256 _netAmount, uint256 _fee) = _calculateFee(_amount, _feeMultiplier);
baseToken.safeTransferFrom(msg.sender, registry.treasury(), _fee);
baseToken.safeTransferFrom(msg.sender, address(this), _netAmount);
unchecked {
// unchecked as no possibility of overflow with baseToken precision
balance += _netAmount;
}
emit EntityDonationReceived(msg.sender, address(this), address(baseToken), _amount, _amount, _fee);
}
/**
* @notice Receive a donated amount of ETH or ERC20 tokens, swaps them to base tokens, and adds the output to the
* entity's balance. Fee calculated using default rate and sent to treasury.
* @param _swapWrapper The swap wrapper to use for the donation. Must be whitelisted on the Registry.
* @param _tokenIn The address of the ERC20 token to swap and donate, or ETH_PLACEHOLDER if donating ETH.
* @param _amountIn The amount of tokens or ETH being swapped and donated.
* @param _data Additional call data required by the ISwapWrapper being used.
*/
function swapAndDonate(
ISwapWrapper _swapWrapper,
address _tokenIn,
uint256 _amountIn,
bytes calldata _data
) external virtual payable {
uint32 _feeMultiplier = registry.getDonationFee(this);
_swapAndDonateWithFeeMultiplier(_swapWrapper, _tokenIn, _amountIn, _data, _feeMultiplier);
}
/**
* @notice Receive a donated amount of ETH or ERC20 tokens, swaps them to base tokens, and adds the output to the
* entity's balance. Fee calculated using override rate and sent to treasury.
* @param _swapWrapper The swap wrapper to use for the donation. Must be whitelisted on the Registry.
* @param _tokenIn The address of the ERC20 token to swap and donate, or ETH_PLACEHOLDER if donating ETH.
* @param _amountIn The amount of tokens or ETH being swapped and donated.
* @param _data Additional call data required by the ISwapWrapper being used.
*/
function swapAndDonateWithOverrides(
ISwapWrapper _swapWrapper,
address _tokenIn,
uint256 _amountIn,
bytes calldata _data
) external virtual payable {
uint32 _feeMultiplier = registry.getDonationFeeWithOverrides(this);
_swapAndDonateWithFeeMultiplier(_swapWrapper, _tokenIn, _amountIn, _data, _feeMultiplier);
}
/// @dev Internal helper implementing swap and donate functionality for any fee multiplier provided.
function _swapAndDonateWithFeeMultiplier(
ISwapWrapper _swapWrapper,
address _tokenIn,
uint256 _amountIn,
bytes calldata _data,
uint32 _feeMultiplier
) nonReentrant internal virtual {
if (!registry.isSwapperSupported(_swapWrapper)) revert InvalidAction();
// THINK: do we need a re-entrancy guard on this method?
if (_tokenIn != ETH_PLACEHOLDER) {
ERC20(_tokenIn).safeTransferFrom(msg.sender, address(this), _amountIn);
ERC20(_tokenIn).safeApprove(address(_swapWrapper), 0);
ERC20(_tokenIn).safeApprove(address(_swapWrapper), _amountIn);
}
uint256 _amountOut = _swapWrapper.swap{value: msg.value}(
_tokenIn,
address(baseToken),
address(this),
_amountIn,
_data
);
(uint256 _netAmount, uint256 _fee) = _calculateFee(_amountOut, _feeMultiplier);
baseToken.safeTransfer(registry.treasury(), _fee);
unchecked {
// unchecked as no possibility of overflow with baseToken precision
balance += _netAmount;
}
if(balance > baseToken.balanceOf(address(this))) revert BalanceMismatch();
emit EntityDonationReceived(msg.sender, address(this), _tokenIn, _amountIn, _amountOut, _fee);
}
/**
* @notice Transfers an amount of base tokens from one entity to another. Transfers default fee to treasury.
* @param _to The entity to receive the tokens.
* @param _amount Contains the amount being donated (denominated in the base token's units).
* @dev Reverts if the entity is inactive or if the token transfer fails.
* @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts with `Unauthorized` if the `msg.sender` is not the entity manager or a privileged role.
*/
function transfer(Entity _to, uint256 _amount) requiresManager external virtual {
uint32 _feeMultiplier = registry.getTransferFee(this, _to);
_transferWithFeeMultiplier(_to, _amount, _feeMultiplier);
}
/**
* @notice Transfers an amount of base tokens from one entity to another. Transfers default or overridden fee to treasury.
* @param _to The entity to receive the tokens.
* @param _amount Contains the amount being donated (denominated in the base token's units).
* @dev Reverts if the entity is inactive or if the token transfer fails.
* @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts with `Unauthorized` if the `msg.sender` is not the entity manager or a privileged role.
*/
function transferWithOverrides(Entity _to, uint256 _amount) requiresManager external virtual {
uint32 _feeMultiplier = registry.getTransferFeeWithOverrides(this, _to);
_transferWithFeeMultiplier(_to, _amount, _feeMultiplier);
}
/**
* @notice Transfers an amount of base tokens from one entity to another. Transfers fee specified by a privileged role.
* @param _to The entity to receive the tokens.
* @param _amount Contains the amount being donated (denominated in the base token's units).
* @param _feeOverride Admin override configured by an Admin
* @dev Reverts if the entity is inactive or if the token transfer fails.
* @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
*/
function transferWithAdminOverrides(Entity _to, uint256 _amount, uint32 _feeOverride) requiresAuth external virtual {
_transferWithFeeMultiplier(_to, _amount, _feeOverride);
}
/**
* @notice Transfers an amount of base tokens from one entity to another. Transfers fee calculated by fee multiplier to treasury.
* @param _to The entity to receive the tokens.
* @param _amount Contains the amount being donated (denominated in the base token's units).
* @param _feeMultiplier Value indicating the percentage of the Endaoment donation fee to go to the Endaoment treasury.
* @dev Reverts with 'Inactive' if the entity sending the transfer or the entity receiving the transfer is inactive.
* @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts with `Unauthorized` if the `msg.sender` is not the entity manager or a privileged role.
* @dev Reverts if the token transfer fails.
*/
function _transferWithFeeMultiplier(Entity _to, uint256 _amount, uint32 _feeMultiplier) internal virtual {
if (!registry.isActiveEntity(this) || !registry.isActiveEntity(_to)) revert EntityInactive();
if (balance < _amount) revert InsufficientFunds();
(uint256 _netAmount, uint256 _fee) = _calculateFee(_amount, _feeMultiplier);
baseToken.safeTransfer(registry.treasury(), _fee);
baseToken.safeTransfer(address(_to), _netAmount);
unchecked {
// unchecked as no possibility of overflow with baseToken precision
balance -= _amount;
_to.receiveTransfer(_netAmount);
}
emit EntityValueTransferred(address(this), address(_to), _amount, _fee);
}
/**
* @notice Updates the receiving entity balance on a transfer.
* @param _transferAmount The amount being received on the transfer.
* @dev This function is external, but is restricted such that it can only be called by other entities.
*/
function receiveTransfer(uint256 _transferAmount) external virtual {
if (!registry.isActiveEntity(Entity(msg.sender))) revert EntityInactive();
unchecked {
// Cannot overflow with realistic balances.
balance += _transferAmount;
}
}
/**
* @notice Deposits an amount of Entity's `baseToken` into an Endaoment-approved Portfolio.
* @param _portfolio An Endaoment-approved portfolio.
* @param _amount Amount of `baseToken` to deposit into the portfolio.
* @param _data Data required by a portfolio to deposit.
* @return _shares Amount of portfolio share tokens Entity received as a result of this deposit.
*/
function portfolioDeposit(Portfolio _portfolio, uint256 _amount, bytes calldata _data) external virtual requiresManager returns (uint256) {
if(!registry.isActivePortfolio(_portfolio)) revert PortfolioInactive();
balance -= _amount;
baseToken.safeApprove(address(_portfolio), _amount);
uint256 _shares = _portfolio.deposit(_amount, _data);
emit EntityDeposit(address(_portfolio), _amount, _shares);
return _shares;
}
/**
* @notice Redeems an amount of Entity's portfolio shares for an amount of `baseToken`.
* @param _portfolio An Endaoment-approved portfolio.
* @param _shares Amount of share tokens to redeem.
* @param _data Data required by a portfolio to redeem.
* @return _received Amount of `baseToken` Entity received as a result of this redemption.
*/
function portfolioRedeem(Portfolio _portfolio, uint256 _shares, bytes calldata _data) external virtual requiresManager returns (uint256) {
if(!registry.isActivePortfolio(_portfolio)) revert PortfolioInactive();
uint256 _received = _portfolio.redeem(_shares, _data);
// unchecked: a realistic balance can never overflow a uint256
unchecked {
balance += _received;
}
emit EntityRedeem(address(_portfolio), _shares, _received);
return _received;
}
/**
* @notice This method should be called to reconcile the Entity's internal baseToken accounting with the baseToken contract's accounting.
* There are a 2 situations where calling this method is appropriate:
* 1. To process amounts of baseToken that arrived at this Entity through methods besides Entity:donate or Entity:transfer. For example,
* if this Entity receives a normal ERC20 transfer of baseToken, the amount received will be unavailable for Entity use until this method
* is called to adjust the balance and process fees. OrgFundFactory.sol:_donate makes use of this method to do this as well.
* 2. Unusually, the Entity's perspective of balance could be lower than `baseToken.balanceOf(this)`. This could happen if
* Entity:callAsEntity is used to transfer baseToken. In this case, this method provides a way of correcting the Entity's internal balance.
*/
function reconcileBalance() external virtual {
uint256 _tokenBalance = baseToken.balanceOf(address(this));
if (_tokenBalance >= balance) {
uint256 _sweepAmount = _tokenBalance - balance;
uint32 _feeMultiplier = registry.getDonationFeeWithOverrides(this);
(uint256 _netAmount, uint256 _fee) = _calculateFee(_sweepAmount, _feeMultiplier);
baseToken.safeTransfer(registry.treasury(), _fee);
unchecked {
balance += _netAmount;
}
emit EntityBalanceReconciled(address(this), _sweepAmount, _fee);
} else {
// Handle abnormal scenario where _tokenBalance < balance (see method docs)
balance = _tokenBalance;
emit EntityBalanceCorrected(address(this), _tokenBalance);
}
}
/**
* @notice Takes stray tokens or ETH sent directly to this Entity, swaps them for base token, then adds them to the
* Entity's balance after paying the appropriate fee to the treasury.
* @param _swapWrapper The swap wrapper to use to convert the assets. Must be whitelisted on the Registry.
* @param _tokenIn The address of the ERC20 token to swap, or ETH_PLACEHOLDER if ETH.
* @param _amountIn The amount of tokens or ETH being swapped and added to the balance.
* @param _data Additional call data required by the ISwapWrapper being used.
*/
function swapAndReconcileBalance(
ISwapWrapper _swapWrapper,
address _tokenIn,
uint256 _amountIn,
bytes calldata _data
) nonReentrant external virtual requiresManager {
if (!registry.isSwapperSupported(_swapWrapper)) revert InvalidAction();
uint32 _feeMultiplier = registry.getDonationFeeWithOverrides(this);
if (_tokenIn != ETH_PLACEHOLDER) {
ERC20(_tokenIn).safeApprove(address(_swapWrapper), 0);
ERC20(_tokenIn).safeApprove(address(_swapWrapper), _amountIn);
}
// Send value only if token in is ETH
uint256 _value = _tokenIn == ETH_PLACEHOLDER ? _amountIn : 0;
uint256 _amountOut = _swapWrapper.swap{value: _value}(
_tokenIn,
address(baseToken),
address(this),
_amountIn,
_data
);
(uint256 _netAmount, uint256 _fee) = _calculateFee(_amountOut, _feeMultiplier);
baseToken.safeTransfer(registry.treasury(), _fee);
unchecked {
// unchecked as no possibility of overflow with baseToken precision
balance += _netAmount;
}
if(balance > baseToken.balanceOf(address(this))) revert BalanceMismatch();
emit EntityBalanceReconciled(address(this), _amountOut, _fee);
}
/**
* @notice Permissioned method that allows Endaoment admin to make arbitrary calls acting as this Entity.
* @param _target The address to which the call will be made.
* @param _value The ETH value that should be forwarded with the call.
* @param _data The calldata that will be sent with the call.
* @return _return The data returned by the call.
*/
function callAsEntity(
address _target,
uint256 _value,
bytes memory _data
) external virtual payable requiresAuth returns (bytes memory) {
(bool _success, bytes memory _response) = payable(_target).call{value: _value}(_data);
if (!_success) revert CallFailed(_response);
return _response;
}
/**
* @notice Pays out an amount of base tokens from the entity to an address. Transfers the fee calculated by the
* default fee multiplier to the treasury.
* @param _to The address to receive the tokens.
* @param _amount Amount donated in base token.
* @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
* @dev Reverts if the fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts if the token transfer fails.
*/
function payout(address _to, uint256 _amount) external virtual requiresAuth {
uint32 _feeMultiplier = registry.getPayoutFee(this);
_payoutWithFeeMultiplier(_to, _amount, _feeMultiplier);
}
/**
* @notice Pays out an amount of base tokens from the entity to an address. Transfers the fee calculated by the
* default fee multiplier to the treasury.
* @param _amount Amount donated in base token.
* @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
* @dev Reverts if the fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts if the token transfer fails.
*/
function payoutWithOverrides(address _to, uint256 _amount) external virtual requiresAuth {
uint32 _feeMultiplier = registry.getPayoutFeeWithOverrides(this);
_payoutWithFeeMultiplier(_to, _amount, _feeMultiplier);
}
/**
* @notice Pays out an amount of base tokens from the entity to an address. Transfers fee specified by a privileged role.
* @param _amount Amount donated in base token.
* @param _feeOverride Payout override configured by an Admin
* @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
* @dev Reverts if the fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
* @dev Reverts if the token transfer fails.
*/
function payoutWithAdminOverrides(address _to, uint256 _amount, uint32 _feeOverride) external virtual requiresAuth {
_payoutWithFeeMultiplier(_to, _amount, _feeOverride);
}
/**
* @notice Pays out an amount of base tokens from the entity to an address. Transfers the fee calculated by fee multiplier to the treasury.
* @param _to The address to receive the tokens.
* @param _amount Contains the amount being paid out (denominated in the base token's units).
* @param _feeMultiplier Value indicating the percentage of the Endaoment fee to go to the Endaoment treasury.
* @dev Reverts if the token transfer fails.
* @dev Reverts if the fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
*/
function _payoutWithFeeMultiplier(address _to, uint256 _amount, uint32 _feeMultiplier) internal virtual {
if (balance < _amount) revert InsufficientFunds();
(uint256 _netAmount, uint256 _fee) = _calculateFee(_amount, _feeMultiplier);
baseToken.safeTransfer(registry.treasury(), _fee);
baseToken.safeTransfer(address(_to), _netAmount);
unchecked {
// unchecked because we've already validated that amount is less than or equal to the balance
balance -= _amount;
}
emit EntityValuePaidOut(address(this), _to, _amount, _fee);
}
/// @dev Internal helper method to calculate the fee on a base token amount for a given fee multiplier.
function _calculateFee(
uint256 _amount,
uint256 _feeMultiplier
) internal virtual pure returns (uint256 _netAmount, uint256 _fee) {
if (_feeMultiplier > Math.ZOC) revert InvalidAction();
unchecked {
// unchecked as no possibility of overflow with baseToken precision
_fee = _amount.zocmul(_feeMultiplier);
// unchecked as the _feeMultiplier check with revert above protects against overflow
_netAmount = _amount - _fee;
}
}
}//SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;
import { Registry } from "./Registry.sol";
import { Entity } from "./Entity.sol";
/**
* @notice This contract controls the Org entity.
*/
contract Org is Entity {
/// @notice Tax ID of org
bytes32 public orgId;
/**
* @notice One time method to be called at deployment to configure the contract. Required so Org
* contracts can be deployed as minimal proxies (clones).
* @param _registry The registry to host the Org Entity.
* @param _orgId The Org's ID for tax purposes.
* @dev The `manager` of the Org is initially set to the zero address and will be updated by role pending an off-chain claim.
*/
function initialize(Registry _registry, bytes32 _orgId) public {
// Call to Entity's initializer ensures this can only be called once.
__initEntity(_registry, address(0));
orgId = _orgId;
}
function setOrgId(bytes32 _orgId) requiresAuth external {
orgId = _orgId;
}
/**
* @inheritdoc Entity
*/
function entityType() public pure override returns (uint8) {
return 1;
}
}//SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;
import { Registry } from "./Registry.sol";
import { Entity } from "./Entity.sol";
/**
* @notice Fund entity
*/
contract Fund is Entity {
/**
* @notice One time method to be called at deployment to configure the contract. Required so Fund
* contracts can be deployed as minimal proxies (clones).
* @param _registry The registry to host the Fund Entity.
* @param _manager The address of the Fund's manager.
*/
function initialize(Registry _registry, address _manager) public {
// Call to Entity's initialization function ensures this can only be called once
__initEntity(_registry, _manager);
}
/**
* @inheritdoc Entity
*/
function entityType() public pure override returns (uint8) {
return 2;
}
}//SPDX-License-Identifier: BSD 3-Clause
pragma solidity >=0.8.0;
error ETHAmountInMismatch();
/**
* @notice ISwapWrapper is the interface that all swap wrappers should implement.
* This will be used to support swap protocols like Uniswap V2 and V3, Sushiswap, 1inch, etc.
*/
interface ISwapWrapper {
/// @notice Event emitted after a successful swap.
event WrapperSwapExecuted(address indexed tokenIn, address indexed tokenOut, address sender, address indexed recipient, uint256 amountIn, uint256 amountOut);
/// @notice Name of swap wrapper for UX readability.
function name() external returns (string memory);
/**
* @notice Swap function. Generally we expect the implementer to call some exactAmountIn-like swap method, and so the documentation
* is written with this in mind. However, the method signature is general enough to support exactAmountOut swaps as well.
* @param _tokenIn Token to be swapped (or 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for ETH).
* @param _tokenOut Token to receive (or 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for ETH).
* @param _recipient Receiver of `_tokenOut`.
* @param _amount Amount of `_tokenIn` that should be swapped.
* @param _data Additional data that the swap wrapper may require to execute the swap.
* @return Amount of _tokenOut received.
*/
function swap(address _tokenIn, address _tokenOut, address _recipient, uint256 _amount, bytes calldata _data) external payable returns (uint256);
}// SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;
library Math {
uint256 internal constant ZOC = 1e4;
/**
* @dev Multiply 2 numbers where at least one is a zoc, return product in original units of the other number.
*/
function zocmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x * y;
unchecked {
z /= ZOC;
}
}
// Below is WAD math from solmate's FixedPointMathLib.
// https://github.com/Rari-Capital/solmate/blob/c8278b3cb948cffda3f1de5a401858035f262060/src/utils/FixedPointMathLib.sol
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
// For tokens with 6 decimals like USDC, these scale by 1e6 (one million).
function mulMilDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, 1e6); // Equivalent to (x * y) / 1e6 rounded down.
}
function divMilDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, 1e6, y); // Equivalent to (x * 1e6) / y rounded down.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
// Store x * y in z for now.
z := mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// Divide z by the denominator.
z := div(z, denominator)
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
// This contract is modified from Solmate only to make requiresAuth virtual on line 26
/// @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 virtual {
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: BSD 3-Clause
pragma solidity 0.8.13;
import { Auth, Authority } from "./lib/auth/Auth.sol";
import { RolesAuthority } from "./lib/auth/authorities/RolesAuthority.sol";
// --- Errors ---
error OwnershipInvalid();
/**
* @notice RegistryAuth - contract to control ownership of the Registry.
*/
contract RegistryAuth is RolesAuthority {
/// @notice Emitted when the first step of an ownership transfer (proposal) is done.
event OwnershipTransferProposed(address indexed user, address indexed newOwner);
/// @notice Emitted when the second step of an ownership transfer (claim) is done.
event OwnershipChanged(address indexed owner, address indexed newOwner);
// --- Storage ---
/// @notice Pending owner for 2 step ownership transfer
address public pendingOwner;
// --- Constructor ---
constructor(address _owner, Authority _authority) RolesAuthority(_owner, _authority) {}
/**
* @notice Starts the 2 step process of transferring registry authorization to a new owner.
* @param _newOwner Proposed new owner of registry authorization.
*/
function transferOwnership(address _newOwner) external requiresAuth {
pendingOwner = _newOwner;
emit OwnershipTransferProposed(msg.sender, _newOwner);
}
/**
* @notice Completes the 2 step process of transferring registry authorization to a new owner.
* This function must be called by the proposed new owner.
*/
function claimOwnership() external {
if (msg.sender != pendingOwner) revert OwnershipInvalid();
emit OwnershipChanged(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
/**
* @notice Old approach of setting a new owner in a single step.
* @dev This function throws an error to force use of the new 2-step approach.
*/
function setOwner(address /*newOwner*/ ) public view override requiresAuth {
revert OwnershipInvalid();
}
}//SPDX-License-Identifier: BSD 3-Clause
pragma solidity >=0.8.0;
import { ERC20 } from "solmate/tokens/ERC20.sol";
import { Registry } from "./Registry.sol";
import { Entity } from "./Entity.sol";
import { EndaomentAuth } from "./lib/auth/EndaomentAuth.sol";
import { RolesAuthority } from "./lib/auth/authorities/RolesAuthority.sol";
import { Math } from "./lib/Math.sol";
abstract contract Portfolio is ERC20, EndaomentAuth {
using Math for uint256;
Registry public immutable registry;
uint256 public cap;
uint256 public depositFee;
uint256 public redemptionFee;
address public immutable asset;
bool public didShutdown;
error InvalidSwapper();
error TransferDisallowed();
error DepositAfterShutdown();
error DidShutdown();
error NotEntity();
error ExceedsCap();
error PercentageOver100();
error RoundsToZero();
error CallFailed(bytes response);
/// @notice `sender` has exchanged `assets` (after fees) for `shares`, and transferred those `shares` to `receiver`.
/// The sender paid a total of `depositAmount` and was charged `fee` for the transaction.
event Deposit(
address indexed sender,
address indexed receiver,
uint256 assets,
uint256 shares,
uint256 depositAmount,
uint256 fee
);
/// @notice `sender` has exchanged `shares` for `assets`, and transferred those `assets` to `receiver`.
/// The sender received a net of `redeemedAmount` after the conversion of `assets` into base tokens
/// and was charged `fee` for the transaction.
event Redeem(
address indexed sender,
address indexed receiver,
uint256 assets,
uint256 shares,
uint256 redeemedAmount,
uint256 fee
);
/// @notice Event emitted when `cap` is set.
event CapSet(uint256 cap);
/// @notice Event emitted when `depositFee` is set.
event DepositFeeSet(uint256 fee);
/// @notice Event emitted when `redemptionFee` is set.
event RedemptionFeeSet(uint256 fee);
/// @notice Event emitted when management takes fees.
event FeesTaken(uint256 amount);
/// @notice Event emitted when admin forcefully swaps portfolio asset balance for baseToken.
event Shutdown(uint256 assetAmount, uint256 baseTokenOut);
/**
* @param _registry Endaoment registry.
* @param _name Name of the ERC20 Portfolio share tokens.
* @param _symbol Symbol of the ERC20 Portfolio share tokens.
* @param _cap Amount in baseToken that value of totalAssets should not exceed.
* @param _depositFee Percentage fee as ZOC that will go to treasury on asset deposit.
* @param _redemptionFee Percentage fee as ZOC that will go to treasury on share redemption.
*/
constructor(Registry _registry, address _asset, string memory _name, string memory _symbol, uint256 _cap, uint256 _depositFee, uint256 _redemptionFee) ERC20(_name, _symbol, ERC20(_asset).decimals()) {
registry = _registry;
if(_redemptionFee > Math.ZOC) revert PercentageOver100();
depositFee = _depositFee;
redemptionFee = _redemptionFee;
cap = _cap;
asset = _asset;
__initEndaomentAuth(_registry, "portfolio");
}
/**
* @notice Function used to determine whether an Entity is active on the registry.
* @param _entity The Entity.
*/
function _isEntity(Entity _entity) internal view returns (bool) {
return registry.isActiveEntity(_entity);
}
/**
* @notice Set the Portfolio cap.
* @param _amount Amount, denominated in baseToken.
*/
function setCap(uint256 _amount) external virtual requiresAuth {
cap = _amount;
emit CapSet(_amount);
}
/**
* @notice Set deposit fee.
* @param _pct Percentage as ZOC (e.g. 1000 = 10%).
*/
function setDepositFee(uint256 _pct) external virtual requiresAuth {
if(_pct > Math.ZOC) revert PercentageOver100();
depositFee = _pct;
emit DepositFeeSet(_pct);
}
/**
* @notice Set redemption fee.
* @param _pct Percentage as ZOC (e.g. 1000 = 10%).
*/
function setRedemptionFee(uint256 _pct) external virtual requiresAuth {
if(_pct > Math.ZOC) revert PercentageOver100();
redemptionFee = _pct;
emit RedemptionFeeSet(_pct);
}
/**
* @notice Total amount of the underlying asset that is managed by the Portfolio.
*/
function totalAssets() external view virtual returns (uint256);
/**
* @notice Takes some amount of assets from this portfolio as assets under management fee.
* @param _amountAssets Amount of assets to take.
*/
function takeFees(uint256 _amountAssets) external virtual;
/**
* @notice Exchange `_amountBaseToken` for some amount of Portfolio shares.
* @param _amountBaseToken The amount of the Entity's baseToken to deposit.
* @param _data Data that the portfolio needs to make the deposit. In some cases, this will be swap parameters.
* @return shares The amount of shares that this deposit yields to the Entity.
*/
function deposit(uint256 _amountBaseToken, bytes calldata _data) virtual external returns (uint256 shares);
/**
* @notice Exchange `_amountShares` for some amount of baseToken.
* @param _amountShares The amount of the Entity's portfolio shares to exchange.
* @param _data Data that the portfolio needs to make the redemption. In some cases, this will be swap parameters.
* @return baseTokenOut The amount of baseToken that this redemption yields to the Entity.
*/
function redeem(uint256 _amountShares, bytes calldata _data) virtual external returns (uint256 baseTokenOut);
/**
* @notice Calculates the amount of shares that the Portfolio should exchange for the amount of assets provided.
* @param _amountAssets Amount of assets.
*/
function convertToShares(uint256 _amountAssets) virtual public view returns (uint256);
/**
* @notice Calculates the amount of assets that the Portfolio should exchange for the amount of shares provided.
* @param _amountShares Amount of shares.
*/
function convertToAssets(uint256 _amountShares) virtual public view returns (uint256);
/**
* @notice Exit out all assets of portfolio for baseToken. Must persist a mechanism for entities to redeem their shares for baseToken.
* @param _data Data that the portfolio needs to exit from asset. In some cases, this will be swap parameters.
* @return baseTokenOut The amount of baseToken that this exit yielded.
*/
function shutdown(bytes calldata _data) virtual external returns (uint256 baseTokenOut);
/// @notice `transfer` disabled on Portfolio tokens.
function transfer(address /** to */, uint256 /** amount */) public pure override returns (bool) {
revert TransferDisallowed();
}
/// @notice `transferFrom` disabled on Portfolio tokens.
function transferFrom(address /** from */, address /** to */, uint256 /** amount */) public pure override returns (bool) {
revert TransferDisallowed();
}
/// @notice `approve` disabled on Portfolio tokens.
function approve(address /** to */, uint256 /** amount */) public pure override returns (bool) {
revert TransferDisallowed();
}
/// @notice `permit` disabled on Portfolio tokens.
function permit(
address /* owner */,
address /* spender */,
uint256 /* value */,
uint256 /* deadline */,
uint8 /* v */,
bytes32 /* r */,
bytes32 /* s */
) public pure override {
revert TransferDisallowed();
}
/**
* @notice Permissioned method that allows Endaoment admin to make arbitrary calls acting as this Portfolio.
* @param _target The address to which the call will be made.
* @param _value The ETH value that should be forwarded with the call.
* @param _data The calldata that will be sent with the call.
* @return _return The data returned by the call.
*/
function callAsPortfolio(
address _target,
uint256 _value,
bytes memory _data
) external payable requiresAuth returns (bytes memory) {
(bool _success, bytes memory _response) = payable(_target).call{value: _value}(_data);
if (!_success) revert CallFailed(_response);
return _response;
}
/// @dev Internal helper method to calculate the fee on a base token amount for a given fee multiplier.
function _calculateFee(
uint256 _amount,
uint256 _feeMultiplier
) internal pure returns (uint256 _netAmount, uint256 _fee) {
if (_feeMultiplier > Math.ZOC) revert PercentageOver100();
unchecked {
// unchecked as no possibility of overflow with baseToken precision
_fee = _amount.zocmul(_feeMultiplier);
// unchecked as the _feeMultiplier check with revert above protects against overflow
_netAmount = _amount - _fee;
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Modified 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 reentrancyStatus;
error Reentrancy();
function __initReentrancyGuard() internal {
if(reentrancyStatus != 0) revert Reentrancy();
reentrancyStatus = 1;
}
modifier nonReentrant() {
if(reentrancyStatus != 1) revert Reentrancy();
reentrancyStatus = 2;
_;
reentrancyStatus = 1;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import { RolesAuthority } from './authorities/RolesAuthority.sol';
/**
* @notice An abstract Auth that contracts in the Endaoment ecosystem can inherit from. It is based on
* the `Auth.sol` contract from Solmate, but does not inherit from it. Most of the functionality
* is either slightly different, or not needed. In particular:
* - EndaomentAuth uses an initializer such that it can be deployed with minimal proxies.
* - EndaomentAuth contracts reference a RolesAuthority, not just an Authority, when looking up permissions.
* In the Endaoment ecosystem, this is assumed to be the Registry.
* - EndaomentAuth contracts do not have an owner, but instead grant ubiquitous permission to its RoleAuthority's
* owner. In the Endaoment ecosystem, this is assumed to be the board of directors multi-sig.
* - EndaomentAuth contracts can optionally declare themselves a "special target" at deploy time. Instead of passing
* their address to the authority when looking up their permissions, they'll instead pass the special target bytes.
* See documentation on `specialTarget` for more information.
*
*/
abstract contract EndaomentAuth {
/// @notice Thrown when an account without proper permissions calls a privileged method.
error Unauthorized();
/// @notice Thrown if there is an attempt to deploy with address 0 as the authority.
error InvalidAuthority();
/// @notice Thrown if there is a second call to initialize.
error AlreadyInitialized();
/// @notice The contract used to source permissions for accounts targeting this contract.
RolesAuthority public authority;
/**
* @notice If set to a non-zero value, this contract will pass these byes as the target contract
* to the RolesAuthority's `canCall` method, rather than its own contract. This allows a single
* RolesAuthority permission to manage permissions simultaneously for a group of contracts that
* identify themselves as a certain type. For example: set a permission for all "entity" contracts.
*/
bytes20 public specialTarget;
/**
* @notice One time method to be called at deployment to configure the contract. Required so EndaomentAuth
* contracts can be deployed as minimal proxies (clones).
* @param _authority Contract that will be used to source permissions for accounts targeting this contract.
* @param _specialTarget The bytes that this contract will pass as the "target" when looking up permissions
* from the authority. If set to empty bytes, this contract will pass its own address instead.
*/
function __initEndaomentAuth(RolesAuthority _authority, bytes20 _specialTarget) internal virtual {
if (address(_authority) == address(0)) revert InvalidAuthority();
if (address(authority) != address(0)) revert AlreadyInitialized();
authority = _authority;
specialTarget = _specialTarget;
}
/**
* @notice Modifier for methods that require authorization to execute.
*/
modifier requiresAuth virtual {
if(!isAuthorized(msg.sender, msg.sig)) revert Unauthorized();
_;
}
/**
* @notice Internal method that asks the authority whether the caller has permission to execute a method.
* @param user The account attempting to call a permissioned method on this contract
* @param functionSig The signature hash of the permissioned method being invoked.
*/
function isAuthorized(address user, bytes4 functionSig) internal virtual view returns (bool) {
RolesAuthority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
address _target = specialTarget == "" ? address(this) : address(specialTarget);
// The caller has permission on authority, or the caller is the RolesAuthority owner
return auth.canCall(user, _target, functionSig) || user == auth.owner();
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
// This contract is modified from Solmate only to import modified Auth.sol on line 5
import {Auth, Authority} from "../Auth.sol";
/// @notice Role based Authority that supports up to 256 roles.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/RolesAuthority.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-roles/blob/master/src/roles.sol)
contract RolesAuthority is Auth, Authority {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled);
event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled);
event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled);
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}
/*///////////////////////////////////////////////////////////////
ROLE/USER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => bytes32) public getUserRoles;
mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic;
mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability;
function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) {
return (uint256(getUserRoles[user]) >> role) & 1 != 0;
}
function doesRoleHaveCapability(
uint8 role,
address target,
bytes4 functionSig
) public view virtual returns (bool) {
return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0;
}
/*///////////////////////////////////////////////////////////////
AUTHORIZATION LOGIC
//////////////////////////////////////////////////////////////*/
function canCall(
address user,
address target,
bytes4 functionSig
) public view virtual override returns (bool) {
return
isCapabilityPublic[target][functionSig] ||
bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig];
}
/*///////////////////////////////////////////////////////////////
ROLE CAPABILITY CONFIGURATION LOGIC
//////////////////////////////////////////////////////////////*/
function setPublicCapability(
address target,
bytes4 functionSig,
bool enabled
) public virtual requiresAuth {
isCapabilityPublic[target][functionSig] = enabled;
emit PublicCapabilityUpdated(target, functionSig, enabled);
}
function setRoleCapability(
uint8 role,
address target,
bytes4 functionSig,
bool enabled
) public virtual requiresAuth {
if (enabled) {
getRolesWithCapability[target][functionSig] |= bytes32(1 << role);
} else {
getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role);
}
emit RoleCapabilityUpdated(role, target, functionSig, enabled);
}
/*///////////////////////////////////////////////////////////////
USER ROLE ASSIGNMENT LOGIC
//////////////////////////////////////////////////////////////*/
function setUserRole(
address user,
uint8 role,
bool enabled
) public virtual requiresAuth {
if (enabled) {
getUserRoles[user] |= bytes32(1 << role);
} else {
getUserRoles[user] &= ~bytes32(1 << role);
}
emit UserRoleUpdated(user, role, enabled);
}
}{
"remappings": [
"ds-test/=lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"murky/=lib/murky/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solmate/=lib/solmate/src/",
"weird-erc20/=lib/solmate/lib/weird-erc20/src/"
],
"optimizer": {
"enabled": true,
"runs": 9999999
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract Registry","name":"_registry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"entity","type":"address"},{"indexed":true,"internalType":"uint8","name":"entityType","type":"uint8"},{"indexed":true,"internalType":"address","name":"entityManager","type":"address"}],"name":"EntityDeployed","type":"event"},{"inputs":[],"name":"ETH_PLACEHOLDER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"computeFundAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_orgId","type":"bytes32"}],"name":"computeOrgAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"deployFund","outputs":[{"internalType":"contract Fund","name":"_fund","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"},{"internalType":"bytes32","name":"_salt","type":"bytes32"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deployFundAndDonate","outputs":[{"internalType":"contract Fund","name":"_fund","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"},{"internalType":"bytes32","name":"_salt","type":"bytes32"},{"internalType":"contract ISwapWrapper","name":"_swapWrapper","type":"address"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"deployFundSwapAndDonate","outputs":[{"internalType":"contract Fund","name":"_fund","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_orgId","type":"bytes32"}],"name":"deployOrg","outputs":[{"internalType":"contract Org","name":"_org","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_orgId","type":"bytes32"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deployOrgAndDonate","outputs":[{"internalType":"contract Org","name":"_org","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_orgId","type":"bytes32"},{"internalType":"contract ISwapWrapper","name":"_swapWrapper","type":"address"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"deployOrgSwapAndDonate","outputs":[{"internalType":"contract Org","name":"_org","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"fundImplementation","outputs":[{"internalType":"contract Fund","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"orgImplementation","outputs":[{"internalType":"contract Org","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract Registry","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6101006040523480156200001257600080fd5b5060405162007e3d38038062007e3d833981016040819052620000359162000221565b6001600160a01b0381166080526040516200005090620001ec565b604051809103906000f0801580156200006d573d6000803e3d6000fd5b506001600160a01b0390811660a0819052604051632f84fd1f60e21b81529183166004830152631253541360e21b60248301529063be13f47c90604401600060405180830381600087803b158015620000c557600080fd5b505af1158015620000da573d6000803e3d6000fd5b50505050604051620000ec90620001fa565b604051809103906000f08015801562000109573d6000803e3d6000fd5b506001600160a01b0390811660c081905260405163485cc95560e01b81529183166004830152600060248301529063485cc95590604401600060405180830381600087803b1580156200015b57600080fd5b505af115801562000170573d6000803e3d6000fd5b50505050806001600160a01b031663c55dae636040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001d9919062000221565b6001600160a01b031660e0525062000248565b61350380620014c583390190565b61347580620049c883390190565b6001600160a01b03811681146200021e57600080fd5b50565b6000602082840312156200023457600080fd5b8151620002418162000208565b9392505050565b60805160a05160c05160e051611204620002c16000396000818161027c0152610c700152600081816101320152818161031d01526104130152600081816101860152818161067801526107140152600081816101ba0152818161048e0152818161054101528181610776015261082801526112046000f3fe6080604052600436106100d25760003560e01c8063a60fe71d1161007f578063c45e15a011610059578063c45e15a014610242578063c55dae631461026a578063db9e30cc1461029e578063e6be99fc146102be57600080fd5b8063a60fe71d146101fc578063ab438d6b1461021c578063bf37a6701461022f57600080fd5b80636fee7ed6116100b05780636fee7ed6146101745780637b103999146101a85780639fb8578d146101dc57600080fd5b8063099a81e3146100d75780630b307674146101205780632a4004b814610154575b600080fd5b3480156100e357600080fd5b506100f76100f2366004610f1f565b6102de565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561012c57600080fd5b506100f77f000000000000000000000000000000000000000000000000000000000000000081565b34801561016057600080fd5b506100f761016f366004610f1f565b6103d4565b34801561018057600080fd5b506100f77f000000000000000000000000000000000000000000000000000000000000000081565b3480156101b457600080fd5b506100f77f000000000000000000000000000000000000000000000000000000000000000081565b3480156101e857600080fd5b506100f76101f7366004610f4b565b610671565b34801561020857600080fd5b506100f7610217366004610f4b565b61070d565b6100f761022a366004610fad565b6109c5565b6100f761023d36600461103c565b6109ec565b34801561024e57600080fd5b506100f773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b34801561027657600080fd5b506100f77f000000000000000000000000000000000000000000000000000000000000000081565b3480156102aa57600080fd5b506100f76102b93660046110b8565b610a11565b3480156102ca57600080fd5b506100f76102d93660046110da565b610a28565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020820152603481018290526000906103cb907f00000000000000000000000000000000000000000000000000000000000000009060540160405160208183030381529060405280519060200120306040517f602d8060093d393df3363d3d373d3d3d363d73000000000000000000000000008152606093841b60138201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006027820152921b6037830152604b8201526036808220606b830152605591012090565b90505b92915050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b16602082015260348101829052600090610451907f00000000000000000000000000000000000000000000000000000000000000009060540160405160208183030381529060405280519060200120610a47565b6040517f485cc95500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600483015285811660248301529192509082169063485cc95590604401600060405180830381600087803b1580156104e657600080fd5b505af11580156104fa573d6000803e3d6000fd5b50506040517f0160a6e200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301527f0000000000000000000000000000000000000000000000000000000000000000169250630160a6e29150602401600060405180830381600087803b15801561058757600080fd5b505af115801561059b573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1663838453fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610601573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610625919061110f565b60ff168273ffffffffffffffffffffffffffffffffffffffff167f1e3e29fb0c05e0c478577b9b3e207cbfe2952b1f9b239ed5d2535d4b24b6c57760405160405180910390a492915050565b60006103ce7f000000000000000000000000000000000000000000000000000000000000000083306040517f602d8060093d393df3363d3d373d3d3d363d73000000000000000000000000008152606093841b60138201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006027820152921b6037830152604b8201526036808220606b830152605591012090565b60006107397f000000000000000000000000000000000000000000000000000000000000000083610a47565b6040517fbe13f47c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018590529192509082169063be13f47c90604401600060405180830381600087803b1580156107cd57600080fd5b505af11580156107e1573d6000803e3d6000fd5b50506040517f0160a6e200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301527f0000000000000000000000000000000000000000000000000000000000000000169250630160a6e29150602401600060405180830381600087803b15801561086e57600080fd5b505af1158015610882573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff1663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f59190611132565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1663838453fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610956573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061097a919061110f565b60ff168273ffffffffffffffffffffffffffffffffffffffff167f1e3e29fb0c05e0c478577b9b3e207cbfe2952b1f9b239ed5d2535d4b24b6c57760405160405180910390a4919050565b60006109d188886103d4565b90506109e1818787878787610b2a565b979650505050505050565b60006109f78761070d565b9050610a07818787878787610b2a565b9695505050505050565b6000610a1c8361070d565b90506103ce8183610c56565b6000610a3484846103d4565b9050610a408183610c56565b9392505050565b60006040517f602d8060093d393df3363d3d373d3d3d363d730000000000000000000000000081528360601b60138201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006027820152826036826000f591505073ffffffffffffffffffffffffffffffffffffffff81166103ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610bc057610b7d73ffffffffffffffffffffffffffffffffffffffff8516333086610cfc565b610b9f73ffffffffffffffffffffffffffffffffffffffff8516876000610de6565b610bc073ffffffffffffffffffffffffffffffffffffffff85168785610de6565b6040517fa2f48b9f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87169063a2f48b9f903490610c1c908990899089908990899060040161114f565b6000604051808303818588803b158015610c3557600080fd5b505af1158015610c49573d6000803e3d6000fd5b5050505050505050505050565b610c9873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016338484610cfc565b8173ffffffffffffffffffffffffffffffffffffffff16630b2ec6726040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ce057600080fd5b505af1158015610cf4573d6000803e3d6000fd5b505050505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af1915050610d7981610eb3565b610ddf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610b21565b5050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050610e4781610eb3565b610ead576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610b21565b50505050565b60003d82610ec557806000803e806000fd5b8060208114610edd578015610eee5760009250610ef3565b816000803e60005115159250610ef3565b600192505b5050919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610f1c57600080fd5b50565b60008060408385031215610f3257600080fd5b8235610f3d81610efa565b946020939093013593505050565b600060208284031215610f5d57600080fd5b5035919050565b60008083601f840112610f7657600080fd5b50813567ffffffffffffffff811115610f8e57600080fd5b602083019150836020828501011115610fa657600080fd5b9250929050565b600080600080600080600060c0888a031215610fc857600080fd5b8735610fd381610efa565b9650602088013595506040880135610fea81610efa565b94506060880135610ffa81610efa565b93506080880135925060a088013567ffffffffffffffff81111561101d57600080fd5b6110298a828b01610f64565b989b979a50959850939692959293505050565b60008060008060008060a0878903121561105557600080fd5b86359550602087013561106781610efa565b9450604087013561107781610efa565b935060608701359250608087013567ffffffffffffffff81111561109a57600080fd5b6110a689828a01610f64565b979a9699509497509295939492505050565b600080604083850312156110cb57600080fd5b50508035926020909101359150565b6000806000606084860312156110ef57600080fd5b83356110fa81610efa565b95602085013595506040909401359392505050565b60006020828403121561112157600080fd5b815160ff81168114610a4057600080fd5b60006020828403121561114457600080fd5b8151610a4081610efa565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501168301019050969550505050505056fea2646970667358221220aa6ac4e89d0355a64ce4d7bfd2a40f6adf7d3c1aa65f8e23bd35a52867ef002164736f6c634300080d0033608060405234801561001057600080fd5b506134e3806100206000396000f3fe6080604052600436106101c25760003560e01c8063a2f48b9f116100f7578063c422c5bd11610095578063d773b04f11610064578063d773b04f1461054b578063df3d067e1461056b578063f14faf6f1461058b578063f7044a5f146105ab57600080fd5b8063c422c5bd146104b6578063c45e15a0146104d6578063c55dae63146104fe578063d0ebdbe71461052b57600080fd5b8063ab3c14fe116100d1578063ab3c14fe14610440578063b69ef8a814610453578063be13f47c14610469578063bf7e214f1461048957600080fd5b8063a2f48b9f146103ed578063a8e405b014610400578063a9059cbb1461042057600080fd5b8063481c6a75116101645780637b1039991161013e5780637b10399914610364578063838453fd1461039157806394b7c5b5146103ad57806395f32d21146103cd57600080fd5b8063481c6a75146102d257806349d55d9d146103245780636a6815411461034457600080fd5b80631730bdfe116101a05780631730bdfe1461021e5780632031ee951461024757806324c67842146102925780632c482f19146102b257600080fd5b80630b2ec672146101c75780630cb6ee6f146101de578063117de2fd146101fe575b600080fd5b3480156101d357600080fd5b506101dc6105cb565b005b3480156101ea57600080fd5b506101dc6101f9366004612ef6565b610866565b34801561020a57600080fd5b506101dc610219366004612ef6565b610970565b34801561022a57600080fd5b5061023460075481565b6040519081526020015b60405180910390f35b34801561025357600080fd5b506001546102619060601b81565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909116815260200161023e565b34801561029e57600080fd5b506102346102ad366004612f6b565b610a2b565b3480156102be57600080fd5b506101dc6102cd366004612fd9565b610c84565b3480156102de57600080fd5b506004546102ff9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161023e565b34801561033057600080fd5b506101dc61033f36600461301b565b610cf3565b610357610352366004613063565b610dc6565b60405161023e919061317a565b34801561037057600080fd5b506003546102ff9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561039d57600080fd5b506040516001815260200161023e565b3480156103b957600080fd5b506101dc6103c8366004612ef6565b610ee7565b3480156103d957600080fd5b506101dc6103e836600461301b565b61101a565b6101dc6103fb3660046131cb565b611083565b34801561040c57600080fd5b506101dc61041b36600461323e565b61112f565b34801561042c57600080fd5b506101dc61043b366004612ef6565b6111a1565b6101dc61044e3660046131cb565b61128a565b34801561045f57600080fd5b5061023460065481565b34801561047557600080fd5b506101dc610484366004612ef6565b6112e1565b34801561049557600080fd5b506000546102ff9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156104c257600080fd5b506102346104d1366004612f6b565b6112f2565b3480156104e257600080fd5b506102ff73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b34801561050a57600080fd5b506005546102ff9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561053757600080fd5b506101dc61054636600461326e565b611571565b34801561055757600080fd5b506101dc610566366004612fd9565b611689565b34801561057757600080fd5b506101dc6105863660046131cb565b6116f8565b34801561059757600080fd5b506101dc6105a636600461301b565b611c27565b3480156105b757600080fd5b506101dc6105c636600461301b565b611cc7565b6005546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa15801561063a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065e9190613292565b905060065481106108285760006006548261067991906132da565b6003546040517f759c45dc00000000000000000000000000000000000000000000000000000000815230600482015291925060009173ffffffffffffffffffffffffffffffffffffffff9091169063759c45dc90602401602060405180830381865afa1580156106ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071191906132f1565b9050600080610726848463ffffffff16611d1e565b915091506107de600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561079a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107be919061330e565b60055473ffffffffffffffffffffffffffffffffffffffff169083611d72565b6006805483019055604080518581526020810183905230917fac791e4e5e1041f59b3427f769f90a58cb39873d7fec687682c09583b425099c910160405180910390a25050505050565b600681905560405181815230907f1cd384df7b960f3950da0f15d6aa8d62512c9f2209adde5ddeba0f4ebd6b58829060200160405180910390a25b50565b610894336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b6108ca576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517fb3d5804500000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063b3d58045906024015b602060405180830381865afa15801561093a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095e91906132f1565b905061096b83838361201b565b505050565b61099e336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b6109d4576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517fb4ea2a9800000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063b4ea2a989060240161091d565b60045460009073ffffffffffffffffffffffffffffffffffffffff163314801590610a815750610a7f336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b155b15610ab8576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f55c6f68300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152909116906355c6f68390602401602060405180830381865afa158015610b28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b4c919061332b565b610b82576040517fbc43451a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fe77c646d00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff87169063e77c646d90610bdb90889088908890600401613396565b6020604051808303816000875af1158015610bfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1e9190613292565b6006805482019055604080518781526020810183905291925073ffffffffffffffffffffffffffffffffffffffff8816917fee0fb9e6d573076d9a07b1d91e0a988c2f988d1d690ff129ff3096408d0afbc891015b60405180910390a295945050505050565b610cb2336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b610ce8576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61096b83838361201b565b6003546040517fea3fff6800000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9091169063ea3fff6890602401602060405180830381865afa158015610d61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d85919061332b565b610dbb576040517f4839ee3e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680549091019055565b6060610df6336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b610e2c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff168585604051610e5591906133b0565b60006040518083038185875af1925050503d8060008114610e92576040519150601f19603f3d011682016040523d82523d6000602084013e610e97565b606091505b509150915081610ede57806040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152600401610ed5919061317a565b60405180910390fd5b95945050505050565b60045473ffffffffffffffffffffffffffffffffffffffff163314801590610f3a5750610f38336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b155b15610f71576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f864ec3f000000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8481166024830152600092169063864ec3f0906044015b602060405180830381865afa158015610fe9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061100d91906132f1565b905061096b838383612166565b611048336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b61107e576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600755565b6003546040517fa96abefe00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063a96abefe906024015b602060405180830381865afa1580156110f3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061111791906132f1565b905061112786868686868661248c565b505050505050565b61115d336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b611193576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61119d82826128a1565b5050565b60045473ffffffffffffffffffffffffffffffffffffffff1633148015906111f457506111f2336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b155b1561122b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517fda93546e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8481166024830152600092169063da93546e90604401610fcc565b6003546040517f759c45dc00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063759c45dc906024016110d6565b6112ec826000612a02565b60075550565b60045460009073ffffffffffffffffffffffffffffffffffffffff1633148015906113485750611346336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b155b1561137f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f55c6f68300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152909116906355c6f68390602401602060405180830381865afa1580156113ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611413919061332b565b611449576040517fbc43451a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836006600082825461145b91906132da565b90915550506005546114849073ffffffffffffffffffffffffffffffffffffffff168686612b90565b6040517f5d30351900000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff871690635d303519906114dd90889088908890600401613396565b6020604051808303816000875af11580156114fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115209190613292565b604080518781526020810183905291925073ffffffffffffffffffffffffffffffffffffffff8816917f17f635bacbd012dd587e39c86059a06f779ee2111f6c5541b40c2941b09cf7c49101610c73565b60045473ffffffffffffffffffffffffffffffffffffffff1633148015906115c457506115c2336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b155b156115fb576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f20b26c6c7ff7d40d049fc212237046ba5f7cde4ba6d317501329880970cbb01590600090a3600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6116b7336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b6116ed576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61096b838383612166565b600254600114611734576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805560045473ffffffffffffffffffffffffffffffffffffffff16331480159061178b5750611789336000357fffffffff0000000000000000000000000000000000000000000000000000000016611e3f565b155b156117c2576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f6ab3905700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015290911690636ab3905790602401602060405180830381865afa158015611832573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611856919061332b565b61188c576040517f4a7f394f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f759c45dc00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063759c45dc90602401602060405180830381865afa1580156118fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191f91906132f1565b905073ffffffffffffffffffffffffffffffffffffffff851673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146119955761197473ffffffffffffffffffffffffffffffffffffffff8616876000612b90565b61199573ffffffffffffffffffffffffffffffffffffffff86168786612b90565b600073ffffffffffffffffffffffffffffffffffffffff861673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146119cf5760006119d1565b845b6005546040517fda14526300000000000000000000000000000000000000000000000000000000815291925060009173ffffffffffffffffffffffffffffffffffffffff808b169263da145263928692611a3a928d9291169030908d908d908d906004016133cc565b60206040518083038185885af1158015611a58573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611a7d9190613292565b9050600080611a92838663ffffffff16611d1e565b91509150611b06600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561079a573d6000803e3d6000fd5b60068054830190556005546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611b7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba09190613292565b6006541115611bdb576040517fca3e0a6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518481526020810183905230917fac791e4e5e1041f59b3427f769f90a58cb39873d7fec687682c09583b425099c910160405180910390a2505060016002555050505050505050565b6003546040517fa96abefe00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063a96abefe906024015b602060405180830381865afa158015611c97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbb91906132f1565b905061119d82826128a1565b6003546040517f759c45dc00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063759c45dc90602401611c7a565b600080612710831115611d5d576040517f4a7f394f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d678484612c57565b938490039492505050565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050611dd381612c6f565b611e39576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610ed5565b50505050565b6000805460015473ffffffffffffffffffffffffffffffffffffffff90911690829060601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001615611ea95760015473ffffffffffffffffffffffffffffffffffffffff16611eab565b305b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015280831660248301527fffffffff00000000000000000000000000000000000000000000000000000000871660448301529192509083169063b700961390606401602060405180830381865afa158015611f4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6f919061332b565b80610ede57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fe4919061330e565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161495945050505050565b816006541015612057576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061206a848463ffffffff16611d1e565b915091506120de600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561079a573d6000803e3d6000fd5b6005546121029073ffffffffffffffffffffffffffffffffffffffff168684611d72565b600680548590039055604080518581526020810183905273ffffffffffffffffffffffffffffffffffffffff87169130917f41ac2430a734173e8b6d39713e824f15d8ab1a9c4af6d07f5fb738822af7523391015b60405180910390a35050505050565b6003546040517fea3fff6800000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9091169063ea3fff6890602401602060405180830381865afa1580156121d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121f8919061332b565b158061229557506003546040517fea3fff6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301529091169063ea3fff6890602401602060405180830381865afa15801561226f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612293919061332b565b155b156122cc576040517f4839ee3e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816006541015612308576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061231b848463ffffffff16611d1e565b9150915061238f600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561079a573d6000803e3d6000fd5b6005546123b39073ffffffffffffffffffffffffffffffffffffffff168684611d72565b6006805485900390556040517f49d55d9d0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff8616906349d55d9d90602401600060405180830381600087803b15801561242457600080fd5b505af1158015612438573d6000803e3d6000fd5b5050604080518781526020810185905273ffffffffffffffffffffffffffffffffffffffff891693503092507f5bf84598f4648ea3b18834387f5f25bd778972e993ae703a68808743824a16189101612157565b6002546001146124c8576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280556003546040517f6ab3905700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015290911690636ab3905790602401602060405180830381865afa15801561253c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612560919061332b565b612596576040517f4a7f394f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff851673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461262c576125e973ffffffffffffffffffffffffffffffffffffffff8616333087612cb6565b61260b73ffffffffffffffffffffffffffffffffffffffff8616876000612b90565b61262c73ffffffffffffffffffffffffffffffffffffffff86168786612b90565b6005546040517fda14526300000000000000000000000000000000000000000000000000000000815260009173ffffffffffffffffffffffffffffffffffffffff808a169263da145263923492612693928c929091169030908c908c908c906004016133cc565b60206040518083038185885af11580156126b1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906126d69190613292565b90506000806126eb838563ffffffff16611d1e565b9150915061275f600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561079a573d6000803e3d6000fd5b60068054830190556005546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa1580156127d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f99190613292565b6006541115612834576040517fca3e0a6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518881526020810185905290810182905273ffffffffffffffffffffffffffffffffffffffff891690309033907fc866822a337be53282ff0bf1ad9c9eb8312fa7b420f9327a2c33ec7e72d50b8f9060600160405180910390a45050600160025550505050505050565b6000806128b4848463ffffffff16611d1e565b9150915061296e33600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015612929573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061294d919061330e565b60055473ffffffffffffffffffffffffffffffffffffffff16919084612cb6565b6005546129939073ffffffffffffffffffffffffffffffffffffffff16333085612cb6565b6006805483019055600554604080518681526020810187905290810183905273ffffffffffffffffffffffffffffffffffffffff90911690309033907fc866822a337be53282ff0bf1ad9c9eb8312fa7b420f9327a2c33ec7e72d50b8f9060600160405180910390a450505050565b6040517f656e74697479000000000000000000000000000000000000000000000000000060208201527f01000000000000000000000000000000000000000000000000000000000000006026820152612a77908390602701604051602081830303815290604052612a7290613420565b612da0565b612a7f612e93565b6003805473ffffffffffffffffffffffffffffffffffffffff8085167fffffffffffffffffffffffff000000000000000000000000000000000000000092831681179093556004805491851691909216178155604080517fc55dae63000000000000000000000000000000000000000000000000000000008152905163c55dae63928281019260209291908290030181865afa158015612b23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b47919061330e565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790555050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050612bf181612c6f565b611e39576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610ed5565b6000612c638284613470565b61271090049392505050565b60003d82612c8157806000803e806000fd5b8060208114612c99578015612caa5760009250612caf565b816000803e60005115159250612caf565b600192505b5050919050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af1915050612d3381612c6f565b612d99576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610ed5565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff8216612ded576040517fded4370e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005473ffffffffffffffffffffffffffffffffffffffff1615612e3d576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff9093167fffffffffffffffffffffffff00000000000000000000000000000000000000009384161790556001805460609290921c91909216179055565b60025415612ecd576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600255565b73ffffffffffffffffffffffffffffffffffffffff8116811461086357600080fd5b60008060408385031215612f0957600080fd5b8235612f1481612ed4565b946020939093013593505050565b60008083601f840112612f3457600080fd5b50813567ffffffffffffffff811115612f4c57600080fd5b602083019150836020828501011115612f6457600080fd5b9250929050565b60008060008060608587031215612f8157600080fd5b8435612f8c81612ed4565b935060208501359250604085013567ffffffffffffffff811115612faf57600080fd5b612fbb87828801612f22565b95989497509550505050565b63ffffffff8116811461086357600080fd5b600080600060608486031215612fee57600080fd5b8335612ff981612ed4565b925060208401359150604084013561301081612fc7565b809150509250925092565b60006020828403121561302d57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561307857600080fd5b833561308381612ed4565b925060208401359150604084013567ffffffffffffffff808211156130a757600080fd5b818601915086601f8301126130bb57600080fd5b8135818111156130cd576130cd613034565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561311357613113613034565b8160405282815289602084870101111561312c57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60005b83811015613169578181015183820152602001613151565b83811115611e395750506000910152565b602081526000825180602084015261319981604085016020870161314e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6000806000806000608086880312156131e357600080fd5b85356131ee81612ed4565b945060208601356131fe81612ed4565b935060408601359250606086013567ffffffffffffffff81111561322157600080fd5b61322d88828901612f22565b969995985093965092949392505050565b6000806040838503121561325157600080fd5b82359150602083013561326381612fc7565b809150509250929050565b60006020828403121561328057600080fd5b813561328b81612ed4565b9392505050565b6000602082840312156132a457600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156132ec576132ec6132ab565b500390565b60006020828403121561330357600080fd5b815161328b81612fc7565b60006020828403121561332057600080fd5b815161328b81612ed4565b60006020828403121561333d57600080fd5b8151801515811461328b57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b838152604060208201526000610ede60408301848661334d565b600082516133c281846020870161314e565b9190910192915050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a0608083015261341460a08301848661334d565b98975050505050505050565b6000815160208301517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808216935060148310156134685780818460140360031b1b83161693505b505050919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156134a8576134a86132ab565b50029056fea2646970667358221220b9d544b962a0fa342717b937eb271f17839ff2caeb4b891db10568b495bffc7664736f6c634300080d0033608060405234801561001057600080fd5b50613455806100206000396000f3fe6080604052600436106101ac5760003560e01c8063a2f48b9f116100ec578063c45e15a01161008a578063d773b04f11610064578063d773b04f146104ff578063df3d067e1461051f578063f14faf6f1461053f578063f7044a5f1461055f57600080fd5b8063c45e15a01461048a578063c55dae63146104b2578063d0ebdbe7146104df57600080fd5b8063ab3c14fe116100c6578063ab3c14fe14610414578063b69ef8a814610427578063bf7e214f1461043d578063c422c5bd1461046a57600080fd5b8063a2f48b9f146103c1578063a8e405b0146103d4578063a9059cbb146103f457600080fd5b8063481c6a75116101595780636a681541116101335780636a681541146103385780637b10399914610358578063838453fd1461038557806394b7c5b5146103a157600080fd5b8063481c6a75146102a6578063485cc955146102f857806349d55d9d1461031857600080fd5b80632031ee951161018a5780632031ee951461020857806324c67842146102585780632c482f191461028657600080fd5b80630b2ec672146101b15780630cb6ee6f146101c8578063117de2fd146101e8575b600080fd5b3480156101bd57600080fd5b506101c661057f565b005b3480156101d457600080fd5b506101c66101e3366004612e3a565b61081a565b3480156101f457600080fd5b506101c6610203366004612e3a565b610924565b34801561021457600080fd5b506001546102229060601b81565b6040517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090911681526020015b60405180910390f35b34801561026457600080fd5b50610278610273366004612eaf565b6109df565b60405190815260200161024f565b34801561029257600080fd5b506101c66102a1366004612f1d565b610c38565b3480156102b257600080fd5b506004546102d39073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161024f565b34801561030457600080fd5b506101c6610313366004612f5f565b610ca7565b34801561032457600080fd5b506101c6610333366004612f98565b610cb5565b61034b610346366004612fe0565b610d88565b60405161024f91906130f7565b34801561036457600080fd5b506003546102d39073ffffffffffffffffffffffffffffffffffffffff1681565b34801561039157600080fd5b506040516002815260200161024f565b3480156103ad57600080fd5b506101c66103bc366004612e3a565b610ea9565b6101c66103cf366004613148565b610fdc565b3480156103e057600080fd5b506101c66103ef3660046131bb565b611088565b34801561040057600080fd5b506101c661040f366004612e3a565b6110f6565b6101c6610422366004613148565b6111df565b34801561043357600080fd5b5061027860065481565b34801561044957600080fd5b506000546102d39073ffffffffffffffffffffffffffffffffffffffff1681565b34801561047657600080fd5b50610278610485366004612eaf565b611236565b34801561049657600080fd5b506102d373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b3480156104be57600080fd5b506005546102d39073ffffffffffffffffffffffffffffffffffffffff1681565b3480156104eb57600080fd5b506101c66104fa3660046131e0565b6114b5565b34801561050b57600080fd5b506101c661051a366004612f1d565b6115cd565b34801561052b57600080fd5b506101c661053a366004613148565b61163c565b34801561054b57600080fd5b506101c661055a366004612f98565b611b6b565b34801561056b57600080fd5b506101c661057a366004612f98565b611c0b565b6005546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa1580156105ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106129190613204565b905060065481106107dc5760006006548261062d919061324c565b6003546040517f759c45dc00000000000000000000000000000000000000000000000000000000815230600482015291925060009173ffffffffffffffffffffffffffffffffffffffff9091169063759c45dc90602401602060405180830381865afa1580156106a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c59190613263565b90506000806106da848463ffffffff16611c62565b91509150610792600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561074e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107729190613280565b60055473ffffffffffffffffffffffffffffffffffffffff169083611cb6565b6006805483019055604080518581526020810183905230917fac791e4e5e1041f59b3427f769f90a58cb39873d7fec687682c09583b425099c910160405180910390a25050505050565b600681905560405181815230907f1cd384df7b960f3950da0f15d6aa8d62512c9f2209adde5ddeba0f4ebd6b58829060200160405180910390a25b50565b610848336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b61087e576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517fb3d5804500000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063b3d58045906024015b602060405180830381865afa1580156108ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109129190613263565b905061091f838383611f5f565b505050565b610952336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b610988576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517fb4ea2a9800000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063b4ea2a98906024016108d1565b60045460009073ffffffffffffffffffffffffffffffffffffffff163314801590610a355750610a33336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b155b15610a6c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f55c6f68300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152909116906355c6f68390602401602060405180830381865afa158015610adc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b00919061329d565b610b36576040517fbc43451a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fe77c646d00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff87169063e77c646d90610b8f90889088908890600401613308565b6020604051808303816000875af1158015610bae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd29190613204565b6006805482019055604080518781526020810183905291925073ffffffffffffffffffffffffffffffffffffffff8816917fee0fb9e6d573076d9a07b1d91e0a988c2f988d1d690ff129ff3096408d0afbc891015b60405180910390a295945050505050565b610c66336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b610c9c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61091f838383611f5f565b610cb182826120aa565b5050565b6003546040517fea3fff6800000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9091169063ea3fff6890602401602060405180830381865afa158015610d23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d47919061329d565b610d7d576040517f4839ee3e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680549091019055565b6060610db8336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b610dee576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff168585604051610e179190613322565b60006040518083038185875af1925050503d8060008114610e54576040519150601f19603f3d011682016040523d82523d6000602084013e610e59565b606091505b509150915081610ea057806040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152600401610e9791906130f7565b60405180910390fd5b95945050505050565b60045473ffffffffffffffffffffffffffffffffffffffff163314801590610efc5750610efa336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b155b15610f33576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f864ec3f000000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8481166024830152600092169063864ec3f0906044015b602060405180830381865afa158015610fab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fcf9190613263565b905061091f838383612238565b6003546040517fa96abefe00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063a96abefe906024015b602060405180830381865afa15801561104c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110709190613263565b905061108086868686868661255e565b505050505050565b6110b6336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b6110ec576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cb18282612973565b60045473ffffffffffffffffffffffffffffffffffffffff1633148015906111495750611147336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b155b15611180576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517fda93546e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8481166024830152600092169063da93546e90604401610f8e565b6003546040517f759c45dc00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063759c45dc9060240161102f565b60045460009073ffffffffffffffffffffffffffffffffffffffff16331480159061128c575061128a336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b155b156112c3576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f55c6f68300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152909116906355c6f68390602401602060405180830381865afa158015611333573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611357919061329d565b61138d576040517fbc43451a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836006600082825461139f919061324c565b90915550506005546113c89073ffffffffffffffffffffffffffffffffffffffff168686612ad4565b6040517f5d30351900000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff871690635d3035199061142190889088908890600401613308565b6020604051808303816000875af1158015611440573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114649190613204565b604080518781526020810183905291925073ffffffffffffffffffffffffffffffffffffffff8816917f17f635bacbd012dd587e39c86059a06f779ee2111f6c5541b40c2941b09cf7c49101610c27565b60045473ffffffffffffffffffffffffffffffffffffffff1633148015906115085750611506336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b155b1561153f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f20b26c6c7ff7d40d049fc212237046ba5f7cde4ba6d317501329880970cbb01590600090a3600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6115fb336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b611631576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61091f838383612238565b600254600114611678576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805560045473ffffffffffffffffffffffffffffffffffffffff1633148015906116cf57506116cd336000357fffffffff0000000000000000000000000000000000000000000000000000000016611d83565b155b15611706576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f6ab3905700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015290911690636ab3905790602401602060405180830381865afa158015611776573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179a919061329d565b6117d0576040517f4a7f394f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f759c45dc00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063759c45dc90602401602060405180830381865afa15801561183f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118639190613263565b905073ffffffffffffffffffffffffffffffffffffffff851673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146118d9576118b873ffffffffffffffffffffffffffffffffffffffff8616876000612ad4565b6118d973ffffffffffffffffffffffffffffffffffffffff86168786612ad4565b600073ffffffffffffffffffffffffffffffffffffffff861673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611913576000611915565b845b6005546040517fda14526300000000000000000000000000000000000000000000000000000000815291925060009173ffffffffffffffffffffffffffffffffffffffff808b169263da14526392869261197e928d9291169030908d908d908d9060040161333e565b60206040518083038185885af115801561199c573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906119c19190613204565b90506000806119d6838663ffffffff16611c62565b91509150611a4a600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561074e573d6000803e3d6000fd5b60068054830190556005546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611ac0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae49190613204565b6006541115611b1f576040517fca3e0a6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518481526020810183905230917fac791e4e5e1041f59b3427f769f90a58cb39873d7fec687682c09583b425099c910160405180910390a2505060016002555050505050505050565b6003546040517fa96abefe00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063a96abefe906024015b602060405180830381865afa158015611bdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bff9190613263565b9050610cb18282612973565b6003546040517f759c45dc00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff169063759c45dc90602401611bbe565b600080612710831115611ca1576040517f4a7f394f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611cab8484612b9b565b938490039492505050565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050611d1781612bb3565b611d7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610e97565b50505050565b6000805460015473ffffffffffffffffffffffffffffffffffffffff90911690829060601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001615611ded5760015473ffffffffffffffffffffffffffffffffffffffff16611def565b305b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015280831660248301527fffffffff00000000000000000000000000000000000000000000000000000000871660448301529192509083169063b700961390606401602060405180830381865afa158015611e8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb3919061329d565b80610ea057508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f289190613280565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161495945050505050565b816006541015611f9b576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080611fae848463ffffffff16611c62565b91509150612022600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561074e573d6000803e3d6000fd5b6005546120469073ffffffffffffffffffffffffffffffffffffffff168684611cb6565b600680548590039055604080518581526020810183905273ffffffffffffffffffffffffffffffffffffffff87169130917f41ac2430a734173e8b6d39713e824f15d8ab1a9c4af6d07f5fb738822af7523391015b60405180910390a35050505050565b6040517f656e74697479000000000000000000000000000000000000000000000000000060208201527f0200000000000000000000000000000000000000000000000000000000000000602682015261211f90839060270160405160208183030381529060405261211a90613392565b612bfa565b612127612ced565b6003805473ffffffffffffffffffffffffffffffffffffffff8085167fffffffffffffffffffffffff000000000000000000000000000000000000000092831681179093556004805491851691909216178155604080517fc55dae63000000000000000000000000000000000000000000000000000000008152905163c55dae63928281019260209291908290030181865afa1580156121cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ef9190613280565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790555050565b6003546040517fea3fff6800000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9091169063ea3fff6890602401602060405180830381865afa1580156122a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ca919061329d565b158061236757506003546040517fea3fff6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301529091169063ea3fff6890602401602060405180830381865afa158015612341573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612365919061329d565b155b1561239e576040517f4839ee3e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160065410156123da576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806123ed848463ffffffff16611c62565b91509150612461600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561074e573d6000803e3d6000fd5b6005546124859073ffffffffffffffffffffffffffffffffffffffff168684611cb6565b6006805485900390556040517f49d55d9d0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff8616906349d55d9d90602401600060405180830381600087803b1580156124f657600080fd5b505af115801561250a573d6000803e3d6000fd5b5050604080518781526020810185905273ffffffffffffffffffffffffffffffffffffffff891693503092507f5bf84598f4648ea3b18834387f5f25bd778972e993ae703a68808743824a1618910161209b565b60025460011461259a576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280556003546040517f6ab3905700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015290911690636ab3905790602401602060405180830381865afa15801561260e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612632919061329d565b612668576040517f4a7f394f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff851673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146126fe576126bb73ffffffffffffffffffffffffffffffffffffffff8616333087612d2e565b6126dd73ffffffffffffffffffffffffffffffffffffffff8616876000612ad4565b6126fe73ffffffffffffffffffffffffffffffffffffffff86168786612ad4565b6005546040517fda14526300000000000000000000000000000000000000000000000000000000815260009173ffffffffffffffffffffffffffffffffffffffff808a169263da145263923492612765928c929091169030908c908c908c9060040161333e565b60206040518083038185885af1158015612783573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906127a89190613204565b90506000806127bd838563ffffffff16611c62565b91509150612831600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561074e573d6000803e3d6000fd5b60068054830190556005546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa1580156128a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128cb9190613204565b6006541115612906576040517fca3e0a6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518881526020810185905290810182905273ffffffffffffffffffffffffffffffffffffffff891690309033907fc866822a337be53282ff0bf1ad9c9eb8312fa7b420f9327a2c33ec7e72d50b8f9060600160405180910390a45050600160025550505050505050565b600080612986848463ffffffff16611c62565b91509150612a4033600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156129fb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a1f9190613280565b60055473ffffffffffffffffffffffffffffffffffffffff16919084612d2e565b600554612a659073ffffffffffffffffffffffffffffffffffffffff16333085612d2e565b6006805483019055600554604080518681526020810187905290810183905273ffffffffffffffffffffffffffffffffffffffff90911690309033907fc866822a337be53282ff0bf1ad9c9eb8312fa7b420f9327a2c33ec7e72d50b8f9060600160405180910390a450505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050612b3581612bb3565b611d7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610e97565b6000612ba782846133e2565b61271090049392505050565b60003d82612bc557806000803e806000fd5b8060208114612bdd578015612bee5760009250612bf3565b816000803e60005115159250612bf3565b600192505b5050919050565b73ffffffffffffffffffffffffffffffffffffffff8216612c47576040517fded4370e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005473ffffffffffffffffffffffffffffffffffffffff1615612c97576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff9093167fffffffffffffffffffffffff00000000000000000000000000000000000000009384161790556001805460609290921c91909216179055565b60025415612d27576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600255565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af1915050612dab81612bb3565b612e11576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610e97565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461081757600080fd5b60008060408385031215612e4d57600080fd5b8235612e5881612e18565b946020939093013593505050565b60008083601f840112612e7857600080fd5b50813567ffffffffffffffff811115612e9057600080fd5b602083019150836020828501011115612ea857600080fd5b9250929050565b60008060008060608587031215612ec557600080fd5b8435612ed081612e18565b935060208501359250604085013567ffffffffffffffff811115612ef357600080fd5b612eff87828801612e66565b95989497509550505050565b63ffffffff8116811461081757600080fd5b600080600060608486031215612f3257600080fd5b8335612f3d81612e18565b9250602084013591506040840135612f5481612f0b565b809150509250925092565b60008060408385031215612f7257600080fd5b8235612f7d81612e18565b91506020830135612f8d81612e18565b809150509250929050565b600060208284031215612faa57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600060608486031215612ff557600080fd5b833561300081612e18565b925060208401359150604084013567ffffffffffffffff8082111561302457600080fd5b818601915086601f83011261303857600080fd5b81358181111561304a5761304a612fb1565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561309057613090612fb1565b816040528281528960208487010111156130a957600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60005b838110156130e65781810151838201526020016130ce565b83811115611d7d5750506000910152565b60208152600082518060208401526131168160408501602087016130cb565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60008060008060006080868803121561316057600080fd5b853561316b81612e18565b9450602086013561317b81612e18565b935060408601359250606086013567ffffffffffffffff81111561319e57600080fd5b6131aa88828901612e66565b969995985093965092949392505050565b600080604083850312156131ce57600080fd5b823591506020830135612f8d81612f0b565b6000602082840312156131f257600080fd5b81356131fd81612e18565b9392505050565b60006020828403121561321657600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561325e5761325e61321d565b500390565b60006020828403121561327557600080fd5b81516131fd81612f0b565b60006020828403121561329257600080fd5b81516131fd81612e18565b6000602082840312156132af57600080fd5b815180151581146131fd57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b838152604060208201526000610ea06040830184866132bf565b600082516133348184602087016130cb565b9190910192915050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a0608083015261338660a0830184866132bf565b98975050505050505050565b6000815160208301517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808216935060148310156133da5780818460140360031b1b83161693505b505050919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561341a5761341a61321d565b50029056fea2646970667358221220d1da1dc779ee329a782304972ef97c2e5db999f8ab42edbadd80bd2f56fb995a64736f6c634300080d003300000000000000000000000094106ca9c7e567109a1d39413052887d1f412183
Deployed Bytecode
0x6080604052600436106100d25760003560e01c8063a60fe71d1161007f578063c45e15a011610059578063c45e15a014610242578063c55dae631461026a578063db9e30cc1461029e578063e6be99fc146102be57600080fd5b8063a60fe71d146101fc578063ab438d6b1461021c578063bf37a6701461022f57600080fd5b80636fee7ed6116100b05780636fee7ed6146101745780637b103999146101a85780639fb8578d146101dc57600080fd5b8063099a81e3146100d75780630b307674146101205780632a4004b814610154575b600080fd5b3480156100e357600080fd5b506100f76100f2366004610f1f565b6102de565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561012c57600080fd5b506100f77f00000000000000000000000054bb5841875826ce524d370d722c7dc4c7613da981565b34801561016057600080fd5b506100f761016f366004610f1f565b6103d4565b34801561018057600080fd5b506100f77f000000000000000000000000dc8518b508b8196d8cc71058176bc4cbcf74e9d381565b3480156101b457600080fd5b506100f77f00000000000000000000000094106ca9c7e567109a1d39413052887d1f41218381565b3480156101e857600080fd5b506100f76101f7366004610f4b565b610671565b34801561020857600080fd5b506100f7610217366004610f4b565b61070d565b6100f761022a366004610fad565b6109c5565b6100f761023d36600461103c565b6109ec565b34801561024e57600080fd5b506100f773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b34801561027657600080fd5b506100f77f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b3480156102aa57600080fd5b506100f76102b93660046110b8565b610a11565b3480156102ca57600080fd5b506100f76102d93660046110da565b610a28565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020820152603481018290526000906103cb907f00000000000000000000000054bb5841875826ce524d370d722c7dc4c7613da99060540160405160208183030381529060405280519060200120306040517f602d8060093d393df3363d3d373d3d3d363d73000000000000000000000000008152606093841b60138201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006027820152921b6037830152604b8201526036808220606b830152605591012090565b90505b92915050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b16602082015260348101829052600090610451907f00000000000000000000000054bb5841875826ce524d370d722c7dc4c7613da99060540160405160208183030381529060405280519060200120610a47565b6040517f485cc95500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000094106ca9c7e567109a1d39413052887d1f4121838116600483015285811660248301529192509082169063485cc95590604401600060405180830381600087803b1580156104e657600080fd5b505af11580156104fa573d6000803e3d6000fd5b50506040517f0160a6e200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301527f00000000000000000000000094106ca9c7e567109a1d39413052887d1f412183169250630160a6e29150602401600060405180830381600087803b15801561058757600080fd5b505af115801561059b573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1663838453fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610601573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610625919061110f565b60ff168273ffffffffffffffffffffffffffffffffffffffff167f1e3e29fb0c05e0c478577b9b3e207cbfe2952b1f9b239ed5d2535d4b24b6c57760405160405180910390a492915050565b60006103ce7f000000000000000000000000dc8518b508b8196d8cc71058176bc4cbcf74e9d383306040517f602d8060093d393df3363d3d373d3d3d363d73000000000000000000000000008152606093841b60138201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006027820152921b6037830152604b8201526036808220606b830152605591012090565b60006107397f000000000000000000000000dc8518b508b8196d8cc71058176bc4cbcf74e9d383610a47565b6040517fbe13f47c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000094106ca9c7e567109a1d39413052887d1f41218381166004830152602482018590529192509082169063be13f47c90604401600060405180830381600087803b1580156107cd57600080fd5b505af11580156107e1573d6000803e3d6000fd5b50506040517f0160a6e200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301527f00000000000000000000000094106ca9c7e567109a1d39413052887d1f412183169250630160a6e29150602401600060405180830381600087803b15801561086e57600080fd5b505af1158015610882573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff1663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f59190611132565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1663838453fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610956573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061097a919061110f565b60ff168273ffffffffffffffffffffffffffffffffffffffff167f1e3e29fb0c05e0c478577b9b3e207cbfe2952b1f9b239ed5d2535d4b24b6c57760405160405180910390a4919050565b60006109d188886103d4565b90506109e1818787878787610b2a565b979650505050505050565b60006109f78761070d565b9050610a07818787878787610b2a565b9695505050505050565b6000610a1c8361070d565b90506103ce8183610c56565b6000610a3484846103d4565b9050610a408183610c56565b9392505050565b60006040517f602d8060093d393df3363d3d373d3d3d363d730000000000000000000000000081528360601b60138201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006027820152826036826000f591505073ffffffffffffffffffffffffffffffffffffffff81166103ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610bc057610b7d73ffffffffffffffffffffffffffffffffffffffff8516333086610cfc565b610b9f73ffffffffffffffffffffffffffffffffffffffff8516876000610de6565b610bc073ffffffffffffffffffffffffffffffffffffffff85168785610de6565b6040517fa2f48b9f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87169063a2f48b9f903490610c1c908990899089908990899060040161114f565b6000604051808303818588803b158015610c3557600080fd5b505af1158015610c49573d6000803e3d6000fd5b5050505050505050505050565b610c9873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816338484610cfc565b8173ffffffffffffffffffffffffffffffffffffffff16630b2ec6726040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ce057600080fd5b505af1158015610cf4573d6000803e3d6000fd5b505050505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af1915050610d7981610eb3565b610ddf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610b21565b5050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050610e4781610eb3565b610ead576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610b21565b50505050565b60003d82610ec557806000803e806000fd5b8060208114610edd578015610eee5760009250610ef3565b816000803e60005115159250610ef3565b600192505b5050919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610f1c57600080fd5b50565b60008060408385031215610f3257600080fd5b8235610f3d81610efa565b946020939093013593505050565b600060208284031215610f5d57600080fd5b5035919050565b60008083601f840112610f7657600080fd5b50813567ffffffffffffffff811115610f8e57600080fd5b602083019150836020828501011115610fa657600080fd5b9250929050565b600080600080600080600060c0888a031215610fc857600080fd5b8735610fd381610efa565b9650602088013595506040880135610fea81610efa565b94506060880135610ffa81610efa565b93506080880135925060a088013567ffffffffffffffff81111561101d57600080fd5b6110298a828b01610f64565b989b979a50959850939692959293505050565b60008060008060008060a0878903121561105557600080fd5b86359550602087013561106781610efa565b9450604087013561107781610efa565b935060608701359250608087013567ffffffffffffffff81111561109a57600080fd5b6110a689828a01610f64565b979a9699509497509295939492505050565b600080604083850312156110cb57600080fd5b50508035926020909101359150565b6000806000606084860312156110ef57600080fd5b83356110fa81610efa565b95602085013595506040909401359392505050565b60006020828403121561112157600080fd5b815160ff81168114610a4057600080fd5b60006020828403121561114457600080fd5b8151610a4081610efa565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501168301019050969550505050505056fea2646970667358221220aa6ac4e89d0355a64ce4d7bfd2a40f6adf7d3c1aa65f8e23bd35a52867ef002164736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000094106ca9c7e567109a1d39413052887d1f412183
-----Decoded View---------------
Arg [0] : _registry (address): 0x94106cA9c7E567109A1D39413052887d1F412183
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000094106ca9c7e567109a1d39413052887d1f412183
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.