ERC-20
Overview
Max Total Supply
43,158.655805135762796676 GTC-PS
Holders
2
Total Transfers
-
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xdC4E3E75...D6F05570F The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
SingleTokenPortfolio
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 {Registry} from "../Registry.sol"; import {Entity} from "../Entity.sol"; import {Portfolio} from "../Portfolio.sol"; import {ISwapWrapper} from "../interfaces/ISwapWrapper.sol"; import {Auth, Authority} from "../lib/auth/Auth.sol"; import {Math} from "../lib/Math.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {ReentrancyGuard} from "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol"; contract SingleTokenPortfolio is Portfolio, ReentrancyGuard { using SafeTransferLib for ERC20; using Math for uint256; ERC20 public immutable baseToken; /** * @param _registry Endaoment registry. * @param _asset Underlying ERC20 asset token for portfolio. * @param _shareTokenName Name of ERC20 portfolio share token. * @param _shareTokenSymbol Symbol of ERC20 portfolio share token. * @param _cap Amount of baseToken that this portfolio's asset balance should not exceed. * @param _redemptionFee Percentage fee as ZOC that should go to treasury on redemption. (100 = 1%). */ constructor( Registry _registry, address _asset, string memory _shareTokenName, string memory _shareTokenSymbol, uint256 _cap, uint256 _depositFee, uint256 _redemptionFee ) Portfolio(_registry, _asset, _shareTokenName, _shareTokenSymbol, _cap, _depositFee, _redemptionFee) { baseToken = _registry.baseToken(); } /** * @notice Takes some amount of assets from this portfolio as assets under management fee. * @dev Importantly, updates exchange rate to change the shares/assets calculations. * @param _amountAssets Amount of assets to take. */ function takeFees(uint256 _amountAssets) external override requiresAuth { ERC20(asset).safeTransfer(registry.treasury(), _amountAssets); emit FeesTaken(_amountAssets); } function totalAssets() public view override returns (uint256) { return ERC20(asset).balanceOf(address(this)); } /** * @inheritdoc Portfolio * @dev Rounding down favors the portfolio, so the user gets slightly less and the portfolio gets slightly more, that way it prevents * a situation where the user is owed x but the vault only has x - epsilon, where epsilon is some tiny number due to rounding error. */ function convertToShares(uint256 _assets) public view override returns (uint256) { uint256 _supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return _supply == 0 ? _assets : _assets.mulDivDown(_supply, totalAssets()); } /** * @notice This method is needed on deposit because we already possess the assets that we want to convert. * @dev Rounding down favors the portfolio, so the user gets slightly less and the portfolio gets slightly more, that way it prevents * a situation where the user is owed x but the vault only has x - epsilon, where epsilon is some tiny number due to rounding error. */ function _convertToSharesLessAssets(uint256 _assets) private view returns (uint256) { uint256 _supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return _supply == 0 ? _assets : _assets.mulDivDown(totalSupply, totalAssets() - _assets); } /** * @inheritdoc Portfolio * @dev Rounding down in both of these favors the portfolio, so the user gets slightly less and the portfolio gets slightly more, * that way it prevents a situation where the user is owed x but the vault only has x - epsilon, where epsilon is some tiny number * due to rounding error. */ function convertToAssets(uint256 _shares) public view override returns (uint256) { uint256 _supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return _supply == 0 ? _shares : _shares.mulDivDown(totalAssets(), _supply); } /** * @dev Rounding down in both of these favors the portfolio, so the user gets slightly less and the portfolio gets slightly more, * that way it prevents a situation where the user is owed x but the vault only has x - epsilon, where epsilon is some tiny number * due to rounding error. */ function convertToAssetsShutdown(uint256 _shares) public view returns (uint256) { uint256 _supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return _supply == 0 ? _shares : _shares.mulDivDown(baseToken.balanceOf(address(this)), _supply); } /** * @inheritdoc Portfolio * @dev We convert `baseToken` to `asset` via a swap wrapper. * `_data` should be a packed swap wrapper address concatenated with the bytes payload your swap wrapper expects. * i.e. `bytes.concat(abi.encodePacked(address swapWrapper), SWAP_WRAPPER_BYTES)`. * To determine if this deposit exceeds the cap, we get the asset/baseToken exchange rate and multiply it by `totalAssets`. */ function deposit(uint256 _amountBaseToken, bytes calldata _data) external override nonReentrant returns (uint256) { if (didShutdown) revert DepositAfterShutdown(); if (!_isEntity(Entity(payable(msg.sender)))) revert NotEntity(); ISwapWrapper _swapWrapper = ISwapWrapper(address(bytes20(_data[:20]))); if (!registry.isSwapperSupported(_swapWrapper)) revert InvalidSwapper(); baseToken.safeTransferFrom(msg.sender, address(this), _amountBaseToken); (uint256 _amountSwap, uint256 _amountFee) = _calculateFee(_amountBaseToken, depositFee); baseToken.safeApprove(address(_swapWrapper), _amountBaseToken); uint256 _assets = _swapWrapper.swap(address(baseToken), asset, address(this), _amountSwap, _data[20:]); // Convert totalAssets to baseToken unit to measure against cap. if (totalAssets() * _amountSwap / _assets > cap) revert ExceedsCap(); uint256 _shares = _convertToSharesLessAssets(_assets); if (_shares == 0) revert RoundsToZero(); _mint(msg.sender, _shares); baseToken.safeTransfer(registry.treasury(), _amountFee); emit Deposit(msg.sender, msg.sender, _assets, _shares, _amountBaseToken, _amountFee); return _shares; } /** * @inheritdoc Portfolio * @dev After converting `shares` to `assets`, we convert `assets` to `baseToken` via a swap wrapper. * `_data` should be a packed swap wrapper address concatenated with the bytes payload your swap wrapper expects. * i.e. `bytes.concat(abi.encodePacked(address swapWrapper), SWAP_WRAPPER_BYTES)`. */ function redeem(uint256 _amountShares, bytes calldata _data) external override nonReentrant returns (uint256) { if (didShutdown) return _redeemShutdown(_amountShares); ISwapWrapper _swapWrapper = ISwapWrapper(address(bytes20(_data[:20]))); if (!registry.isSwapperSupported(_swapWrapper)) revert InvalidSwapper(); uint256 _assetsOut = convertToAssets(_amountShares); _burn(msg.sender, _amountShares); ERC20(asset).safeApprove(address(_swapWrapper), 0); ERC20(asset).safeApprove(address(_swapWrapper), _assetsOut); uint256 _baseTokenOut = _swapWrapper.swap(asset, address(baseToken), address(this), _assetsOut, _data[20:]); (uint256 _netAmount, uint256 _fee) = _calculateFee(_baseTokenOut, redemptionFee); baseToken.safeTransfer(registry.treasury(), _fee); baseToken.safeTransfer(msg.sender, _netAmount); emit Redeem(msg.sender, msg.sender, _assetsOut, _amountShares, _netAmount, _fee); return _netAmount; } /** * @inheritdoc Portfolio */ function shutdown(bytes calldata _data) external override requiresAuth returns (uint256) { if (didShutdown) revert DidShutdown(); ISwapWrapper _swapWrapper = ISwapWrapper(address(bytes20(_data[:20]))); if (!registry.isSwapperSupported(_swapWrapper)) revert InvalidSwapper(); didShutdown = true; uint256 _assetsOut = totalAssets(); ERC20(asset).safeApprove(address(_swapWrapper), 0); ERC20(asset).safeApprove(address(_swapWrapper), _assetsOut); uint256 _baseTokenOut = _swapWrapper.swap(asset, address(baseToken), address(this), _assetsOut, _data[20:]); emit Shutdown(_assetsOut, _baseTokenOut); return _baseTokenOut; } /** * @notice Handles redemption after shutdown, exchanging shares for baseToken. * @param _amountShares Shares being redeemed. * @return Amount of baseToken received. */ function _redeemShutdown(uint256 _amountShares) private returns (uint256) { uint256 _baseTokenOut = convertToAssetsShutdown(_amountShares); _burn(msg.sender, _amountShares); (uint256 _netAmount, uint256 _fee) = _calculateFee(_baseTokenOut, redemptionFee); baseToken.safeTransfer(registry.treasury(), _fee); baseToken.safeTransfer(msg.sender, _netAmount); emit Redeem(msg.sender, msg.sender, _baseTokenOut, _amountShares, _netAmount, _fee); return _netAmount; } }
//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 "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 Emitted when ether is received. event EntityEthReceived(address indexed sender, uint256 amount); /** * @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) external virtual requiresAuth { _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 payable virtual { 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 payable virtual { 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 ) internal virtual nonReentrant { 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. * @dev Renamed from `transfer` to distinguish from ERC20 transfer in 3rd party tools. */ function transferToEntity(Entity _to, uint256 _amount) external virtual requiresManager { 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 transferToEntityWithOverrides(Entity _to, uint256 _amount) external virtual requiresManager { 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 transferToEntityWithAdminOverrides(Entity _to, uint256 _amount, uint32 _feeOverride) external virtual requiresAuth { _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(payable(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 ) external virtual nonReentrant 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 payable virtual 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 pure virtual 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; } } receive() external payable virtual { emit EntityEthReceived(msg.sender, msg.value); } }
//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) external virtual 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) external virtual 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) public view virtual 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) public view virtual 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) external virtual 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: 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: 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; 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; /// @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: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
//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: 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 view virtual 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/", "eth-gas-reporter/=node_modules/eth-gas-reporter/", "forge-std/=lib/forge-std/src/", "hardhat/=node_modules/hardhat/", "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
[{"inputs":[{"internalType":"contract Registry","name":"_registry","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"string","name":"_shareTokenName","type":"string"},{"internalType":"string","name":"_shareTokenSymbol","type":"string"},{"internalType":"uint256","name":"_cap","type":"uint256"},{"internalType":"uint256","name":"_depositFee","type":"uint256"},{"internalType":"uint256","name":"_redemptionFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"name":"CallFailed","type":"error"},{"inputs":[],"name":"DepositAfterShutdown","type":"error"},{"inputs":[],"name":"DidShutdown","type":"error"},{"inputs":[],"name":"ExceedsCap","type":"error"},{"inputs":[],"name":"InvalidAuthority","type":"error"},{"inputs":[],"name":"InvalidSwapper","type":"error"},{"inputs":[],"name":"NotEntity","type":"error"},{"inputs":[],"name":"PercentageOver100","type":"error"},{"inputs":[],"name":"RoundsToZero","type":"error"},{"inputs":[],"name":"TransferDisallowed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cap","type":"uint256"}],"name":"CapSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"DepositFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesTaken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"RedemptionFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseTokenOut","type":"uint256"}],"name":"Shutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract RolesAuthority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"callAsPortfolio","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"convertToAssetsShutdown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountBaseToken","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"didShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountShares","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redemptionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract Registry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pct","type":"uint256"}],"name":"setDepositFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pct","type":"uint256"}],"name":"setRedemptionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"shutdown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"specialTarget","outputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountAssets","type":"uint256"}],"name":"takeFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]
Contract Creation Code
6101406040523480156200001257600080fd5b50604051620034083803806200340883398101604081905262000035916200048a565b868686868686868484876001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200007d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000a391906200053c565b8251620000b8906000906020860190620002fe565b508151620000ce906001906020850190620002fe565b5060ff81166080524660a052620000e4620001df565b60c0525050506001600160a01b03871660e0526127108111156200011b576040516378418ce360e11b815260040160405180910390fd5b6009829055600a81905560088390556001600160a01b03861661010052620001508768706f7274666f6c696f60b81b6200027b565b505050505050506001600c81905550866001600160a01b031663c55dae636040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200019e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001c4919062000568565b6001600160a01b031661012052506200066795505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620002139190620005c4565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b038216620002a357604051636f6a1b8760e11b815260040160405180910390fd5b6006546001600160a01b031615620002cd5760405162dc149f60e41b815260040160405180910390fd5b600680546001600160a01b039093166001600160a01b03199384161790556007805460609290921c91909216179055565b8280546200030c9062000588565b90600052602060002090601f0160209004810192826200033057600085556200037b565b82601f106200034b57805160ff19168380011785556200037b565b828001600101855582156200037b579182015b828111156200037b5782518255916020019190600101906200035e565b50620003899291506200038d565b5090565b5b808211156200038957600081556001016200038e565b6001600160a01b0381168114620003ba57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620003e557600080fd5b81516001600160401b0380821115620004025762000402620003bd565b604051601f8301601f19908116603f011681019082821181831017156200042d576200042d620003bd565b816040528381526020925086838588010111156200044a57600080fd5b600091505b838210156200046e57858201830151818301840152908201906200044f565b83821115620004805760008385830101525b9695505050505050565b600080600080600080600060e0888a031215620004a657600080fd5b8751620004b381620003a4565b6020890151909750620004c681620003a4565b60408901519096506001600160401b0380821115620004e457600080fd5b620004f28b838c01620003d3565b965060608a01519150808211156200050957600080fd5b50620005188a828b01620003d3565b9450506080880151925060a0880151915060c0880151905092959891949750929550565b6000602082840312156200054f57600080fd5b815160ff811681146200056157600080fd5b9392505050565b6000602082840312156200057b57600080fd5b81516200056181620003a4565b600181811c908216806200059d57607f821691505b602082108103620005be57634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c915080831680620005e157607f831692505b602080841082036200060157634e487b7160e01b86526022600452602486fd5b8180156200061857600181146200062a5762000659565b60ff1986168952848901965062000659565b60008a81526020902060005b86811015620006515781548b82015290850190830162000636565b505084890196505b509498975050505050505050565b60805160a05160c05160e0516101005161012051612c9462000774600039600081816105fc01528181610b51015281816110060152818161105c015281816110a3015281816112b10152818161183d0152818161198d015281816119ce01528181611a90015261239b0152600081816104250152818161073a01528181610aa701528181610ae901528181610b30015281816110c40152818161154601528181611793015281816117d5015261181c015260008181610539015281816109b501528181610f4b0152818161120d015281816114a2015281816116c1015281816118e901528181611fd2015261231801526000610c5c01526000610c2c015260006103b40152612c946000f3fe6080604052600436106102195760003560e01c80635d3035191161011d578063bf7e214f116100b0578063d505accf1161007f578063e77c646d11610064578063e77c646d146106b6578063f029748d146106d6578063fef78497146106f657600080fd5b8063d505accf1461065e578063dd62ed3e1461067e57600080fd5b8063bf7e214f146105bd578063c55dae63146105ea578063c6e6f5921461061e578063cbb943591461063e57600080fd5b80637dbc1df0116100ec5780637dbc1df01461055b5780637ecebe001461057b57806395d89b41146105a8578063a9059cbb1461028857600080fd5b80635d303519146104c457806367a52793146104e457806370a08231146104fa5780637b1039991461052757600080fd5b806323b872dd116101b05780633644e5151161017f578063458f581511610164578063458f58151461046c57806347786d3714610482578063490ae210146104a457600080fd5b80633644e515146103fe57806338d52e0f1461041357600080fd5b806323b872dd1461035357806330adf81f1461036e578063313ce567146103a2578063355274ea146103e857600080fd5b8063106f276f116101ec578063106f276f146102b857806318160ddd146102d85780631e7ee237146102ee5780632031ee951461030857600080fd5b806301e1d1141461021e57806306fdde031461024657806307a2d13a14610268578063095ea7b314610288575b600080fd5b34801561022a57600080fd5b50610233610709565b6040519081526020015b60405180910390f35b34801561025257600080fd5b5061025b6107bf565b60405161023d9190612579565b34801561027457600080fd5b5061023361028336600461258c565b61084d565b34801561029457600080fd5b506102a86102a33660046125ca565b61087a565b604051901515815260200161023d565b3480156102c457600080fd5b506102336102d336600461263f565b6108ae565b3480156102e457600080fd5b5061023360025481565b3480156102fa57600080fd5b50600b546102a89060ff1681565b34801561031457600080fd5b506007546103229060601b81565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909116815260200161023d565b34801561035f57600080fd5b506102a86102a3366004612681565b34801561037a57600080fd5b506102337f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b3480156103ae57600080fd5b506103d67f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161023d565b3480156103f457600080fd5b5061023360085481565b34801561040a57600080fd5b50610233610c28565b34801561041f57600080fd5b506104477f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161023d565b34801561047857600080fd5b50610233600a5481565b34801561048e57600080fd5b506104a261049d36600461258c565b610c7e565b005b3480156104b057600080fd5b506104a26104bf36600461258c565b610d1e565b3480156104d057600080fd5b506102336104df3660046126c2565b610df3565b3480156104f057600080fd5b5061023360095481565b34801561050657600080fd5b5061023361051536600461270e565b60036020526000908152604090205481565b34801561053357600080fd5b506104477f000000000000000000000000000000000000000000000000000000000000000081565b34801561056757600080fd5b506104a261057636600461258c565b611337565b34801561058757600080fd5b5061023361059636600461270e565b60056020526000908152604090205481565b3480156105b457600080fd5b5061025b61140c565b3480156105c957600080fd5b506006546104479073ffffffffffffffffffffffffffffffffffffffff1681565b3480156105f657600080fd5b506104477f000000000000000000000000000000000000000000000000000000000000000081565b34801561062a57600080fd5b5061023361063936600461258c565b611419565b34801561064a57600080fd5b506104a261065936600461258c565b611439565b34801561066a57600080fd5b506104a261067936600461272b565b61159d565b34801561068a57600080fd5b506102336106993660046127a2565b600460209081526000928352604080842090915290825290205481565b3480156106c257600080fd5b506102336106d13660046126c2565b6115cf565b3480156106e257600080fd5b506102336106f136600461258c565b611a52565b61025b61070436600461280a565b611b10565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610796573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ba91906128f5565b905090565b600080546107cc9061290e565b80601f01602080910402602001604051908101604052809291908181526020018280546107f89061290e565b80156108455780601f1061081a57610100808354040283529160200191610845565b820191906000526020600020905b81548152906001019060200180831161082857829003601f168201915b505050505081565b60025460009080156108715761086c610864610709565b849083611c28565b610873565b825b9392505050565b60006040517fc31c949300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108de336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b610914576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b5460ff1615610951576040517f2a542da400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109606014828587612961565b6109699161298b565b6040517f6ab3905700000000000000000000000000000000000000000000000000000000815260609190911c60048201819052915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690636ab3905790602401602060405180830381865afa1580156109fc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2091906129d3565b610a56576040517fc94f7a9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556000610a8b610709565b9050610acf73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016836000611e23565b610b1073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168383611e23565b600073ffffffffffffffffffffffffffffffffffffffff831663da1452637f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000003086610b7f8b6014818f612961565b6040518763ffffffff1660e01b8152600401610ba0969594939291906129f5565b6020604051808303816000875af1158015610bbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be391906128f5565b60408051848152602081018390529192507fbf74a2393b907f335184e6dbeb4daa93812b077b8e034c2e61c8e6864002dda7910160405180910390a195945050505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610c59576107ba611ef0565b507f000000000000000000000000000000000000000000000000000000000000000090565b610cac336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b610ce2576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60088190556040518181527f9872d5eb566b79923d043f1b59aca655ca80a2bb5b6bca4824e515b0e398902f906020015b60405180910390a150565b610d4c336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b610d82576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710811115610dbe576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60098190556040518181527f12865465a7036a0232cbf9fb63ce880a3ee54f702775fe7abbc3c416e7968cd590602001610d13565b60006002600c5403610e66576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6002600c55600b5460ff1615610ea8576040517f326eab7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610eb133611f8a565b610ee7576040517f184849cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610ef66014828587612961565b610eff9161298b565b6040517f6ab3905700000000000000000000000000000000000000000000000000000000815260609190911c60048201819052915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690636ab3905790602401602060405180830381865afa158015610f92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb691906129d3565b610fec576040517fc94f7a9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61102e73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016333088612045565b60008061103d8760095461212f565b909250905061108373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168489611e23565b60008373ffffffffffffffffffffffffffffffffffffffff1663da1452637f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000030878c8c60149080926110f893929190612961565b6040518763ffffffff1660e01b8152600401611119969594939291906129f5565b6020604051808303816000875af1158015611138573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115c91906128f5565b9050600854818461116b610709565b6111759190612aac565b61117f9190612ae9565b11156111b7576040517f2edaff4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111c282612183565b9050806000036111fe576040517fc440e0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61120833826121a8565b6112d87f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611276573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129a9190612b24565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169085612221565b60408051838152602081018390529081018a905260608101849052339081907f5f971bd00bf3ffbca8a6d72cdd4fd92cfd4f62636161921d1e5a64f0b64ccb6d9060800160405180910390a39450505050505b6001600c559392505050565b611365336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b61139b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6127108111156113d7576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a8190556040518181527f91cc643d187eb250905520d3dae0b1017edd16961d27ab9a4a61fea5e38f717d90602001610d13565b600180546107cc9061290e565b60025460009080156108715761086c81611431610709565b859190611c28565b611467336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b61149d576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61156d7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561150b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061152f9190612b24565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169083612221565b6040518181527ffba1cbbf893e6a61412440b48fca1c80a5bf24d2f7deb6d5544717745077a36090602001610d13565b6040517fc31c949300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006002600c540361163d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610e5d565b6002600c55600b5460ff161561165d57611656846122e8565b905061132b565b600061166c6014828587612961565b6116759161298b565b6040517f6ab3905700000000000000000000000000000000000000000000000000000000815260609190911c60048201819052915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690636ab3905790602401602060405180830381865afa158015611708573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061172c91906129d3565b611762576040517fc94f7a9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061176d8661084d565b90506117793387612416565b6117bb73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016836000611e23565b6117fc73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168383611e23565b600073ffffffffffffffffffffffffffffffffffffffff831663da1452637f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000308661186b8b6014818f612961565b6040518763ffffffff1660e01b815260040161188c969594939291906129f5565b6020604051808303816000875af11580156118ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cf91906128f5565b90506000806118e083600a5461212f565b915091506119b47f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611952573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119769190612b24565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169083612221565b6119f573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163384612221565b60408051858152602081018b905290810183905260608101829052339081907f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf9060800160405180910390a3506001600c55979650505050505050565b6002546000908015610871576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015261086c907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611aec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086491906128f5565b6060611b40336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b611b76576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff168585604051611b9f9190612b41565b60006040518083038185875af1925050503d8060008114611bdc576040519150601f19603f3d011682016040523d82523d6000602084013e611be1565b606091505b509150915081611c1f57806040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152600401610e5d9190612579565b95945050505050565b828202811515841585830485141716611c4057600080fd5b0492915050565b60065460075460009173ffffffffffffffffffffffffffffffffffffffff1690829060601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001615611cb15760075473ffffffffffffffffffffffffffffffffffffffff16611cb3565b305b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015280831660248301527fffffffff00000000000000000000000000000000000000000000000000000000871660448301529192509083169063b700961390606401602060405180830381865afa158015611d53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7791906129d3565b80611c1f57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dec9190612b24565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161495945050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050611e84816124a4565b611eea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610e5d565b50505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051611f229190612b5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6040517fea3fff6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063ea3fff6890602401602060405180830381865afa15801561201b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061203f91906129d3565b92915050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af19150506120c2816124a4565b612128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610e5d565b5050505050565b60008061271083111561216e576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61217884846124eb565b938490039492505050565b60025460009080156108715761086c6002548461219e610709565b6114319190612c2f565b80600260008282546121ba9190612c46565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050612282816124a4565b611eea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610e5d565b6000806122f483611a52565b90506123003384612416565b60008061230f83600a5461212f565b915091506123817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611952573d6000803e3d6000fd5b6123c273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163384612221565b604080518481526020810187905290810183905260608101829052339081907f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf9060800160405180910390a3509392505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600360205260408120805483929061244b908490612c2f565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612215565b60003d826124b657806000803e806000fd5b80602081146124ce5780156124df57600092506124e4565b816000803e600051151592506124e4565b600192505b5050919050565b60006124f78284612aac565b61271090049392505050565b60005b8381101561251e578181015183820152602001612506565b83811115611eea5750506000910152565b60008151808452612547816020860160208601612503565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610873602083018461252f565b60006020828403121561259e57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146125c757600080fd5b50565b600080604083850312156125dd57600080fd5b82356125e8816125a5565b946020939093013593505050565b60008083601f84011261260857600080fd5b50813567ffffffffffffffff81111561262057600080fd5b60208301915083602082850101111561263857600080fd5b9250929050565b6000806020838503121561265257600080fd5b823567ffffffffffffffff81111561266957600080fd5b612675858286016125f6565b90969095509350505050565b60008060006060848603121561269657600080fd5b83356126a1816125a5565b925060208401356126b1816125a5565b929592945050506040919091013590565b6000806000604084860312156126d757600080fd5b83359250602084013567ffffffffffffffff8111156126f557600080fd5b612701868287016125f6565b9497909650939450505050565b60006020828403121561272057600080fd5b8135610873816125a5565b600080600080600080600060e0888a03121561274657600080fd5b8735612751816125a5565b96506020880135612761816125a5565b95506040880135945060608801359350608088013560ff8116811461278557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156127b557600080fd5b82356127c0816125a5565b915060208301356127d0816125a5565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561281f57600080fd5b833561282a816125a5565b925060208401359150604084013567ffffffffffffffff8082111561284e57600080fd5b818601915086601f83011261286257600080fd5b813581811115612874576128746127db565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156128ba576128ba6127db565b816040528281528960208487010111156128d357600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561290757600080fd5b5051919050565b600181811c9082168061292257607f821691505b60208210810361295b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000808585111561297157600080fd5b8386111561297e57600080fd5b5050820193919092039150565b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081358181169160148510156129cb5780818660140360031b1b83161692505b505092915050565b6000602082840312156129e557600080fd5b8151801515811461087357600080fd5b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a060808301528260a0830152828460c0840137600060c0848401015260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501168301019050979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612ae457612ae4612a7d565b500290565b600082612b1f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208284031215612b3657600080fd5b8151610873816125a5565b60008251612b53818460208701612503565b9190910192915050565b600080835481600182811c915080831680612b7957607f831692505b60208084108203612bb1577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015612bc55760018114612bf457612c21565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528489019650612c21565b60008a81526020902060005b86811015612c195781548b820152908501908301612c00565b505084890196505b509498975050505050505050565b600082821015612c4157612c41612a7d565b500390565b60008219821115612c5957612c59612a7d565b50019056fea264697066735822122050aeca61619a7df50c46c97de1c9906657afddc8170c43aa194d4b940a89e1cb64736f6c634300080d003300000000000000000000000094106ca9c7e567109a1d39413052887d1f412183000000000000000000000000ae78736cd615f374d3085123a210448e74fc639300000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000746a52880000000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f526f636b6574706f6f6c2045544820506f7274666f6c696f20536861726573000000000000000000000000000000000000000000000000000000000000000007724554482d505300000000000000000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436106102195760003560e01c80635d3035191161011d578063bf7e214f116100b0578063d505accf1161007f578063e77c646d11610064578063e77c646d146106b6578063f029748d146106d6578063fef78497146106f657600080fd5b8063d505accf1461065e578063dd62ed3e1461067e57600080fd5b8063bf7e214f146105bd578063c55dae63146105ea578063c6e6f5921461061e578063cbb943591461063e57600080fd5b80637dbc1df0116100ec5780637dbc1df01461055b5780637ecebe001461057b57806395d89b41146105a8578063a9059cbb1461028857600080fd5b80635d303519146104c457806367a52793146104e457806370a08231146104fa5780637b1039991461052757600080fd5b806323b872dd116101b05780633644e5151161017f578063458f581511610164578063458f58151461046c57806347786d3714610482578063490ae210146104a457600080fd5b80633644e515146103fe57806338d52e0f1461041357600080fd5b806323b872dd1461035357806330adf81f1461036e578063313ce567146103a2578063355274ea146103e857600080fd5b8063106f276f116101ec578063106f276f146102b857806318160ddd146102d85780631e7ee237146102ee5780632031ee951461030857600080fd5b806301e1d1141461021e57806306fdde031461024657806307a2d13a14610268578063095ea7b314610288575b600080fd5b34801561022a57600080fd5b50610233610709565b6040519081526020015b60405180910390f35b34801561025257600080fd5b5061025b6107bf565b60405161023d9190612579565b34801561027457600080fd5b5061023361028336600461258c565b61084d565b34801561029457600080fd5b506102a86102a33660046125ca565b61087a565b604051901515815260200161023d565b3480156102c457600080fd5b506102336102d336600461263f565b6108ae565b3480156102e457600080fd5b5061023360025481565b3480156102fa57600080fd5b50600b546102a89060ff1681565b34801561031457600080fd5b506007546103229060601b81565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909116815260200161023d565b34801561035f57600080fd5b506102a86102a3366004612681565b34801561037a57600080fd5b506102337f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b3480156103ae57600080fd5b506103d67f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff909116815260200161023d565b3480156103f457600080fd5b5061023360085481565b34801561040a57600080fd5b50610233610c28565b34801561041f57600080fd5b506104477f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639381565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161023d565b34801561047857600080fd5b50610233600a5481565b34801561048e57600080fd5b506104a261049d36600461258c565b610c7e565b005b3480156104b057600080fd5b506104a26104bf36600461258c565b610d1e565b3480156104d057600080fd5b506102336104df3660046126c2565b610df3565b3480156104f057600080fd5b5061023360095481565b34801561050657600080fd5b5061023361051536600461270e565b60036020526000908152604090205481565b34801561053357600080fd5b506104477f00000000000000000000000094106ca9c7e567109a1d39413052887d1f41218381565b34801561056757600080fd5b506104a261057636600461258c565b611337565b34801561058757600080fd5b5061023361059636600461270e565b60056020526000908152604090205481565b3480156105b457600080fd5b5061025b61140c565b3480156105c957600080fd5b506006546104479073ffffffffffffffffffffffffffffffffffffffff1681565b3480156105f657600080fd5b506104477f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b34801561062a57600080fd5b5061023361063936600461258c565b611419565b34801561064a57600080fd5b506104a261065936600461258c565b611439565b34801561066a57600080fd5b506104a261067936600461272b565b61159d565b34801561068a57600080fd5b506102336106993660046127a2565b600460209081526000928352604080842090915290825290205481565b3480156106c257600080fd5b506102336106d13660046126c2565b6115cf565b3480156106e257600080fd5b506102336106f136600461258c565b611a52565b61025b61070436600461280a565b611b10565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639373ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610796573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ba91906128f5565b905090565b600080546107cc9061290e565b80601f01602080910402602001604051908101604052809291908181526020018280546107f89061290e565b80156108455780601f1061081a57610100808354040283529160200191610845565b820191906000526020600020905b81548152906001019060200180831161082857829003601f168201915b505050505081565b60025460009080156108715761086c610864610709565b849083611c28565b610873565b825b9392505050565b60006040517fc31c949300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108de336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b610914576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b5460ff1615610951576040517f2a542da400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109606014828587612961565b6109699161298b565b6040517f6ab3905700000000000000000000000000000000000000000000000000000000815260609190911c60048201819052915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000094106ca9c7e567109a1d39413052887d1f4121831690636ab3905790602401602060405180830381865afa1580156109fc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2091906129d3565b610a56576040517fc94f7a9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556000610a8b610709565b9050610acf73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639316836000611e23565b610b1073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ae78736cd615f374d3085123a210448e74fc6393168383611e23565b600073ffffffffffffffffffffffffffffffffffffffff831663da1452637f000000000000000000000000ae78736cd615f374d3085123a210448e74fc63937f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb483086610b7f8b6014818f612961565b6040518763ffffffff1660e01b8152600401610ba0969594939291906129f5565b6020604051808303816000875af1158015610bbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be391906128f5565b60408051848152602081018390529192507fbf74a2393b907f335184e6dbeb4daa93812b077b8e034c2e61c8e6864002dda7910160405180910390a195945050505050565b60007f00000000000000000000000000000000000000000000000000000000000000014614610c59576107ba611ef0565b507fe0cbe3f33f940107053d882269cb196457c62646db502f2cd1905e6e59c137df90565b610cac336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b610ce2576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60088190556040518181527f9872d5eb566b79923d043f1b59aca655ca80a2bb5b6bca4824e515b0e398902f906020015b60405180910390a150565b610d4c336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b610d82576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710811115610dbe576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60098190556040518181527f12865465a7036a0232cbf9fb63ce880a3ee54f702775fe7abbc3c416e7968cd590602001610d13565b60006002600c5403610e66576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6002600c55600b5460ff1615610ea8576040517f326eab7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610eb133611f8a565b610ee7576040517f184849cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610ef66014828587612961565b610eff9161298b565b6040517f6ab3905700000000000000000000000000000000000000000000000000000000815260609190911c60048201819052915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000094106ca9c7e567109a1d39413052887d1f4121831690636ab3905790602401602060405180830381865afa158015610f92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb691906129d3565b610fec576040517fc94f7a9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61102e73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816333088612045565b60008061103d8760095461212f565b909250905061108373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168489611e23565b60008373ffffffffffffffffffffffffffffffffffffffff1663da1452637f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb487f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639330878c8c60149080926110f893929190612961565b6040518763ffffffff1660e01b8152600401611119969594939291906129f5565b6020604051808303816000875af1158015611138573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115c91906128f5565b9050600854818461116b610709565b6111759190612aac565b61117f9190612ae9565b11156111b7576040517f2edaff4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111c282612183565b9050806000036111fe576040517fc440e0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61120833826121a8565b6112d87f00000000000000000000000094106ca9c7e567109a1d39413052887d1f41218373ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611276573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129a9190612b24565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169085612221565b60408051838152602081018390529081018a905260608101849052339081907f5f971bd00bf3ffbca8a6d72cdd4fd92cfd4f62636161921d1e5a64f0b64ccb6d9060800160405180910390a39450505050505b6001600c559392505050565b611365336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b61139b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6127108111156113d7576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a8190556040518181527f91cc643d187eb250905520d3dae0b1017edd16961d27ab9a4a61fea5e38f717d90602001610d13565b600180546107cc9061290e565b60025460009080156108715761086c81611431610709565b859190611c28565b611467336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b61149d576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61156d7f00000000000000000000000094106ca9c7e567109a1d39413052887d1f41218373ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561150b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061152f9190612b24565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ae78736cd615f374d3085123a210448e74fc6393169083612221565b6040518181527ffba1cbbf893e6a61412440b48fca1c80a5bf24d2f7deb6d5544717745077a36090602001610d13565b6040517fc31c949300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006002600c540361163d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610e5d565b6002600c55600b5460ff161561165d57611656846122e8565b905061132b565b600061166c6014828587612961565b6116759161298b565b6040517f6ab3905700000000000000000000000000000000000000000000000000000000815260609190911c60048201819052915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000094106ca9c7e567109a1d39413052887d1f4121831690636ab3905790602401602060405180830381865afa158015611708573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061172c91906129d3565b611762576040517fc94f7a9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061176d8661084d565b90506117793387612416565b6117bb73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639316836000611e23565b6117fc73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ae78736cd615f374d3085123a210448e74fc6393168383611e23565b600073ffffffffffffffffffffffffffffffffffffffff831663da1452637f000000000000000000000000ae78736cd615f374d3085123a210448e74fc63937f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48308661186b8b6014818f612961565b6040518763ffffffff1660e01b815260040161188c969594939291906129f5565b6020604051808303816000875af11580156118ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cf91906128f5565b90506000806118e083600a5461212f565b915091506119b47f00000000000000000000000094106ca9c7e567109a1d39413052887d1f41218373ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611952573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119769190612b24565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169083612221565b6119f573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48163384612221565b60408051858152602081018b905290810183905260608101829052339081907f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf9060800160405180910390a3506001600c55979650505050505050565b6002546000908015610871576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015261086c907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611aec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086491906128f5565b6060611b40336000357fffffffff0000000000000000000000000000000000000000000000000000000016611c47565b611b76576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff168585604051611b9f9190612b41565b60006040518083038185875af1925050503d8060008114611bdc576040519150601f19603f3d011682016040523d82523d6000602084013e611be1565b606091505b509150915081611c1f57806040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152600401610e5d9190612579565b95945050505050565b828202811515841585830485141716611c4057600080fd5b0492915050565b60065460075460009173ffffffffffffffffffffffffffffffffffffffff1690829060601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001615611cb15760075473ffffffffffffffffffffffffffffffffffffffff16611cb3565b305b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015280831660248301527fffffffff00000000000000000000000000000000000000000000000000000000871660448301529192509083169063b700961390606401602060405180830381865afa158015611d53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7791906129d3565b80611c1f57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dec9190612b24565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161495945050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050611e84816124a4565b611eea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610e5d565b50505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051611f229190612b5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6040517fea3fff6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f00000000000000000000000094106ca9c7e567109a1d39413052887d1f4121839091169063ea3fff6890602401602060405180830381865afa15801561201b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061203f91906129d3565b92915050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af19150506120c2816124a4565b612128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610e5d565b5050505050565b60008061271083111561216e576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61217884846124eb565b938490039492505050565b60025460009080156108715761086c6002548461219e610709565b6114319190612c2f565b80600260008282546121ba9190612c46565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050612282816124a4565b611eea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610e5d565b6000806122f483611a52565b90506123003384612416565b60008061230f83600a5461212f565b915091506123817f00000000000000000000000094106ca9c7e567109a1d39413052887d1f41218373ffffffffffffffffffffffffffffffffffffffff166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611952573d6000803e3d6000fd5b6123c273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48163384612221565b604080518481526020810187905290810183905260608101829052339081907f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf9060800160405180910390a3509392505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600360205260408120805483929061244b908490612c2f565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612215565b60003d826124b657806000803e806000fd5b80602081146124ce5780156124df57600092506124e4565b816000803e600051151592506124e4565b600192505b5050919050565b60006124f78284612aac565b61271090049392505050565b60005b8381101561251e578181015183820152602001612506565b83811115611eea5750506000910152565b60008151808452612547816020860160208601612503565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610873602083018461252f565b60006020828403121561259e57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146125c757600080fd5b50565b600080604083850312156125dd57600080fd5b82356125e8816125a5565b946020939093013593505050565b60008083601f84011261260857600080fd5b50813567ffffffffffffffff81111561262057600080fd5b60208301915083602082850101111561263857600080fd5b9250929050565b6000806020838503121561265257600080fd5b823567ffffffffffffffff81111561266957600080fd5b612675858286016125f6565b90969095509350505050565b60008060006060848603121561269657600080fd5b83356126a1816125a5565b925060208401356126b1816125a5565b929592945050506040919091013590565b6000806000604084860312156126d757600080fd5b83359250602084013567ffffffffffffffff8111156126f557600080fd5b612701868287016125f6565b9497909650939450505050565b60006020828403121561272057600080fd5b8135610873816125a5565b600080600080600080600060e0888a03121561274657600080fd5b8735612751816125a5565b96506020880135612761816125a5565b95506040880135945060608801359350608088013560ff8116811461278557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156127b557600080fd5b82356127c0816125a5565b915060208301356127d0816125a5565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561281f57600080fd5b833561282a816125a5565b925060208401359150604084013567ffffffffffffffff8082111561284e57600080fd5b818601915086601f83011261286257600080fd5b813581811115612874576128746127db565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156128ba576128ba6127db565b816040528281528960208487010111156128d357600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561290757600080fd5b5051919050565b600181811c9082168061292257607f821691505b60208210810361295b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000808585111561297157600080fd5b8386111561297e57600080fd5b5050820193919092039150565b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081358181169160148510156129cb5780818660140360031b1b83161692505b505092915050565b6000602082840312156129e557600080fd5b8151801515811461087357600080fd5b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a060808301528260a0830152828460c0840137600060c0848401015260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501168301019050979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612ae457612ae4612a7d565b500290565b600082612b1f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208284031215612b3657600080fd5b8151610873816125a5565b60008251612b53818460208701612503565b9190910192915050565b600080835481600182811c915080831680612b7957607f831692505b60208084108203612bb1577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015612bc55760018114612bf457612c21565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528489019650612c21565b60008a81526020902060005b86811015612c195781548b820152908501908301612c00565b505084890196505b509498975050505050505050565b600082821015612c4157612c41612a7d565b500390565b60008219821115612c5957612c59612a7d565b50019056fea264697066735822122050aeca61619a7df50c46c97de1c9906657afddc8170c43aa194d4b940a89e1cb64736f6c634300080d0033
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.