Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
MigrationSpell
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 500 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.8.26; import {IRoot} from "src/interfaces/IRoot.sol"; import {IPoolManager} from "src/interfaces/IPoolManager.sol"; import {RestrictionUpdate} from "src/interfaces/token/IRestrictionManager.sol"; import {ITranche} from "src/interfaces/token/ITranche.sol"; import {IInvestmentManager} from "src/interfaces/IInvestmentManager.sol"; import {IAuth} from "src/interfaces/IAuth.sol"; import {MigrationSpellBase} from "src/spell/MigrationSpellBase.sol"; interface ITrancheOld { function authTransferFrom(address from, address to, uint256 value) external returns (bool); } // spell to migrate tranche tokens contract MigrationSpell is MigrationSpellBase { constructor() { NETWORK = "ethereum-mainnet"; // old deployment addresses ROOT_OLD = 0x498016d30Cd5f0db50d7ACE329C07313a0420502; ADMIN_MULTISIG = 0xD9D30ab47c0f096b0AA67e9B8B1624504a63e7FD; GUARDIAN_OLD = 0x2559998026796Ca6fd057f3aa66F2d6ecdEd9028; // old pool addresses VAULT_OLD = 0x110379504D933BeD2E485E281bc3909D1E7C9E5D; // new deployment addresses ROOT_NEW = 0x0C1fDfd6a1331a875EA013F3897fc8a76ada5DfC; GUARDIAN_NEW = 0x09ab10a9c3E6Eac1d18270a2322B6113F4C7f5E8; POOLMANAGER_NEW = 0x91808B5E2F6d7483D41A681034D7c9DbB64B9E29; RESTRICTIONMANAGER_NEW = 0x4737C3f62Cc265e786b280153fC666cEA2fBc0c0; // information to deploy the new tranche token & liquidity pool to be able to migrate the tokens NAME = "Anemoy DeFi Yield Fund 1"; SYMBOL = "DYF"; NAME_OLD = "DYF (deprecated)"; SYMBOL_OLD = "DYF-DEPRECATED"; memberlistMembers = [0xd595E1483c507E74E2E6A3dE8e7D08d8f6F74936, 0x30d3bbAE8623d0e9C0db5c27B82dCDA39De40997]; validUntil[0xd595E1483c507E74E2E6A3dE8e7D08d8f6F74936] = type(uint64).max; validUntil[0x30d3bbAE8623d0e9C0db5c27B82dCDA39De40997] = 2032178598; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; import {IMessageHandler} from "src/interfaces/gateway/IGateway.sol"; interface IRecoverable { /// @notice Used to recover any ERC-20 token. /// @dev This method is called only by authorized entities /// @param token It could be 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee /// to recover locked native ETH or any ERC20 compatible token. /// @param to Receiver of the funds /// @param amount Amount to send to the receiver. function recoverTokens(address token, address to, uint256 amount) external; } interface IRoot is IMessageHandler { // --- Events --- event File(bytes32 indexed what, uint256 data); event Pause(); event Unpause(); event ScheduleRely(address indexed target, uint256 indexed scheduledTime); event CancelRely(address indexed target); event RelyContract(address indexed target, address indexed user); event DenyContract(address indexed target, address indexed user); event RecoverTokens(address indexed target, address indexed token, address indexed to, uint256 amount); event Endorse(address indexed user); event Veto(address indexed user); /// @notice Returns whether the root is paused function paused() external view returns (bool); /// @notice Returns the current timelock for adding new wards function delay() external view returns (uint256); /// @notice Returns when `relyTarget` has passed the timelock function schedule(address relyTarget) external view returns (uint256 timestamp); // --- Administration --- /// @notice Updates a contract parameter /// @param what Accepts a bytes32 representation of 'delay' function file(bytes32 what, uint256 data) external; /// --- Endorsements --- /// @notice Endorses the `user` /// @dev Endorsed users are trusted contracts in the system. They are allowed to bypass /// token restrictions (e.g. the Escrow can automatically receive tranche tokens by being endorsed), and /// can automatically set operators in ERC-7540 vaults (e.g. the CentrifugeRouter) is always an operator. function endorse(address user) external; /// @notice Removes the endorsed user function veto(address user) external; /// @notice Returns whether the user is endorsed function endorsed(address user) external view returns (bool); // --- Pause management --- /// @notice Pause any contracts that depend on `Root.paused()` function pause() external; /// @notice Unpause any contracts that depend on `Root.paused()` function unpause() external; /// --- Timelocked ward management --- /// @notice Schedule relying a new ward after the delay has passed function scheduleRely(address target) external; /// @notice Cancel a pending scheduled rely function cancelRely(address target) external; /// @notice Execute a scheduled rely /// @dev Can be triggered by anyone since the scheduling is protected function executeScheduledRely(address target) external; /// --- Incoming message handling --- function handle(bytes calldata message) external; /// --- External contract ward management --- /// @notice Make an address a ward on any contract that Root is a ward on function relyContract(address target, address user) external; /// @notice Removes an address as a ward on any contract that Root is a ward on function denyContract(address target, address user) external; /// --- Token Recovery --- /// @notice Allows Governance to recover tokens sent to the wrong contract by mistake function recoverTokens(address target, address token, address to, uint256 amount) external; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; import {IMessageHandler} from "src/interfaces/gateway/IGateway.sol"; import {IRecoverable} from "src/interfaces/IRoot.sol"; /// @dev Centrifuge pools struct Pool { uint256 createdAt; mapping(bytes16 trancheId => TrancheDetails) tranches; mapping(address asset => bool) allowedAssets; } /// @dev Each Centrifuge pool is associated to 1 or more tranches struct TrancheDetails { address token; /// @dev Each tranche can have multiple vaults deployed, /// each linked to a unique asset mapping(address asset => address vault) vaults; /// @dev Each tranche has a price per vault mapping(address vault => TranchePrice) prices; } struct TranchePrice { uint128 price; uint64 computedAt; } /// @dev Temporary storage that is only present between addTranche and deployTranche struct UndeployedTranche { /// @dev The decimals of the leading pool asset. Vault shares have /// to be denomatimated with the same precision. uint8 decimals; /// @dev Metadata of the to be deployed erc20 token string tokenName; string tokenSymbol; /// @dev Address of the hook address hook; } struct VaultAsset { /// @dev Address of the asset address asset; /// @dev Whether this wrapper conforms to the IERC20Wrapper interface bool isWrapper; } enum Domain { Centrifuge, EVM } interface IPoolManager is IMessageHandler, IRecoverable { event File(bytes32 indexed what, address data); event AddAsset(uint128 indexed assetId, address indexed asset); event AddPool(uint64 indexed poolId); event AllowAsset(uint64 indexed poolId, address indexed asset); event DisallowAsset(uint64 indexed poolId, address indexed asset); event AddTranche(uint64 indexed poolId, bytes16 indexed trancheId); event DeployTranche(uint64 indexed poolId, bytes16 indexed trancheId, address indexed tranche); event DeployVault(uint64 indexed poolId, bytes16 indexed trancheId, address indexed asset, address vault); event RemoveVault(uint64 indexed poolId, bytes16 indexed trancheId, address indexed asset, address vault); event PriceUpdate( uint64 indexed poolId, bytes16 indexed trancheId, address indexed asset, uint256 price, uint64 computedAt ); event TransferAssets(address indexed asset, address indexed sender, bytes32 indexed recipient, uint128 amount); event TransferTrancheTokens( uint64 indexed poolId, bytes16 indexed trancheId, address indexed sender, Domain destinationDomain, uint64 destinationId, bytes32 destinationAddress, uint128 amount ); /// @notice returns the investmentManager address /// @dev can be set using file function investmentManager() external view returns (address); /// @notice returns the asset address associated with a given asset id function idToAsset(uint128 assetId) external view returns (address asset); /// @notice returns the asset id associated with a given address function assetToId(address) external view returns (uint128 assetId); /// @notice returns the asset address associated with a given vault address /// @return asset The asset address /// @return isWrapper Whether the asset is a wrapped ERC20 token function vaultToAsset(address) external view returns (address asset, bool isWrapper); /// @notice Updates a contract parameter /// @param what Accepts a bytes32 representation of 'gateway', 'investmentManager', 'trancheFactory', /// 'vaultFactory', or 'gasService' function file(bytes32 what, address data) external; /// @notice transfers assets to a cross-chain recipient address /// @dev Addresses on centrifuge chain are represented as bytes32 function transferAssets(address asset, bytes32 recipient, uint128 amount) external; /// @notice transfers tranche tokens to a cross-chain recipient address /// @dev To transfer to evm chains, pad a 20 byte evm address with 12 bytes of 0 /// @param poolId The centrifuge pool id /// @param trancheId The tranche id /// @param destinationDomain an enum representing the destination domain (Centrifuge or EVM) /// @param destinationId The destination chain id /// @param recipient A bytes32 representation of the recipient address /// @param amount The amount of tokens to transfer function transferTrancheTokens( uint64 poolId, bytes16 trancheId, Domain destinationDomain, uint64 destinationId, bytes32 recipient, uint128 amount ) external; /// @notice New pool details from an existing Centrifuge pool are added. /// @dev The function can only be executed by the gateway contract. function addPool(uint64 poolId) external; /// @notice Centrifuge pools can support multiple currencies for investing. this function adds /// a new supported asset to the pool details. /// Adding new currencies allow the creation of new vaults for the underlying Centrifuge pool. /// @dev The function can only be executed by the gateway contract. function allowAsset(uint64 poolId, uint128 assetId) external; /// @notice Centrifuge pools can support multiple currencies for investing. this function removes /// a supported asset from the pool details. /// @dev The function can only be executed by the gateway contract. function disallowAsset(uint64 poolId, uint128 assetId) external; /// @notice New tranche details from an existing Centrifuge pool are added. /// @dev The function can only be executed by the gateway contract. function addTranche( uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint8 decimals, address hook ) external; /// @notice Updates the tokenName and tokenSymbol of a tranche token /// @dev The function can only be executed by the gateway contract. function updateTrancheMetadata(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) external; /// @notice Updates the price of a tranche token /// @dev The function can only be executed by the gateway contract. function updateTranchePrice(uint64 poolId, bytes16 trancheId, uint128 assetId, uint128 price, uint64 computedAt) external; /// @notice Updates the restrictions on a tranche token for a specific user /// @param poolId The centrifuge pool id /// @param trancheId The tranche id /// @param update The restriction update in the form of a bytes array indicating /// the restriction to be updated, the user to be updated, and a validUntil timestamp. function updateRestriction(uint64 poolId, bytes16 trancheId, bytes memory update) external; /// @notice Updates the hook of a tranche token /// @param poolId The centrifuge pool id /// @param trancheId The tranche id /// @param hook The new hook addres function updateTrancheHook(uint64 poolId, bytes16 trancheId, address hook) external; /// @notice A global chain agnostic asset index is maintained on Centrifuge. This function maps /// a asset from the Centrifuge index to its corresponding address on the evm chain. /// The chain agnostic asset id has to be used to pass asset information to the Centrifuge. /// @dev This function can only be executed by the gateway contract. function addAsset(uint128 assetId, address asset) external; /// @notice Executes a message from the gateway /// @dev The function can only be executed by the gateway contract. function handle(bytes calldata message) external; /// @notice Transfers assets to a recipient from the escrow contract /// @dev The function can only be executed internally or by the gateway contract. function handleTransfer(uint128 assetId, address recipient, uint128 amount) external; /// @notice Mints tranche tokens to a recipient /// @dev The function can only be executed internally or by the gateway contract. function handleTransferTrancheTokens(uint64 poolId, bytes16 trancheId, address destinationAddress, uint128 amount) external; /// @notice Deploys a created tranche /// @dev The function can only be executed by the gateway contract. function deployTranche(uint64 poolId, bytes16 trancheId) external returns (address); /// @notice Deploys a vault for a given asset and tranche token /// @dev The function can only be executed by the gateway contract. function deployVault(uint64 poolId, bytes16 trancheId, address asset) external returns (address); /// @notice Removes a vault for a given asset and tranche token /// @dev The function can only be executed by the gateway contract. function removeVault(uint64 poolId, bytes16 trancheId, address asset) external; /// @notice Returns the tranche token for a given pool and tranche id function getTranche(uint64 poolId, bytes16 trancheId) external view returns (address); /// @notice Returns the vault for a given pool, tranche id, and asset id function getVault(uint64 poolId, bytes16 trancheId, uint128 assetId) external view returns (address); /// @notice Returns the vault for a given pool, tranche id, and asset address function getVault(uint64 poolId, bytes16 trancheId, address asset) external view returns (address); /// @notice Retuns the latest tranche token price for a given pool, tranche id, and asset id function getTranchePrice(uint64 poolId, bytes16 trancheId, address asset) external view returns (uint128 price, uint64 computedAt); /// @notice Function to get the vault's underlying asset /// @dev Function vaultToAsset which is a state variable getter could be used /// but in that case each caller MUST make sure they handle the case /// where a 0 address is returned. Using this method, that handling is done /// on the behalf the caller. function getVaultAsset(address vault) external view returns (address asset, bool isWrapper); /// @notice Checks whether a given asset is allowed for a given pool function isAllowedAsset(uint64 poolId, address asset) external view returns (bool); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; enum RestrictionUpdate { Invalid, UpdateMember, Freeze, Unfreeze } interface IRestrictionManager { // --- Events --- event UpdateMember(address indexed token, address indexed user, uint64 validUntil); event Freeze(address indexed token, address indexed user); event Unfreeze(address indexed token, address indexed user); // --- Handling freezes --- /// @notice Freeze a user balance. Frozen users cannot receive nor send tokens function freeze(address token, address user) external; /// @notice Unfreeze a user balance function unfreeze(address token, address user) external; /// @notice Returns whether the user's tokens are frozen function isFrozen(address token, address user) external view returns (bool); // --- Managing members --- /// @notice Add a member. Non-members cannot receive tokens, but can send tokens to valid members /// @param validUntil Timestamp until which the user will be a valid member function updateMember(address token, address user, uint64 validUntil) external; /// @notice Returns whether the user is a valid member of the token function isMember(address token, address user) external view returns (bool isValid, uint64 validUntil); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; import {IERC20Metadata} from "src/interfaces/IERC20.sol"; import {IERC7575Share} from "src/interfaces/IERC7575.sol"; interface IERC1404 { /// @notice Detects if a transfer will be reverted and if so returns an appropriate reference code /// @param from Sending address /// @param to Receiving address /// @param value Amount of tokens being transferred /// @return Code by which to reference message for rejection reasoning /// @dev Overwrite with your custom transfer restriction logic function detectTransferRestriction(address from, address to, uint256 value) external view returns (uint8); /// @notice Returns a human-readable message for a given restriction code /// @param restrictionCode Identifier for looking up a message /// @return Text showing the restriction's reasoning /// @dev Overwrite with your custom message and restrictionCode handling function messageForTransferRestriction(uint8 restrictionCode) external view returns (string memory); } interface ITranche is IERC20Metadata, IERC7575Share, IERC1404 { // --- Events --- event File(bytes32 indexed what, address data); event SetHookData(address indexed user, bytes16 data); // --- Administration --- /// @notice returns the hook that transfers perform callbacks to /// @dev MUST comply to `IHook` interface function hook() external view returns (address); /// @notice Updates a contract parameter /// @param what Accepts a bytes32 representation of 'name', 'symbol' function file(bytes32 what, string memory data) external; /// @notice Updates a contract parameter /// @param what Accepts a bytes32 representation of 'hook' function file(bytes32 what, address data) external; /// @notice updates the vault for a given `asset` function updateVault(address asset, address vault_) external; // --- ERC20 overrides --- /// @notice returns the 16 byte hook data of the given `user`. /// @dev Stored in the 128 most significant bits of the user balance function hookDataOf(address user) external view returns (bytes16); /// @notice update the 16 byte hook data of the given `user` function setHookData(address user, bytes16 hookData) external; /// @notice Function to mint tokens function mint(address user, uint256 value) external; /// @notice Function to burn tokens function burn(address user, uint256 value) external; /// @notice Checks if the tokens can be transferred given the input values function checkTransferRestriction(address from, address to, uint256 value) external view returns (bool); /// @notice Performs an authorized transfer, with `sender` as the given sender. /// @dev Requires allowance if `sender` != `from` function authTransferFrom(address sender, address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; import {IMessageHandler} from "src/interfaces/gateway/IGateway.sol"; import {IRecoverable} from "src/interfaces/IRoot.sol"; /// @dev Vault orders and deposit/redeem bookkeeping per user struct InvestmentState { /// @dev Shares that can be claimed using `mint()` uint128 maxMint; /// @dev Weighted average price of deposits, used to convert maxMint to maxDeposit uint256 depositPrice; /// @dev Currency that can be claimed using `withdraw()` uint128 maxWithdraw; /// @dev Weighted average price of redemptions, used to convert maxWithdraw to maxRedeem uint256 redeemPrice; /// @dev Remaining deposit order in assets uint128 pendingDepositRequest; /// @dev Remaining redeem order in shares uint128 pendingRedeemRequest; /// @dev Currency that can be claimed using `claimCancelDepositRequest()` uint128 claimableCancelDepositRequest; /// @dev Shares that can be claimed using `claimCancelRedeemRequest()` uint128 claimableCancelRedeemRequest; /// @dev Indicates whether the depositRequest was requested to be cancelled bool pendingCancelDepositRequest; /// @dev Indicates whether the redeemRequest was requested to be cancelled bool pendingCancelRedeemRequest; } interface IInvestmentManager is IMessageHandler, IRecoverable { // --- Events --- event File(bytes32 indexed what, address data); event TriggerRedeemRequest( uint64 indexed poolId, bytes16 indexed trancheId, address user, address asset, uint128 shares ); /// @notice Updates contract parameters of type address. /// @param what The bytes32 representation of 'gateway' or 'poolManager'. /// @param data The new contract address. function file(bytes32 what, address data) external; // --- Outgoing message handling --- /// @notice Requests assets deposit. Liquidity pools have to request investments from Centrifuge before /// shares can be minted. The deposit requests are added to the order book /// on Centrifuge. Once the next epoch is executed on Centrifuge, vaults can /// proceed with share payouts in case the order got fulfilled. /// @dev The assets required to fulfill the deposit request have to be locked and are transferred from the /// owner to the escrow, even though the share payout can only happen after epoch execution. /// The receiver becomes the owner of deposit request fulfillment. function requestDeposit(address vault, uint256 assets, address receiver, address owner, address source) external returns (bool); /// @notice Requests share redemption. Liquidity pools have to request redemptions /// from Centrifuge before actual asset payouts can be done. The redemption /// requests are added to the order book on Centrifuge. Once the next epoch is /// executed on Centrifuge, vaults can proceed with asset payouts /// in case the order got fulfilled. /// @dev The shares required to fulfill the redemption request have to be locked and are transferred from the /// owner to the escrow, even though the asset payout can only happen after epoch execution. /// The receiver becomes the owner of redeem request fulfillment. function requestRedeem(address vault, uint256 shares, address receiver, address, /* owner */ address source) external returns (bool); /// @notice Requests the cancellation of a pending deposit request. Liquidity pools have to request the /// cancellation of outstanding requests from Centrifuge before actual assets can be unlocked and /// transferred /// to the owner. /// While users have outstanding cancellation requests no new deposit requests can be submitted. /// Once the next epoch is executed on Centrifuge, vaults can proceed with asset payouts /// if orders could be cancelled successfully. /// @dev The cancellation request might fail in case the pending deposit order already got fulfilled on /// Centrifuge. function cancelDepositRequest(address vault, address owner, address source) external; /// @notice Requests the cancellation of an pending redeem request. Liquidity pools have to request the /// cancellation of outstanding requests from Centrifuge before actual shares can be unlocked and /// transferred to the owner. /// While users have outstanding cancellation requests no new redeem requests can be submitted (exception: /// trigger through governance). /// Once the next epoch is executed on Centrifuge, vaults can proceed with share payouts /// if the orders could be cancelled successfully. /// @dev The cancellation request might fail in case the pending redeem order already got fulfilled on /// Centrifuge. function cancelRedeemRequest(address vault, address owner, address source) external; // --- Incoming message handling --- /// @notice Handle incoming messages from Centrifuge. Parse the function params and forward to the corresponding /// handler function. function handle(bytes calldata message) external; /// @notice Fulfills pending deposit requests after successful epoch execution on Centrifuge. /// The amount of shares that can be claimed by the user is minted and moved to the escrow contract. /// The MaxMint bookkeeping value is updated. /// The request fulfillment can be partial. /// @dev The shares in the escrow are reserved for the user and are transferred to the user on deposit /// and mint calls. function fulfillDepositRequest( uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 assets, uint128 shares ) external; /// @notice Fulfills pending redeem requests after successful epoch execution on Centrifuge. /// The amount of redeemed shares is burned. The amount of assets that can be claimed by the user in /// return is locked in the escrow contract. The MaxWithdraw bookkeeping value is updated. /// The request fulfillment can be partial. /// @dev The assets in the escrow are reserved for the user and are transferred to the user on redeem /// and withdraw calls. function fulfillRedeemRequest( uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 assets, uint128 shares ) external; /// @notice Fulfills deposit request cancellation after successful epoch execution on Centrifuge. /// The amount of assets that can be claimed by the user is locked in the escrow contract. /// Updates claimableCancelDepositRequest bookkeeping value. The cancellation order execution can be /// partial. /// @dev The assets in the escrow are reserved for the user and are transferred to the user during /// claimCancelDepositRequest calls. function fulfillCancelDepositRequest( uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 assets, uint128 fulfillment ) external; /// @notice Fulfills redeem request cancellation after successful epoch execution on Centrifuge. /// The amount of shares that can be claimed by the user is locked in the escrow contract. /// Updates claimableCancelRedeemRequest bookkeeping value. The cancellation order execution can also be /// partial. /// @dev The shares in the escrow are reserved for the user and are transferred to the user during /// claimCancelRedeemRequest calls. function fulfillCancelRedeemRequest(uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 shares) external; /// @notice Triggers a redeem request on behalf of the user through Centrifuge governance. /// This function is required for legal/compliance reasons and rare scenarios, like share contract /// migrations. /// Once the next epoch is executed on Centrifuge, vaults can proceed with asset payouts in case the orders /// got fulfilled. /// @dev The user share amount required to fulfill the redeem request has to be locked in escrow, /// even though the asset payout can only happen after epoch execution. function triggerRedeemRequest(uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 shares) external; // --- View functions --- /// @notice Converts the assets value to share decimals. function convertToShares(address vault, uint256 _assets) external view returns (uint256 shares); /// @notice Converts the shares value to assets decimals. function convertToAssets(address vault, uint256 _shares) external view returns (uint256 assets); /// @notice Returns the max amount of assets based on the unclaimed amount of shares after at least one successful /// deposit order fulfillment on Centrifuge. function maxDeposit(address vault, address user) external view returns (uint256); /// @notice Returns the max amount of shares a user can claim after at least one successful deposit order /// fulfillment on Centrifuge. function maxMint(address vault, address user) external view returns (uint256 shares); /// @notice Returns the max amount of assets a user can claim after at least one successful redeem order fulfillment /// on Centrifuge. function maxWithdraw(address vault, address user) external view returns (uint256 assets); /// @notice Returns the max amount of shares based on the unclaimed number of assets after at least one successful /// redeem order fulfillment on Centrifuge. function maxRedeem(address vault, address user) external view returns (uint256 shares); /// @notice Indicates whether a user has pending deposit requests and returns the total deposit request asset /// request value. function pendingDepositRequest(address vault, address user) external view returns (uint256 assets); /// @notice Indicates whether a user has pending redeem requests and returns the total share request value. function pendingRedeemRequest(address vault, address user) external view returns (uint256 shares); /// @notice Indicates whether a user has pending deposit request cancellations. function pendingCancelDepositRequest(address vault, address user) external view returns (bool isPending); /// @notice Indicates whether a user has pending redeem request cancellations. function pendingCancelRedeemRequest(address vault, address user) external view returns (bool isPending); /// @notice Indicates whether a user has claimable deposit request cancellation and returns the total claim /// value in assets. function claimableCancelDepositRequest(address vault, address user) external view returns (uint256 assets); /// @notice Indicates whether a user has claimable redeem request cancellation and returns the total claim /// value in shares. function claimableCancelRedeemRequest(address vault, address user) external view returns (uint256 shares); /// @notice Returns the timestamp of the last share price update for a vault. function priceLastUpdated(address vault) external view returns (uint64 lastUpdated); // --- Vault claim functions --- /// @notice Processes owner's asset deposit after the epoch has been executed on Centrifuge and the deposit order /// has been successfully processed (partial fulfillment possible). /// Shares are transferred from the escrow to the receiver. Amount of shares is computed based of the amount /// of assets and the owner's share price. /// @dev The assets required to fulfill the deposit are already locked in escrow upon calling requestDeposit. /// The shares required to fulfill the deposit have already been minted and transferred to the escrow on /// fulfillDepositRequest. /// Receiver has to pass all the share token restrictions in order to receive the shares. function deposit(address vault, uint256 assets, address receiver, address owner) external returns (uint256 shares); /// @notice Processes owner's share mint after the epoch has been executed on Centrifuge and the deposit order has /// been successfully processed (partial fulfillment possible). /// Shares are transferred from the escrow to the receiver. Amount of assets is computed based of the amount /// of shares and the owner's share price. /// @dev The assets required to fulfill the mint are already locked in escrow upon calling requestDeposit. /// The shares required to fulfill the mint have already been minted and transferred to the escrow on /// fulfillDepositRequest. /// Receiver has to pass all the share token restrictions in order to receive the shares. function mint(address vault, uint256 shares, address receiver, address owner) external returns (uint256 assets); /// @notice Processes owner's share redemption after the epoch has been executed on Centrifuge and the redeem order /// has been successfully processed (partial fulfillment possible). /// Assets are transferred from the escrow to the receiver. Amount of assets is computed based of the amount /// of shares and the owner's share price. /// @dev The shares required to fulfill the redemption were already locked in escrow on requestRedeem and burned /// on fulfillRedeemRequest. /// The assets required to fulfill the redemption have already been reserved in escrow on /// fulfillRedeemtRequest. function redeem(address vault, uint256 shares, address receiver, address owner) external returns (uint256 assets); /// @notice Processes owner's asset withdrawal after the epoch has been executed on Centrifuge and the redeem order /// has been successfully processed (partial fulfillment possible). /// Assets are transferred from the escrow to the receiver. Amount of shares is computed based of the amount /// of shares and the owner's share price. /// @dev The shares required to fulfill the withdrawal were already locked in escrow on requestRedeem and burned /// on fulfillRedeemRequest. /// The assets required to fulfill the withdrawal have already been reserved in escrow on /// fulfillRedeemtRequest. function withdraw(address vault, uint256 assets, address receiver, address owner) external returns (uint256 shares); /// @notice Processes owner's deposit request cancellation after the epoch has been executed on Centrifuge and the /// deposit order cancellation has been successfully processed (partial fulfillment possible). /// Assets are transferred from the escrow to the receiver. /// @dev The assets required to fulfill the claim have already been reserved for the owner in escrow on /// fulfillCancelDepositRequest. function claimCancelDepositRequest(address vault, address receiver, address owner) external returns (uint256 assets); /// @notice Processes owner's redeem request cancellation after the epoch has been executed on Centrifuge and the /// redeem order cancellation has been successfully processed (partial fulfillment possible). /// Shares are transferred from the escrow to the receiver. /// @dev The shares required to fulfill the claim have already been reserved for the owner in escrow on /// fulfillCancelRedeemRequest. /// Receiver has to pass all the share token restrictions in order to receive the shares. function claimCancelRedeemRequest(address vault, address receiver, address owner) external returns (uint256 shares); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; interface IAuth { event Rely(address indexed user); event Deny(address indexed user); /// @notice Make user a ward (give them admin access) function rely(address user) external; /// @notice Remove user as a ward (remove admin access) function deny(address user) external; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; import {IRoot} from "src/interfaces/IRoot.sol"; import {IPoolManager} from "src/interfaces/IPoolManager.sol"; import {RestrictionUpdate} from "src/interfaces/token/IRestrictionManager.sol"; import {ITranche} from "src/interfaces/token/ITranche.sol"; import {InvestmentManager} from "src/InvestmentManager.sol"; import {CastLib} from "src/libraries/CastLib.sol"; import {IAuth} from "src/interfaces/IAuth.sol"; interface IPoolManagerNew is IPoolManager { function isPoolActive(uint64 poolId) external view returns (bool); } interface ITrancheOld { function authTransferFrom(address from, address to, uint256 value) external returns (bool); } interface IVaultOld { function poolId() external view returns (uint64); function trancheId() external view returns (bytes16); function share() external view returns (address); function manager() external view returns (address); function escrow() external view returns (address); function asset() external view returns (address); } interface IPoolManagerOld { function currencyAddressToId(address currency) external view returns (uint128); } contract MigrationSpellBase { using CastLib for *; string public NETWORK; address public ROOT_NEW; IRoot public rootNew; address public GUARDIAN_NEW; address public POOLMANAGER_NEW; address public RESTRICTIONMANAGER_NEW; ITranche public trancheTokenNew; IPoolManagerNew poolManager; address public ROOT_OLD; IRoot public rootOld; address public ADMIN_MULTISIG; address public GUARDIAN_OLD; address public VAULT_OLD; uint64 POOL_ID; bytes16 TRANCHE_ID; uint8 DECIMALS; uint128 CURRENCY_ID; IVaultOld public vaultOld; ITranche trancheTokenOld; InvestmentManager investmentManagerOld; IPoolManagerOld poolManagerOld; string public NAME; string public SYMBOL; string public NAME_OLD; string public SYMBOL_OLD; address[] public memberlistMembers; mapping(address => uint64) public validUntil; bool public partOneDone; bool public partTwoDone; uint256 constant ONE = 10 ** 27; address self; function castPartOne() public { require(!partOneDone, "spell-already-cast"); partOneDone = true; executePartOne(); } function castPartTwo() public { require(partOneDone, "part-one-not-cast"); require(!partTwoDone, "spell-already-cast"); partTwoDone = true; executePartTwo(); } function executePartOne() internal { self = address(this); rootOld = IRoot(address(ROOT_OLD)); rootNew = IRoot(address(ROOT_NEW)); vaultOld = IVaultOld(VAULT_OLD); POOL_ID = vaultOld.poolId(); TRANCHE_ID = vaultOld.trancheId(); poolManager = IPoolManagerNew(address(POOLMANAGER_NEW)); trancheTokenOld = ITranche(vaultOld.share()); DECIMALS = trancheTokenOld.decimals(); investmentManagerOld = InvestmentManager(vaultOld.manager()); poolManagerOld = IPoolManagerOld(address(investmentManagerOld.poolManager())); CURRENCY_ID = poolManagerOld.currencyAddressToId(vaultOld.asset()); rootOld.relyContract(address(investmentManagerOld), self); rootOld.relyContract(address(trancheTokenOld), self); // deploy new tranche token rootNew.relyContract(address(POOLMANAGER_NEW), self); if (!poolManager.isPoolActive(POOL_ID)) { poolManager.addPool(POOL_ID); } poolManager.addTranche(POOL_ID, TRANCHE_ID, NAME, SYMBOL, DECIMALS, RESTRICTIONMANAGER_NEW); if (poolManager.assetToId(vaultOld.asset()) == 0) { poolManager.addAsset(CURRENCY_ID, vaultOld.asset()); } poolManager.allowAsset(POOL_ID, CURRENCY_ID); trancheTokenNew = ITranche(poolManager.deployTranche(POOL_ID, TRANCHE_ID)); rootNew.relyContract(address(trancheTokenNew), self); poolManager.deployVault(POOL_ID, TRANCHE_ID, vaultOld.asset()); } function executePartTwo() internal { // add all old members to new memberlist and claim any tokens uint256 holderBalance; for (uint8 i; i < memberlistMembers.length; i++) { // add member to new memberlist poolManager.updateRestriction( POOL_ID, TRANCHE_ID, abi.encodePacked( uint8(RestrictionUpdate.UpdateMember), address(memberlistMembers[i]).toBytes32(), validUntil[memberlistMembers[i]] ) ); if (memberlistMembers[i] != vaultOld.escrow()) { uint256 maxMint = investmentManagerOld.maxMint(VAULT_OLD, memberlistMembers[i]); if (maxMint > 0) { // Claim any unclaimed tokens the user may have investmentManagerOld.mint(VAULT_OLD, maxMint, memberlistMembers[i], memberlistMembers[i]); } holderBalance = trancheTokenOld.balanceOf(memberlistMembers[i]); if (holderBalance > 0) { // mint new token to the holder's wallet trancheTokenNew.mint(memberlistMembers[i], holderBalance); // transfer old tokens from holders' wallets ITrancheOld(vaultOld.share()).authTransferFrom(memberlistMembers[i], self, holderBalance); } } } // burn entire supply of old tranche tokens trancheTokenOld.burn(self, trancheTokenOld.balanceOf(self)); // rename old tranche token trancheTokenOld.file("name", NAME_OLD); trancheTokenOld.file("symbol", SYMBOL_OLD); // denies rootNew.denyContract(address(POOLMANAGER_NEW), self); rootNew.denyContract(address(trancheTokenNew), self); rootOld.denyContract(address(trancheTokenOld), self); rootOld.denyContract(address(investmentManagerOld), self); IAuth(address(rootOld)).deny(self); IAuth(address(rootNew)).deny(self); } function getNumberOfMigratedMembers() public view returns (uint256) { return memberlistMembers.length; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; uint8 constant MAX_ADAPTER_COUNT = 8; interface IGateway { /// @dev Each adapter struct is packed with the quorum to reduce SLOADs on handle struct Adapter { /// @notice Starts at 1 and maps to id - 1 as the index on the adapters array uint8 id; /// @notice Number of votes required for a message to be executed uint8 quorum; /// @notice Each time the quorum is decreased, a new session starts which invalidates old votes uint64 activeSessionId; } struct Message { /// @dev Counts are stored as integers (instead of boolean values) to accommodate duplicate /// messages (e.g. two investments from the same user with the same amount) being /// processed in parallel. The entire struct is packed in a single bytes32 slot. /// Max uint16 = 65,535 so at most 65,535 duplicate messages can be processed in parallel. uint16[MAX_ADAPTER_COUNT] votes; /// @notice Each time adapters are updated, a new session starts which invalidates old votes uint64 sessionId; bytes pendingMessage; } // --- Events --- event HandleMessage(bytes message, address adapter); event HandleProof(bytes32 messageHash, address adapter); event ExecuteMessage(bytes message, address adapter); event SendMessage(bytes message); event RecoverMessage(address adapter, bytes message); event RecoverProof(address adapter, bytes32 messageHash); event InitiateMessageRecovery(bytes32 messageHash, address adapter); event DisputeMessageRecovery(bytes32 messageHash, address adapter); event ExecuteMessageRecovery(bytes message, address adapter); event File(bytes32 indexed what, address[] adapters); event File(bytes32 indexed what, address instance); event File(bytes32 indexed what, uint8 messageId, address manager); event Received(address indexed sender, uint256 amount); // --- Administration --- /// @notice Used to update an array of addresses ( state variable ) on very rare occasions. /// @dev Currently it is used to update the supported adapters. /// @param what The name of the variable to be updated. /// @param value New addresses. function file(bytes32 what, address[] calldata value) external; /// @notice Used to update an address ( state variable ) on very rare occasions. /// @dev Currently used to update addresses of contract instances. /// @param what The name of the variable to be updated. /// @param data New address. function file(bytes32 what, address data) external; /// @notice Used to update a mapping ( state variables ) on very rare occasions. /// @dev Currently used to update any custom handlers for a specific message type. /// data1 is the message id from MessagesLib.Call and data2 could be any /// custom instance of a contract that will handle that call. /// @param what The name of the variable to be updated. /// @param data1 The key of the mapping. /// @param data2 The value of the mapping function file(bytes32 what, uint8 data1, address data2) external; // --- Incoming --- /// @notice Handles incoming messages, proofs, and recoveries. /// @dev Assumes adapters ensure messages cannot be confirmed more than once. /// @param payload Incoming message from the Centrifuge Chain passed through adapters. function handle(bytes calldata payload) external; /// @notice Governance on Centrifuge Chain can initiate message recovery. After the challenge period, /// the recovery can be executed. If a malign adapter initiates message recovery, governance on /// Centrifuge Chain can dispute and immediately cancel the recovery, using any other valid adapter. /// @param adapter Adapter that the recovery was targeting /// @param messageHash Hash of the message being disputed function disputeMessageRecovery(address adapter, bytes32 messageHash) external; /// @notice Governance on Centrifuge Chain can initiate message recovery. After the challenge period, /// the recovery can be executed. If a malign adapter initiates message recovery, governance on /// Centrifuge Chain can dispute and immediately cancel the recovery, using any other valid adapter. /// /// Only 1 recovery can be outstanding per message hash. If multiple adapters fail at the same time, /// these will need to be recovered serially (increasing the challenge period for each failed adapter). /// @param adapter Adapter's address that the recovery is targeting /// @param message Hash of the message to be recovered function executeMessageRecovery(address adapter, bytes calldata message) external; // --- Outgoing --- /// @notice Sends outgoing messages to the Centrifuge Chain. /// @dev Sends 1 message to the first adapter with the full message, /// and n-1 messages to the other adapters with proofs (hash of message). /// This ensures message uniqueness (can only be executed on the destination once). /// Source could be either Centrifuge router or EoA or any contract /// that calls the ERC7540Vault contract directly. /// @param message Message to be send. Either the message itself or a hash value of it ( proof ). /// @param source Entry point of the transaction. /// Used to determine whether it is eligible for TX cost payment. function send(bytes calldata message, address source) external payable; /// @notice Prepays for the TX cost for sending through the adapters /// and Centrifuge Chain /// @dev It can be called only through endorsed contracts. /// Currently being called from Centrifuge Router only. /// In order to prepay, the method MUST be called with `msg.value`. /// Called is assumed to have called IGateway.estimate before calling this. function topUp() external payable; // --- Helpers --- /// @notice A view method of the current quorum.abi /// @dev Quorum shows the amount of votes needed in order for a message to be dispatched further. /// The quorum is taken from the first adapter. /// Current quorum is the amount of all adapters. /// return Needed amount function quorum() external view returns (uint8); /// @notice Gets the current active routers session id. /// @dev When the adapters are updated with new ones, /// each new set of adapters has their own sessionId. /// Currently it uses sessionId of the previous set and /// increments it by 1. The idea of an activeSessionId is /// to invalidate any incoming messages from previously used adapters. function activeSessionId() external view returns (uint64); /// @notice Counts how many times each incoming messages has been received per adapter. /// @dev It supports parallel messages ( duplicates ). That means that the incoming messages could be /// the result of two or more independ request from the user of the same type. /// i.e. Same user would like to deposit same underlying asset with the same amount more then once. /// @param messageHash The hash value of the incoming message. function votes(bytes32 messageHash) external view returns (uint16[MAX_ADAPTER_COUNT] memory); /// @notice Used to calculate overall cost for bridging a payload on the first adapter and settling /// on the destination chain and bridging its payload proofs on n-1 adapter /// and settling on the destination chain. /// @param payload Used in gas cost calculations. /// @dev Currenly the payload is not taken into consideration. /// @return tranches An array of cost values per adapter. Each value is how much it's going to cost /// for a message / proof to be passed through one router and executed on Centrifuge Chain /// @return total Total cost for sending one message and corresponding proofs on through all adapters function estimate(bytes calldata payload) external view returns (uint256[] memory tranches, uint256 total); } interface IMessageHandler { /// @notice Handling incoming messages from Centrifuge Chain. /// @param message Incoming message function handle(bytes memory message) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.0; /// @title IERC20 /// @dev Interface of the ERC20 standard as defined in the EIP. /// @author Modified from OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); } /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); } /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); } interface IERC20Wrapper { /** * @dev Returns the address of the underlying ERC-20 token that is being wrapped. */ function underlying() external view returns (address); /** * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. */ function depositFor(address account, uint256 value) external returns (bool); /** * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens. */ function withdrawTo(address account, uint256 value) external returns (bool); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } interface IERC7575 is IERC165 { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the address of the share token * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function share() external view returns (address shareTokenAddress); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); } interface IERC7575Share is IERC165 { event VaultUpdate(address indexed asset, address vault); /** * @dev Returns the address of the Vault for the given asset. * * @param asset the ERC-20 token to deposit with into the Vault */ function vault(address asset) external view returns (address); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; import {Auth} from "src/Auth.sol"; import {CastLib} from "src/libraries/CastLib.sol"; import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; import {MessagesLib} from "src/libraries/MessagesLib.sol"; import {BytesLib} from "src/libraries/BytesLib.sol"; import {IERC20, IERC20Metadata} from "src/interfaces/IERC20.sol"; import {IPoolManager} from "src/interfaces/IPoolManager.sol"; import {IInvestmentManager, InvestmentState} from "src/interfaces/IInvestmentManager.sol"; import {ITranche} from "src/interfaces/token/ITranche.sol"; import {IERC7540Vault} from "src/interfaces/IERC7540.sol"; import {IGateway} from "src/interfaces/gateway/IGateway.sol"; import {IRecoverable} from "src/interfaces/IRoot.sol"; /// @title Investment Manager /// @notice This is the main contract vaults interact with for /// both incoming and outgoing investment transactions. contract InvestmentManager is Auth, IInvestmentManager { using BytesLib for bytes; using MathLib for uint256; using CastLib for *; /// @dev Prices are fixed-point integers with 18 decimals uint8 internal constant PRICE_DECIMALS = 18; address public immutable root; address public immutable escrow; IGateway public gateway; IPoolManager public poolManager; mapping(address vault => mapping(address investor => InvestmentState)) public investments; constructor(address root_, address escrow_) { root = root_; escrow = escrow_; wards[msg.sender] = 1; emit Rely(msg.sender); } // --- Administration --- /// @inheritdoc IInvestmentManager function file(bytes32 what, address data) external auth { if (what == "gateway") gateway = IGateway(data); else if (what == "poolManager") poolManager = IPoolManager(data); else revert("InvestmentManager/file-unrecognized-param"); emit File(what, data); } /// @inheritdoc IRecoverable function recoverTokens(address token, address to, uint256 amount) external auth { SafeTransferLib.safeTransfer(token, to, amount); } // --- Outgoing message handling --- /// @inheritdoc IInvestmentManager function requestDeposit(address vault, uint256 assets, address controller, address, /* owner */ address source) public auth returns (bool) { IERC7540Vault vault_ = IERC7540Vault(vault); uint128 _assets = assets.toUint128(); require(_assets != 0, "InvestmentManager/zero-amount-not-allowed"); uint64 poolId = vault_.poolId(); address asset = vault_.asset(); require(poolManager.isAllowedAsset(poolId, asset), "InvestmentManager/asset-not-allowed"); require( _canTransfer(vault, address(0), controller, convertToShares(vault, assets)), "InvestmentManager/transfer-not-allowed" ); InvestmentState storage state = investments[vault][controller]; require(state.pendingCancelDepositRequest != true, "InvestmentManager/cancellation-is-pending"); state.pendingDepositRequest = state.pendingDepositRequest + _assets; gateway.send( abi.encodePacked( uint8(MessagesLib.Call.DepositRequest), poolId, vault_.trancheId(), controller.toBytes32(), poolManager.assetToId(asset), _assets ), source ); return true; } /// @inheritdoc IInvestmentManager function requestRedeem(address vault, uint256 shares, address controller, /* owner */ address, address source) public auth returns (bool) { uint128 _shares = shares.toUint128(); require(_shares != 0, "InvestmentManager/zero-amount-not-allowed"); IERC7540Vault vault_ = IERC7540Vault(vault); // You cannot redeem using a disallowed asset, instead another vault will have to be used require(poolManager.isAllowedAsset(vault_.poolId(), vault_.asset()), "InvestmentManager/asset-not-allowed"); return _processRedeemRequest(vault, _shares, controller, source, false); } /// @dev triggered indicates if the the _processRedeemRequest call was triggered from centrifugeChain function _processRedeemRequest(address vault, uint128 shares, address controller, address source, bool triggered) internal returns (bool) { IERC7540Vault vault_ = IERC7540Vault(vault); InvestmentState storage state = investments[vault][controller]; require(state.pendingCancelRedeemRequest != true || triggered, "InvestmentManager/cancellation-is-pending"); state.pendingRedeemRequest = state.pendingRedeemRequest + shares; gateway.send( abi.encodePacked( uint8(MessagesLib.Call.RedeemRequest), vault_.poolId(), vault_.trancheId(), controller.toBytes32(), poolManager.assetToId(vault_.asset()), shares ), source ); return true; } /// @inheritdoc IInvestmentManager function cancelDepositRequest(address vault, address controller, address source) public auth { IERC7540Vault _vault = IERC7540Vault(vault); InvestmentState storage state = investments[vault][controller]; require(state.pendingCancelDepositRequest != true, "InvestmentManager/cancellation-is-pending"); state.pendingCancelDepositRequest = true; gateway.send( abi.encodePacked( uint8(MessagesLib.Call.CancelDepositRequest), _vault.poolId(), _vault.trancheId(), controller.toBytes32(), poolManager.assetToId(_vault.asset()) ), source ); } /// @inheritdoc IInvestmentManager function cancelRedeemRequest(address vault, address controller, address source) public auth { IERC7540Vault _vault = IERC7540Vault(vault); uint256 approximateTranchesPayout = pendingRedeemRequest(vault, controller); require( _canTransfer(vault, address(0), controller, approximateTranchesPayout), "InvestmentManager/transfer-not-allowed" ); InvestmentState storage state = investments[vault][controller]; require(state.pendingCancelRedeemRequest != true, "InvestmentManager/cancellation-is-pending"); state.pendingCancelRedeemRequest = true; gateway.send( abi.encodePacked( uint8(MessagesLib.Call.CancelRedeemRequest), _vault.poolId(), _vault.trancheId(), controller.toBytes32(), poolManager.assetToId(_vault.asset()) ), source ); } // --- Incoming message handling --- /// @inheritdoc IInvestmentManager function handle(bytes calldata message) public auth { MessagesLib.Call call = MessagesLib.messageType(message); if (call == MessagesLib.Call.FulfilledDepositRequest) { fulfillDepositRequest( message.toUint64(1), message.toBytes16(9), message.toAddress(25), message.toUint128(57), message.toUint128(73), message.toUint128(89) ); } else if (call == MessagesLib.Call.FulfilledRedeemRequest) { fulfillRedeemRequest( message.toUint64(1), message.toBytes16(9), message.toAddress(25), message.toUint128(57), message.toUint128(73), message.toUint128(89) ); } else if (call == MessagesLib.Call.FulfilledCancelDepositRequest) { fulfillCancelDepositRequest( message.toUint64(1), message.toBytes16(9), message.toAddress(25), message.toUint128(57), message.toUint128(73), message.toUint128(89) ); } else if (call == MessagesLib.Call.FulfilledCancelRedeemRequest) { fulfillCancelRedeemRequest( message.toUint64(1), message.toBytes16(9), message.toAddress(25), message.toUint128(57), message.toUint128(73) ); } else if (call == MessagesLib.Call.TriggerRedeemRequest) { triggerRedeemRequest( message.toUint64(1), message.toBytes16(9), message.toAddress(25), message.toUint128(57), message.toUint128(73) ); } else { revert("InvestmentManager/invalid-message"); } } /// @inheritdoc IInvestmentManager function fulfillDepositRequest( uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 assets, uint128 shares ) public auth { address vault = poolManager.getVault(poolId, trancheId, assetId); InvestmentState storage state = investments[vault][user]; require(state.pendingDepositRequest != 0, "InvestmentManager/no-pending-deposit-request"); state.depositPrice = _calculatePrice(vault, _maxDeposit(vault, user) + assets, state.maxMint + shares); state.maxMint = state.maxMint + shares; state.pendingDepositRequest = state.pendingDepositRequest > assets ? state.pendingDepositRequest - assets : 0; if (state.pendingDepositRequest == 0) delete state.pendingCancelDepositRequest; // Mint to escrow. Recipient can claim by calling withdraw / redeem ITranche tranche = ITranche(IERC7540Vault(vault).share()); tranche.mint(address(escrow), shares); IERC7540Vault(vault).onDepositClaimable(user, assets, shares); } /// @inheritdoc IInvestmentManager function fulfillRedeemRequest( uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 assets, uint128 shares ) public auth { address vault = poolManager.getVault(poolId, trancheId, assetId); InvestmentState storage state = investments[vault][user]; require(state.pendingRedeemRequest != 0, "InvestmentManager/no-pending-redeem-request"); // Calculate new weighted average redeem price and update order book values state.redeemPrice = _calculatePrice(vault, state.maxWithdraw + assets, ((maxRedeem(vault, user)) + shares).toUint128()); state.maxWithdraw = state.maxWithdraw + assets; state.pendingRedeemRequest = state.pendingRedeemRequest > shares ? state.pendingRedeemRequest - shares : 0; if (state.pendingRedeemRequest == 0) delete state.pendingCancelRedeemRequest; // Burn redeemed tranche tokens from escrow ITranche tranche = ITranche(IERC7540Vault(vault).share()); tranche.burn(address(escrow), shares); IERC7540Vault(vault).onRedeemClaimable(user, assets, shares); } /// @inheritdoc IInvestmentManager function fulfillCancelDepositRequest( uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 assets, uint128 fulfillment ) public auth { address vault = poolManager.getVault(poolId, trancheId, assetId); InvestmentState storage state = investments[vault][user]; require(state.pendingCancelDepositRequest == true, "InvestmentManager/no-pending-cancel-deposit-request"); state.claimableCancelDepositRequest = state.claimableCancelDepositRequest + assets; state.pendingDepositRequest = state.pendingDepositRequest > fulfillment ? state.pendingDepositRequest - fulfillment : 0; if (state.pendingDepositRequest == 0) delete state.pendingCancelDepositRequest; IERC7540Vault(vault).onCancelDepositClaimable(user, assets); } /// @inheritdoc IInvestmentManager function fulfillCancelRedeemRequest(uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 shares) public auth { address vault = poolManager.getVault(poolId, trancheId, assetId); InvestmentState storage state = investments[vault][user]; require(state.pendingCancelRedeemRequest == true, "InvestmentManager/no-pending-cancel-redeem-request"); state.claimableCancelRedeemRequest = state.claimableCancelRedeemRequest + shares; state.pendingRedeemRequest = state.pendingRedeemRequest > shares ? state.pendingRedeemRequest - shares : 0; if (state.pendingRedeemRequest == 0) delete state.pendingCancelRedeemRequest; IERC7540Vault(vault).onCancelRedeemClaimable(user, shares); } /// @inheritdoc IInvestmentManager function triggerRedeemRequest(uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 shares) public auth { require(shares != 0, "InvestmentManager/tranche-token-amount-is-zero"); address vault = poolManager.getVault(poolId, trancheId, assetId); // If there's any unclaimed deposits, claim those first InvestmentState storage state = investments[vault][user]; uint128 tokensToTransfer = shares; if (state.maxMint >= shares) { // The full redeem request is covered by the claimable amount tokensToTransfer = 0; state.maxMint = state.maxMint - shares; } else if (state.maxMint != 0) { // The redeem request is only partially covered by the claimable amount tokensToTransfer = shares - state.maxMint; state.maxMint = 0; } require(_processRedeemRequest(vault, shares, user, msg.sender, true), "InvestmentManager/failed-redeem-request"); // Transfer the tranche token amount that was not covered by tokens still in escrow for claims, // from user to escrow (lock tranche tokens in escrow) if (tokensToTransfer != 0) { require( ITranche(address(IERC7540Vault(vault).share())).authTransferFrom( user, user, address(escrow), tokensToTransfer ), "InvestmentManager/transfer-failed" ); } emit TriggerRedeemRequest(poolId, trancheId, user, poolManager.idToAsset(assetId), shares); } // --- View functions --- /// @inheritdoc IInvestmentManager function convertToShares(address vault, uint256 _assets) public view returns (uint256 shares) { IERC7540Vault vault_ = IERC7540Vault(vault); (uint128 latestPrice,) = poolManager.getTranchePrice(vault_.poolId(), vault_.trancheId(), vault_.asset()); shares = uint256(_calculateShares(_assets.toUint128(), vault, latestPrice)); } /// @inheritdoc IInvestmentManager function convertToAssets(address vault, uint256 _shares) public view returns (uint256 assets) { IERC7540Vault vault_ = IERC7540Vault(vault); (uint128 latestPrice,) = poolManager.getTranchePrice(vault_.poolId(), vault_.trancheId(), vault_.asset()); assets = uint256(_calculateAssets(_shares.toUint128(), vault, latestPrice)); } /// @inheritdoc IInvestmentManager function maxDeposit(address vault, address user) public view returns (uint256 assets) { if (!_canTransfer(vault, address(escrow), user, 0)) return 0; assets = uint256(_maxDeposit(vault, user)); } function _maxDeposit(address vault, address user) internal view returns (uint128 assets) { InvestmentState memory state = investments[vault][user]; assets = _calculateAssets(state.maxMint, vault, state.depositPrice); } /// @inheritdoc IInvestmentManager function maxMint(address vault, address user) public view returns (uint256 shares) { if (!_canTransfer(vault, address(escrow), user, 0)) return 0; shares = uint256(investments[vault][user].maxMint); } /// @inheritdoc IInvestmentManager function maxWithdraw(address vault, address user) public view returns (uint256 assets) { assets = uint256(investments[vault][user].maxWithdraw); } /// @inheritdoc IInvestmentManager function maxRedeem(address vault, address user) public view returns (uint256 shares) { InvestmentState memory state = investments[vault][user]; shares = uint256(_calculateShares(state.maxWithdraw, vault, state.redeemPrice)); } /// @inheritdoc IInvestmentManager function pendingDepositRequest(address vault, address user) public view returns (uint256 assets) { assets = uint256(investments[vault][user].pendingDepositRequest); } /// @inheritdoc IInvestmentManager function pendingRedeemRequest(address vault, address user) public view returns (uint256 shares) { shares = uint256(investments[vault][user].pendingRedeemRequest); } /// @inheritdoc IInvestmentManager function pendingCancelDepositRequest(address vault, address user) public view returns (bool isPending) { isPending = investments[vault][user].pendingCancelDepositRequest; } /// @inheritdoc IInvestmentManager function pendingCancelRedeemRequest(address vault, address user) public view returns (bool isPending) { isPending = investments[vault][user].pendingCancelRedeemRequest; } /// @inheritdoc IInvestmentManager function claimableCancelDepositRequest(address vault, address user) public view returns (uint256 assets) { assets = investments[vault][user].claimableCancelDepositRequest; } /// @inheritdoc IInvestmentManager function claimableCancelRedeemRequest(address vault, address user) public view returns (uint256 shares) { shares = investments[vault][user].claimableCancelRedeemRequest; } /// @inheritdoc IInvestmentManager function priceLastUpdated(address vault) public view returns (uint64 lastUpdated) { IERC7540Vault vault_ = IERC7540Vault(vault); (, lastUpdated) = poolManager.getTranchePrice(vault_.poolId(), vault_.trancheId(), vault_.asset()); } // --- Vault claim functions --- /// @inheritdoc IInvestmentManager function deposit(address vault, uint256 assets, address receiver, address controller) public auth returns (uint256 shares) { InvestmentState storage state = investments[vault][controller]; uint128 shares_ = _calculateShares(assets.toUint128(), vault, state.depositPrice); _processDeposit(state, shares_, vault, receiver); shares = uint256(shares_); } /// @inheritdoc IInvestmentManager function mint(address vault, uint256 shares, address receiver, address controller) public auth returns (uint256 assets) { InvestmentState storage state = investments[vault][controller]; _processDeposit(state, shares.toUint128(), vault, receiver); assets = uint256(_calculateAssets(shares.toUint128(), vault, state.depositPrice)); } function _processDeposit(InvestmentState storage state, uint128 shares, address vault, address receiver) internal { require(shares != 0, "InvestmentManager/tranche-token-amount-is-zero"); require(shares <= state.maxMint, "InvestmentManager/exceeds-deposit-limits"); state.maxMint = state.maxMint - shares; require( IERC20(IERC7540Vault(vault).share()).transferFrom(address(escrow), receiver, shares), "InvestmentManager/tranche-tokens-transfer-failed" ); } /// @inheritdoc IInvestmentManager function redeem(address vault, uint256 shares, address receiver, address controller) public auth returns (uint256 assets) { InvestmentState storage state = investments[vault][controller]; uint128 assets_ = _calculateAssets(shares.toUint128(), vault, state.redeemPrice); _processRedeem(state, assets_, vault, receiver); assets = uint256(assets_); } /// @inheritdoc IInvestmentManager function withdraw(address vault, uint256 assets, address receiver, address controller) public auth returns (uint256 shares) { InvestmentState storage state = investments[vault][controller]; _processRedeem(state, assets.toUint128(), vault, receiver); shares = uint256(_calculateShares(assets.toUint128(), vault, state.redeemPrice)); } function _processRedeem(InvestmentState storage state, uint128 assets, address vault, address receiver) internal { IERC7540Vault vault_ = IERC7540Vault(vault); require(assets != 0, "InvestmentManager/asset-amount-is-zero"); require(assets <= state.maxWithdraw, "InvestmentManager/exceeds-redeem-limits"); state.maxWithdraw = state.maxWithdraw - assets; SafeTransferLib.safeTransferFrom(vault_.asset(), address(escrow), receiver, assets); } /// @inheritdoc IInvestmentManager function claimCancelDepositRequest(address vault, address receiver, address controller) public auth returns (uint256 assets) { InvestmentState storage state = investments[vault][controller]; assets = state.claimableCancelDepositRequest; state.claimableCancelDepositRequest = 0; SafeTransferLib.safeTransferFrom(IERC7540Vault(vault).asset(), address(escrow), receiver, assets); } /// @inheritdoc IInvestmentManager function claimCancelRedeemRequest(address vault, address receiver, address controller) public auth returns (uint256 shares) { InvestmentState storage state = investments[vault][controller]; shares = state.claimableCancelRedeemRequest; state.claimableCancelRedeemRequest = 0; require( IERC20(IERC7540Vault(vault).share()).transferFrom(address(escrow), receiver, shares), "InvestmentManager/tranche-tokens-transfer-failed" ); } // --- Helpers --- /// @dev Calculates share amount based on asset amount and share price. Returned value is in share decimals. function _calculateShares(uint128 assets, address vault, uint256 price) internal view returns (uint128 shares) { if (price == 0 || assets == 0) { shares = 0; } else { (uint8 assetDecimals, uint8 shareDecimals) = _getPoolDecimals(vault); uint256 sharesInPriceDecimals = _toPriceDecimals(assets, assetDecimals).mulDiv(10 ** PRICE_DECIMALS, price, MathLib.Rounding.Down); shares = _fromPriceDecimals(sharesInPriceDecimals, shareDecimals); } } /// @dev Calculates asset amount based on share amount and share price. Returned value is in asset decimals. function _calculateAssets(uint128 shares, address vault, uint256 price) internal view returns (uint128 assets) { if (price == 0 || shares == 0) { assets = 0; } else { (uint8 assetDecimals, uint8 shareDecimals) = _getPoolDecimals(vault); uint256 assetsInPriceDecimals = _toPriceDecimals(shares, shareDecimals).mulDiv(price, 10 ** PRICE_DECIMALS, MathLib.Rounding.Down); assets = _fromPriceDecimals(assetsInPriceDecimals, assetDecimals); } } /// @dev Calculates share price and returns the value in price decimals function _calculatePrice(address vault, uint128 assets, uint128 shares) internal view returns (uint256) { if (assets == 0 || shares == 0) { return 0; } (uint8 assetDecimals, uint8 shareDecimals) = _getPoolDecimals(vault); return _toPriceDecimals(assets, assetDecimals).mulDiv( 10 ** PRICE_DECIMALS, _toPriceDecimals(shares, shareDecimals), MathLib.Rounding.Down ); } /// @dev When converting assets to shares using the price, /// all values are normalized to PRICE_DECIMALS function _toPriceDecimals(uint128 _value, uint8 decimals) internal pure returns (uint256) { if (PRICE_DECIMALS == decimals) return uint256(_value); return uint256(_value) * 10 ** (PRICE_DECIMALS - decimals); } /// @dev Converts decimals of the value from the price decimals back to the intended decimals function _fromPriceDecimals(uint256 _value, uint8 decimals) internal pure returns (uint128) { if (PRICE_DECIMALS == decimals) return _value.toUint128(); return (_value / 10 ** (PRICE_DECIMALS - decimals)).toUint128(); } /// @dev Returns the asset decimals and the share decimals for a given vault function _getPoolDecimals(address vault) internal view returns (uint8 assetDecimals, uint8 shareDecimals) { assetDecimals = IERC20Metadata(IERC7540Vault(vault).asset()).decimals(); shareDecimals = IERC20Metadata(IERC7540Vault(vault).share()).decimals(); } /// @dev Checks transfer restrictions for the vault shares. Sender (from) and receiver (to) have both to pass the /// restrictions for a successful share transfer. function _canTransfer(address vault, address from, address to, uint256 value) internal view returns (bool) { ITranche share = ITranche(IERC7540Vault(vault).share()); return share.checkTransferRestriction(from, to, value); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; /// @title CastLib library CastLib { function toBytes32(address addr) internal pure returns (bytes32) { return bytes32(bytes20(addr)); } /// @dev Adds zero padding function toBytes32(string memory source) internal pure returns (bytes32) { return bytes32(bytes(source)); } /// @dev Removes zero padding function bytes128ToString(bytes memory _bytes128) internal pure returns (string memory) { require(_bytes128.length == 128, "Input should be 128 bytes"); uint8 i = 0; while (i < 128 && _bytes128[i] != 0) { i++; } bytes memory bytesArray = new bytes(i); for (uint8 j = 0; j < i; j++) { bytesArray[j] = _bytes128[j]; } return string(bytesArray); } function toString(bytes32 _bytes32) internal pure returns (string memory) { uint8 i = 0; while (i < 32 && _bytes32[i] != 0) { i++; } bytes memory bytesArray = new bytes(i); for (i = 0; i < 32 && _bytes32[i] != 0; i++) { bytesArray[i] = _bytes32[i]; } return string(bytesArray); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; import {IAuth} from "src/interfaces/IAuth.sol"; /// @title Auth /// @notice Simple authentication pattern /// @author Based on code from https://github.com/makerdao/dss contract Auth is IAuth { mapping(address => uint256) public wards; /// @dev Check if the msg.sender has permissions modifier auth() { require(wards[msg.sender] == 1, "Auth/not-authorized"); _; } /// @dev Give permissions to the user function rely(address user) external auth { wards[user] = 1; emit Rely(user); } /// @dev Remove permissions from the user function deny(address user) external auth { wards[user] = 0; emit Deny(user); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; /// @title Math Lib /// @dev Standard math utilities missing in the Solidity language. /// @author Modified from OpenZeppelin Contracts v4.9.3 (utils/math/Math.sol) library MathLib { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /// @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or /// denominator == 0 /// @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) /// with further edits by Uniswap Labs also under MIT license. // slither-disable-start divide-before-multiply function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } // slither-disable-end divide-before-multiply /// @notice Calculates x * y / denominator with full precision, following the selected rounding direction. function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /// @notice Safe type conversion from uint256 to uint128. function toUint128(uint256 _value) internal pure returns (uint128 value) { if (_value > type(uint128).max) { revert("MathLib/uint128-overflow"); } else { value = uint128(_value); } } /// @notice Returns the smallest of two numbers. function min(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? b : a; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; import {IERC20} from "src/interfaces/IERC20.sol"; /// @title Safe Transfer Lib /// @author Modified from Uniswap v3 Periphery (libraries/TransferHelper.sol) library SafeTransferLib { /// @notice Transfers tokens from the targeted address to the given destination /// @notice Errors if transfer fails /// @param token The contract address of the token to be transferred /// @param from The originating address from which the tokens will be transferred /// @param to The destination address of the transfer /// @param value The amount to be transferred function safeTransferFrom(address token, address from, address to, uint256 value) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeTransferLib/safe-transfer-from-failed"); } /// @notice Transfers tokens from msg.sender to a recipient /// @dev Errors if transfer fails /// @param token The contract address of the token which will be transferred /// @param to The recipient of the transfer /// @param value The value of the transfer function safeTransfer(address token, address to, uint256 value) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeTransferLib/safe-transfer-failed"); } /// @notice Approves the stipulated contract to spend the given allowance in the given token /// @dev Errors if approval fails /// @param token The contract address of the token to be approved /// @param to The target of the approval /// @param value The amount of the given token the target will be allowed to spend function safeApprove(address token, address to, uint256 value) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeTransferLib/safe-approve-failed"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; import {BytesLib} from "src/libraries/BytesLib.sol"; /// @title MessagesLib /// @dev Library for encoding and decoding messages. library MessagesLib { using BytesLib for bytes; enum Call { /// 0 - An invalid message Invalid, // --- Gateway --- /// 1 - Proof MessageProof, /// 2 - Initiate Message Recovery InitiateMessageRecovery, /// 3 - Dispute Message Recovery DisputeMessageRecovery, /// 4 - Batch Messages Batch, // --- Root --- /// 5 - Schedule an upgrade contract to be granted admin rights ScheduleUpgrade, /// 6 - Cancel a previously scheduled upgrade CancelUpgrade, /// 7 - Recover tokens sent to the wrong contract RecoverTokens, // --- Gas service --- /// 8 - Update Centrifuge gas price UpdateCentrifugeGasPrice, // --- Pool Manager --- /// 9 - Add an asset id -> EVM address mapping AddAsset, /// 10 - Add Pool AddPool, /// 11 - Add a Pool's Tranche Token AddTranche, /// 12 - Allow an asset to be used as an asset for investing in pools AllowAsset, /// 13 - Disallow an asset to be used as an asset for investing in pools DisallowAsset, /// 14 - Update the price of a Tranche Token UpdateTranchePrice, /// 15 - Update tranche token metadata UpdateTrancheMetadata, /// 16 - Update Tranche Hook UpdateTrancheHook, /// 17 - A transfer of assets TransferAssets, /// 18 - A transfer of tranche tokens TransferTrancheTokens, /// 19 - Update a user restriction UpdateRestriction, /// --- Investment Manager --- /// 20 - Increase an investment order by a given amount DepositRequest, /// 21 - Increase a Redeem order by a given amount RedeemRequest, /// 22 - Executed Collect Invest FulfilledDepositRequest, /// 23 - Executed Collect Redeem FulfilledRedeemRequest, /// 24 - Cancel an investment order CancelDepositRequest, /// 25 - Cancel a redeem order CancelRedeemRequest, /// 26 - Executed Decrease Invest Order FulfilledCancelDepositRequest, /// 27 - Executed Decrease Redeem Order FulfilledCancelRedeemRequest, /// 28 - Request redeem investor TriggerRedeemRequest } function messageType(bytes memory _msg) internal pure returns (Call _call) { _call = Call(_msg.toUint8(0)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.26; /// @title Bytes Lib /// @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. /// The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. /// @author Modified from Solidity Bytes Arrays Utils v0.8.0 library BytesLib { function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1, "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function toBytes16(bytes memory _bytes, uint256 _start) internal pure returns (bytes16) { require(_bytes.length >= _start + 16, "toBytes16_outOfBounds"); bytes16 tempBytes16; assembly { tempBytes16 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes16; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; import {IERC7575} from "src/interfaces/IERC7575.sol"; import {IRecoverable} from "src/interfaces/IRoot.sol"; interface IERC7540Operator { /** * @dev The event emitted when an operator is set. * * @param controller The address of the controller. * @param operator The address of the operator. * @param approved The approval status. */ event OperatorSet(address indexed controller, address indexed operator, bool approved); /** * @dev Sets or removes an operator for the caller. * * @param operator The address of the operator. * @param approved The approval status. */ function setOperator(address operator, bool approved) external returns (bool); /** * @dev Returns `true` if the `operator` is approved as an operator for an `controller`. * * @param controller The address of the controller. * @param operator The address of the operator. * @return status The approval status */ function isOperator(address controller, address operator) external view returns (bool status); } interface IERC7540Deposit is IERC7540Operator { event DepositRequest( address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets ); /** * @dev Transfers assets from sender into the Vault and submits a Request for asynchronous deposit. * * - MUST support ERC-20 approve / transferFrom on asset as a deposit Request flow. * - MUST revert if all of assets cannot be requested for deposit. * - owner MUST be msg.sender unless some unspecified explicit approval is given by the caller, * approval of ERC-20 tokens from owner to sender is NOT enough. * * @param assets the amount of deposit assets to transfer from owner * @param controller the controller of the request who will be able to operate the request * @param owner the source of the deposit assets * * NOTE: most implementations will require pre-approval of the Vault with the Vault's underlying asset token. */ function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256 requestId); /** * @dev Returns the amount of requested assets in Pending state. * * - MUST NOT include any assets in Claimable state for deposit or mint. * - MUST NOT show any variations depending on the caller. * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. */ function pendingDepositRequest(uint256 requestId, address controller) external view returns (uint256 pendingAssets); /** * @dev Returns the amount of requested assets in Claimable state for the controller to deposit or mint. * * - MUST NOT include any assets in Pending state. * - MUST NOT show any variations depending on the caller. * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. */ function claimableDepositRequest(uint256 requestId, address controller) external view returns (uint256 claimableAssets); /** * @dev Mints shares Vault shares to receiver by claiming the Request of the controller. * * - MUST emit the Deposit event. * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator. */ function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares); /** * @dev Mints exactly shares Vault shares to receiver by claiming the Request of the controller. * * - MUST emit the Deposit event. * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator. */ function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets); } interface IERC7540Redeem is IERC7540Operator { event RedeemRequest( address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets ); /** * @dev Assumes control of shares from sender into the Vault and submits a Request for asynchronous redeem. * * - MUST support a redeem Request flow where the control of shares is taken from sender directly * where msg.sender has ERC-20 approval over the shares of owner. * - MUST revert if all of shares cannot be requested for redeem. * * @param shares the amount of shares to be redeemed to transfer from owner * @param controller the controller of the request who will be able to operate the request * @param owner the source of the shares to be redeemed * * NOTE: most implementations will require pre-approval of the Vault with the Vault's share token. */ function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId); /** * @dev Returns the amount of requested shares in Pending state. * * - MUST NOT include any shares in Claimable state for redeem or withdraw. * - MUST NOT show any variations depending on the caller. * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. */ function pendingRedeemRequest(uint256 requestId, address controller) external view returns (uint256 pendingShares); /** * @dev Returns the amount of requested shares in Claimable state for the controller to redeem or withdraw. * * - MUST NOT include any shares in Pending state for redeem or withdraw. * - MUST NOT show any variations depending on the caller. * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. */ function claimableRedeemRequest(uint256 requestId, address controller) external view returns (uint256 claimableShares); } interface IERC7540CancelDeposit { event CancelDepositRequest(address indexed controller, uint256 indexed requestId, address sender); event CancelDepositClaim( address indexed receiver, address indexed controller, uint256 indexed requestId, address sender, uint256 assets ); /** * @dev Submits a Request for cancelling the pending deposit Request * * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller, * approval of ERC-20 tokens from controller to sender is NOT enough. * - MUST set pendingCancelDepositRequest to `true` for the returned requestId after request * - MUST increase claimableCancelDepositRequest for the returned requestId after fulfillment * - SHOULD be claimable using `claimCancelDepositRequest` * Note: while `pendingCancelDepositRequest` is `true`, `requestDeposit` cannot be called */ function cancelDepositRequest(uint256 requestId, address controller) external; /** * @dev Returns whether the deposit Request is pending cancelation * * - MUST NOT show any variations depending on the caller. */ function pendingCancelDepositRequest(uint256 requestId, address controller) external view returns (bool isPending); /** * @dev Returns the amount of assets that were canceled from a deposit Request, and can now be claimed. * * - MUST NOT show any variations depending on the caller. */ function claimableCancelDepositRequest(uint256 requestId, address controller) external view returns (uint256 claimableAssets); /** * @dev Claims the canceled deposit assets, and removes the pending cancelation Request * * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller, * approval of ERC-20 tokens from controller to sender is NOT enough. * - MUST set pendingCancelDepositRequest to `false` for the returned requestId after request * - MUST set claimableCancelDepositRequest to 0 for the returned requestId after fulfillment */ function claimCancelDepositRequest(uint256 requestId, address receiver, address controller) external returns (uint256 assets); } interface IERC7540CancelRedeem { event CancelRedeemRequest(address indexed controller, uint256 indexed requestId, address sender); event CancelRedeemClaim( address indexed receiver, address indexed controller, uint256 indexed requestId, address sender, uint256 shares ); /** * @dev Submits a Request for cancelling the pending redeem Request * * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller, * approval of ERC-20 tokens from controller to sender is NOT enough. * - MUST set pendingCancelRedeemRequest to `true` for the returned requestId after request * - MUST increase claimableCancelRedeemRequest for the returned requestId after fulfillment * - SHOULD be claimable using `claimCancelRedeemRequest` * Note: while `pendingCancelRedeemRequest` is `true`, `requestRedeem` cannot be called */ function cancelRedeemRequest(uint256 requestId, address controller) external; /** * @dev Returns whether the redeem Request is pending cancelation * * - MUST NOT show any variations depending on the caller. */ function pendingCancelRedeemRequest(uint256 requestId, address controller) external view returns (bool isPending); /** * @dev Returns the amount of shares that were canceled from a redeem Request, and can now be claimed. * * - MUST NOT show any variations depending on the caller. */ function claimableCancelRedeemRequest(uint256 requestId, address controller) external view returns (uint256 claimableShares); /** * @dev Claims the canceled redeem shares, and removes the pending cancelation Request * * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller, * approval of ERC-20 tokens from controller to sender is NOT enough. * - MUST set pendingCancelRedeemRequest to `false` for the returned requestId after request * - MUST set claimableCancelRedeemRequest to 0 for the returned requestId after fulfillment */ function claimCancelRedeemRequest(uint256 requestId, address receiver, address controller) external returns (uint256 shares); } interface IERC7741 { /** * @dev Grants or revokes permissions for `operator` to manage Requests on behalf of the * `msg.sender`, using an [EIP-712](./eip-712.md) signature. */ function authorizeOperator( address controller, address operator, bool approved, uint256 deadline, bytes32 nonce, bytes memory signature ) external returns (bool); /** * @dev Revokes the given `nonce` for `msg.sender` as the `owner`. */ function invalidateNonce(bytes32 nonce) external; /** * @dev Returns whether the given `nonce` has been used for the `controller`. */ function authorizations(address controller, bytes32 nonce) external view returns (bool used); /** * @dev Returns the `DOMAIN_SEPARATOR` as defined according to EIP-712. The `DOMAIN_SEPARATOR * should be unique to the contract and chain to prevent replay attacks from other domains, * and satisfy the requirements of EIP-712, but is otherwise unconstrained. */ function DOMAIN_SEPARATOR() external view returns (bytes32); } interface IERC7714 { /** * @dev Returns `true` if the `user` is permissioned to interact with the contract. */ function isPermissioned(address controller) external view returns (bool); } /** * @title IERC7540Vault * @dev This is the specific set of interfaces used by the Centrifuge impelmentation of ERC7540, * as a fully asynchronous Vault, with cancelation support, and authorize operator signature support. */ interface IERC7540Vault is IERC7540Deposit, IERC7540Redeem, IERC7540CancelDeposit, IERC7540CancelRedeem, IERC7575, IERC7741, IERC7714, IRecoverable { event DepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares); event RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares); event CancelDepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets); event CancelRedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 shares); /// @notice Identifier of the Centrifuge pool function poolId() external view returns (uint64); /// @notice Identifier of the tranche of the Centrifuge pool function trancheId() external view returns (bytes16); /// @notice Set msg.sender as operator of owner, to `approved` status /// @dev MUST be called by endorsed sender function setEndorsedOperator(address owner, bool approved) external; /// @notice Callback when a deposit Request becomes claimable function onDepositClaimable(address owner, uint256 assets, uint256 shares) external; /// @notice Callback when a redeem Request becomes claimable function onRedeemClaimable(address owner, uint256 assets, uint256 shares) external; /// @notice Callback when a claim deposit Request becomes claimable function onCancelDepositClaimable(address owner, uint256 assets) external; /// @notice Callback when a claim redeem Request becomes claimable function onCancelRedeemClaimable(address owner, uint256 shares) external; }
{ "remappings": [ "forge-std/=lib/forge-std/src/", "ds-test/=lib/forge-std/lib/ds-test/src/" ], "optimizer": { "enabled": true, "runs": 500 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ADMIN_MULTISIG","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GUARDIAN_NEW","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GUARDIAN_OLD","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME_OLD","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NETWORK","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOLMANAGER_NEW","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESTRICTIONMANAGER_NEW","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_NEW","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_OLD","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SYMBOL","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SYMBOL_OLD","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_OLD","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"castPartOne","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"castPartTwo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getNumberOfMigratedMembers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"memberlistMembers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"partOneDone","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"partTwoDone","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rootNew","outputs":[{"internalType":"contract IRoot","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rootOld","outputs":[{"internalType":"contract IRoot","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trancheTokenNew","outputs":[{"internalType":"contract ITranche","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"validUntil","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultOld","outputs":[{"internalType":"contract IVaultOld","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
608060405234801561000f575f80fd5b5060408051808201909152601081526f195d1a195c995d5b4b5b585a5b9b995d60821b60208201525f9061004390826103f5565b50600880546001600160a01b031990811673498016d30cd5f0db50d7ace329c07313a042050217909155600a8054821673d9d30ab47c0f096b0aa67e9b8b1624504a63e7fd179055600b80548216732559998026796ca6fd057f3aa66f2d6ecded9028179055600c8054821673110379504d933bed2e485e281bc3909d1e7c9e5d179055600180548216730c1fdfd6a1331a875ea013f3897fc8a76ada5dfc1790556003805482167309ab10a9c3e6eac1d18270a2322b6113f4c7f5e81790556004805482167391808b5e2f6d7483d41a681034d7c9dbb64b9e2917905560058054909116734737c3f62cc265e786b280153fc666cea2fbc0c017905560408051808201909152601881527f416e656d6f792044654669205969656c642046756e6420310000000000000000602082015260139061018190826103f5565b50604080518082019091526003815262222ca360e91b60208201526014906101a990826103f5565b5060408051808201909152601081526f4459462028646570726563617465642960801b60208201526015906101de90826103f5565b5060408051808201909152600e81526d1116518b511154149150d055115160921b602082015260169061021190826103f5565b506040805180820190915273d595e1483c507e74e2e6a3de8e7d08d8f6f7493681527330d3bbae8623d0e9c0db5c27b82dcda39de40997602082015261025b9060179060026102e6565b5060186020527fde4ac3826de75deea1dcf83daef6e91ebc9817d98baa804591dddc9970b2d04680546001600160401b03199081166001600160401b03179091557330d3bbae8623d0e9c0db5c27b82dcda39de409975f527f56092bf370a2103bb68e723dff0efb11389903bfa87d4be4862ef83f1ed0a956805490911663792095a61790556104af565b828054828255905f5260205f20908101928215610339579160200282015b8281111561033957825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190610304565b50610345929150610349565b5090565b5b80821115610345575f815560010161034a565b634e487b7160e01b5f52604160045260245ffd5b600181811c9082168061038557607f821691505b6020821081036103a357634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156103f057805f5260205f20601f840160051c810160208510156103ce5750805b601f840160051c820191505b818110156103ed575f81556001016103da565b50505b505050565b81516001600160401b0381111561040e5761040e61035d565b6104228161041c8454610371565b846103a9565b6020601f821160018114610454575f831561043d5750848201515b5f19600385901b1c1916600184901b1784556103ed565b5f84815260208120601f198516915b828110156104835787850151825560209485019460019092019101610463565b50848210156104a057868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b611f6f806104bc5f395ff3fe608060405234801561000f575f80fd5b506004361061018f575f3560e01c8063a4a8e1be116100dd578063d1345dad11610088578063f76f8d7811610063578063f76f8d7814610342578063f9e742591461034a578063ff91f1221461035d575f80fd5b8063d1345dad14610320578063d3f0545b14610332578063e5d797191461033a575f80fd5b8063b0214982116100b8578063b0214982146102f0578063c482328014610303578063d071d0aa1461030d575f80fd5b8063a4a8e1be146102b9578063a9192cdc146102ca578063a9634ab8146102dd575f80fd5b80636a18ba7f1161013d5780638bb7cfa2116101185780638bb7cfa21461025c5780639604fd851461026f578063a3f4df7e146102b1575f80fd5b80636a18ba7f146102245780637a0df290146102375780638759e6d114610254575f80fd5b80633c27b4681161016d5780633c27b468146101eb5780633f33a480146101fe57806346eb378114610211575f80fd5b80630a7810321461019357806314b57192146101c35780631d013c37146101d8575b5f80fd5b6101a66101a1366004611bce565b610370565b6040516001600160a01b0390911681526020015b60405180910390f35b6101cb610398565b6040516101ba9190611c13565b6009546101a6906001600160a01b031681565b600f546101a6906001600160a01b031681565b6005546101a6906001600160a01b031681565b6006546101a6906001600160a01b031681565b6001546101a6906001600160a01b031681565b6019546102449060ff1681565b60405190151581526020016101ba565b6101cb610424565b6004546101a6906001600160a01b031681565b61029861027d366004611c40565b60186020525f908152604090205467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101ba565b6101cb610430565b6017546040519081526020016101ba565b600b546101a6906001600160a01b031681565b600c546101a6906001600160a01b031681565b6008546101a6906001600160a01b031681565b61030b61043d565b005b600a546101a6906001600160a01b031681565b60195461024490610100900460ff1681565b61030b6104fa565b6101cb610557565b6101cb610564565b6003546101a6906001600160a01b031681565b6002546101a6906001600160a01b031681565b6017818154811061037f575f80fd5b5f918252602090912001546001600160a01b0316905081565b601680546103a590611c5b565b80601f01602080910402602001604051908101604052809291908181526020018280546103d190611c5b565b801561041c5780601f106103f35761010080835404028352916020019161041c565b820191905f5260205f20905b8154815290600101906020018083116103ff57829003601f168201915b505050505081565b5f80546103a590611c5b565b601380546103a590611c5b565b60195460ff166104945760405162461bcd60e51b815260206004820152601160248201527f706172742d6f6e652d6e6f742d6361737400000000000000000000000000000060448201526064015b60405180910390fd5b601954610100900460ff16156104e15760405162461bcd60e51b81526020600482015260126024820152711cdc195b1b0b585b1c9958591e4b58d85cdd60721b604482015260640161048b565b6019805461ff0019166101001790556104f8610571565b565b60195460ff16156105425760405162461bcd60e51b81526020600482015260126024820152711cdc195b1b0b585b1c9958591e4b58d85cdd60721b604482015260640161048b565b6019805460ff191660011790556104f8610fd1565b601580546103a590611c5b565b601480546103a590611c5b565b5f805b60175460ff82161015610bbe57600754600c54600d546001600160a01b0390921691631f1b5aba91600160a01b900467ffffffffffffffff169060801b60016105ec60178760ff16815481106105cc576105cc611c93565b5f9182526020909120015460601b6bffffffffffffffffffffffff191690565b60185f60178960ff168154811061060557610605611c93565b5f9182526020808320909101546001600160a01b0316835282810193909352604091820190205490516106a494939267ffffffffffffffff909216910160f89390931b7fff00000000000000000000000000000000000000000000000000000000000000168352600183019190915260c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016602182015260290190565b6040516020818303038152906040526040518463ffffffff1660e01b81526004016106d193929190611ca7565b5f604051808303815f87803b1580156106e8575f80fd5b505af11580156106fa573d5f803e3d5ffd5b50505050600f5f9054906101000a90046001600160a01b03166001600160a01b031663e2fdcc176040518163ffffffff1660e01b8152600401602060405180830381865afa15801561074e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107729190611ce2565b6001600160a01b031660178260ff168154811061079157610791611c93565b5f918252602090912001546001600160a01b031614610bac57601154600c54601780545f936001600160a01b039081169363f2e586db9391169160ff87169081106107de576107de611c93565b5f9182526020909120015460405160e084901b6001600160e01b03191681526001600160a01b03928316600482015291166024820152604401602060405180830381865afa158015610832573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108569190611cfd565b9050801561094c57601154600c54601780546001600160a01b0393841693633a4d2cd1931691859160ff881690811061089157610891611c93565b5f91825260209091200154601780546001600160a01b039092169160ff89169081106108bf576108bf611c93565b5f9182526020909120015460405160e086901b6001600160e01b03191681526001600160a01b039485166004820152602481019390935290831660448301529190911660648201526084016020604051808303815f875af1158015610926573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061094a9190611cfd565b505b601054601780546001600160a01b03909216916370a08231919060ff861690811061097957610979611c93565b5f9182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602401602060405180830381865afa1580156109c6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109ea9190611cfd565b92508215610baa57600654601780546001600160a01b03909216916340c10f19919060ff8616908110610a1f57610a1f611c93565b5f9182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602481018690526044015f604051808303815f87803b158015610a6f575f80fd5b505af1158015610a81573d5f803e3d5ffd5b50505050600f5f9054906101000a90046001600160a01b03166001600160a01b031663a8d5fd656040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ad5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610af99190611ce2565b6001600160a01b031663951ef30c60178460ff1681548110610b1d57610b1d611c93565b5f9182526020909120015460195460405160e084901b6001600160e01b03191681526001600160a01b039283166004820152620100009091049091166024820152604481018690526064016020604051808303815f875af1158015610b84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ba89190611d14565b505b505b80610bb681611d33565b915050610574565b506010546019546040516370a0823160e01b8152620100009091046001600160a01b0390811660048301819052921691639dc29fac9183906370a0823190602401602060405180830381865afa158015610c1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3e9190611cfd565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610c81575f80fd5b505af1158015610c93573d5f803e3d5ffd5b50506010546040516304bd623760e11b81526001600160a01b03909116925063097ac46e9150610cc890601590600401611dfb565b5f604051808303815f87803b158015610cdf575f80fd5b505af1158015610cf1573d5f803e3d5ffd5b50506010546040516304bd623760e11b81526001600160a01b03909116925063097ac46e9150610d2690601690600401611e1a565b5f604051808303815f87803b158015610d3d575f80fd5b505af1158015610d4f573d5f803e3d5ffd5b50506002546004805460195460405163fe0ac3e760e01b81526001600160a01b039283169381019390935262010000900481166024830152909116925063fe0ac3e791506044015f604051808303815f87803b158015610dad575f80fd5b505af1158015610dbf573d5f803e3d5ffd5b505060025460065460195460405163fe0ac3e760e01b81526001600160a01b03928316600482015262010000909104821660248201529116925063fe0ac3e791506044015f604051808303815f87803b158015610e1a575f80fd5b505af1158015610e2c573d5f803e3d5ffd5b505060095460105460195460405163fe0ac3e760e01b81526001600160a01b03928316600482015262010000909104821660248201529116925063fe0ac3e791506044015f604051808303815f87803b158015610e87575f80fd5b505af1158015610e99573d5f803e3d5ffd5b505060095460115460195460405163fe0ac3e760e01b81526001600160a01b03928316600482015262010000909104821660248201529116925063fe0ac3e791506044015f604051808303815f87803b158015610ef4575f80fd5b505af1158015610f06573d5f803e3d5ffd5b5050600954601954604051639c52a7f160e01b81526001600160a01b03620100009092048216600482015291169250639c52a7f191506024015f604051808303815f87803b158015610f56575f80fd5b505af1158015610f68573d5f803e3d5ffd5b5050600254601954604051639c52a7f160e01b81526001600160a01b03620100009092048216600482015291169250639c52a7f191506024015f604051808303815f87803b158015610fb8575f80fd5b505af1158015610fca573d5f803e3d5ffd5b5050505050565b6019805462010000300275ffffffffffffffffffffffffffffffffffffffff000019909116179055600854600980546001600160a01b03199081166001600160a01b0393841617909155600154600280548316918416919091179055600c54600f80549092169216918217905560408051631f06e1a760e11b81529051633e0dc34e916004808201926020929091908290030181865afa158015611077573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061109b9190611e3b565b600c60146101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550600f5f9054906101000a90046001600160a01b03166001600160a01b031663b7adb4c56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611113573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111379190611e62565b600d805460809290921c6001600160801b031990921691909117905560048054600780546001600160a01b0319166001600160a01b03928316179055600f546040805163a8d5fd6560e01b81529051919092169263a8d5fd65928082019260209290918290030181865afa1580156111b1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111d59190611ce2565b601080546001600160a01b0319166001600160a01b039290921691821790556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801561122d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112519190611e89565b600d60106101000a81548160ff021916908360ff160217905550600f5f9054906101000a90046001600160a01b03166001600160a01b031663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112bb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112df9190611ce2565b601180546001600160a01b0319166001600160a01b039290921691821790556040805163dc4c90d360e01b8152905163dc4c90d3916004808201926020929091908290030181865afa158015611337573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061135b9190611ce2565b601280546001600160a01b0319166001600160a01b03928316908117909155600f54604080516338d52e0f60e01b815290519293638246a451939216916338d52e0f916004808201926020929091908290030181865afa1580156113c1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113e59190611ce2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611427573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061144b9190611ea9565b600e80546001600160801b0319166001600160801b03929092169190911790556009546011546019546040516306e7ce7f60e31b81526001600160a01b039283166004820152620100009091048216602482015291169063373e73f8906044015f604051808303815f87803b1580156114c2575f80fd5b505af11580156114d4573d5f803e3d5ffd5b50506009546010546019546040516306e7ce7f60e31b81526001600160a01b03928316600482015262010000909104821660248201529116925063373e73f891506044015f604051808303815f87803b15801561152f575f80fd5b505af1158015611541573d5f803e3d5ffd5b5050600254600480546019546040516306e7ce7f60e31b81526001600160a01b039283169381019390935262010000900481166024830152909116925063373e73f891506044015f604051808303815f87803b15801561159f575f80fd5b505af11580156115b1573d5f803e3d5ffd5b5050600754600c54604051633194981d60e21b8152600160a01b90910467ffffffffffffffff1660048201526001600160a01b03909116925063c65260749150602401602060405180830381865afa15801561160f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116339190611d14565b6116a457600754600c54604051632b89610160e01b8152600160a01b90910467ffffffffffffffff1660048201526001600160a01b0390911690632b896101906024015f604051808303815f87803b15801561168d575f80fd5b505af115801561169f573d5f803e3d5ffd5b505050505b600754600c54600d546005546040516363beaff360e11b81526001600160a01b039485169463c77d5fe69461171494600160a01b90910467ffffffffffffffff1693608082901b93601393601493700100000000000000000000000000000000900460ff16921690600401611ecf565b5f604051808303815f87803b15801561172b575f80fd5b505af115801561173d573d5f803e3d5ffd5b5050600754600f54604080516338d52e0f60e01b815290516001600160a01b039384169550632fc165ba945091909216916338d52e0f9160048083019260209291908290030181865afa158015611796573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117ba9190611ce2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156117fc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118209190611ea9565b6001600160801b03165f0361191557600754600e54600f54604080516338d52e0f60e01b815290516001600160a01b0394851694638aa2e5b0946001600160801b03169316916338d52e0f9160048083019260209291908290030181865afa15801561188e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118b29190611ce2565b6040516001600160e01b031960e085901b1681526001600160801b0390921660048301526001600160a01b031660248201526044015f604051808303815f87803b1580156118fe575f80fd5b505af1158015611910573d5f803e3d5ffd5b505050505b600754600c54600e5460405163089948b560e01b8152600160a01b90920467ffffffffffffffff1660048301526001600160801b031660248201526001600160a01b039091169063089948b5906044015f604051808303815f87803b15801561197c575f80fd5b505af115801561198e573d5f803e3d5ffd5b5050600754600c54600d5460405163b6405b3960e01b8152600160a01b90920467ffffffffffffffff1660048301526001600160801b031960809190911b1660248201526001600160a01b03909116925063b6405b3991506044016020604051808303815f875af1158015611a05573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a299190611ce2565b600680546001600160a01b0319166001600160a01b039283169081179091556002546019546040516306e7ce7f60e31b81526004810193909352620100009004831660248301529091169063373e73f8906044015f604051808303815f87803b158015611a94575f80fd5b505af1158015611aa6573d5f803e3d5ffd5b5050600754600c54600d54600f54604080516338d52e0f60e01b815290516001600160a01b0395861697506304dbf1989650600160a01b90940467ffffffffffffffff169460809390931b9392909116916338d52e0f916004808201926020929091908290030181865afa158015611b20573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b449190611ce2565b6040516001600160e01b031960e086901b16815267ffffffffffffffff90931660048401526001600160801b031990911660248301526001600160a01b031660448201526064016020604051808303815f875af1158015611ba7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bcb9190611ce2565b50565b5f60208284031215611bde575f80fd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f611c256020830184611be5565b9392505050565b6001600160a01b0381168114611bcb575f80fd5b5f60208284031215611c50575f80fd5b8135611c2581611c2c565b600181811c90821680611c6f57607f821691505b602082108103611c8d57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b67ffffffffffffffff841681526001600160801b031983166020820152606060408201525f611cd96060830184611be5565b95945050505050565b5f60208284031215611cf2575f80fd5b8151611c2581611c2c565b5f60208284031215611d0d575f80fd5b5051919050565b5f60208284031215611d24575f80fd5b81518015158114611c25575f80fd5b5f60ff821660ff8103611d5457634e487b7160e01b5f52601160045260245ffd5b60010192915050565b80545f90600181811c90821680611d7557607f821691505b602082108103611d9357634e487b7160e01b5f52602260045260245ffd5b81865260208601818015611dae5760018114611dc457611df0565b60ff198516825283151560051b82019550611df0565b5f878152602090205f5b85811015611dea57815484820152600190910190602001611dce565b83019650505b505050505092915050565b636e616d6560e01b8152604060208201525f611c256040830184611d5d565b651cde5b589bdb60d21b8152604060208201525f611c256040830184611d5d565b5f60208284031215611e4b575f80fd5b815167ffffffffffffffff81168114611c25575f80fd5b5f60208284031215611e72575f80fd5b81516001600160801b031981168114611c25575f80fd5b5f60208284031215611e99575f80fd5b815160ff81168114611c25575f80fd5b5f60208284031215611eb9575f80fd5b81516001600160801b0381168114611c25575f80fd5b67ffffffffffffffff871681526001600160801b03198616602082015260c060408201525f611f0160c0830187611d5d565b8281036060840152611f138187611d5d565b91505060ff841660808301526001600160a01b03831660a083015297965050505050505056fea2646970667358221220bbde8a1b4769ae02a167e5eaf00ed0d396e709de913a3361990a14e628e6d99364736f6c634300081a0033
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061018f575f3560e01c8063a4a8e1be116100dd578063d1345dad11610088578063f76f8d7811610063578063f76f8d7814610342578063f9e742591461034a578063ff91f1221461035d575f80fd5b8063d1345dad14610320578063d3f0545b14610332578063e5d797191461033a575f80fd5b8063b0214982116100b8578063b0214982146102f0578063c482328014610303578063d071d0aa1461030d575f80fd5b8063a4a8e1be146102b9578063a9192cdc146102ca578063a9634ab8146102dd575f80fd5b80636a18ba7f1161013d5780638bb7cfa2116101185780638bb7cfa21461025c5780639604fd851461026f578063a3f4df7e146102b1575f80fd5b80636a18ba7f146102245780637a0df290146102375780638759e6d114610254575f80fd5b80633c27b4681161016d5780633c27b468146101eb5780633f33a480146101fe57806346eb378114610211575f80fd5b80630a7810321461019357806314b57192146101c35780631d013c37146101d8575b5f80fd5b6101a66101a1366004611bce565b610370565b6040516001600160a01b0390911681526020015b60405180910390f35b6101cb610398565b6040516101ba9190611c13565b6009546101a6906001600160a01b031681565b600f546101a6906001600160a01b031681565b6005546101a6906001600160a01b031681565b6006546101a6906001600160a01b031681565b6001546101a6906001600160a01b031681565b6019546102449060ff1681565b60405190151581526020016101ba565b6101cb610424565b6004546101a6906001600160a01b031681565b61029861027d366004611c40565b60186020525f908152604090205467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101ba565b6101cb610430565b6017546040519081526020016101ba565b600b546101a6906001600160a01b031681565b600c546101a6906001600160a01b031681565b6008546101a6906001600160a01b031681565b61030b61043d565b005b600a546101a6906001600160a01b031681565b60195461024490610100900460ff1681565b61030b6104fa565b6101cb610557565b6101cb610564565b6003546101a6906001600160a01b031681565b6002546101a6906001600160a01b031681565b6017818154811061037f575f80fd5b5f918252602090912001546001600160a01b0316905081565b601680546103a590611c5b565b80601f01602080910402602001604051908101604052809291908181526020018280546103d190611c5b565b801561041c5780601f106103f35761010080835404028352916020019161041c565b820191905f5260205f20905b8154815290600101906020018083116103ff57829003601f168201915b505050505081565b5f80546103a590611c5b565b601380546103a590611c5b565b60195460ff166104945760405162461bcd60e51b815260206004820152601160248201527f706172742d6f6e652d6e6f742d6361737400000000000000000000000000000060448201526064015b60405180910390fd5b601954610100900460ff16156104e15760405162461bcd60e51b81526020600482015260126024820152711cdc195b1b0b585b1c9958591e4b58d85cdd60721b604482015260640161048b565b6019805461ff0019166101001790556104f8610571565b565b60195460ff16156105425760405162461bcd60e51b81526020600482015260126024820152711cdc195b1b0b585b1c9958591e4b58d85cdd60721b604482015260640161048b565b6019805460ff191660011790556104f8610fd1565b601580546103a590611c5b565b601480546103a590611c5b565b5f805b60175460ff82161015610bbe57600754600c54600d546001600160a01b0390921691631f1b5aba91600160a01b900467ffffffffffffffff169060801b60016105ec60178760ff16815481106105cc576105cc611c93565b5f9182526020909120015460601b6bffffffffffffffffffffffff191690565b60185f60178960ff168154811061060557610605611c93565b5f9182526020808320909101546001600160a01b0316835282810193909352604091820190205490516106a494939267ffffffffffffffff909216910160f89390931b7fff00000000000000000000000000000000000000000000000000000000000000168352600183019190915260c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016602182015260290190565b6040516020818303038152906040526040518463ffffffff1660e01b81526004016106d193929190611ca7565b5f604051808303815f87803b1580156106e8575f80fd5b505af11580156106fa573d5f803e3d5ffd5b50505050600f5f9054906101000a90046001600160a01b03166001600160a01b031663e2fdcc176040518163ffffffff1660e01b8152600401602060405180830381865afa15801561074e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107729190611ce2565b6001600160a01b031660178260ff168154811061079157610791611c93565b5f918252602090912001546001600160a01b031614610bac57601154600c54601780545f936001600160a01b039081169363f2e586db9391169160ff87169081106107de576107de611c93565b5f9182526020909120015460405160e084901b6001600160e01b03191681526001600160a01b03928316600482015291166024820152604401602060405180830381865afa158015610832573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108569190611cfd565b9050801561094c57601154600c54601780546001600160a01b0393841693633a4d2cd1931691859160ff881690811061089157610891611c93565b5f91825260209091200154601780546001600160a01b039092169160ff89169081106108bf576108bf611c93565b5f9182526020909120015460405160e086901b6001600160e01b03191681526001600160a01b039485166004820152602481019390935290831660448301529190911660648201526084016020604051808303815f875af1158015610926573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061094a9190611cfd565b505b601054601780546001600160a01b03909216916370a08231919060ff861690811061097957610979611c93565b5f9182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602401602060405180830381865afa1580156109c6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109ea9190611cfd565b92508215610baa57600654601780546001600160a01b03909216916340c10f19919060ff8616908110610a1f57610a1f611c93565b5f9182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602481018690526044015f604051808303815f87803b158015610a6f575f80fd5b505af1158015610a81573d5f803e3d5ffd5b50505050600f5f9054906101000a90046001600160a01b03166001600160a01b031663a8d5fd656040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ad5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610af99190611ce2565b6001600160a01b031663951ef30c60178460ff1681548110610b1d57610b1d611c93565b5f9182526020909120015460195460405160e084901b6001600160e01b03191681526001600160a01b039283166004820152620100009091049091166024820152604481018690526064016020604051808303815f875af1158015610b84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ba89190611d14565b505b505b80610bb681611d33565b915050610574565b506010546019546040516370a0823160e01b8152620100009091046001600160a01b0390811660048301819052921691639dc29fac9183906370a0823190602401602060405180830381865afa158015610c1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3e9190611cfd565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610c81575f80fd5b505af1158015610c93573d5f803e3d5ffd5b50506010546040516304bd623760e11b81526001600160a01b03909116925063097ac46e9150610cc890601590600401611dfb565b5f604051808303815f87803b158015610cdf575f80fd5b505af1158015610cf1573d5f803e3d5ffd5b50506010546040516304bd623760e11b81526001600160a01b03909116925063097ac46e9150610d2690601690600401611e1a565b5f604051808303815f87803b158015610d3d575f80fd5b505af1158015610d4f573d5f803e3d5ffd5b50506002546004805460195460405163fe0ac3e760e01b81526001600160a01b039283169381019390935262010000900481166024830152909116925063fe0ac3e791506044015f604051808303815f87803b158015610dad575f80fd5b505af1158015610dbf573d5f803e3d5ffd5b505060025460065460195460405163fe0ac3e760e01b81526001600160a01b03928316600482015262010000909104821660248201529116925063fe0ac3e791506044015f604051808303815f87803b158015610e1a575f80fd5b505af1158015610e2c573d5f803e3d5ffd5b505060095460105460195460405163fe0ac3e760e01b81526001600160a01b03928316600482015262010000909104821660248201529116925063fe0ac3e791506044015f604051808303815f87803b158015610e87575f80fd5b505af1158015610e99573d5f803e3d5ffd5b505060095460115460195460405163fe0ac3e760e01b81526001600160a01b03928316600482015262010000909104821660248201529116925063fe0ac3e791506044015f604051808303815f87803b158015610ef4575f80fd5b505af1158015610f06573d5f803e3d5ffd5b5050600954601954604051639c52a7f160e01b81526001600160a01b03620100009092048216600482015291169250639c52a7f191506024015f604051808303815f87803b158015610f56575f80fd5b505af1158015610f68573d5f803e3d5ffd5b5050600254601954604051639c52a7f160e01b81526001600160a01b03620100009092048216600482015291169250639c52a7f191506024015f604051808303815f87803b158015610fb8575f80fd5b505af1158015610fca573d5f803e3d5ffd5b5050505050565b6019805462010000300275ffffffffffffffffffffffffffffffffffffffff000019909116179055600854600980546001600160a01b03199081166001600160a01b0393841617909155600154600280548316918416919091179055600c54600f80549092169216918217905560408051631f06e1a760e11b81529051633e0dc34e916004808201926020929091908290030181865afa158015611077573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061109b9190611e3b565b600c60146101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550600f5f9054906101000a90046001600160a01b03166001600160a01b031663b7adb4c56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611113573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111379190611e62565b600d805460809290921c6001600160801b031990921691909117905560048054600780546001600160a01b0319166001600160a01b03928316179055600f546040805163a8d5fd6560e01b81529051919092169263a8d5fd65928082019260209290918290030181865afa1580156111b1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111d59190611ce2565b601080546001600160a01b0319166001600160a01b039290921691821790556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801561122d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112519190611e89565b600d60106101000a81548160ff021916908360ff160217905550600f5f9054906101000a90046001600160a01b03166001600160a01b031663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112bb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112df9190611ce2565b601180546001600160a01b0319166001600160a01b039290921691821790556040805163dc4c90d360e01b8152905163dc4c90d3916004808201926020929091908290030181865afa158015611337573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061135b9190611ce2565b601280546001600160a01b0319166001600160a01b03928316908117909155600f54604080516338d52e0f60e01b815290519293638246a451939216916338d52e0f916004808201926020929091908290030181865afa1580156113c1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113e59190611ce2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611427573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061144b9190611ea9565b600e80546001600160801b0319166001600160801b03929092169190911790556009546011546019546040516306e7ce7f60e31b81526001600160a01b039283166004820152620100009091048216602482015291169063373e73f8906044015f604051808303815f87803b1580156114c2575f80fd5b505af11580156114d4573d5f803e3d5ffd5b50506009546010546019546040516306e7ce7f60e31b81526001600160a01b03928316600482015262010000909104821660248201529116925063373e73f891506044015f604051808303815f87803b15801561152f575f80fd5b505af1158015611541573d5f803e3d5ffd5b5050600254600480546019546040516306e7ce7f60e31b81526001600160a01b039283169381019390935262010000900481166024830152909116925063373e73f891506044015f604051808303815f87803b15801561159f575f80fd5b505af11580156115b1573d5f803e3d5ffd5b5050600754600c54604051633194981d60e21b8152600160a01b90910467ffffffffffffffff1660048201526001600160a01b03909116925063c65260749150602401602060405180830381865afa15801561160f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116339190611d14565b6116a457600754600c54604051632b89610160e01b8152600160a01b90910467ffffffffffffffff1660048201526001600160a01b0390911690632b896101906024015f604051808303815f87803b15801561168d575f80fd5b505af115801561169f573d5f803e3d5ffd5b505050505b600754600c54600d546005546040516363beaff360e11b81526001600160a01b039485169463c77d5fe69461171494600160a01b90910467ffffffffffffffff1693608082901b93601393601493700100000000000000000000000000000000900460ff16921690600401611ecf565b5f604051808303815f87803b15801561172b575f80fd5b505af115801561173d573d5f803e3d5ffd5b5050600754600f54604080516338d52e0f60e01b815290516001600160a01b039384169550632fc165ba945091909216916338d52e0f9160048083019260209291908290030181865afa158015611796573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117ba9190611ce2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156117fc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118209190611ea9565b6001600160801b03165f0361191557600754600e54600f54604080516338d52e0f60e01b815290516001600160a01b0394851694638aa2e5b0946001600160801b03169316916338d52e0f9160048083019260209291908290030181865afa15801561188e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118b29190611ce2565b6040516001600160e01b031960e085901b1681526001600160801b0390921660048301526001600160a01b031660248201526044015f604051808303815f87803b1580156118fe575f80fd5b505af1158015611910573d5f803e3d5ffd5b505050505b600754600c54600e5460405163089948b560e01b8152600160a01b90920467ffffffffffffffff1660048301526001600160801b031660248201526001600160a01b039091169063089948b5906044015f604051808303815f87803b15801561197c575f80fd5b505af115801561198e573d5f803e3d5ffd5b5050600754600c54600d5460405163b6405b3960e01b8152600160a01b90920467ffffffffffffffff1660048301526001600160801b031960809190911b1660248201526001600160a01b03909116925063b6405b3991506044016020604051808303815f875af1158015611a05573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a299190611ce2565b600680546001600160a01b0319166001600160a01b039283169081179091556002546019546040516306e7ce7f60e31b81526004810193909352620100009004831660248301529091169063373e73f8906044015f604051808303815f87803b158015611a94575f80fd5b505af1158015611aa6573d5f803e3d5ffd5b5050600754600c54600d54600f54604080516338d52e0f60e01b815290516001600160a01b0395861697506304dbf1989650600160a01b90940467ffffffffffffffff169460809390931b9392909116916338d52e0f916004808201926020929091908290030181865afa158015611b20573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b449190611ce2565b6040516001600160e01b031960e086901b16815267ffffffffffffffff90931660048401526001600160801b031990911660248301526001600160a01b031660448201526064016020604051808303815f875af1158015611ba7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bcb9190611ce2565b50565b5f60208284031215611bde575f80fd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f611c256020830184611be5565b9392505050565b6001600160a01b0381168114611bcb575f80fd5b5f60208284031215611c50575f80fd5b8135611c2581611c2c565b600181811c90821680611c6f57607f821691505b602082108103611c8d57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b67ffffffffffffffff841681526001600160801b031983166020820152606060408201525f611cd96060830184611be5565b95945050505050565b5f60208284031215611cf2575f80fd5b8151611c2581611c2c565b5f60208284031215611d0d575f80fd5b5051919050565b5f60208284031215611d24575f80fd5b81518015158114611c25575f80fd5b5f60ff821660ff8103611d5457634e487b7160e01b5f52601160045260245ffd5b60010192915050565b80545f90600181811c90821680611d7557607f821691505b602082108103611d9357634e487b7160e01b5f52602260045260245ffd5b81865260208601818015611dae5760018114611dc457611df0565b60ff198516825283151560051b82019550611df0565b5f878152602090205f5b85811015611dea57815484820152600190910190602001611dce565b83019650505b505050505092915050565b636e616d6560e01b8152604060208201525f611c256040830184611d5d565b651cde5b589bdb60d21b8152604060208201525f611c256040830184611d5d565b5f60208284031215611e4b575f80fd5b815167ffffffffffffffff81168114611c25575f80fd5b5f60208284031215611e72575f80fd5b81516001600160801b031981168114611c25575f80fd5b5f60208284031215611e99575f80fd5b815160ff81168114611c25575f80fd5b5f60208284031215611eb9575f80fd5b81516001600160801b0381168114611c25575f80fd5b67ffffffffffffffff871681526001600160801b03198616602082015260c060408201525f611f0160c0830187611d5d565b8281036060840152611f138187611d5d565b91505060ff841660808301526001600160a01b03831660a083015297965050505050505056fea2646970667358221220bbde8a1b4769ae02a167e5eaf00ed0d396e709de913a3361990a14e628e6d99364736f6c634300081a0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.