ERC-20
Overview
Max Total Supply
76.580884369293961172 WormholeOptimismBaseBlast
Holders
1,673
Market
Price
$0.00 @ 0.000000 ETH
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Balance
0.0015045547131396 WormholeOptimismBaseBlastValue
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
Minimal Proxy Contract for 0x000000004abe0d620b25b8b06b0712bdcff21899
Contract Name:
CatalystVaultAmplified
Compiler Version
v0.8.22+commit.4fc1097e
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; import { ERC20 } from 'solady/tokens/ERC20.sol'; import { SafeTransferLib } from 'solady/utils/SafeTransferLib.sol'; import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol"; import { ICatalystChainInterface } from "./interfaces/ICatalystChainInterface.sol"; import { WADWAD } from "./utils/MathConstants.sol"; import { CatalystVaultCommon } from "./CatalystVaultCommon.sol"; import { IntegralsAmplified } from "./IntegralsAmplified.sol"; import { ICatalystReceiver} from "./interfaces/IOnCatalyst.sol"; import "./ICatalystV1Vault.sol"; /** * @title Catalyst: Fixed rate cross-chain swap vault. * @author Cata Labs Inc. * @notice Catalyst multi-chain vault using the asset specific * pricing curve: 1/w^\theta (1 - \theta) where \theta is * the vault amplification and w is the vault asset balance. * * The following contract supports between 1 and 3 assets for * atomic swaps. To increase the number of tokens supported, * change MAX_ASSETS to the desired maximum token amount. * This constant is set in "CatalystVaultCommon.sol" * * This vault implements the ERC20 specification, such that the * contract will be its own vault token. * @dev This contract is deployed inactive: It cannot be used as a * vault as is. To use it, a proxy contract duplicating the * logic of this contract needs to be deployed. In Vyper, this * can be done through (vy >= 0.3.4) create_minimal_proxy_to. * In Solidity, this can be done through OZ clones: Clones.clone(...) * After deployment of the proxy, call setup(...) AND initializeSwapCurves(...). * This will initialize the vault and prepare it for cross-chain transactions. * However, only the Catalyst factory is allowed to perform these functions. * It is important that both setup(...) AND initializeSwapCurves(...) are called * in the same transaciton, as otherwise the vault is not fully finalised and * may be configured incorrectly by a third-party. * * If connected to a supported cross-chain interface, call * setConnection to connect the vault with vaults on other chains. * * Finally, call finishSetup to give up the creator's control over the vault. * !If finishSetup is not called, the vault can be drained by the creator! */ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { //--- Storage ---// /** * @notice Adjustment used to remove a certain escrow amount from the balance * computation */ mapping(address => uint256) public _underwriteEscrowMatchBalance0; //--- ERRORS ---// // Errors are defined in interfaces/ICatalystV1VaultErrors.sol //--- Config ---// /** @notice Minimum time parameter adjustments can be made over. */ uint256 constant MIN_ADJUSTMENT_TIME = 7 days; /** * @dev When the swap is a very small size of the vault, the * swaps returns slightly more. To counteract this, an additional * fee slightly larger than the error is added. The below * constants determines when this fee is added and the size. */ uint256 constant SMALL_SWAP_RATIO = 1e12; uint256 constant SMALL_SWAP_RETURN = 95e16; // For other config options, see CatalystVaultCommon.sol //-- Variables --// int64 public _oneMinusAmp; int64 public _targetAmplification; /** * @dev To keep track of pool ownership, the vault needs to keep track of * the local unit balance. That is, do other vaults own or owe assets to this vault? */ int256 public _unitTracker; constructor(address factory_, address mathlib_) payable CatalystVaultCommon(factory_, mathlib_) {} /** * @notice Configures an empty vault. * @dev The initial token amounts should have been sent to the vault before setup is called. * Since someone can call setup can claim the initial tokens, this needs to be * done atomically! * * If 0 of a token in assets is provided, the setup reverts. * @param assets A list of token addresses to be associated with the vault. * @param weights Weights brings the price into a true 1:1 swap. That is: * i_t \cdot W_i = j_t \cdot W_j \forall i, j when P_i(i_t) = P_j(j_t). * in other words, weights are used to compensate for the difference in decimals. (or non 1:1.) * @param amp Amplification factor. Should be < 10**18. * @param depositor The address to mint tokens to. */ function initializeSwapCurves( address[] calldata assets, uint256[] calldata weights, uint64 amp, address depositor ) external override { // May only be invoked by the FACTORY. The factory only invokes this function for proxy contracts. require(msg.sender == FACTORY); // dev: swap curves may only be initialized once by the factory require(_tokenIndexing[0] == address(0)); // dev: swap curves may only be initialized once by the factory // Check that the amplification is correct. require(amp < FixedPointMathLib.WAD); // dev: amplification not set correctly. // Note there is no need to check whether assets.length/weights.length are valid, as invalid arguments // will either cause the function to fail (e.g. if assets.length > MAX_ASSETS the assignment // to initialBalances[it] will fail) or will cause the vault to get initialized with an undesired state // (and the vault shouldn't be used by anyone until its configuration has been finalised). // In any case, the factory does check for valid assets/weights arguments to prevent erroneous configurations. // Note Since assets.len != 0 is not checked, the initial depositor may invoke this function many times, resulting // in vault tokens being minted for the 'depositor' every time. This is not an issue, since 'INITIAL_MINT_AMOUNT' is // an arbitrary number; the value of the vault tokens is determined by the ratio of the vault asset balances and vault // tokens supply once setup has finalized. Furthermore, the vault should not be used until setup has finished and the // vault configuration has been verified. unchecked { // Amplification is stored as 1 - amp since most equations uses amp this way. _oneMinusAmp = int64(uint64(FixedPointMathLib.WAD - amp)); _targetAmplification = int64(uint64(FixedPointMathLib.WAD - amp)); } // Compute the security limit. uint256[] memory initialBalances = new uint256[](MAX_ASSETS); uint256 maxUnitCapacity = 0; uint assetLength = assets.length; for (uint256 it; it < assetLength;) { address tokenAddress = assets[it]; _tokenIndexing[it] = tokenAddress; uint256 weight = weights[it]; require(weight != 0); // dev: invalid 0-valued weight provided _weight[tokenAddress] = weight; // The contract expects the tokens to have been sent to it before setup is // called. Make sure the vault has more than 0 tokens. // Reverts if tokenAddress is address(0). // This contract uses safeTransferLib from Solady. When "safeTransfering", there is no // check for smart contract code. This could be an issue if non-tokens are allowed to enter // the pool, as then the pool could be expoited by later deploying an address to the addres. // The below check ensure that there is a token deployed to the contract. uint256 balanceOfSelf = ERC20(tokenAddress).balanceOf(address(this)); require(balanceOfSelf != 0); // dev: 0 tokens provided in setup. initialBalances[it] = balanceOfSelf; maxUnitCapacity += weight * balanceOfSelf; unchecked { ++it; } } // The security limit is implemented as being 50% of the current balance. // Since the security limit is evaluated after balance changes, the limit in // storage should be the current balance. _maxUnitCapacity = maxUnitCapacity; // Mint vault tokens to the vault creator. _mint(depositor, INITIAL_MINT_AMOUNT); emit VaultDeposit(depositor, INITIAL_MINT_AMOUNT, initialBalances); } /** * @notice Returns the current cross-chain swap capacity. * @dev Overwrites the common implementation because of the * differences as to how it is used. As a result, this always returns * half of the common implementation (or of _maxUnitCapacity) */ function getUnitCapacity() public view override returns (uint256) { return super.getUnitCapacity() >> 1; // Equal to divide by 2. } /** * @notice Re-computes the security limit incase funds have been sent to the vault. */ function updateMaxUnitCapacity() external { uint256 maxUnitCapacity; for (uint256 it; it < MAX_ASSETS;) { address asset = _tokenIndexing[it]; if (asset == address(0)) break; maxUnitCapacity += (ERC20(asset).balanceOf(address(this)) - _escrowedTokens[asset]) * _weight[asset]; unchecked { ++it; } } _maxUnitCapacity = maxUnitCapacity; } //--- Swap integrals ---// // Inherited from the Integrals file. /** * @notice Computes the return of SendAsset excluding fees. * @dev Reverts if 'fromAsset' is not a token in the vault or if * 'amount' and the vault asset balance are both 0. * Does not contain the swap fee. * @param fromAsset Address of the token to sell. * @param amount Amount of from token to sell. * @return uint256 Units. */ function calcSendAsset( address fromAsset, uint256 amount ) public view override returns (uint256) { // A high => fewer units returned. Do not subtract the escrow amount uint256 A = ERC20(fromAsset).balanceOf(address(this)); uint256 W = _weight[fromAsset]; // If 'fromAsset' is not part of the vault (i.e. W is 0) or if 'amount' and // the vault asset balance (i.e. 'A') are both 0 this will revert, since 0**p is // implemented as exp(ln(0) * p) and ln(0) is undefined. uint256 U = _calcPriceCurveArea(amount, A, W, _oneMinusAmp); // If the swap is a very small portion of the vault add an additional fee. This covers mathematical errors. unchecked { //SMALL_SWAP_RATIO is not zero, and if U * SMALL_SWAP_RETURN overflows, less is returned to the user. // Also U * SMALL_SWAP_RETURN cannot overflow, since U depends heavily on amount/A. If this number is small (which it is in this case) then U is also "small". if (A/SMALL_SWAP_RATIO >= amount) return U * SMALL_SWAP_RETURN / FixedPointMathLib.WAD; } return U; } /** * @notice Computes the output of ReceiveAsset excluding fees. * @dev Reverts if 'toAsset' is not a token in the vault. * Does not contain the swap fee. * @param toAsset Address of the token to buy. * @param U Number of units to convert. * @return uint256 Ppurchased tokens. */ function calcReceiveAsset( address toAsset, uint256 U ) public view override returns (uint256) { // B low => fewer tokens returned. Subtract the escrow amount to decrease the balance. uint256 B = ERC20(toAsset).balanceOf(address(this)) - _escrowedTokens[toAsset]; uint256 W = _weight[toAsset]; // If someone were to purchase a token that is not part of the vault on setup // they would just add value to the vault. We don't care about it. // However, it will revert since the solved integral contains U/W and when // W = 0 then U/W returns division by 0 error. return _calcPriceCurveLimit(U, B, W, _oneMinusAmp); } /** * @notice Computes the output of localSwap excluding fees. * @dev Implemented through _calcCombinedPriceCurves. * Reverts if either 'fromAsset' or 'toAsset' is not in the vault, or if the vault 'fromAsset' * balance and 'amount' are both 0. * Does not contain the swap fee. * @param fromAsset Address of the token to sell. * @param toAsset Address of the token to buy. * @param amount Amount of from token to sell for to token. * @return output Output denominated in toAsset. */ function calcLocalSwap( address fromAsset, address toAsset, uint256 amount ) public view override returns (uint256 output) { uint256 A = ERC20(fromAsset).balanceOf(address(this)); uint256 B = ERC20(toAsset).balanceOf(address(this)) - _escrowedTokens[toAsset]; uint256 W_A = _weight[fromAsset]; uint256 W_B = _weight[toAsset]; int256 oneMinusAmp = _oneMinusAmp; output = _calcPriceCurveLimit(_calcPriceCurveArea(amount, A, W_A, oneMinusAmp), B, W_B, oneMinusAmp); // If the swap is a very small portion of the vault add an additional fee. This covers mathematical errors. unchecked { //SMALL_SWAP_RATIO is not zero, and if output * SMALL_SWAP_RETURN overflows, less is returned to the user if (A/SMALL_SWAP_RATIO >= amount) return output * SMALL_SWAP_RETURN / FixedPointMathLib.WAD; } } /** * @notice Deposits a user-configurable amount of tokens. * @dev The swap fee is imposed on deposits. * Requires approvals for all tokens within the vault. * It is advised that the deposit matches the vault's %token distribution. * Deposit is done by converting tokenAmounts into units and then using * the macro for units to vault tokens. (_calcPriceCurveLimitShare). * The elements of tokenAmounts correspond to _tokenIndexing[0...N]. * @param tokenAmounts Array of the tokens amounts to be deposited. * @param minOut Minimum number of vault tokens to be minted. * @return vaultTokens Number of minted vault tokens. */ function depositMixed( uint256[] memory tokenAmounts, uint256 minOut ) nonReentrant external override returns(uint256 vaultTokens) { // _updateAmplification(); int256 oneMinusAmp = _oneMinusAmp; uint256 it_times_walpha_amped; // There is a Stack too deep issue in a later branch. To counteract this, // wab is stored short-lived. This requires letting U get negative. // As such, we define an additional variable called intU which is signed int256 intU; // Compute walpha_0 to find the reference balances. This lets us evaluate the // number of tokens the vault should have If the price in the pool is 1:1. // walpha_0 is computed several times in this contract: // - DepositMixed // - WithdrawMixed // - WithdrawAll // - sendLiquidity // - receiveLiquidity // Since the implementation is very similar, it could be computed seperatly. // However, some of the implementations differ notably: // - DepositMixed: The for loop is reused for computing the value of incoming assets. // - WithdrawMixed: The for loop is used to cache tokenIndexed, effAssetBalances, and assetWeight. // - WithdrawAll: The for loop is used to cache tokenIndexed, effWeightAssetBalances. // - Both sendLiquidity and receiveLiquidity implements the reference computation: computeBalance0(). // Before each implementation, there will be a short comment to describe how the implementation is different. { int256 weightedAssetBalanceSum = 0; uint256 assetDepositSum = 0; for (uint256 it; it < MAX_ASSETS;) { address token = _tokenIndexing[it]; if (token == address(0)) break; uint256 weight = _weight[token]; uint256 tokenAmount = tokenAmounts[it]; { // Whenever balance0 is computed, the true balance should be used. uint256 weightAssetBalance = weight * ERC20(token).balanceOf(address(this)); { // wa^(1-k) is required twice. It is F(A) in the // sendAsset equation and part of the wa_0^(1-k) calculation. // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0. int256 wab = 0; if (weightAssetBalance != 0) { // calculate balance 0 with the underwritten amount subtracted. wab = FixedPointMathLib.powWad( int256((weightAssetBalance - _underwriteEscrowMatchBalance0[token] * weight) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); // if wab == 0, there is no need to add it. So only add if != 0. weightedAssetBalanceSum += wab; wab = FixedPointMathLib.powWad( int256(weightAssetBalance * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); } // This line is the origin of the stack too deep issue. // Moving intU += before this section would solve the issue but it is not possible since it would evaluate incorrectly. // Save gas if the user provides no tokens, as the rest of the loop has no effect in that case if (tokenAmount == 0) { unchecked { ++it; } continue; } // int_A^{A+x} f(w) dw = F(A+x) - F(A). unchecked { // This is -F(A). Since we are subtracting first, U (i.e. intU) must be able to go negative. // |intU| < weightedAssetBalanceSum since F(A+x) is added to intU in the lines after this. intU -= wab; } } // Add F(A+x). // This computation will not revert, since we know tokenAmount != 0. intU += FixedPointMathLib.powWad( int256((weightAssetBalance + weight * tokenAmount) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); } assetDepositSum += tokenAmount * weight; SafeTransferLib.safeTransferFrom( token, msg.sender, address(this), tokenAmount ); // dev: Token withdrawal from user failed. unchecked { ++it; } } // Increase the security limit by the amount deposited. _maxUnitCapacity += assetDepositSum; // Short term decrease the security limit by the amount deposited. // While one may assume _usedUnitCapacity < _maxUnitCapacity, this is not always the case. As such, this remains checked. _usedUnitCapacity += assetDepositSum; // Compute the reference liquidity. // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus // _unitTracker < weightedAssetBalance0Sum unchecked { // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. // The result will be correct once it is casted to uint256. it_times_walpha_amped = uint256(weightedAssetBalanceSum - _unitTracker); // By design, weightedAssetBalanceSum > _unitTracker // Notice that we are not dividing by it. That is because we would then later have to multiply by it. } } // Subtract fee from U (intU). This prevents people from using deposit and withdrawal as a method of swapping. // To reduce costs, the governance fee is not taken. As a result, swapping through deposit+withdrawal circumvents // the governance fee. No incentives align for traders to abuse this and is nativly disincentivised by the higher gas cost. // intU should not be negative. But in the case it is, the result is very bad. For safety, check it is above 0. require(intU >= 0); // dev: U needs to be positive, otherwise, the uint256 casting becomes too larger. unchecked { // U (intU) is generally small, so the below equation should not overflow. // If it does, it has to be (uint256(intU) * (FixedPointMathLib.WAD - _vaultFee)) that overflows. // In which case, something close to 0 will be returned. When divided by FixedPointMathLib.WAD // it will return 0. The casting to int256 is then 0. intU = int256( // intU shouldn't be negative but the above check ensures it is ALWAYS positive. (uint256(intU) * (FixedPointMathLib.WAD - _vaultFee))/FixedPointMathLib.WAD ); } int256 oneMinusAmpInverse = WADWAD / oneMinusAmp; // On totalSupply(). Do not add escrow amount, as higher amount results in a larger return. vaultTokens = _calcPriceCurveLimitShare(uint256(intU), totalSupply(), it_times_walpha_amped, oneMinusAmpInverse); // uint256: intU is positive by design. // Check that the minimum output is honoured. if (minOut > vaultTokens) revert ReturnInsufficient(vaultTokens, minOut); // Mint the desired number of vault tokens to the user. _mint(msg.sender, vaultTokens); // Emit the deposit event emit VaultDeposit(msg.sender, vaultTokens, tokenAmounts); } /** * @notice Burns vault tokens and releases the symmetrical share of tokens to the burner. * This can impact the vault prices. * @dev This is the cheapest way to withdraw and only way to withdraw 100% of the liquidity. * @param vaultTokens Number of vault tokens to burn. * @param minOut Minimum token output. If less is returned, the transaction reverts. * @return amounts Array containing the amounts withdrawn. */ function withdrawAll( uint256 vaultTokens, uint256[] memory minOut ) nonReentrant external override returns(uint256[] memory amounts) { // _updateAmplification(); // Burn the desired number of vault tokens to the user. // If they don't have it, it saves gas. // * Remember to add vaultTokens when accessing totalSupply() _burn(msg.sender, vaultTokens); // (For everyone else, it is probably cheaper to burn last. However, burning here makes // the implementation more similar to the volatile one) int256 oneMinusAmp = _oneMinusAmp; // Cache weights and balances. address[MAX_ASSETS] memory tokenIndexed; uint256[MAX_ASSETS] memory effWeightAssetBalances; // The 'effective' balances (compensated with the escrowed balances) uint256 walpha_0_ampped; // Compute walpha_0 to find the reference balances. This lets us evaluate the // number of tokens the vault should have If the price in the pool is 1:1. // This is a balance0 implementation. The for loop is used to cache tokenIndexed and effWeightAssetBalances. { int256 weightedAssetBalanceSum = 0; // The number of iterations, "it", is needed briefly outside the loop. uint256 it; for (it = 0; it < MAX_ASSETS;) { address token = _tokenIndexing[it]; if (token == address(0)) break; tokenIndexed[it] = token; uint256 weight = _weight[token]; // Whenever balance0 is computed, the true balance should be used. uint256 weightAssetBalance = weight * ERC20(token).balanceOf(address(this)); // Since this is used for a withdrawal, the escrow amount needs to be subtracted to return less. effWeightAssetBalances[it] = weightAssetBalance - _escrowedTokens[token] * weight; // Store // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0. if (weightAssetBalance != 0) { int256 wab = FixedPointMathLib.powWad( int256((weightAssetBalance - _underwriteEscrowMatchBalance0[token] * weight) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); // if wab == 0, there is no need to add it. So only add if != 0. weightedAssetBalanceSum += wab; } unchecked { ++it; } } // Compute the reference liquidity. // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus // _unitTracker < weightedAssetBalance0Sum unchecked { // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. The result will // be correct once it is casted to uint256. walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / it; // By design, weightedAssetBalanceSum > _unitTracker } } // For later event logging, the amounts transferred from the vault are stored. amounts = new uint256[](MAX_ASSETS); // The vault token to assets equation is: // wtk = wa ·(1 - ((wa^(1-k) - wa_0^(1-k) · (1 - (PT-pt)/PT)^(1-k))/wa^(1-k))^(1/(1-k)) // The inner diff is wa_0^(1-k) · (1 - (PT-pt)/PT)^(1-k). // since it doesn't depend on the token, it should only be computed once. uint256 innerdiff; { // Remember to add the number of vault tokens burned to totalSupply() // _escrowedVaultTokens is added, since it makes pt_fraction smaller uint256 ts = (totalSupply() + _escrowedVaultTokens + vaultTokens); uint256 pt_fraction = ((ts - vaultTokens) * FixedPointMathLib.WAD) / ts; // If pt_fraction == 0 => 0^oneMinusAmp = powWad(0, oneMinusAmp) => exp(ln(0) * oneMinusAmp) which is undefined. // However, we know what 0^oneMinusAmp is: 0!. So we just set it to 0. innerdiff = pt_fraction == 0 ? walpha_0_ampped : FixedPointMathLib.mulWad( walpha_0_ampped, FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // Always casts a positive value int256(pt_fraction), // Casting always safe, as pt_fraction < 1 oneMinusAmp )) ); } uint256 totalWithdrawn; int256 oneMinusAmpInverse = WADWAD / oneMinusAmp; for (uint256 it; it < MAX_ASSETS;) { address token = tokenIndexed[it]; if (token == address(0)) break; // ampWeightAssetBalance cannot be cached because the balance0 computation does it without the escrow. // This computation needs to do it with the escrow. uint256 ampWeightAssetBalance = uint256(FixedPointMathLib.powWad( // Powwad is always positive. int256(effWeightAssetBalances[it] * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp )); //! If the vault doesn't have enough assets for a withdrawal, then //! withdraw all of the vaults assets. This should be protected against by setting minOut != 0. //! This happens because the vault expects assets to come back. (it is owed assets) //! We don't want to keep track of debt so we simply return less uint256 weightedTokenAmount = effWeightAssetBalances[it]; //! The above happens if innerdiff >= ampWeightAssetBalance. So if that isn't //! the case, we should compute the true value. if (innerdiff < ampWeightAssetBalance) { // wtk = wa ·(1 - ((wa^(1-k) - wa_0^(1-k) · (1 - (PT-pt)/PT)^(1-k))/wa^(1-k))^(1/(1-k)) // wtk = wa ·(1 - ((wa^(1-k) - innerdiff)/wa^(1-k))^(1/(1-k)) // Since ampWeightAssetBalance ** (1/(1-amp)) == effWeightAssetBalances but the // mathematical lib returns ampWeightAssetBalance ** (1/(1-amp)) < effWeightAssetBalances. // the result is that if innerdiff isn't big enough to make up for the difference // the transaction reverts. If that is the case, use withdrawAll. // This quirk is "okay", since it means fewer tokens are always returned. // Since tokens are withdrawn, the change is negative. As such, multiply the equation by -1. weightedTokenAmount = FixedPointMathLib.mulWad( weightedTokenAmount, FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // The inner is between 0 and 1. Power of < 1 is always between 0 and 1. int256(FixedPointMathLib.divWadUp( // 0 < innerdiff < ampWeightAssetBalance => < 1 thus casting never overflows. ampWeightAssetBalance - innerdiff, ampWeightAssetBalance )), oneMinusAmpInverse // 1/(1-amp) )) ); } // Store the amount withdrawn to subtract from the security limit later. totalWithdrawn += weightedTokenAmount; unchecked { // remove the weight from weightedTokenAmount. weightedTokenAmount /= _weight[token]; } // Check if the user is satisfied with the output. uint256 tokenMinOut = minOut[it]; // GAS SAVING if (tokenMinOut > weightedTokenAmount) revert ReturnInsufficient(weightedTokenAmount, tokenMinOut); // Store the token amount. amounts[it] = weightedTokenAmount; // Transfer the released tokens to the user. SafeTransferLib.safeTransfer(token, msg.sender, weightedTokenAmount); unchecked { ++it; } } // Decrease the security limit by the amount withdrawn. _maxUnitCapacity -= totalWithdrawn; if (_usedUnitCapacity <= totalWithdrawn) { _usedUnitCapacity = 0; } else { unchecked { // We know: _usedUnitCapacity > totalWithdrawn. _usedUnitCapacity -= totalWithdrawn; } } // Emit the event emit VaultWithdraw(msg.sender, vaultTokens, amounts); } /** * @notice Burns vaultTokens and release a token distribution set by the user. * @dev It is advised that the withdrawal matches the vault's %token distribution. * Notice the special scheme for the ratios used. This is done to optimise gas since it doesn't require a sum or ratios. * Cannot be used to withdraw all liquidity. For that, withdrawAll should be used. * @param vaultTokens Number of vault tokens to withdraw. * @param withdrawRatio Percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD. * @param minOut Minimum number of tokens withdrawn. * @return amounts Array containing the amounts withdrawn. */ function withdrawMixed( uint256 vaultTokens, uint256[] calldata withdrawRatio, uint256[] calldata minOut ) nonReentrant external override returns(uint256[] memory amounts) { // _updateAmplification(); // Burn the desired number of vault tokens to the user. If they don't have it, it saves gas. // * Remember to add vaultTokens when accessing totalSupply() _burn(msg.sender, vaultTokens); // (For everyone else, it is probably cheaper to burn last. However, burning here makes // the implementation more similar to the volatile one). int256 oneMinusAmp = _oneMinusAmp; // Cache weights and balances. address[MAX_ASSETS] memory tokenIndexed; uint256[MAX_ASSETS] memory effAssetBalances; // The 'effective' balances (compensated with the escrowed balances) uint256 U = 0; // Compute walpha_0 to find the reference balances. This lets us evaluate the // number of tokens the vault should have if the price in the pool is 1:1. // unlike in withdrawAll, this value is needed to compute U. { // As such, we don't need to remember the value beyond this section. uint256 walpha_0_ampped; // This is a balance0 implementation. The for loop is used to cache tokenIndexed, effAssetBalances and assetWeight. { int256 weightedAssetBalanceSum = 0; // A very careful stack optimisation is made here. // The number of iterations, "it", is needed briefly outside the loop. // To reduce the number of items in the stack, U = it. for (U = 0; U < MAX_ASSETS;) { address token = _tokenIndexing[U]; if (token == address(0)) break; tokenIndexed[U] = token; uint256 weight = _weight[token]; // Whenever balance0 is computed, the true balance should be used. uint256 ab = ERC20(token).balanceOf(address(this)); // Later we need to use the asset balances. Since it is for a withdrawal, we should subtract the escrowed tokens // such that less is returned. effAssetBalances[U] = ab - _escrowedTokens[token]; // subtract _underwriteEscrowMatchBalance0 since this is used for balance0. uint256 weightAssetBalance = weight * (ab - _underwriteEscrowMatchBalance0[token]); // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0. int256 wab = 0; if (weightAssetBalance != 0) { wab = FixedPointMathLib.powWad( int256(weightAssetBalance * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); // if wab == 0, there is no need to add it. So only add if != 0. weightedAssetBalanceSum += wab; } unchecked { ++U; } } // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus // _unitTracker < weightedAssetBalance0Sum unchecked { // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. The result will // be correct once it is casted to uint256. walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / U; // By design, weightedAssetBalanceSum > _unitTracker } // set U = number of tokens in the vault. But that is exactly what it is. } // Remember to add the number of vault tokens burned to totalSupply() uint256 ts = totalSupply() + _escrowedVaultTokens + vaultTokens; // Since vault tokens are getting subtracted from the total supply, remember // to add a negative sign to vault tokens. uint256 pt_fraction = FixedPointMathLib.divWad(ts - vaultTokens, ts); // Compute the unit worth of the vault tokens. // Recall that U is equal to N already. So we only need to multiply by the right side. // Since pt_fraction < 1, the units are negative. This is expected for swap to tokens. As such // FixedPointMathLib.WAD is moved in front to make U positive. U *= FixedPointMathLib.mulWad( walpha_0_ampped, FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // Always casts a positive value int256(pt_fraction), // If casting overflows to a negative number, powWad fails oneMinusAmp )) ); } // For later event logging, the amounts transferred to the vault are stored. amounts = new uint256[](MAX_ASSETS); uint256 totalWithdrawn; for (uint256 it; it < MAX_ASSETS;) { // Ideally we would collect the token into memory to save gas but there isn't space in the stack. if (tokenIndexed[it] == address(0)) break; // Units allocated for the specific token. uint256 U_i = FixedPointMathLib.mulWad(U, withdrawRatio[it]); if (U_i == 0) { // After a withdrawRatio of 1, all other withdrawRatios should be 0. Otherwise, there was an input error. if (withdrawRatio[it] != 0) revert WithdrawRatioNotZero(); // Check the minimum output. This is important, since the normal check is skipped. if (minOut[it] != 0) revert ReturnInsufficient(0, minOut[it]); unchecked { ++it; } continue; } U -= U_i; // Subtract the number of units used. This will underflow for malicious withdrawRatios > 1. uint256 assetWeight = _weight[tokenIndexed[it]]; // Units are shared between "liquidity units" and "token units". As such, we just need to convert the units to tokens. uint256 tokenAmount = _calcPriceCurveLimit(U_i, effAssetBalances[it], assetWeight, oneMinusAmp); // Ensure the output satisfies the user. if (minOut[it] > tokenAmount) revert ReturnInsufficient(tokenAmount, minOut[it]); // Store amount for withdraw event. amounts[it] = tokenAmount; // Transfer the released tokens to the user. SafeTransferLib.safeTransfer(tokenIndexed[it], msg.sender, tokenAmount); // Decrease the security limit by the amount withdrawn. totalWithdrawn += tokenAmount * assetWeight; unchecked { ++it; } } // Ensure all units are used. This should be done by setting at least one withdrawRatio to 1 (WAD). if (U != 0) revert UnusedUnitsAfterWithdrawal(U); // Decrease the security limit by the amount withdrawn. _maxUnitCapacity -= totalWithdrawn; if (_usedUnitCapacity <= totalWithdrawn) { _usedUnitCapacity = 0; } else { unchecked { // We know: _usedUnitCapacity > totalWithdrawn >= 0. _usedUnitCapacity -= totalWithdrawn; } } // Emit the event emit VaultWithdraw(msg.sender, vaultTokens, amounts); } /** * @notice A swap between 2 assets within the vault. Is atomic. * @param fromAsset Asset the user wants to sell. * @param toAsset Asset the user wants to buy. * @param amount Amount of fromAsset the user wants to sell. * @param minOut Minimum output the user wants. Otherwise, the transaction reverts. * @return out The number of tokens purchased. */ function localSwap( address fromAsset, address toAsset, uint256 amount, uint256 minOut ) nonReentrant external override returns (uint256 out) { // _updateAmplification(); uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee); // Calculate the return value. out = calcLocalSwap(fromAsset, toAsset, amount - fee); // Ensure the return value is more than the minimum output. if (minOut > out) revert ReturnInsufficient(out, minOut); // Transfer tokens to the user and collect tokens from the user. // The order doesn't matter, since the function is reentrant protected. // The transaction that is most likly to revert is first. SafeTransferLib.safeTransferFrom(fromAsset, msg.sender, address(this), amount); SafeTransferLib.safeTransfer(toAsset, msg.sender, out); // Collect potential governance fee _collectGovernanceFee(fromAsset, fee); // For amplified vaults, the security limit is based on the sum of the tokens in the vault. uint256 weightedAmount = amount * _weight[fromAsset]; uint256 weightedOut = out * _weight[toAsset]; // The if statement ensures the independent calculations never under or overflow. if (weightedOut > weightedAmount) { _maxUnitCapacity -= weightedOut - weightedAmount; } else { _maxUnitCapacity += weightedAmount - weightedOut; } emit LocalSwap(msg.sender, fromAsset, toAsset, amount, out); } /** @notice Common logic between sendAsset implementations */ function _sendAsset( RouteDescription calldata routeDescription, address fromAsset, uint8 toAssetIndex, uint256 U, uint256 amount, uint256 fee, uint256 minOut, address fallbackUser, uint16 underwriteIncentiveX16, bytes calldata calldata_ ) internal { // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow. // It would also be a silly fallback address. require(fallbackUser != address(0)); // onSendAssetSuccess requires casting U to int256 to update the _unitTracker and must never revert. Check for overflow here. require(U < uint256(type(int256).max)); // int256 max fits in uint256 _unitTracker += int256(U); // Send the purchased units to the target vault on the target chain. ICatalystChainInterface(_chainInterface).sendCrossChainAsset{value: msg.value}( routeDescription, toAssetIndex, U, minOut, amount - fee, fromAsset, underwriteIncentiveX16, calldata_ ); // Store the escrow information. For that, an index is required. Since we need this index twice, we store it. // Only information that is relevant for the escrow has to be hashed. (+ some extra for randomisation) // No need to hash context (as token/liquidity escrow data is different), fromVault, toVault, targetAssetIndex, minOut, CallData bytes32 sendAssetHash = _computeSendAssetHash( routeDescription.toAccount, // Ensures no collisions between different users U, // Used to randomise the hash amount - fee, // Required! to validate release escrow data fromAsset, // Required! to validate release escrow data uint32(block.number) // May overflow, but this is desired (% 2**32) ); // Escrow the tokens used to purchase units. These will be sent back if transaction doesn't arrive / timeout. _setTokenEscrow( sendAssetHash, fallbackUser, fromAsset, amount - fee ); // Notice that the fee is subtracted from the escrow. If this is not done, the escrow can be used as a cheap denial of service vector. // This is unfortunate. // Collect the tokens from the user. SafeTransferLib.safeTransferFrom(fromAsset, msg.sender, address(this), amount); // Governance Fee _collectGovernanceFee(fromAsset, fee); // Adjustment of the security limit is delayed until ack to avoid a router abusing timeout to circumvent the security limit. emit SendAsset( routeDescription.chainIdentifier, routeDescription.toVault, routeDescription.toAccount, fromAsset, toAssetIndex, amount, minOut, U, fee, underwriteIncentiveX16 ); } /** * @notice Initiate a cross-chain swap by purchasing units and transfering the units to the target vault. * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param fromAsset Asset the user wants to sell. * @param toAssetIndex Index of the asset the user wants to buy in the target vault. * @param amount Number of fromAsset to sell to the vault. * @param minOut Minimum number output of tokens on the target chain. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16).max). * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(<address>)), <data>). At maximum 65535 bytes can be passed. * @return U Number of units bought. */ function sendAsset( RouteDescription calldata routeDescription, address fromAsset, uint8 toAssetIndex, uint256 amount, uint256 minOut, address fallbackUser, uint16 underwriteIncentiveX16, bytes calldata calldata_ ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) { // _updateAmplification(); uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee); // Calculate the units bought. U = calcSendAsset(fromAsset, amount - fee); // Execute the common sendAsset logic. _sendAsset( routeDescription, fromAsset, toAssetIndex, U, amount, fee, minOut, fallbackUser, underwriteIncentiveX16, calldata_ ); } /** * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units. * @dev This function is intended to match an existing underwrite. Since normal sendAssets aren't "exact" in regards to U, * this functions makes it easier to hit a specific underwrite. * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param fromAsset Asset the user wants to sell. * @param toAssetIndex Index of the asset the user wants to buy in the target vault. * @param amount Number of fromAsset to sell to the vault. * @param minOut Minimum number output of tokens on the target chain. * @param minU Minimum and exact number of units sent. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16).max). * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(<address>)), <data>). At maximum 65535 bytes can be passed.. * @return U Always equal to minOut, as that is the number of units to be used on the destination chain. */ function sendAssetFixedUnit( ICatalystV1Structs.RouteDescription calldata routeDescription, address fromAsset, uint8 toAssetIndex, uint256 amount, uint256 minOut, uint256 minU, address fallbackUser, uint16 underwriteIncentiveX16, bytes calldata calldata_ ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) { // _updateAmplification(); uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee); // Calculate the units bought. U = calcSendAsset(fromAsset, amount - fee); if (U < minU) revert ReturnInsufficient(U, minU); // The set number of units bought to minU. U = minU; // Execute the common sendAsset logic. _sendAsset( routeDescription, fromAsset, toAssetIndex, U, amount, fee, minOut, fallbackUser, underwriteIncentiveX16, calldata_ ); } /** * @notice Handles common logic associated with the completion of a cross-chain swap. * This function convert incoming Units (expected to be from an incoming cross-chain swap) into a specific token. * @dev This function is intended to finalise a receiveAsset call. * @param toAsset Asset to buy with the units. * @param U Incoming units to be turned into vaults tokens. * @param minOut Minimum number of tokens to purchase. Will revert if less. * @return purchasedTokens Number of toAsset bought. */ function _receiveAsset( address toAsset, uint256 U, uint256 minOut ) internal override returns (uint256 purchasedTokens) { // _updateAmplification(); // Calculate the swap return value. Fee is always taken on the sending token. purchasedTokens = calcReceiveAsset(toAsset, U); // Check if the swap is according to the swap limits. uint256 deltaSecurityLimit = purchasedTokens * _weight[toAsset]; if (_maxUnitCapacity <= deltaSecurityLimit) revert ExceedsSecurityLimit(); unchecked { // We know that _maxUnitCapacity > deltaSecurityLimit so it cannot underflow. _maxUnitCapacity -= deltaSecurityLimit; } _updateUnitCapacity(deltaSecurityLimit); // Ensure the user is satisfied with the number of tokens. if (minOut > purchasedTokens) revert ReturnInsufficient(purchasedTokens, minOut); // Track units for balance0 computation. _unitTracker -= int256(U); } /** * @notice Completes a cross-chain swap by converting units to the desired token. * @dev Security checks are performed by _receiveAsset. * @param channelId Source chain identifier. * @param fromVault Source vault. * @param toAssetIndex Index of the asset to be purchased. * @param toAccount Recipient of assets on destination chain. * @param U Incoming units. * @param minOut Minimum number of token to buy. Reverts back to the sending side. * @param fromAmount Used to match cross-chain swap events. The input amount minus fees on the sending chain. * @param fromAsset Used to match cross-chain swap events. The input asset on the source chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the source chain. * @param purchasedTokens Number of toAsset bought. */ function receiveAsset( bytes32 channelId, bytes calldata fromVault, uint256 toAssetIndex, address toAccount, uint256 U, uint256 minOut, uint256 fromAmount, bytes calldata fromAsset, uint32 blockNumberMod ) nonReentrant onlyChainInterface onlyConnectedPool(channelId, fromVault) external override returns(uint256 purchasedTokens) { // Convert the asset index (toAsset) into the asset to be purchased. address toAsset = _tokenIndexing[toAssetIndex]; purchasedTokens = _receiveAsset( toAsset, U, minOut ); // Send the assets to the user. SafeTransferLib.safeTransfer(toAsset, toAccount, purchasedTokens); emit ReceiveAsset( channelId, fromVault, toAccount, toAsset, U, purchasedTokens, fromAmount, fromAsset, blockNumberMod ); } //--- Liquidity swapping ---// // Because of the way vault tokens work in a pool, there needs to be a way for users to easily get // a distributed stake. Liquidity swaps is a macro implemented at the smart contract level equivalent to: // 1. Withdraw tokens. // 2. Convert tokens to units & transfer to target vault. // 3. Convert units to an even mix of tokens. // 4. Deposit the even mix of tokens. // In 1 user invocation. /** * @notice Computes balance0**(1-amp) without any special caching. * @dev Whenever balance0 is computed, the true balance should be used instead of the one * modifed by the escrow. This is because balance0 is constant during swaps. Thus, if the * balance was modified, it would not be constant during swaps. * The function also returns the vault asset count as it is always used in conjunction with walpha_0_ampped. * The external function does not. * @return walpha_0_ampped Balance0**(1-amp) * @return it the vault asset count */ function _computeBalance0(int256 oneMinusAmp) internal view returns(uint256 walpha_0_ampped, uint256 it) { // Compute walpha_0 to find the reference balances. This lets us evaluate the // number of tokens the vault should have IF the price in the pool is 1:1. // This is a balance0 implementation. The balance 0 implementation here is reference. int256 weightedAssetBalanceSum = 0; for (it; it < MAX_ASSETS;) { address token = _tokenIndexing[it]; if (token == address(0)) break; uint256 weight = _weight[token]; uint256 weightAssetBalance = weight * (ERC20(token).balanceOf(address(this)) - _underwriteEscrowMatchBalance0[token]); // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0. int256 wab = 0; if (weightAssetBalance != 0){ wab = FixedPointMathLib.powWad( int256(weightAssetBalance * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); // if wab == 0, there is no need to add it. So only add if != 0. weightedAssetBalanceSum += wab; } unchecked { ++it; } } // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus // _unitTracker < weightedAssetBalance0Sum unchecked { // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. The result will // be correct once it is casted to uint256. walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / it; // By design, weightedAssetBalanceSum > _unitTracker } } /** * @notice Computes balance0 for the pool. * @dev This can be used as a local invariant. Is constant (or slowly increasing) for swaps. * Deposits and withdrawals change balance0 and as such, it cannot be used to examine if a vault is secure * by it self. * This function does not return balance0 as it, is returns a weighted amplified form. * For pretty much any real world usage of the function, this is the relevant form. * @return walpha_0 Balance0**(1-amp) */ function computeBalance0() external view returns(uint256 walpha_0) { int256 oneMinusAmp = _oneMinusAmp; (uint256 walpha_0_ampped, ) = _computeBalance0(oneMinusAmp); walpha_0 = uint256( // casting: powWad is not negative. FixedPointMathLib.powWad( int256(walpha_0_ampped), // Casting: If overflow, then powWad fails as the overflow is into negative. WADWAD / oneMinusAmp ) ); } /** * @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units. * @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted * directly into units through the following equation: U = N · wa^(1-k) · (((PT + pt)/PT)^(1-k) - 1) * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault, and relaying incentive. * @param vaultTokens Number of vault tokens to exchange. * @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed. * @return U Number of units bought. */ function sendLiquidity( RouteDescription calldata routeDescription, uint256 vaultTokens, uint256[2] calldata minOut, address fallbackUser, bytes calldata calldata_ ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) { // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow. // It would also be a silly fallback address. require(fallbackUser != address(0)); // Correct address format is checked on the cross-chain interface. // _updateAmplification(); // When accesssing totalSupply, remember that we already burnt the incoming vaultTokens. _burn(msg.sender, vaultTokens); int256 oneMinusAmp = _oneMinusAmp; // Compute walpha_0 to find the reference balances. This lets us evaluate the // number of tokens the vault should have If the price in the pool is 1:1. (uint256 walpha_0_ampped, uint256 it) = _computeBalance0(oneMinusAmp); { // Plus _escrowedVaultTokens since we want the withdrawal to return less. Adding vaultTokens as these have already been burnt. uint256 ts = totalSupply() + _escrowedVaultTokens + vaultTokens; uint256 pt_fraction = FixedPointMathLib.divWad(ts + vaultTokens, ts); U = it * FixedPointMathLib.mulWad( walpha_0_ampped, uint256(FixedPointMathLib.powWad( // Always casts a positive value int256(pt_fraction), // If casting overflows to a negative number, powWad fails oneMinusAmp )) - FixedPointMathLib.WAD ); // onSendLiquiditySuccess requires casting U to int256 to update the _unitTracker and must never revert. Check for overflow here. require(U < uint256(type(int256).max)); // int256 max fits in uint256 _unitTracker += int256(U); } // Transfer the units to the target vault. ICatalystChainInterface(_chainInterface).sendCrossChainLiquidity{value: msg.value}( routeDescription, U, minOut, vaultTokens, calldata_ ); // Store the escrow information. For that, an index is required. Since we need this index twice, we store it. // Only information that is relevant for the escrow has to be hashed. (+ some extra for randomisation) // No need to hash context (as token/liquidity escrow data is different), fromVault, toVault, targetAssetIndex, minOut, CallData bytes32 sendLiquidityHash = _computeSendLiquidityHash( routeDescription.toAccount, // Ensures no collisions between different users U, // Used to randomise the hash vaultTokens, // Required! to validate release escrow data uint32(block.number) // May overflow, but this is desired (% 2**32) ); // Emit event before setting escrow to clear up variables from stack. emit SendLiquidity( routeDescription.chainIdentifier, routeDescription.toVault, routeDescription.toAccount, vaultTokens, minOut, U ); // Escrow the vault token used to purchase units. These will be sent back if transaction doesn't arrive / timeout. _setLiquidityEscrow( sendLiquidityHash, fallbackUser, vaultTokens ); // Adjustment of the security limit is delayed until ack to avoid // a router abusing timeout to circumvent the security limit at a low cost. } /** * @notice Handles common logic assocaited with the completion of a cross-chain liquidity swap. * This function convert incoming units directly to vault tokens. * @dev This function is meant to finalise a receiveLiquidity call. * @param U Incoming units to be turned into vault tokens. * @param minVaultTokens Minimum number of vault tokens to mint (revert if less). * @param minReferenceAsset Minimum number of reference tokens the vaults tokens are worth (revert if less). * @return vaultTokens Minted vault tokens. */ function _receiveLiquidity( uint256 U, uint256 minVaultTokens, uint256 minReferenceAsset ) internal returns (uint256 vaultTokens) { // _updateAmplification(); int256 oneMinusAmp = _oneMinusAmp; // Compute walpha_0 to find the reference balances. This lets us evaluate the // number of tokens the vault should have If the price in the pool is 1:1. (uint256 walpha_0_ampped, uint256 it) = _computeBalance0(oneMinusAmp); int256 oneMinusAmpInverse = WADWAD / oneMinusAmp; uint256 it_times_walpha_amped = it * walpha_0_ampped; // On totalSupply(). Do not add escrow amount, as higher amount results in a larger return. vaultTokens = _calcPriceCurveLimitShare(U, totalSupply(), it_times_walpha_amped, oneMinusAmpInverse); // Check if more vault tokens than the minimum can be minted. if (minVaultTokens > vaultTokens) revert ReturnInsufficient(vaultTokens, minVaultTokens); // Then check if the minimum number of reference assets is honoured. if (minReferenceAsset != 0) { uint256 walpha_0 = uint256(FixedPointMathLib.powWad( // uint256 casting: Is always positive. int256(walpha_0_ampped), // int256 casting: If casts to a negative number, powWad fails because it uses ln which can't take negative numbers. oneMinusAmpInverse )); // Add escrow to ensure that even if all ongoing transaction revert, the user gets their expected amount. // Add vault tokens because they are going to be minted (We want the reference value after mint). uint256 walpha_0_owned = ((walpha_0 * vaultTokens) / (totalSupply() + _escrowedVaultTokens + vaultTokens)) / FixedPointMathLib.WAD; if (minReferenceAsset > walpha_0_owned) revert ReturnInsufficient(walpha_0_owned, minReferenceAsset); } // Update the unit tracker: _unitTracker -= int256(U); // Security limit { // To calculate the vaultTokenEquiv, we set \alpha_t = \alpha_0. // This should be a close enough approximation. // If U > it_times_walpha_amped, then U can purchase more than 50% of the vault. // And the below calculation doesn't work. if (it_times_walpha_amped <= U) revert ExceedsSecurityLimit(); uint256 vaultTokenEquiv = FixedPointMathLib.mulWadUp( uint256(FixedPointMathLib.powWad( // Always casts a positive value int256(it_times_walpha_amped), // If casting overflows to a negative number, powWad fails oneMinusAmpInverse )), FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // powWad is always <= 1, as 'base' is always <= 1 int256(FixedPointMathLib.divWad( // Casting never overflows, as division result is always <= 1 it_times_walpha_amped - U, it_times_walpha_amped )), oneMinusAmpInverse )) ); // Check if the swap is according to the swap limits _updateUnitCapacity(FixedPointMathLib.mulWad(2, vaultTokenEquiv)); } } /** * @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing. * @dev Security checks are performed by _receiveLiquidity. * While the description says units are converted to tokens and then deposited, units are converted * directly to vault tokens through the following equation: pt = PT · (((N · wa_0^(1-k) + U)/(N · wa_0^(1-k))^(1/(1-k)) - 1) * @param channelId Source chain identifier. * @param fromVault Source vault. * @param toAccount Recipient of vault tokens. * @param U Incoming units to be turned into vault tokens. * @param minVaultTokens Minimum number of vault tokens to mint (revert if less). * @param minReferenceAsset Minimum number of reference tokens the vaults tokens are worth (revert if less). * @param fromAmount Used to match cross-chain swap events. The input amount on the source chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the source chain. * @return purchasedVaultTokens Minted vault tokens. */ function receiveLiquidity( bytes32 channelId, bytes calldata fromVault, address toAccount, uint256 U, uint256 minVaultTokens, uint256 minReferenceAsset, uint256 fromAmount, uint32 blockNumberMod ) nonReentrant onlyChainInterface onlyConnectedPool(channelId, fromVault) external override returns(uint256 purchasedVaultTokens) { purchasedVaultTokens = _receiveLiquidity( U, minVaultTokens, minReferenceAsset ); emit ReceiveLiquidity(channelId, fromVault, toAccount, U, purchasedVaultTokens, fromAmount, blockNumberMod); // Mint vault tokens for the user. _mint(toAccount, purchasedVaultTokens); } //-- Escrow Functions --// /** * @notice Deletes and releases escrowed tokens to the vault and updates the security limit. * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. The function adds security limit * adjustment to the implementation to swap volume supported. * @param toAccount Recipient of the transaction on the target chain. * @param U Number of units purchased. * @param escrowAmount Number of tokens escrowed. * @param escrowToken Token escrowed. * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendAssetSuccess( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, address escrowToken, uint32 blockNumberMod ) public override { // Execute common escrow logic. super.onSendAssetSuccess(channelId, toAccount, U, escrowAmount, escrowToken, blockNumberMod); // Received assets should be subtracted from the used unit capacity. // It is assumed if the router was fraudulent no-one would execute a trade. // As a result, if people swap into the vault, we should expect that there is exactly // the inswapped amount of trust in the vault. If this wasn't implemented, there would be // a maximum daily cross chain volume, which is bad for liquidity providers. unchecked { // escrowAmount * _weight[escrowToken] has been calculated before. uint256 escrowAmountTimesWeight = escrowAmount * _weight[escrowToken]; uint256 UC = _usedUnitCapacity; // If UC < escrowAmount and we do UC - escrowAmount < 0 underflow => bad. if (UC > escrowAmountTimesWeight) { _usedUnitCapacity = UC - escrowAmountTimesWeight; // Does not underflow since _usedUnitCapacity > escrowAmount. } else if (UC != 0) { // If UC == 0, then we shouldn't do anything. Skip that case. // when UC <= escrowAmount => UC - escrowAmount <= 0 => max(UC - escrowAmount, 0) = 0 _usedUnitCapacity = 0; } // There is a chance that _maxUnitCapacity + escrowAmount * _weight[escrowToken] will overflow. // since the number has never been calculated before. This function should never revert so the computation // has to be done unchecked. uint256 muc = _maxUnitCapacity; uint256 new_muc = muc + escrowAmountTimesWeight; // Might overflow. Can be checked by comparing it against MUC. // If new_muc < muc, then new_muc has overflown. As a result, we should set muc = uint256::MAX if (new_muc < muc) { _maxUnitCapacity = type(uint256).max; } else { _maxUnitCapacity = new_muc; } } } /** * @notice Deletes and releases escrowed tokens to the vault and updates the security limit. * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. The function adds security limit * adjustment to the implementation to swap volume supported. * @param toAccount Recipient of the transaction on the target chain. * @param U Number of units acquired. * @param escrowAmount Number of tokens escrowed. * @param escrowToken Token escrowed. * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendAssetFailure( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, address escrowToken, uint32 blockNumberMod ) public override { // Execute common escrow logic. super.onSendAssetFailure(channelId, toAccount, U, escrowAmount, escrowToken, blockNumberMod); // Removed timed-out units from the unit tracker. This will keep the // balance0 in balance, since tokens also leave the vault _unitTracker -= int256(U); // It has already been checked on sendAsset that casting to int256 will not overflow. // Cannot be manipulated by the router as, otherwise, the swapHash check will fail } // onSendLiquiditySuccess is not overwritten since we are unable to increase // the security limit. This is because it is very expensive to compute the update // to the security limit. If someone liquidity swapped a significant amount of assets // it is assumed the vault has low liquidity. In these cases, liquidity swaps shouldn't be used. /** * @notice Deletes and releases liquidity escrowed tokens to the vault and updates the security limit. * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32. * @param U Number of units initially acquired. * @param escrowAmount Number of vault tokens escrowed. * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendLiquidityFailure( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, uint32 blockNumberMod ) public override { super.onSendLiquidityFailure(channelId, toAccount, U, escrowAmount, blockNumberMod); // Removed timed-out units from the unit tracker. This will keep the // balance0 in balance, since tokens also leave the vault _unitTracker -= int256(U); // It has already been checked on sendAsset that casting to int256 will not overflow. // Cannot be manipulated by the router as, otherwise, the swapHash check will fail } function underwriteAsset( bytes32 identifier, address toAsset, uint256 U, uint256 minOut ) override public returns (uint256 purchasedTokens) { // We need to ensure that the conversion in deleteUnderwrite (and receiveAsset) doesn't overflow. require(U < uint256(type(int256).max)); // int256 max fits in uint256 purchasedTokens = super.underwriteAsset(identifier, toAsset, U, minOut); // unit tracking is handled by _receiveAsset. // since _unitTrakcer -= int256(U) has been set but no tokens have // let the pool, we need to remove the corresponding amount from // the balance 0 computation to ensure it is still correct. unchecked { // Must be less than the vault balance. _underwriteEscrowMatchBalance0[toAsset] += purchasedTokens; } } function releaseUnderwriteAsset( address refundTo, bytes32 identifier, uint256 escrowAmount, address escrowToken, bytes32 sourceIdentifier, bytes calldata fromVault ) override public { super.releaseUnderwriteAsset(refundTo, identifier, escrowAmount, escrowToken, sourceIdentifier, fromVault); unchecked { _underwriteEscrowMatchBalance0[escrowToken] -= escrowAmount; } } function deleteUnderwriteAsset( bytes32 identifier, uint256 U, uint256 escrowAmount, address escrowToken ) override public { super.deleteUnderwriteAsset(identifier, U, escrowAmount, escrowToken); // update the unit tracker. When underwriteAsset was called, the _unitTracker was updated with // _unitTrakcer -= int256(U) so we need to cancel that. _unitTracker += int256(U); // It has already been checked on sendAsset that casting to int256 will not overflow. // Cannot be manipulated by the router as, otherwise, the swapHash check will fail unchecked { _underwriteEscrowMatchBalance0[escrowToken] -= escrowAmount; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple ERC20 + EIP-2612 implementation. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) /// /// @dev Note: /// - The ERC20 standard allows minting and transferring to and from the zero address, /// minting and transferring zero tokens, as well as self-approvals. /// For performance, this implementation WILL NOT revert for such actions. /// Please add any checks with overrides if desired. /// - The `permit` function uses the ecrecover precompile (0x1). /// /// If you are overriding: /// - NEVER violate the ERC20 invariant: /// the total sum of all balances must be equal to `totalSupply()`. /// - Check that the overridden function is actually used in the function you want to /// change the behavior of. Much of the code has been manually inlined for performance. abstract contract ERC20 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The total supply has overflowed. error TotalSupplyOverflow(); /// @dev The allowance has overflowed. error AllowanceOverflow(); /// @dev The allowance has underflowed. error AllowanceUnderflow(); /// @dev Insufficient balance. error InsufficientBalance(); /// @dev Insufficient allowance. error InsufficientAllowance(); /// @dev The permit is invalid. error InvalidPermit(); /// @dev The permit has expired. error PermitExpired(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Emitted when `amount` tokens is transferred from `from` to `to`. event Transfer(address indexed from, address indexed to, uint256 amount); /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`. event Approval(address indexed owner, address indexed spender, uint256 amount); /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. uint256 private constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. uint256 private constant _APPROVAL_EVENT_SIGNATURE = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The storage slot for the total supply. uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c; /// @dev The balance slot of `owner` is given by: /// ``` /// mstore(0x0c, _BALANCE_SLOT_SEED) /// mstore(0x00, owner) /// let balanceSlot := keccak256(0x0c, 0x20) /// ``` uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2; /// @dev The allowance slot of (`owner`, `spender`) is given by: /// ``` /// mstore(0x20, spender) /// mstore(0x0c, _ALLOWANCE_SLOT_SEED) /// mstore(0x00, owner) /// let allowanceSlot := keccak256(0x0c, 0x34) /// ``` uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20; /// @dev The nonce slot of `owner` is given by: /// ``` /// mstore(0x0c, _NONCES_SLOT_SEED) /// mstore(0x00, owner) /// let nonceSlot := keccak256(0x0c, 0x20) /// ``` uint256 private constant _NONCES_SLOT_SEED = 0x38377508; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`. uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901; /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. bytes32 private constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; /// @dev `keccak256("1")`. bytes32 private constant _VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`. bytes32 private constant _PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 METADATA */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the name of the token. function name() public view virtual returns (string memory); /// @dev Returns the symbol of the token. function symbol() public view virtual returns (string memory); /// @dev Returns the decimals places of the token. function decimals() public view virtual returns (uint8) { return 18; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the amount of tokens in existence. function totalSupply() public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := sload(_TOTAL_SUPPLY_SLOT) } } /// @dev Returns the amount of tokens owned by `owner`. function balanceOf(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`. function allowance(address owner, address spender) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x34)) } } /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens. /// /// Emits a {Approval} event. function approve(address spender, uint256 amount) public virtual returns (bool) { /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c))) } return true; } /// @dev Transfer `amount` tokens from the caller to `to`. /// /// Requirements: /// - `from` must at least have `amount`. /// /// Emits a {Transfer} event. function transfer(address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(msg.sender, to, amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, caller()) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c))) } _afterTokenTransfer(msg.sender, to, amount); return true; } /// @dev Transfers `amount` tokens from `from` to `to`. /// /// Note: Does not update the allowance if it is the maximum uint256 value. /// /// Requirements: /// - `from` must at least have `amount`. /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`. /// /// Emits a {Transfer} event. function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(from, to, amount); /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the allowance slot and load its value. mstore(0x20, caller()) mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED)) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } _afterTokenTransfer(from, to, amount); return true; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EIP-2612 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev For more performance, override to return the constant value /// of `keccak256(bytes(name()))` if `name()` will never change. function _constantNameHash() internal view virtual returns (bytes32 result) {} /// @dev Returns the current nonce for `owner`. /// This value is used to compute the signature for EIP-2612 permit. function nonces(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the nonce slot and load its value. mstore(0x0c, _NONCES_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x20)) } } /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`, /// authorized by a signed approval by `owner`. /// /// Emits a {Approval} event. function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { bytes32 nameHash = _constantNameHash(); // We simply calculate it on-the-fly to allow for cases where the `name` may change. if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); /// @solidity memory-safe-assembly assembly { // Revert if the block timestamp is greater than `deadline`. if gt(timestamp(), deadline) { mstore(0x00, 0x1a15a3cc) // `PermitExpired()`. revert(0x1c, 0x04) } let m := mload(0x40) // Grab the free memory pointer. // Clean the upper 96 bits. owner := shr(96, shl(96, owner)) spender := shr(96, shl(96, spender)) // Compute the nonce slot and load its value. mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX) mstore(0x00, owner) let nonceSlot := keccak256(0x0c, 0x20) let nonceValue := sload(nonceSlot) // Prepare the domain separator. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), _VERSION_HASH) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) mstore(0x2e, keccak256(m, 0xa0)) // Prepare the struct hash. mstore(m, _PERMIT_TYPEHASH) mstore(add(m, 0x20), owner) mstore(add(m, 0x40), spender) mstore(add(m, 0x60), value) mstore(add(m, 0x80), nonceValue) mstore(add(m, 0xa0), deadline) mstore(0x4e, keccak256(m, 0xc0)) // Prepare the ecrecover calldata. mstore(0x00, keccak256(0x2c, 0x42)) mstore(0x20, and(0xff, v)) mstore(0x40, r) mstore(0x60, s) let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20) // If the ecrecover fails, the returndatasize will be 0x00, // `owner` will be checked if it equals the hash at 0x00, // which evaluates to false (i.e. 0), and we will revert. // If the ecrecover succeeds, the returndatasize will be 0x20, // `owner` will be compared against the returned address at 0x20. if iszero(eq(mload(returndatasize()), owner)) { mstore(0x00, 0xddafbaef) // `InvalidPermit()`. revert(0x1c, 0x04) } // Increment and store the updated nonce. sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds. // Compute the allowance slot and store the value. // The `owner` is already at slot 0x20. mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender)) sstore(keccak256(0x2c, 0x34), value) // Emit the {Approval} event. log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero pointer. } } /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit. function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) { bytes32 nameHash = _constantNameHash(); // We simply calculate it on-the-fly to allow for cases where the `name` may change. if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Grab the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), _VERSION_HASH) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) result := keccak256(m, 0xa0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL MINT FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Mints `amount` tokens to `to`, increasing the total supply. /// /// Emits a {Transfer} event. function _mint(address to, uint256 amount) internal virtual { _beforeTokenTransfer(address(0), to, amount); /// @solidity memory-safe-assembly assembly { let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT) let totalSupplyAfter := add(totalSupplyBefore, amount) // Revert if the total supply overflows. if lt(totalSupplyAfter, totalSupplyBefore) { mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`. revert(0x1c, 0x04) } // Store the updated total supply. sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter) // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c))) } _afterTokenTransfer(address(0), to, amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL BURN FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Burns `amount` tokens from `from`, reducing the total supply. /// /// Emits a {Transfer} event. function _burn(address from, uint256 amount) internal virtual { _beforeTokenTransfer(from, address(0), amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, from) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Subtract and store the updated total supply. sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount)) // Emit the {Transfer} event. mstore(0x00, amount) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0) } _afterTokenTransfer(from, address(0), amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL TRANSFER FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Moves `amount` of tokens from `from` to `to`. function _transfer(address from, address to, uint256 amount) internal virtual { _beforeTokenTransfer(from, to, amount); /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } _afterTokenTransfer(from, to, amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL ALLOWANCE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`. function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and load its value. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, owner) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } } } /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`. /// /// Emits a {Approval} event. function _approve(address owner, address spender, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { let owner_ := shl(96, owner) // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED)) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HOOKS TO OVERRIDE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Hook that is called before any transfer of tokens. /// This includes minting and burning. function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /// @dev Hook that is called after any transfer of tokens. /// This includes minting and burning. function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// /// @dev Note: /// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection. /// - For ERC20s, this implementation won't check that a token has code, /// responsibility is delegated to the caller. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes. uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ETH OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants. // // The regular variants: // - Forwards all remaining gas to the target. // - Reverts if the target reverts. // - Reverts if the current contract has insufficient balance. // // The force variants: // - Forwards with an optional gas stipend // (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases). // - If the target reverts, or if the gas stipend is exhausted, // creates a temporary contract to force send the ETH via `SELFDESTRUCT`. // Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758. // - Reverts if the current contract has insufficient balance. // // The try variants: // - Forwards with a mandatory gas stipend. // - Instead of reverting, returns whether the transfer succeeded. /// @dev Sends `amount` (in wei) ETH to `to`. function safeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } } } /// @dev Sends all the ETH in the current contract to `to`. function safeTransferAllETH(address to) internal { /// @solidity memory-safe-assembly assembly { // Transfer all the ETH and check if it succeeded or not. if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { if lt(selfbalance(), amount) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. } } } /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`. function forceSafeTransferAllETH(address to, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`. function forceSafeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { if lt(selfbalance(), amount) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. } } } /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`. function forceSafeTransferAllETH(address to) internal { /// @solidity memory-safe-assembly assembly { // forgefmt: disable-next-item if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. } } } /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00) } } /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`. function trySafeTransferAllETH(address to, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have their entire balance approved for /// the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`. // Read the balance, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`. amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. // Read the balance, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x14, to) // Store the `to` argument. amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it. mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. // Perform the approval, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// If the initial attempt to approve fails, attempts to reset the approved amount to zero, /// then retries the approval again (some tokens, e.g. USDT, requires this). /// Reverts upon failure. function safeApproveWithRetry(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. // Perform the approval, retrying upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x34, 0) // Store 0 for the `amount`. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval. mstore(0x34, amount) // Store back the original `amount`. // Retry the approval, reverting upon failure. if iszero( and( or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. revert(0x1c, 0x04) } } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x14, account) // Store the `account` argument. mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`. amount := mul( mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) ) ) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) library FixedPointMathLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The operation failed, as the output exceeds the maximum value of uint256. error ExpOverflow(); /// @dev The operation failed, as the output exceeds the maximum value of uint256. error FactorialOverflow(); /// @dev The operation failed, due to an overflow. error RPowOverflow(); /// @dev The mantissa is too big to fit. error MantissaOverflow(); /// @dev The operation failed, due to an multiplication overflow. error MulWadFailed(); /// @dev The operation failed, due to an multiplication overflow. error SMulWadFailed(); /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. error DivWadFailed(); /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. error SDivWadFailed(); /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. error MulDivFailed(); /// @dev The division failed, as the denominator is zero. error DivFailed(); /// @dev The full precision multiply-divide operation failed, either due /// to the result being larger than 256 bits, or a division by a zero. error FullMulDivFailed(); /// @dev The output is undefined, as the input is less-than-or-equal to zero. error LnWadUndefined(); /// @dev The input outside the acceptable domain. error OutOfDomain(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The scalar of ETH and most ERC20s. uint256 internal constant WAD = 1e18; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SIMPLIFIED FIXED POINT OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `(x * y) / WAD` rounded down. function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if mul(y, gt(x, div(not(0), y))) { mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. revert(0x1c, 0x04) } z := div(mul(x, y), WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded down. function sMulWad(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := mul(x, y) // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`. if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) { mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`. revert(0x1c, 0x04) } z := sdiv(z, WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks. function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(mul(x, y), WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks. function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := sdiv(mul(x, y), WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded up. function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if mul(y, gt(x, div(not(0), y))) { mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD)) } } /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks. function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD)) } } /// @dev Equivalent to `(x * WAD) / y` rounded down. function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. revert(0x1c, 0x04) } z := div(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded down. function sDivWad(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := mul(x, WAD) // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`. if iszero(and(iszero(iszero(y)), eq(sdiv(z, WAD), x))) { mstore(0x00, 0x5c43740d) // `SDivWadFailed()`. revert(0x1c, 0x04) } z := sdiv(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks. function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks. function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := sdiv(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded up. function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) } } /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks. function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) } } /// @dev Equivalent to `x` to the power of `y`. /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`. function powWad(int256 x, int256 y) internal pure returns (int256) { // Using `ln(x)` means `x` must be greater than 0. return expWad((lnWad(x) * y) / int256(WAD)); } /// @dev Returns `exp(x)`, denominated in `WAD`. /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln function expWad(int256 x) internal pure returns (int256 r) { unchecked { // When the result is less than 0.5 we return zero. // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`. if (x <= -41446531673892822313) return r; /// @solidity memory-safe-assembly assembly { // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`. if iszero(slt(x, 135305999368893231589)) { mstore(0x00, 0xa37bfec9) // `ExpOverflow()`. revert(0x1c, 0x04) } } // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96` // for more intermediate precision and a binary basis. This base conversion // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. x = (x << 78) / 5 ** 18; // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers // of two such that exp(x) = exp(x') * 2**k, where k is an integer. // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96; x = x - k * 54916777467707473351141471128; // `k` is in the range `[-61, 195]`. // Evaluate using a (6, 7)-term rational approximation. // `p` is made monic, we'll multiply by a scale factor later. int256 y = x + 1346386616545796478920950773328; y = ((y * x) >> 96) + 57155421227552351082224309758442; int256 p = y + x - 94201549194550492254356042504812; p = ((p * y) >> 96) + 28719021644029726153956944680412240; p = p * x + (4385272521454847904659076985693276 << 96); // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. int256 q = x - 2855989394907223263936484059900; q = ((q * x) >> 96) + 50020603652535783019961831881945; q = ((q * x) >> 96) - 533845033583426703283633433725380; q = ((q * x) >> 96) + 3604857256930695427073651918091429; q = ((q * x) >> 96) - 14423608567350463180887372962807573; q = ((q * x) >> 96) + 26449188498355588339934803723976023; /// @solidity memory-safe-assembly assembly { // Div in assembly because solidity adds a zero check despite the unchecked. // The q polynomial won't have zeros in the domain as all its roots are complex. // No scaling is necessary because p is already `2**96` too large. r := sdiv(p, q) } // r should be in the range `(0.09, 0.25) * 2**96`. // We now need to multiply r by: // - The scale factor `s ≈ 6.031367120`. // - The `2**k` factor from the range reduction. // - The `1e18 / 2**96` factor for base conversion. // We do this all at once, with an intermediate result in `2**213` // basis, so the final right shift is always by a positive amount. r = int256( (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k) ); } } /// @dev Returns `ln(x)`, denominated in `WAD`. /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln function lnWad(int256 x) internal pure returns (int256 r) { /// @solidity memory-safe-assembly assembly { // We want to convert `x` from `10**18` fixed point to `2**96` fixed point. // We do this by multiplying by `2**96 / 10**18`. But since // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here // and add `ln(2**96 / 10**18)` at the end. // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`. r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // We place the check here for more optimal stack operations. if iszero(sgt(x, 0)) { mstore(0x00, 0x1615e638) // `LnWadUndefined()`. revert(0x1c, 0x04) } // forgefmt: disable-next-item r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)), 0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)) // Reduce range of x to (1, 2) * 2**96 // ln(2^k * x) = k * ln(2) + ln(x) x := shr(159, shl(r, x)) // Evaluate using a (8, 8)-term rational approximation. // `p` is made monic, we will multiply by a scale factor later. // forgefmt: disable-next-item let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir. sar(96, mul(add(43456485725739037958740375743393, sar(96, mul(add(24828157081833163892658089445524, sar(96, mul(add(3273285459638523848632254066296, x), x))), x))), x)), 11111509109440967052023855526967) p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857) p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526) p := sub(mul(p, x), shl(96, 795164235651350426258249787498)) // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. // `q` is monic by convention. let q := add(5573035233440673466300451813936, x) q := add(71694874799317883764090561454958, sar(96, mul(x, q))) q := add(283447036172924575727196451306956, sar(96, mul(x, q))) q := add(401686690394027663651624208769553, sar(96, mul(x, q))) q := add(204048457590392012362485061816622, sar(96, mul(x, q))) q := add(31853899698501571402653359427138, sar(96, mul(x, q))) q := add(909429971244387300277376558375, sar(96, mul(x, q))) // `p / q` is in the range `(0, 0.125) * 2**96`. // Finalization, we need to: // - Multiply by the scale factor `s = 5.549…`. // - Add `ln(2**96 / 10**18)`. // - Add `k * ln(2)`. // - Multiply by `10**18 / 2**96 = 5**18 >> 78`. // The q polynomial is known not to have zeros in the domain. // No scaling required because p is already `2**96` too large. p := sdiv(p, q) // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`. p := mul(1677202110996718588342820967067443963516166, p) // Add `ln(2) * k * 5**18 * 2**192`. // forgefmt: disable-next-item p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p) // Add `ln(2**96 / 10**18) * 5**18 * 2**192`. p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p) // Base conversion: mul `2**18 / 2**192`. r := sar(174, p) } } /// @dev Returns `W_0(x)`, denominated in `WAD`. /// See: https://en.wikipedia.org/wiki/Lambert_W_function /// a.k.a. Product log function. This is an approximation of the principal branch. function lambertW0Wad(int256 x) internal pure returns (int256 w) { // forgefmt: disable-next-item unchecked { if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`. int256 wad = int256(WAD); int256 p = x; uint256 c; // Whether we need to avoid catastrophic cancellation. uint256 i = 4; // Number of iterations. if (w <= 0x1ffffffffffff) { if (-0x4000000000000 <= w) { i = 1; // Inputs near zero only take one step to converge. } else if (w <= -0x3ffffffffffffff) { i = 32; // Inputs near `-1/e` take very long to converge. } } else if (w >> 63 == 0) { /// @solidity memory-safe-assembly assembly { // Inline log2 for more performance, since the range is small. let v := shr(49, w) let l := shl(3, lt(0xff, v)) l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)), 0x0706060506020504060203020504030106050205030304010505030400000000)), 49) w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13)) c := gt(l, 60) i := add(2, add(gt(l, 53), c)) } } else { int256 ll = lnWad(w = lnWad(w)); /// @solidity memory-safe-assembly assembly { // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`. w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll)) i := add(3, iszero(shr(68, x))) c := iszero(shr(143, x)) } if (c == 0) { do { // If `x` is big, use Newton's so that intermediate values won't overflow. int256 e = expWad(w); /// @solidity memory-safe-assembly assembly { let t := mul(w, div(e, wad)) w := sub(w, sdiv(sub(t, x), div(add(e, t), wad))) } if (p <= w) break; p = w; } while (--i != 0); /// @solidity memory-safe-assembly assembly { w := sub(w, sgt(w, 2)) } return w; } } do { // Otherwise, use Halley's for faster convergence. int256 e = expWad(w); /// @solidity memory-safe-assembly assembly { let t := add(w, wad) let s := sub(mul(w, e), mul(x, wad)) w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t))))) } if (p <= w) break; p = w; } while (--i != c); /// @solidity memory-safe-assembly assembly { w := sub(w, sgt(w, 2)) } // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation. if (c != 0) { int256 t = w | 1; /// @solidity memory-safe-assembly assembly { x := sdiv(mul(x, wad), t) } x = (t * (wad + lnWad(x))); /// @solidity memory-safe-assembly assembly { w := sdiv(x, add(wad, t)) } } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* GENERAL NUMBER UTILITIES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Calculates `floor(x * y / d)` with full precision. /// Throws if result overflows a uint256 or when `d` is zero. /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { // 512-bit multiply `[p1 p0] = x * y`. // Compute the product mod `2**256` and mod `2**256 - 1` // then use the Chinese Remainder Theorem to reconstruct // the 512 bit result. The result is stored in two 256 // variables such that `product = p1 * 2**256 + p0`. // Least significant 256 bits of the product. result := mul(x, y) // Temporarily use `result` as `p0` to save gas. let mm := mulmod(x, y, not(0)) // Most significant 256 bits of the product. let p1 := sub(mm, add(result, lt(mm, result))) // Handle non-overflow cases, 256 by 256 division. if iszero(p1) { if iszero(d) { mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. revert(0x1c, 0x04) } result := div(result, d) break } // Make sure the result is less than `2**256`. Also prevents `d == 0`. if iszero(gt(d, p1)) { mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. revert(0x1c, 0x04) } /*------------------- 512 by 256 division --------------------*/ // Make division exact by subtracting the remainder from `[p1 p0]`. // Compute remainder using mulmod. let r := mulmod(x, y, d) // `t` is the least significant bit of `d`. // Always greater or equal to 1. let t := and(d, sub(0, d)) // Divide `d` by `t`, which is a power of two. d := div(d, t) // Invert `d mod 2**256` // Now that `d` is an odd number, it has an inverse // modulo `2**256` such that `d * inv = 1 mod 2**256`. // Compute the inverse by starting with a seed that is correct // correct for four bits. That is, `d * inv = 1 mod 2**4`. let inv := xor(2, mul(3, d)) // Now use Newton-Raphson iteration to improve the precision. // Thanks to Hensel's lifting lemma, this also works in modular // arithmetic, doubling the correct bits in each step. inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128 result := mul( // Divide [p1 p0] by the factors of two. // Shift in bits from `p1` into `p0`. For this we need // to flip `t` such that it is `2**256 / t`. or( mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)), div(sub(result, r), t) ), // inverse mod 2**256 mul(inv, sub(2, mul(d, inv))) ) break } } } /// @dev Calculates `floor(x * y / d)` with full precision, rounded up. /// Throws if result overflows a uint256 or when `d` is zero. /// Credit to Uniswap-v3-core under MIT license: /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { result = fullMulDiv(x, y, d); /// @solidity memory-safe-assembly assembly { if mulmod(x, y, d) { result := add(result, 1) if iszero(result) { mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. revert(0x1c, 0x04) } } } } /// @dev Returns `floor(x * y / d)`. /// Reverts if `x * y` overflows, or `d` is zero. function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { mstore(0x00, 0xad251c27) // `MulDivFailed()`. revert(0x1c, 0x04) } z := div(mul(x, y), d) } } /// @dev Returns `ceil(x * y / d)`. /// Reverts if `x * y` overflows, or `d` is zero. function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { mstore(0x00, 0xad251c27) // `MulDivFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d)) } } /// @dev Returns `ceil(x / d)`. /// Reverts if `d` is zero. function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { if iszero(d) { mstore(0x00, 0x65244e4e) // `DivFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(x, d))), div(x, d)) } } /// @dev Returns `max(0, x - y)`. function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mul(gt(x, y), sub(x, y)) } } /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`. /// Reverts if the computation overflows. function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`. if x { z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x` let half := shr(1, b) // Divide `b` by 2. // Divide `y` by 2 every iteration. for { y := shr(1, y) } y { y := shr(1, y) } { let xx := mul(x, x) // Store x squared. let xxRound := add(xx, half) // Round to the nearest number. // Revert if `xx + half` overflowed, or if `x ** 2` overflows. if or(lt(xxRound, xx), shr(128, x)) { mstore(0x00, 0x49f7642b) // `RPowOverflow()`. revert(0x1c, 0x04) } x := div(xxRound, b) // Set `x` to scaled `xxRound`. // If `y` is odd: if and(y, 1) { let zx := mul(z, x) // Compute `z * x`. let zxRound := add(zx, half) // Round to the nearest number. // If `z * x` overflowed or `zx + half` overflowed: if or(xor(div(zx, x), z), lt(zxRound, zx)) { // Revert if `x` is non-zero. if iszero(iszero(x)) { mstore(0x00, 0x49f7642b) // `RPowOverflow()`. revert(0x1c, 0x04) } } z := div(zxRound, b) // Return properly scaled `zxRound`. } } } } } /// @dev Returns the square root of `x`. function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // Let `y = x / 2**r`. We check `y >= 2**(k + 8)` // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`. let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffffff, shr(r, x)))) z := shl(shr(1, r), z) // Goal was to get `z*z*y` within a small factor of `x`. More iterations could // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`. // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small. // That's not possible if `x < 256` but we can just verify those cases exhaustively. // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`. // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`. // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps. // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)` // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`, // with largest error when `s = 1` and when `s = 256` or `1/256`. // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`. // Then we can estimate `sqrt(y)` using // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`. // There is no overflow risk here since `y < 2**136` after the first branch above. z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If `x+1` is a perfect square, the Babylonian method cycles between // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division z := sub(z, lt(div(x, z), z)) } } /// @dev Returns the cube root of `x`. /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license: /// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy function cbrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3))) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := sub(z, lt(div(x, mul(z, z)), z)) } } /// @dev Returns the square root of `x`, denominated in `WAD`. function sqrtWad(uint256 x) internal pure returns (uint256 z) { unchecked { z = 10 ** 9; if (x <= type(uint256).max / 10 ** 36 - 1) { x *= 10 ** 18; z = 1; } z *= sqrt(x); } } /// @dev Returns the cube root of `x`, denominated in `WAD`. function cbrtWad(uint256 x) internal pure returns (uint256 z) { unchecked { z = 10 ** 12; if (x <= (type(uint256).max / 10 ** 36) * 10 ** 18 - 1) { if (x >= type(uint256).max / 10 ** 36) { x *= 10 ** 18; z = 10 ** 6; } else { x *= 10 ** 36; z = 1; } } z *= cbrt(x); } } /// @dev Returns the factorial of `x`. function factorial(uint256 x) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if iszero(lt(x, 58)) { mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`. revert(0x1c, 0x04) } for { result := 1 } x { x := sub(x, 1) } { result := mul(result, x) } } } /// @dev Returns the log2 of `x`. /// Equivalent to computing the index of the most significant bit (MSB) of `x`. /// Returns 0 if `x` is zero. function log2(uint256 x) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // forgefmt: disable-next-item r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)), 0x0706060506020504060203020504030106050205030304010505030400000000)) } } /// @dev Returns the log2 of `x`, rounded up. /// Returns 0 if `x` is zero. function log2Up(uint256 x) internal pure returns (uint256 r) { r = log2(x); /// @solidity memory-safe-assembly assembly { r := add(r, lt(shl(r, 1), x)) } } /// @dev Returns the log10 of `x`. /// Returns 0 if `x` is zero. function log10(uint256 x) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { if iszero(lt(x, 100000000000000000000000000000000000000)) { x := div(x, 100000000000000000000000000000000000000) r := 38 } if iszero(lt(x, 100000000000000000000)) { x := div(x, 100000000000000000000) r := add(r, 20) } if iszero(lt(x, 10000000000)) { x := div(x, 10000000000) r := add(r, 10) } if iszero(lt(x, 100000)) { x := div(x, 100000) r := add(r, 5) } r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999))))) } } /// @dev Returns the log10 of `x`, rounded up. /// Returns 0 if `x` is zero. function log10Up(uint256 x) internal pure returns (uint256 r) { r = log10(x); /// @solidity memory-safe-assembly assembly { r := add(r, lt(exp(10, r), x)) } } /// @dev Returns the log256 of `x`. /// Returns 0 if `x` is zero. function log256(uint256 x) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(shr(3, r), lt(0xff, shr(r, x))) } } /// @dev Returns the log256 of `x`, rounded up. /// Returns 0 if `x` is zero. function log256Up(uint256 x) internal pure returns (uint256 r) { r = log256(x); /// @solidity memory-safe-assembly assembly { r := add(r, lt(shl(shl(3, r), 1), x)) } } /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`. /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent). function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) { /// @solidity memory-safe-assembly assembly { mantissa := x if mantissa { if iszero(mod(mantissa, 1000000000000000000000000000000000)) { mantissa := div(mantissa, 1000000000000000000000000000000000) exponent := 33 } if iszero(mod(mantissa, 10000000000000000000)) { mantissa := div(mantissa, 10000000000000000000) exponent := add(exponent, 19) } if iszero(mod(mantissa, 1000000000000)) { mantissa := div(mantissa, 1000000000000) exponent := add(exponent, 12) } if iszero(mod(mantissa, 1000000)) { mantissa := div(mantissa, 1000000) exponent := add(exponent, 6) } if iszero(mod(mantissa, 10000)) { mantissa := div(mantissa, 10000) exponent := add(exponent, 4) } if iszero(mod(mantissa, 100)) { mantissa := div(mantissa, 100) exponent := add(exponent, 2) } if iszero(mod(mantissa, 10)) { mantissa := div(mantissa, 10) exponent := add(exponent, 1) } } } } /// @dev Convenience function for packing `x` into a smaller number using `sci`. /// The `mantissa` will be in bits [7..255] (the upper 249 bits). /// The `exponent` will be in bits [0..6] (the lower 7 bits). /// Use `SafeCastLib` to safely ensure that the `packed` number is small /// enough to fit in the desired unsigned integer type: /// ``` /// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether)); /// ``` function packSci(uint256 x) internal pure returns (uint256 packed) { (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`. /// @solidity memory-safe-assembly assembly { if shr(249, x) { mstore(0x00, 0xce30380c) // `MantissaOverflow()`. revert(0x1c, 0x04) } packed := or(shl(7, x), packed) } } /// @dev Convenience function for unpacking a packed number from `packSci`. function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) { unchecked { unpacked = (packed >> 7) * 10 ** (packed & 0x7f); } } /// @dev Returns the average of `x` and `y`. function avg(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = (x & y) + ((x ^ y) >> 1); } } /// @dev Returns the average of `x` and `y`. function avg(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = (x >> 1) + (y >> 1) + (((x & 1) + (y & 1)) >> 1); } } /// @dev Returns the absolute value of `x`. function abs(int256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(sub(0, shr(255, x)), add(sub(0, shr(255, x)), x)) } } /// @dev Returns the absolute distance between `x` and `y`. function dist(int256 x, int256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(mul(xor(sub(y, x), sub(x, y)), sgt(x, y)), sub(y, x)) } } /// @dev Returns the minimum of `x` and `y`. function min(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), lt(y, x))) } } /// @dev Returns the minimum of `x` and `y`. function min(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), slt(y, x))) } } /// @dev Returns the maximum of `x` and `y`. function max(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), gt(y, x))) } } /// @dev Returns the maximum of `x` and `y`. function max(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), sgt(y, x))) } } /// @dev Returns `x`, bounded to `minValue` and `maxValue`. function clamp(uint256 x, uint256 minValue, uint256 maxValue) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, minValue), gt(minValue, x))) z := xor(z, mul(xor(z, maxValue), lt(maxValue, z))) } } /// @dev Returns `x`, bounded to `minValue` and `maxValue`. function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, minValue), sgt(minValue, x))) z := xor(z, mul(xor(z, maxValue), slt(maxValue, z))) } } /// @dev Returns greatest common divisor of `x` and `y`. function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { for { z := x } y {} { let t := y y := mod(z, y) z := t } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RAW NUMBER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns `x + y`, without checking for overflow. function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x + y; } } /// @dev Returns `x + y`, without checking for overflow. function rawAdd(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x + y; } } /// @dev Returns `x - y`, without checking for underflow. function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x - y; } } /// @dev Returns `x - y`, without checking for underflow. function rawSub(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x - y; } } /// @dev Returns `x * y`, without checking for overflow. function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x * y; } } /// @dev Returns `x * y`, without checking for overflow. function rawMul(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x * y; } } /// @dev Returns `x / y`, returning 0 if `y` is zero. function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(x, y) } } /// @dev Returns `x / y`, returning 0 if `y` is zero. function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := sdiv(x, y) } } /// @dev Returns `x % y`, returning 0 if `y` is zero. function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mod(x, y) } } /// @dev Returns `x % y`, returning 0 if `y` is zero. function rawSMod(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := smod(x, y) } } /// @dev Returns `(x + y) % d`, return 0 if `d` if zero. function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := addmod(x, y, d) } } /// @dev Returns `(x * y) % d`, return 0 if `d` if zero. function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mulmod(x, y, d) } } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import { ICatalystV1Structs } from "./ICatalystV1VaultState.sol"; import { ICrossChainReceiver } from "GeneralisedIncentives/src/interfaces/ICrossChainReceiver.sol"; // Autogenerated interface ICatalystChainInterface is ICatalystV1Structs, ICrossChainReceiver { function EXPIRE_CALLER_REWARD() external view returns (uint256); function EXPIRE_CALLER_REWARD_DENOMINATOR() external view returns (uint256); function UNDERWRITING_COLLATERAL() external view returns (uint256); function UNDERWRITING_COLLATERAL_DENOMINATOR() external view returns (uint256); function chainIdentifierToDestinationAddress(bytes32) external view returns (bytes memory); function connectNewChain(bytes32 chainIdentifier, bytes memory remoteCCI, bytes memory remoteGARP) external; function estimateAdditionalCost() external view returns (address asset, uint256 amount); function expireUnderwrite( address targetVault, address toAsset, uint256 U, uint256 minOut, address toAccount, uint16 underwriteIncentiveX16, bytes memory cdata ) external; function getUnderwriteIdentifier( address targetVault, address toAsset, uint256 U, uint256 minOut, address toAccount, uint16 underwriteIncentiveX16, bytes memory cdata ) external pure returns (bytes32 identifier); function maxUnderwritingDuration() external view returns (uint256); function minGasFor(bytes32) external view returns (uint48); function sendCrossChainAsset( RouteDescription memory routeDescription, uint8 toAssetIndex, uint256 U, uint256 minOut, uint256 fromAmount, address fromAsset, uint16 underwriteIncentiveX16, bytes memory calldata_ ) external payable; function sendCrossChainLiquidity( RouteDescription memory routeDescription, uint256 U, uint256[2] memory minOut, uint256 fromAmount, bytes memory calldata_ ) external payable; function setMaxUnderwritingDuration(uint256 newMaxUnderwriteDuration) external; function setMinGasFor(bytes32 chainIdentifier, uint48 minGas) external; function underwrite( address targetVault, address toAsset, uint256 U, uint256 minOut, address toAccount, uint16 underwriteIncentiveX16, bytes memory cdata ) external returns (bytes32 identifier); function underwriteAndCheckConnection( bytes32 sourceIdentifier, bytes memory fromVault, address targetVault, address toAsset, uint256 U, uint256 minOut, address toAccount, uint16 underwriteIncentiveX16, bytes memory cdata ) external; function underwritingStorage(bytes32) external view returns (uint256 tokens, address refundTo, uint96 expiry); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; int256 constant WADWAD = 1e36; uint256 constant LN2 = 693147180559945344; // from numpy import np; int(np.log(2)*10**18). // library MathConstants { // int256 internal constant WADWAD = 1e36; // uint256 internal constant LN2 = 693147180559945344; // from numpy import np; int(np.log(2)*10**18). // }
//SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; import { Ownable } from "solady/auth/Ownable.sol"; import {ERC20} from 'solady/tokens/ERC20.sol'; import {SafeTransferLib} from 'solady/utils/SafeTransferLib.sol'; import { ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol"; import { Initializable } from "solady/utils/Initializable.sol"; import { Multicallable } from "solady/utils/Multicallable.sol"; import { FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol"; import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMessageEscrowStructs.sol"; import { ICatalystReceiver} from "./interfaces/IOnCatalyst.sol"; import { ICatalystV1Factory } from "./interfaces/ICatalystV1Factory.sol"; import "./interfaces/ICatalystV1VaultErrors.sol"; import { MAX_GOVERNANCE_FEE_SHARE } from"./CatalystFactory.sol"; import { ICatalystV1Vault } from "./ICatalystV1Vault.sol"; /** * @title Catalyst: Common Vault Logic * @author Cata Labs Inc. * @notice This abstract contract defines general logic of a Catalyst vault like: * - Vault Token through Solmate's ERC20 implementation. * - Connection management * - Security limit * - Swap Escrow * * By inheriting this abstract contract, a Vault automatically implements common vault logic. * @dev This contract uses the following special notation: * CAPITAL_LETTER_VARIABLES are constants or immutable. * _ prefixed varaibles are storage. * _ prefixed functions are internal. * Unless otherwise required, variables are exposed directly. Such that storage functions are * prefixed with _. * * Upon deleting the escrow, this contract special logic in case of refunds. We want to ensure that * any acks does not revert to clear up the escrows. However, some tokens can revert on demand (blacklist tokens) * For these tokens, we make an optimistic call to the token to send the assets to the user. We don't * care if it actually goes through. Cases where this call would fail the user does not get anything. * A caveat of the implementation is that tokens that revert by PANIC'ing or spending all gas, are not supported * since there is a catch of OOO gas that reverts. It is then dependent on replaying the ack to release the escrow. */ abstract contract CatalystVaultCommon is Initializable, Multicallable, ReentrancyGuard, ERC20, ICatalystV1Vault { /** @notice The fallback user used as a FALLBACK placeholder when underwrite escrows are set. */ address constant UNDERWRITE_FALLBACK_USER = address(uint160(1)); //--- Config ---// // The following section contains the configurable variables. /** * @notice Determines how fast the security limit decreases. * @dev Needs to be long enough for vault token providers to be notified of a breach but short enough for volatility to not soft-freeze the vault. */ uint256 constant DECAY_RATE = 1 days; /** @notice Number of decimals used by the vault's vault tokens */ uint8 constant DECIMALS = 18; /** * @notice The vault tokens initially minted to the user who set up the vault. * @dev The initial deposit along with this value determines the base value of a vault token. */ uint256 constant INITIAL_MINT_AMOUNT = 1e18; // 10**decimals /** * @notice Maximum number of assets supported * @dev Impacts the cost of some for loops. Can be changed without breaking compatibility. */ uint8 constant MAX_ASSETS = 3; //-- ERC20 --// string _name; string _symbol; function name() public override view returns(string memory) { return _name; } function symbol() public override view returns(string memory) { return _symbol; } //-- Variables --// // immutable variables can be read by proxies, thus it is safe to set this on the constructor. address public immutable FACTORY; address public _chainInterface; /** * @notice The approved connections for this vault, stored as _vaultConnection[connectionId][toVault] * @dev to vault is encoded as 64 + 1 bytes. */ mapping(bytes32 => mapping(bytes => bool)) public _vaultConnection; /** * @notice To indicate which token is desired on the target vault, * the desired tokens are provided as an integer which maps to the * asset address. This variable is the map. */ mapping(uint256 => address) public _tokenIndexing; /** @notice The token weights. Used for maintaining a non-symmetric vault asset balance. */ mapping(address => uint256) public _weight; //-- Parameter Flow & Change variables --// // Use getUnitCapacity to indirectly access these variables uint256 _usedUnitCapacity; uint48 public _adjustmentTarget; uint48 public _lastModificationTime; uint48 _usedUnitCapacityTimestamp; //-- Vault fee variables --// /** * @notice The total vault fee. Multiplied by 10**18. * @dev To compute the respective fee, use mulWad: FixedPointMathLib.mulWad(amount, _vaultFee); */ uint64 public _vaultFee; /** * @notice The governance's cut of _vaultFee. * @dev Usage: FixedPointMathLib.mulWad(FixedPointMathLib.mulWad(amount, _vaultFee), _governanceFeeShare); */ uint64 public _governanceFeeShare; /** @notice The vault fee can be changed. _feeAdministrator is the address allowed to change it */ address public _feeAdministrator; /** * @notice The setupMaster is the short-term owner of the vault. * They can connect the vault to vaults on other chains. * @dev !Can extract all of the vault value! Should be set to address(0) once setup is complete via 'finishSetup()'. */ address public _setupMaster; //--- Messaging router limit ---// // The router is not completely trusted. Some limits are // imposed on the DECAY_RATE-ly unidirectional liquidity flow. That is: // if the vault observes more than _maxUnitCapacity of incoming // units, then it will not accept further incoming units. This means the router // can only drain a prefigured percentage of the vault every DECAY_RATE. // For amplified vaults, the security limit is denominated in assets rather than Units. // Outgoing flow is subtracted from incoming flow until 0. /** @notice The max incoming liquidity flow from the router. */ uint256 public _maxUnitCapacity; // Escrow reference /** @notice Total current escrowed tokens. */ mapping(address => uint256) public _escrowedTokens; /** @notice Total current escrowed vault tokens. */ uint256 public _escrowedVaultTokens; /** @notice Find escrow information. Used for both normal swaps and liquidity swaps. */ mapping(bytes32 => address) public _escrowLookup; /** * @notice A mathematical lib that describes various properties of this contract. * These helper functions are not contained in the swap template, since they notisably inflate * the contract side which reduceses the number of optimizer runs => increase the gas cost. */ address immutable public MATHLIB; constructor(address factory_, address mathlib) payable { FACTORY = factory_; MATHLIB = mathlib; _name = "Catalyst Vault Template"; _symbol = ""; // Disable the contract from being initialized. This ensures the factory is // used to deploy vaults. _disableInitializers(); } /** @notice Get the factory owner. That is the owner that can configure this vault post finishSetup(). */ function factoryOwner() public view override returns (address) { return Ownable(FACTORY).owner(); } /** @notice Governance fee destination. This is the address the governance fee is sent to. */ function governanceFeeDestination() public view override returns (address) { return ICatalystV1Factory(FACTORY)._governanceFeeDestination(); } /** * @notice Only allow Governance to change vault parameters * @dev Because of dangerous permissions (setConnection, weight changes): * !CatalystFactory(_factory).owner() must be set to a timelock! */ modifier onlyFactoryOwner() { require(msg.sender == factoryOwner()); // dev: Only factory owner _; } /** * @notice Require the sender of the transaction to be the chain interface. */ modifier onlyChainInterface() { require(msg.sender == _chainInterface); // dev: Only chain interface _; } /** * @notice Verify a connected pool. */ modifier onlyConnectedPool(bytes32 channelId, bytes memory vault) { // Only allow connected vaults if (!_vaultConnection[channelId][vault]) revert VaultNotConnected(); _; } // -- Receive Abstract Functions -- // function _receiveAsset( address toAsset, uint256 U, uint256 minOut ) virtual internal returns (uint256); // -- Setup Functions -- // /** * @notice Setup a vault. * @dev Is initializer. * @param name_ Name of the vault token. * @param symbol_ Symbol of the vault token. * @param chainInterface The chain interface to use. Set address(0) to disable cross-chain swaps. * @param vaultFee Initial vault fee, that is the fee charged on swaps. * @param governanceFee Initial governance fee, that is the percentage of the vault fee that is sent to designated address. * @param feeAdministrator Special address that can modify the pool fees. * @param setupMaster User that is configuring the vault. */ function setup( string calldata name_, string calldata symbol_, address chainInterface, uint64 vaultFee, uint64 governanceFee, address feeAdministrator, address setupMaster ) initializer external override { // The vault is designed to be used by a proxy and not as a standalone vault. // initializer ensures this function is only called once. _chainInterface = chainInterface; _setupMaster = setupMaster; _setVaultFee(vaultFee); _setGovernanceFee(governanceFee); _setFeeAdministrator(feeAdministrator); // Name the ERC20 vault token _name = name_; _symbol = symbol_; } /** * @notice Creates a connection to toVault on the channelId. * @dev Encoding addresses in 64 + 1 bytes for EVM. * For Solidity, this can be done as bytes.concat(bytes1(0x14), bytes32(0), abi.encode(toAddress)) * @param channelId Target chain identifier. Varies from AMB to AMB. * @param toVault 64 + 1 bytes representation of the target vault. * @param state Boolean indicating if the connection should be open or closed. */ function setConnection( bytes32 channelId, bytes calldata toVault, bool state ) external override { require(msg.sender == _setupMaster); // dev: No auth require(toVault.length == 65); // dev: Vault addresses are uint8 + 64 bytes. _vaultConnection[channelId][toVault] = state; emit SetConnection(channelId, toVault, state); } /** * @notice Gives up short-term ownership of the vault. This makes the vault unstoppable. * @dev This function should ALWAYS be called before other liquidity providers deposit liquidity. * While it is not recommended, swapping should be relativly safe since because of the escrow (assuming a minimum output is set). */ function finishSetup() external override { require(msg.sender == _setupMaster); // dev: No auth _setupMaster = address(0); emit FinishSetup(); } /** * @notice View function to signal if a vault is safe to use. * @dev Checks if setupMaster has been set to ZERO_ADDRESS. In other words, has finishSetup been called? * This function is not "safe()". To properly verify if a pool is "ready", verify: * - The vault template is trusted. * - The cross-chain interfce is trusted. * - All connections are correctly set. (Valid chains, valid vaults) * * If you are providing liquidity to a vault, furthermore check: * - All assets in the pool are trusted. * * The above checks have to be done on every vault in the pool. */ function ready() external view override returns (bool) { // _setupMaster == address(0) ensures a safe pool cannot be made unsafe. The setup master can drain the pool! // _tokenIndexing[0] != address(0) check if the pool has been initialized correctly. // The additional check is there to ensure that the initial deployment returns false. return _setupMaster == address(0) && _tokenIndexing[0] != address(0); } /** * @notice Returns the current cross-chain swap capacity. * @dev can be overridden to implement other limit curves. */ function getUnitCapacity() public view virtual override returns (uint256) { uint256 MUC = _maxUnitCapacity; // The delta change to the limit is: timePassed · slope = timePassed · Max/decayrate uint256 unitCapacityReleased; unchecked { // block.timestamp >= _usedUnitCapacityTimestamp, always. // MUC is generally low. unitCapacityReleased = block.timestamp - _usedUnitCapacityTimestamp; // This line can overflow. While unlikely to, if it does it would semi-freeze the pool until // some assets are withdrawn. As a fix, let if overflow. If it overflows, then // unitCapacityReleased becomes smaller so there are no security implications. unitCapacityReleased *= MUC; // DECAY_RATE != 0. unitCapacityReleased /= DECAY_RATE; } uint256 UC = _usedUnitCapacity; // If the change is greater than the units than the spent security limit return maximum. //If we computed it as (MUC - UC + unitCapacityReleased > MUC) it would obviously be wrong. if (UC <= unitCapacityReleased) return MUC; // Amplified vaults can have MUC <= UC since MUC is modified when swapping. unchecked { // We know UC > unitCapacityReleased. if (MUC <= UC - unitCapacityReleased) return 0; // We know UC > unitCapacityReleased and with the above statement we know // MUC > (UC - unitCapacityReleased). Thus we can compute the difference unchecked. return MUC - (UC - unitCapacityReleased); } } // -- Utils -- // /** * @notice Check if the vault supports an inflow of units and decrease * unit capacity by the inflow. * @dev Implement a lot of similar logic to getUnitCapacity. * @param Units The number of units to check and set. */ function _updateUnitCapacity(uint256 Units) internal { uint256 MUC = _maxUnitCapacity; // The delta change to the limit is: timePassed · slope = timePassed · Max/decayrate uint256 unitCapacityReleased; unchecked { // block.timestamp > _usedUnitCapacityTimestamp, always. // MUC is generally low. unitCapacityReleased = (block.timestamp - _usedUnitCapacityTimestamp); // This line can overflow. While unlikely to, if it does it would semi-freeze the pool until // some assets are withdrawn. As a fix, let if overflow. If it overflows, then // unitCapacityReleased becomes smaller so there are no security implications. unitCapacityReleased *= MUC; // DECAY_RATE != 0. unitCapacityReleased /= DECAY_RATE; } uint256 UC = _usedUnitCapacity; // If the change is greater than the units than the spent security limit it is at max. if (UC <= unitCapacityReleased) { // The new limit is MUC, check if more units than MUC are getting spent. if (Units > MUC) revert ExceedsSecurityLimit(); // Set last change and the new spent security limit. _usedUnitCapacityTimestamp = uint48(block.timestamp); _usedUnitCapacity = Units; return; } // Compute the new spent security limit. Start by adding used unit capacity // and to be spent units uint256 newUnitFlow = UC + Units; unchecked { // Then subtract the units that have been decayed. We know UC + Units >= UC > unitCapacityReleased newUnitFlow -= unitCapacityReleased; } // Then check if the new spent security limit is larger than maximum. if (newUnitFlow > MUC) revert ExceedsSecurityLimit(); // Set last change and the new spent security limit. _usedUnitCapacityTimestamp = uint48(block.timestamp); _usedUnitCapacity = newUnitFlow; } // -- Governance Functions -- // /** @notice Sets a new fee administrator that can configure vault fees. */ function _setFeeAdministrator(address administrator) internal { _feeAdministrator = administrator; emit SetFeeAdministrator(administrator); } /** * @notice Sets a new vault fee, taken from input amount. * @param fee Fee in WAD terms. 12e16 is 12%. */ function _setVaultFee(uint64 fee) internal { require(fee <= FixedPointMathLib.WAD); // dev: VaultFee is maximum 100%. _vaultFee = fee; emit SetVaultFee(fee); } /** * @notice Sets a new governance fee. Taken of the vault fee. * @param fee Fee in WAD terms. 12e16 is 12%. */ function _setGovernanceFee(uint64 fee) internal { require(fee <= MAX_GOVERNANCE_FEE_SHARE); // dev: Maximum GovernanceFeeSare exceeded. _governanceFeeShare = fee; emit SetGovernanceFee(fee); } /** @notice Allows the factory owner to set a new fee administrator. */ function setFeeAdministrator(address administrator) public override onlyFactoryOwner { _setFeeAdministrator(administrator); } /** * @notice Allows the factory owner to set the governance fee. * @dev Can only be called by factory owner. * @param fee Fee in WAD terms. 12e16 is 12%. */ function setGovernanceFee(uint64 fee) public override onlyFactoryOwner { _setGovernanceFee(fee); } /** * @notice Allows the feeAdministrator to modify the vault fee. * @dev Can only be called by feeAdministrator * @param fee Fee in WAD terms. 12e16 is 12%. */ function setVaultFee(uint64 fee) public override { require(msg.sender == _feeAdministrator); // dev: Only feeAdministrator can set new fee _setVaultFee(fee); } /** * @notice Collect the governance fee share of the specified vault fee. * @dev The governance fee share is transfered to governanceFeeDestination. */ function _collectGovernanceFee(address asset, uint256 vaultFeeAmount) internal { uint256 governanceFeeShare = _governanceFeeShare; // If governanceFeeShare == 0, then skip the rest of the logic. if (governanceFeeShare != 0) { uint256 governanceFeeAmount = FixedPointMathLib.mulWad(vaultFeeAmount, governanceFeeShare); SafeTransferLib.safeTransfer(asset, governanceFeeDestination(), governanceFeeAmount); } } //-- Escrow Functions --// /** * @notice Create a token escrow for a swap. * @dev It is not checked if fallbackUser is set to address(0) but if it is, the escrow is lost. * @param sendAssetHash The escrow context hash. Will be used to recover the escrow. * From a implementation / usage perspective, if this hash contains fromAsset and amount * it will improves by allowing on to verify these values independently. * @param fallbackUser The user who the escrow belongs to. Do not set to address(0). * @param fromAsset Asset to escrow. * @param amount Amount to escrow. */ function _setTokenEscrow( bytes32 sendAssetHash, address fallbackUser, address fromAsset, uint256 amount ) internal { if (_escrowLookup[sendAssetHash] != address(0)) revert EscrowAlreadyExists(); _escrowLookup[sendAssetHash] = fallbackUser; unchecked { // Must be less than the vault balance. _escrowedTokens[fromAsset] += amount; } } /** * @notice Create a liquidity escrow for a swap. * @dev It is not checked if fallbackUser is set to address(0) but if it is, the escrow is lost. * @param sendLiquidityHash The escrow context hash. Will be used to recover the escrow. * From a implementation / usage perspective, if this hash contains vaultTokens * it will improves by allowing on to verify these values independently. * @param fallbackUser The user who the escrow belongs to. Do not set to address(0). * @param vaultTokens Number of vault tokens to escrow. */ function _setLiquidityEscrow( bytes32 sendLiquidityHash, address fallbackUser, uint256 vaultTokens ) internal { if (_escrowLookup[sendLiquidityHash] != address(0)) revert EscrowAlreadyExists(); _escrowLookup[sendLiquidityHash] = fallbackUser; // Escrow vault tokens are first burned and then escrowed. As a result, this may overflow unlike _escrowedTokens. _escrowedVaultTokens += vaultTokens; } /** * @notice Returns the fallbackUser for the escrow and cleans up the escrow information. * @dev 'delete _escrowLookup[sendAssetHash]' ensures this function can only be called once. */ function _releaseAssetEscrow( bytes32 sendAssetHash, uint256 escrowAmount, address escrowToken ) internal returns(address) { address fallbackUser = _escrowLookup[sendAssetHash]; // Passing in an invalid swapHash returns address(0). require(fallbackUser != address(0)); // dev: Invalid swapHash. Alt: Escrow doesn't exist. delete _escrowLookup[sendAssetHash]; // Stops timeout and further acks from being called. unchecked { // escrowAmount \subseteq _escrowedTokens => escrowAmount <= _escrowedTokens. // Cannot be called twice since the 3 lines before ensure this can only be reached once. _escrowedTokens[escrowToken] -= escrowAmount; } return fallbackUser; } /** * @notice Returns the fallbackUser for the escrow and cleans up the escrow information. * @dev 'delete _escrowLookup[sendAssetHash]' ensures this function can only be called once. */ function _releaseLiquidityEscrow( bytes32 sendLiquidityHash, uint256 escrowAmount ) internal returns(address) { address fallbackUser = _escrowLookup[sendLiquidityHash]; // Passing in an invalid swapHash returns address(0). require(fallbackUser != address(0)); // dev: Invalid swapHash. Alt: Escrow doesn't exist. delete _escrowLookup[sendLiquidityHash]; // Stops timeout and further acks from being called. unchecked { // escrowAmount \subseteq _escrowedVaultTokens => escrowAmount <= _escrowedVaultTokens. // Cannot be called twice since the 3 lines before ensure this can only be reached once. _escrowedVaultTokens -= escrowAmount; } return fallbackUser; } /** * @notice Implements basic ack logic: Deletes and releases tokens to the vault * @dev Should never revert! For security limit adjustments, the implementation may have to be overwritten. * @param toAccount Recipient of the transaction on the target chain. * @param U Number of units initially purchased. * @param escrowAmount Number of tokens escrowed. * @param escrowToken Address of the escrowed token. * @param blockNumberMod Block number when the transaction was commited (mod 32) */ function onSendAssetSuccess( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, address escrowToken, uint32 blockNumberMod ) onlyChainInterface public override virtual { // We need to find the location of the escrow using the information below. // We need to do this twice: 1. Get the address. 2. Delete the escrow. // To save a bit of gas, this hash is computed, cached, and then used. bytes32 sendAssetHash = _computeSendAssetHash( // Computing the hash doesn't revert. toAccount, // Ensures no collisions between different users U, // Used to randomise the hash escrowAmount, // Required! to validate release escrow data escrowToken, // Required! to validate release escrow data blockNumberMod // Used to randomize the hash. ); _releaseAssetEscrow(sendAssetHash, escrowAmount, escrowToken); // Only reverts for missing escrow emit SendAssetSuccess( channelId, toAccount, U, escrowAmount, escrowToken, blockNumberMod ); } /** * @notice Implements basic timeout logic: Deletes and sends tokens to the user. * @dev Should never revert! * For blacklist tokens, this function contains custom logic to support failing ERC20 transfer. * If an ERC20 transfer fails (say because of a blocklist), we only revert if it was because of OOO. * This implies that if an ERC20 transfer fails, then the escrow is lost. This shouldn't be the cast * except if a token is paused, blacklisted, or the vault is exploited such that it has less assets * than the escrow. * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32. * @param U Number of units initially purchased. * @param escrowAmount Number of tokens escrowed. * @param escrowToken Token escrowed. * @param blockNumberMod Block number of the transaction that commited the swap (mod 32) */ function onSendAssetFailure( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, address escrowToken, uint32 blockNumberMod ) onlyChainInterface public override virtual { // We need to find the location of the escrow using the information below. // We need to do this twice: 1. Get the address. 2. Delete the escrow. // To save a bit of gas, this hash is computed and saved and then used. bytes32 sendAssetHash = _computeSendAssetHash( // Computing the hash doesn't revert. toAccount, // Ensures no collisions between different users U, // Used to randomise the hash escrowAmount, // Required! to validate release escrow data escrowToken, // Required! to validate release escrow data blockNumberMod // Used to randomize the hash. ); // This call provides re-entry protection against re-entering this call. Otherwise, this call can always be called. address fallbackAddress = _releaseAssetEscrow(sendAssetHash, escrowAmount, escrowToken); // Only reverts for missing escrow, // We are going to make a low-level call. It may revert (see comment below) but it should not revert if it runs out of gas (that should be raised). // As such, get the current gas in the contract. uint256 gasLeftBeforeCall = gasleft(); bool success; // Make a low level call such that the transfer never fails. This is important for tokens using block lists. // This also implies that if you get blacklisted between when you initiated the swap and the swap failed, you would lose the tokens. bytes memory payload = abi.encodeWithSignature("transfer(address,uint256)", fallbackAddress, escrowAmount); assembly ("memory-safe") { // The gas limit is set to 0x8000000000000000000000000000000000000000000000000000000000000000. // This is essentially all gas since the actual gas forwarded is min(gasForwarded, gasleft * 63/64). success := call(0x8000000000000000000000000000000000000000000000000000000000000000, escrowToken, 0, add(payload, 0x20), mload(payload), 0, 0) // SafeTransferLib.safeTransferFrom(escrowToken, fallbackAddress, escrowAmount); } // If the call failed, check if it failed with OOO (If the call used all of the gas it has available). if (!success) if (gasleft() < gasLeftBeforeCall * 1 / 63) revert NotEnoughGas(); emit SendAssetFailure( channelId, toAccount, U, escrowAmount, escrowToken, blockNumberMod ); } /** * @notice Implements basic liquidity ack logic: Deletes and releases vault tokens to the vault. * @dev Should never revert! For security limit adjustments, the implementation should be overwritten. * @param toAccount Recipient of the transaction on the target chain. * @param U Number of units initially acquired. * @param escrowAmount Number of vault tokens escrowed. * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendLiquiditySuccess( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, uint32 blockNumberMod ) onlyChainInterface public override virtual { // We need to find the location of the escrow using the information below. // We need to do this twice: 1. Get the address. 2. Delete the escrow. // To save a bit of gas, this hash is computed and saved and then used. bytes32 sendLiquidityHash = _computeSendLiquidityHash( toAccount, // Ensures no collisions between different users U, // Used to randomise the hash escrowAmount, // Required! to validate release escrow data blockNumberMod // Used to randomize the hash. ); _releaseLiquidityEscrow(sendLiquidityHash, escrowAmount); // Reverts for missing escrow emit SendLiquiditySuccess( channelId, toAccount, U, escrowAmount, blockNumberMod ); } /** * @notice Implements basic liquidity timeout logic: Deletes and sends vault tokens to the user. * @dev Should never revert! * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32. * @param U Number of units initially acquired. * @param escrowAmount Number of vault tokens escrowed. * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendLiquidityFailure( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, uint32 blockNumberMod ) onlyChainInterface public override virtual { bytes32 sendLiquidityHash = _computeSendLiquidityHash( toAccount, // Ensures no collisions between different users U, // Used to randomise the hash escrowAmount, // Required! to validate release escrow data blockNumberMod // Used to randomize the hash. ); // This function only allows entering this function once. Once called, it can never be called without reverting again. address fallbackAddress = _releaseLiquidityEscrow(sendLiquidityHash, escrowAmount); // Reverts for missing escrow _mint(fallbackAddress, escrowAmount); // Never reverts. emit SendLiquidityFailure( channelId, toAccount, U, escrowAmount, blockNumberMod ); } /** * @notice Computes a unique identifier for a swap. This unique identifier can be used to identify swaps cross-chain. * However, it is never exposed. This is done to let the hashing algorithm be flexible between implementations. */ function _computeSendAssetHash( bytes calldata toAccount, uint256 U, uint256 amount, address fromAsset, uint32 blockNumberMod ) internal pure returns(bytes32) { return keccak256( bytes.concat( toAccount, // Ensures no collisions between different users. bytes32(U), // Used to randomise the hash. bytes32(amount), // Required! to validate release escrow data. bytes20(fromAsset), // Required! to validate release escrow data. bytes4(blockNumberMod) // Used to randomize the hash. ) ); } /** * @notice Computes a unique identifier for a swap. This unique identifier can be used to identify swaps cross-chain. * However, it is never exposed. This is done to let the hashing algorithm be flexible between implementations. */ function _computeSendLiquidityHash( bytes calldata toAccount, uint256 U, uint256 amount, uint32 blockNumberMod ) internal pure returns(bytes32) { return keccak256( bytes.concat( toAccount, // Ensures no collisions between different users. bytes32(U), // Used to randomise the hash. bytes32(amount), // Required! to validate release escrow data. bytes4(blockNumberMod) // Used to randomize the hash. ) ); } // -- Underwrite Asset Swaps -- // function underwriteAsset( bytes32 identifier, address toAsset, uint256 U, uint256 minOut ) onlyChainInterface virtual public returns (uint256 purchasedTokens) { // Simulate a receiveAsset call. This gets us the purchased tokens. purchasedTokens = _receiveAsset(toAsset, U, minOut); // Set the escrow. _setTokenEscrow( identifier, UNDERWRITE_FALLBACK_USER, toAsset, purchasedTokens ); emit SwapUnderwritten( identifier, toAsset, U, purchasedTokens ); } /** * @notice Release assets associated with an underwrite escrow. * @param refundTo Released assets are sent to this address. * @param identifier Underwriting identifier. Is used to index the storage for valid escrows. * @param escrowAmount Number of tokens escrowed. * @param escrowToken Escrowed token address. * @param sourceIdentifier The source chain identifier. * @param fromVault The originating vault. */ function releaseUnderwriteAsset( address refundTo, bytes32 identifier, uint256 escrowAmount, address escrowToken, bytes32 sourceIdentifier, bytes calldata fromVault ) onlyChainInterface onlyConnectedPool(sourceIdentifier, fromVault) virtual public { _releaseAssetEscrow(identifier, escrowAmount, escrowToken); // Reverts for missing escrow. // Send the assets to the user. SafeTransferLib.safeTransfer(escrowToken, refundTo, escrowAmount); } /** * @notice Delete an underwrite escrow without releasing any tokens. * @dev The unsued parameter U is used for overwrites. (see CataulystVaultAmplified.sol) * @param identifier Underwriting identifier. Is used to index the storage for valid escrows. * param U Number of underwritten units. Is used for Amplified vaults to modify the unit tracker. * @param escrowAmount Number of tokens escrowed. * @param escrowToken Escrowed token address. */ function deleteUnderwriteAsset( bytes32 identifier, uint256 /* U */, uint256 escrowAmount, address escrowToken ) onlyChainInterface virtual public { _releaseAssetEscrow(identifier, escrowAmount, escrowToken); // Reverts for missing escrow. } }
//SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol"; import { WADWAD } from "./utils/MathConstants.sol"; /** * @title Catalyst: Amplified Integrals * @author Catalyst Labs Inc. */ contract IntegralsAmplified { /** * @notice Computes the integral \int_{wA}^{wA+wx} 1/w^k · (1-k) dw * = (wA + wx)^(1-k) - wA^(1-k) * The value is returned as units, which is always WAD. * @dev All input amounts should be the raw numbers and not WAD. * Since units are always denominated in WAD, the function should be treated as mathematically *native*. * @param input Input amount. * @param A Current vault balance of the x token. * @param W Weight of the x token. * @param oneMinusAmp Amplification. Provided as (1-k). * @return uint256 Units (units are **always** WAD). */ function _calcPriceCurveArea( uint256 input, uint256 A, uint256 W, int256 oneMinusAmp ) internal pure returns (uint256) { // Will revert if W = 0. // Or if A + input == 0. int256 calc = FixedPointMathLib.powWad( int256(W * (A + input) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails. oneMinusAmp ); // If the vault contains 0 assets, the below computation will fail. This is bad. // Instead, check if A is 0. If it is then skip because: (W · A)^(1-k) = (W · 0)^(1-k) = 0 if (A != 0) { unchecked { // W * A * FixedPointMathLib.WAD < W * (A + input) * FixedPointMathLib.WAD calc -= FixedPointMathLib.powWad( int256(W * A * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); } } return uint256(calc); // Casting always safe, as calc always > =0 } /** * @notice Solves the equation U = \int_{wA-_wy}^{wA} W/w^k · (1-k) dw for y * = B · (1 - ( * (wB^(1-k) - U) / (wB^(1-k)) * )^(1/(1-k)) * ) * The value is returned as output token. (not WAD) * @dev All input amounts should be the raw numbers and not WAD. * Since units are always multiplied by WAD, the function should be treated as mathematically *native*. * @param U Incoming vault specific units. * @param B Current vault balance of the y token. * @param W Weight of the y token. * @return uint25 Output denominated in output token. (not WAD) */ function _calcPriceCurveLimit( uint256 U, uint256 B, uint256 W, int256 oneMinusAmp ) internal pure returns (uint256) { // W_B · B^(1-k) is repeated twice and requires 1 power. As a result, we compute it and cache it. uint256 W_BxBtoOMA = uint256( // Always casts a positive value. FixedPointMathLib.powWad( int256(W * B * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails. oneMinusAmp ) ); return FixedPointMathLib.mulWad( B, FixedPointMathLib.WAD - uint256( // Always casts a positive value FixedPointMathLib.powWad( int256(FixedPointMathLib.divWadUp(W_BxBtoOMA - U, W_BxBtoOMA)), // Casting never overflows, as division result is always < 1 WADWAD / oneMinusAmp ) ) ); } /** * @notice Solves the equation * \int_{wA}^{wA + wx} 1/w^k · (1-k) dw = \int_{wB-wy}^{wB} 1/w^k · (1-k) dw for y * => out = B · (1 - ( * (wB^(1-k) - (wA+wx)^(1-k) - wA^(1-k)) / (wB^(1-k)) * )^(1/(1-k)) * ) * Alternatively, the integral can be computed through: * _calcPriceCurveLimit(_calcPriceCurveArea(input, A, W_A, amp), B, W_B, amp). * @dev All input amounts should be the raw numbers and not WAD. * @param input Input amount. * @param A Current vault balance of the x token. * @param B Current vault balance of the y token. * @param W_A Weight of the x token. * @param W_B Weight of the y token. * @param oneMinusAmp Amplification. * @return uint256 Output denominated in output token. */ function _calcCombinedPriceCurves( uint256 input, uint256 A, uint256 B, uint256 W_A, uint256 W_B, int256 oneMinusAmp ) internal pure returns (uint256) { // uint256 W_BxBtoOMA = uint256(FixedPointMathLib.powWad( // int256(W_B * B * FixedPointMathLib.WAD), // oneMinusAmp // )); // uint256 U = uint256(FixedPointMathLib.powWad( // int256(W_A * (A + input) * FixedPointMathLib.WAD), // oneMinusAmp // ) - FixedPointMathLib.powWad( // int256(W_A * A * FixedPointMathLib.WAD), // oneMinusAmp // )); // _calcPriceCurveArea(input, A, W_A, amp) // return B * (FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // int256(FixedPointMathLib.divWadUp(W_BxBtoOMA - U, W_BxBtoOMA)), // int256(FixedPointMathLib.WAD * FixedPointMathLib.WAD / uint256(oneMinusAmp))) // )) / FixedPointMathLib.WAD; // _calcPriceCurveLimit return _calcPriceCurveLimit(_calcPriceCurveArea(input, A, W_A, oneMinusAmp), B, W_B, oneMinusAmp); } /** * @notice Converts units into vault tokens with the below formula * pt = PT · (((N · wa_0^(1-k) + U)/(N · wa_0^(1-k))^(1/(1-k)) - 1) * @dev The function leaves a lot of computation to the external implementation. This is done to avoid recomputing values several times. * @param U Number of units to convert into vault tokens. * @param ts Current vault token supply. The escrowed vault tokens should not be added, since the function then returns more. * @param it_times_walpha_amped wa_0^(1-k) * @param oneMinusAmpInverse Vault amplification. * @return uint256 Output denominated in vault tokens. */ function _calcPriceCurveLimitShare(uint256 U, uint256 ts, uint256 it_times_walpha_amped, int256 oneMinusAmpInverse) internal pure returns (uint256) { uint256 vaultTokens = FixedPointMathLib.mulWad( ts, uint256( // Always casts a positive value, as powWad >= 1, hence powWad - WAD >= 0 FixedPointMathLib.powWad( // poWad always >= 1, as the 'base' is always >= 1 int256(FixedPointMathLib.divWad( // If casting overflows to a negative number, powWad fails it_times_walpha_amped + U, it_times_walpha_amped )), oneMinusAmpInverse ) - int256(FixedPointMathLib.WAD) ) ); return vaultTokens; } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; interface ICatalystReceiver { /** * @notice The callback from a catalyst call. To determine if the swap was an asset or liquidity swap, either the current balance should be checked or it should be encoded into data. * @dev If you want full finality (not just economical finality) * revert on underwritten == true. */ function onCatalystCall(uint256 purchasedTokens, bytes calldata data, bool underwritten) external; }
//SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; import "./interfaces/ICatalystV1VaultSuccessFailure.sol"; import "./interfaces/ICatalystV1VaultAdministration.sol"; import "./interfaces/ICatalystV1VaultDerived.sol"; import "./interfaces/ICatalystV1VaultErrors.sol"; import "./interfaces/ICatalystV1VaultEvents.sol"; import "./interfaces/ICatalystV1VaultImmutables.sol"; import "./interfaces/ICatalystV1VaultPermissionless.sol"; import "./interfaces/ICatalystV1VaultState.sol"; import "./interfaces/ICatalystV1Underwriting.sol"; interface ICatalystV1Vault is ICatalystV1VaultSuccessFailure, ICatalystV1VaultAdministration, ICatalystV1VaultDerived, ICatalystV1VaultEvents, ICatalystV1VaultImmutables, ICatalystV1VaultPermissionless, ICatalystV1VaultState, ICatalystV1Structs, ICatalystV1Underwriting {}
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMessageEscrowStructs.sol"; interface ICatalystV1Structs is IMessageEscrowStructs { /** * @param chainIdentifier target chain identifier. * @param toVault Target vault on the target chain. Encoded in 64 + 1 bytes. * @param toAccount Recipient of the transaction on the target chain. Encoded in 64 + 1 bytes. * @param incentive Cross-chain relaying incentive description. */ struct RouteDescription { bytes32 chainIdentifier; bytes toVault; bytes toAccount; IncentiveDescription incentive; uint64 deadline; } } /** * @title Vault state * @notice Contains all vault storage which depends on the vault state. */ interface ICatalystV1VaultState { /** @notice Token weights. Used for maintaining a non symmetric vault asset balance. */ function _weight(address token) external view returns (uint256); function _adjustmentTarget() external view returns (uint48); function _lastModificationTime() external view returns (uint48); /** @notice The vault fee in WAD. Implementation of fee: mulWadDown(_amount, _vaultFee) */ function _vaultFee() external view returns (uint64); function _governanceFeeShare() external view returns (uint64); /** @notice The address of the responsible for adjusting the fees. */ function _feeAdministrator() external view returns (address); /** @notice The setupMaster is the short-term owner of the vault. They can connect the vault to vaults on other chains. */ function _setupMaster() external view returns (address); // Security limit /** @notice The max incoming liquidity flow from the router. */ function _maxUnitCapacity() external view returns (uint256); // Escrow reference /** @notice Total current escrowed tokens */ function _escrowedTokens(address token) external view returns (uint256); /** @notice Find escrow information. Used for both normal swaps and liquidity swaps. */ function _escrowLookup(bytes32 sendAssetHash) external view returns (address); /** @notice Total current escrowed vault tokens */ function _escrowedVaultTokens() external view returns (uint256); /** @notice Checks if there is a connection to the described vault */ function _vaultConnection(bytes32 sourceIdentifier, bytes calldata fromVault) external view returns (bool); function factoryOwner() external view returns (address); function governanceFeeDestination() external view returns (address); /** * @notice External view function purely used to signal if a vault is safe to use. * @dev Just checks if the setup master has been set to ZERO_ADDRESS. In other words, has finishSetup been called? */ function ready() external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface ICrossChainReceiver { /** * @notice Handles the acknowledgement from the destination * @dev acknowledgement is exactly the output of receiveMessage except if receiveMessage failed, then it is error code (0xff or 0xfe) + original message. * If an acknowledgement isn't needed, this can be implemented as {}. * - This function can be called by someone else again! Ensure that if this endpoint is called twice with the same message nothing bad happens. * - If the application expects that the maxGasAck will be provided, then it should check that it got enough and revert if it didn't. * Otherwise, it is assumed that you didn't need the extra gas. * @param destinationIdentifier An identifier for the destination chain. * @param messageIdentifier A unique identifier for the message. The identifier matches the identifier returned when escrowed the message. * This identifier can be mismanaged by the messaging protocol. * @param acknowledgement The acknowledgement sent back by receiveMessage. Is 0xff if receiveMessage reverted. */ function receiveAck(bytes32 destinationIdentifier, bytes32 messageIdentifier, bytes calldata acknowledgement) external; /** * @notice receiveMessage from a cross-chain call. * @dev The application needs to check the fromApplication combined with sourceIdentifierbytes to figure out if the call is authenticated. * - If the application expects that the maxGasDelivery will be provided, then it should check that it got enough and revert if it didn't. * Otherwise, it is assumed that you didn't need the extra gas. * @return acknowledgement Information which is passed to receiveAck. * If you return 0xff, you cannot know the difference between Executed but "failed" and outright failed. */ function receiveMessage(bytes32 sourceIdentifierbytes, bytes32 messageIdentifier, bytes calldata fromApplication, bytes calldata message) external returns(bytes memory acknowledgement); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. /// You MUST call the `_initializeOwner` in the constructor / initializer. /// /// While the ownable portion follows /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, /// the nomenclature for the 2-step ownership handover may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev Cannot double-initialize. error AlreadyInitialized(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`. /// It is intentionally chosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. bytes32 internal constant _OWNER_SLOT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. function _guardInitializeOwner() internal pure virtual returns (bool guard) {} /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT if sload(ownerSlot) { mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`. revert(0x1c, 0x04) } // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } else { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(_OWNER_SLOT, newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) } } else { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(_OWNER_SLOT))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. /// Override to return a different value if needed. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ownershipHandoverValidFor() internal view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + _ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(_OWNER_SLOT) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Reentrancy guard mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol) abstract contract ReentrancyGuard { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Unauthorized reentrant call. error Reentrancy(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`. /// 9 bytes is large enough to avoid collisions with lower slots, /// but not too large to result in excessive bytecode bloat. uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* REENTRANCY GUARD */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Guards a function from reentrancy. modifier nonReentrant() virtual { /// @solidity memory-safe-assembly assembly { if eq(sload(_REENTRANCY_GUARD_SLOT), address()) { mstore(0x00, 0xab143c06) // `Reentrancy()`. revert(0x1c, 0x04) } sstore(_REENTRANCY_GUARD_SLOT, address()) } _; /// @solidity memory-safe-assembly assembly { sstore(_REENTRANCY_GUARD_SLOT, codesize()) } } /// @dev Guards a view function from read-only reentrancy. modifier nonReadReentrant() virtual { /// @solidity memory-safe-assembly assembly { if eq(sload(_REENTRANCY_GUARD_SLOT), address()) { mstore(0x00, 0xab143c06) // `Reentrancy()`. revert(0x1c, 0x04) } } _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Initializable mixin for the upgradeable contracts. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Initializable.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/utils/Initializable.sol) abstract contract Initializable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The contract is already initialized. error InvalidInitialization(); /// @dev The contract is not initializing. error NotInitializing(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Triggered when the contract has been initialized. event Initialized(uint64 version); /// @dev `keccak256(bytes("Initialized(uint64)"))`. bytes32 private constant _INTIALIZED_EVENT_SIGNATURE = 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The default initializable slot is given by: /// `bytes32(~uint256(uint32(bytes4(keccak256("_INITIALIZABLE_SLOT")))))`. /// /// Bits Layout: /// - [0] `initializing` /// - [1..64] `initializedVersion` bytes32 private constant _INITIALIZABLE_SLOT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return a custom storage slot if required. function _initializableSlot() internal pure virtual returns (bytes32) { return _INITIALIZABLE_SLOT; } /// @dev Guards an initializer function so that it can be invoked at most once. /// /// You can guard a function with `onlyInitializing` such that it can be called /// through a function guarded with `initializer`. /// /// This is similar to `reinitializer(1)`, except that in the context of a constructor, /// an `initializer` guarded function can be invoked multiple times. /// This can be useful during testing and is not expected to be used in production. /// /// Emits an {Initialized} event. modifier initializer() virtual { bytes32 s = _initializableSlot(); /// @solidity memory-safe-assembly assembly { let i := sload(s) // Set `initializing` to 1, `initializedVersion` to 1. sstore(s, 3) // If `!(initializing == 0 && initializedVersion == 0)`. if i { // If `!(address(this).code.length == 0 && initializedVersion == 1)`. if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) { mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`. revert(0x1c, 0x04) } s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`. } } _; /// @solidity memory-safe-assembly assembly { if s { // Set `initializing` to 0, `initializedVersion` to 1. sstore(s, 2) // Emit the {Initialized} event. mstore(0x20, 1) log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE) } } } /// @dev Guards an reinitialzer function so that it can be invoked at most once. /// /// You can guard a function with `onlyInitializing` such that it can be called /// through a function guarded with `reinitializer`. /// /// Emits an {Initialized} event. modifier reinitializer(uint64 version) virtual { bytes32 s = _initializableSlot(); /// @solidity memory-safe-assembly assembly { version := and(version, 0xffffffffffffffff) // Clean upper bits. let i := sload(s) // If `initializing == 1 || initializedVersion >= version`. if iszero(lt(and(i, 1), lt(shr(1, i), version))) { mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`. revert(0x1c, 0x04) } // Set `initializing` to 1, `initializedVersion` to `version`. sstore(s, or(1, shl(1, version))) } _; /// @solidity memory-safe-assembly assembly { // Set `initializing` to 0, `initializedVersion` to `version`. sstore(s, shl(1, version)) // Emit the {Initialized} event. mstore(0x20, version) log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE) } } /// @dev Guards a function such that it can only be called in the scope /// of a function guarded with `initializer` or `reinitializer`. modifier onlyInitializing() virtual { _checkInitializing(); _; } /// @dev Reverts if the contract is not initializing. function _checkInitializing() internal view virtual { bytes32 s = _initializableSlot(); /// @solidity memory-safe-assembly assembly { if iszero(and(1, sload(s))) { mstore(0x00, 0xd7e6bcf8) // `NotInitializing()`. revert(0x1c, 0x04) } } } /// @dev Locks any future initializations by setting the initialized version to `2**64 - 1`. /// /// Calling this in the constructor will prevent the contract from being initialized /// or reinitialized. It is recommended to use this to lock implementation contracts /// that are designed to be called through proxies. /// /// Emits an {Initialized} event the first time it is successfully called. function _disableInitializers() internal virtual { bytes32 s = _initializableSlot(); /// @solidity memory-safe-assembly assembly { let i := sload(s) if and(i, 1) { mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`. revert(0x1c, 0x04) } let uint64max := shr(192, s) // Computed to save bytecode. if iszero(eq(shr(1, i), uint64max)) { // Set `initializing` to 0, `initializedVersion` to `2**64 - 1`. sstore(s, shl(1, uint64max)) // Emit the {Initialized} event. mstore(0x20, uint64max) log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE) } } } /// @dev Returns the highest version that has been initialized. function _getInitializedVersion() internal view virtual returns (uint64 version) { bytes32 s = _initializableSlot(); /// @solidity memory-safe-assembly assembly { version := shr(1, sload(s)) } } /// @dev Returns whether the contract is currently initializing. function _isInitializing() internal view virtual returns (bool result) { bytes32 s = _initializableSlot(); /// @solidity memory-safe-assembly assembly { result := and(1, sload(s)) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Contract that enables a single call to call multiple methods on itself. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol) /// /// WARNING: /// This implementation is NOT to be used with ERC2771 out-of-the-box. /// https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure /// This also applies to potentially other ERCs / patterns appending to the back of calldata. /// /// We do NOT have a check for ERC2771, as we do not inherit from OpenZeppelin's context. /// Moreover, it is infeasible and inefficient for us to add checks and mitigations /// for all possible ERC / patterns appending to the back of calldata. /// /// We would highly recommend using an alternative pattern such as /// https://github.com/Vectorized/multicaller /// which is more flexible, futureproof, and safer by default. abstract contract Multicallable { /// @dev Apply `DELEGATECALL` with the current contract to each calldata in `data`, /// and store the `abi.encode` formatted results of each `DELEGATECALL` into `results`. /// If any of the `DELEGATECALL`s reverts, the entire context is reverted, /// and the error is bubbled up. /// /// This function is deliberately made non-payable to guard against double-spending. /// (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong) /// /// For efficiency, this function will directly return the results, terminating the context. /// If called internally, it must be called at the end of a function /// that returns `(bytes[] memory)`. function multicall(bytes[] calldata data) public virtual returns (bytes[] memory) { assembly { mstore(0x00, 0x20) mstore(0x20, data.length) // Store `data.length` into `results`. // Early return if no data. if iszero(data.length) { return(0x00, 0x40) } let results := 0x40 // `shl` 5 is equivalent to multiplying by 0x20. let end := shl(5, data.length) // Copy the offsets from calldata into memory. calldatacopy(0x40, data.offset, end) // Offset into `results`. let resultsOffset := end // Pointer to the end of `results`. end := add(results, end) for {} 1 {} { // The offset of the current bytes in the calldata. let o := add(data.offset, mload(results)) let m := add(resultsOffset, 0x40) // Copy the current bytes from calldata to the memory. calldatacopy( m, add(o, 0x20), // The offset of the current bytes' bytes. calldataload(o) // The length of the current bytes. ) if iszero(delegatecall(gas(), address(), m, calldataload(o), codesize(), 0x00)) { // Bubble up the revert if the delegatecall reverts. returndatacopy(0x00, 0x00, returndatasize()) revert(0x00, returndatasize()) } // Append the current `resultsOffset` into `results`. mstore(results, resultsOffset) results := add(results, 0x20) // Append the `returndatasize()`, and the return data. mstore(m, returndatasize()) returndatacopy(add(m, 0x20), 0x00, returndatasize()) // Advance the `resultsOffset` by `returndatasize() + 0x20`, // rounded up to the next multiple of 32. resultsOffset := and(add(add(resultsOffset, returndatasize()), 0x3f), 0xffffffffffffffe0) if iszero(lt(results, end)) { break } } return(0x00, add(resultsOffset, 0x40)) } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IMessageEscrowStructs { struct IncentiveDescription { uint48 maxGasDelivery; // 0: 6/32 bytes uint48 maxGasAck; // 0: 12/32 bytes address refundGasTo; // 0: 32/32 bytes uint96 priceOfDeliveryGas; // 1: 12/32 bytes uint96 priceOfAckGas; // 1: 24/32 bytes uint64 targetDelta; // 1: 32/32 bytes } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import { ICatalystV1FactoryEvents } from "./ICatalystV1FactoryEvents.sol"; interface ICatalystV1Factory is ICatalystV1FactoryEvents { function _defaultGovernanceFee() external view returns (uint64); function _governanceFeeDestination() external view returns (address); function deployVault( address vaultTemplate, address[] memory assets, uint256[] memory init_balances, uint256[] memory weights, uint64 amp, uint64 vaultFee, string memory name, string memory symbol, address chainInterface ) external returns (address); function isCreatedByFactory(address, address) external view returns (bool); function setDefaultGovernanceFee(uint64 fee) external; function setGovernanceFeeDestination(address feeDestination) external; }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; error EscrowAlreadyExists(); // 0xed778779 error ExceedsSecurityLimit(); // 0x7c1e66d0 error NotEnoughGas(); // 0xdd629f86 error ReturnInsufficient(uint256,uint256); // 0x24557f05 error UnusedUnitsAfterWithdrawal(uint256); // 0x0289311f error VaultNotConnected(); // 0x2c64c1b2 error WithdrawRatioNotZero(); // 0xb8003bfa
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; import { Ownable } from "solady/auth/Ownable.sol"; import { SafeTransferLib } from 'solady/utils/SafeTransferLib.sol'; import { LibClone } from "solady/utils/LibClone.sol"; import { ICatalystV1Factory } from "./interfaces/ICatalystV1Factory.sol"; import { ICatalystV1Vault } from "./ICatalystV1Vault.sol"; uint256 constant MAX_GOVERNANCE_FEE_SHARE = 75e16; // 75% /** * @title Catalyst Swap Factory * @author Cata Labs Inc. * @notice Allows permissionless deployment Catalyst vaults and defines governance address for vaults to read. * Importantly, this vault allows anyone to deploy a vault using any vault template and vault cross-chain interface. * As a result, just because a vault was deployed using this contract does not imply that it is safe. Vaults should * be cross-checked for their template, cross-chain interface, and if they are setup correctly. It may even be * that some vault templates only work with some cross-chain interfaces. * * Using the reference Catalyst Vault Templates, the owner of the factory is also the _owner_ of the Vaults. * They have certain privilege that may be able to be abused depending on the vault. One of these is configurating * fees. As a result: * !The owner of the factory must be a timelock! */ contract CatalystFactory is Ownable, ICatalystV1Factory { error InvalidAssetCount(); error InvalidWeightCount(); error FeeDestinationAddress0(); error MaximumGovernanceFeeShare(); /** * @notice A mapping which describes if a vault has been created by this factory. * Indexed by chainInterface then vault address. */ mapping(address => mapping(address => bool)) public isCreatedByFactory; /** * @notice Default governance fee. When a vault is created, this is the governance fee applied to that vault. */ uint64 public _defaultGovernanceFee; /** * @notice The address to send governance fees to. * @dev Not enforced by the factory but vaults are expected to follow it. */ address public _governanceFeeDestination; // The 2 above storage slots are packed together. constructor(address defaultOwner) payable { _initializeOwner(defaultOwner); _governanceFeeDestination = defaultOwner; } /** * @notice Set default governance fee share. * @dev The set governance fee only applies to newly created vaults. Vaults have to be individual modified post creation. * Is in WAD, (1e18 terms). So 1e16 is 1%. Cannot be set larger than 75% (75e16). */ function setDefaultGovernanceFee(uint64 fee) override public onlyOwner { if (fee > MAX_GOVERNANCE_FEE_SHARE) revert MaximumGovernanceFeeShare(); emit SetDefaultGovernanceFee(fee); _defaultGovernanceFee = fee; } /** * @notice Set the recipient of the governance. * @dev It is expected that vaults read this value and send their governance fees here. * This contract has no way to enforce if vaults honour this value. * Cannot be set to address(0). If wish to burn, set to 0xdead. */ function setGovernanceFeeDestination(address feeDestination) override public onlyOwner { if (feeDestination == address(0)) revert FeeDestinationAddress0(); emit SetGovernanceFeeDestination(feeDestination); _governanceFeeDestination = feeDestination; } /** * @notice Deploys a Catalyst vault, funds the vault with tokens, and calls setup. * When deploying vaults, there are 2 stages that needs to happen: * 1. We need to setup the vaults with the correct configuration. * 2. We need to set the vault swap curve. This consists of setting assets, vaults, amplification, etc. * The reason it is done in 2 steps is because of the stack limit. By spreading it across 2 calls, it is * cheaper gas wise. * This is done in a safe way by expecting both of these init. calls to be done in a single transaction. * As a result, the vaults are never left in a vulnerable state. It is expected that the latter call * (initializeSwapCurves) completes initialization and blocks the setup functions from being called again. * @dev The deployer needs to set relevant approvals to this contract before calling deployVault. * @param vaultTemplate The template the transparent proxy should target. * @param assets The list of assets the vault should support. * @param init_balances The initial balances of the vault. (Should be approved) * @param weights The weights of the tokens. * @param amp Token parameter 1. (Amplification) * @param vaultFee The vault fee. * @param name Name of the Vault token. * @param symbol Symbol for the Vault token. * @param chainInterface The cross chain interface used for cross-chain swaps. (Can be address(0) to disable cross-chain swaps.) * @return vault The address of the created Catalyst Vault. (minimal transparent proxy) */ function deployVault( address vaultTemplate, address[] calldata assets, uint256[] calldata init_balances, uint256[] calldata weights, uint64 amp, uint64 vaultFee, string memory name, string memory symbol, address chainInterface ) override external returns (address vault) { // Check if an invalid asset count has been provided if (assets.length == 0) revert InvalidAssetCount(); // Check if an invalid weight count has been provided if (weights.length != assets.length) revert InvalidWeightCount(); // init_balances length not checked: if shorter than assets, the funds transfer loop // will fail. If longer, values will just be ignored. // Create a minimal transparent proxy: vault = LibClone.clone(vaultTemplate); // The vault expects the balances to exist in the vault when setup is called. uint256 assetLength = assets.length; for (uint256 it; it < assetLength;) { SafeTransferLib.safeTransferFrom( assets[it], msg.sender, vault, init_balances[it] ); unchecked { ++it; } } // Call setup ICatalystV1Vault(vault).setup( name, symbol, chainInterface, vaultFee, _defaultGovernanceFee, owner(), // Fee administrator msg.sender // setup master ); // Initialize swap curves ICatalystV1Vault(vault).initializeSwapCurves( assets, weights, amp, msg.sender ); // Emit event for vault discovery. emit VaultDeployed( vaultTemplate, chainInterface, msg.sender, vault, assets, amp ); isCreatedByFactory[chainInterface][vault] = true; return vault; } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title Escrow related functions defined by Catalyst v1 Vaults * @notice Contains the functions used to manage escrows by the cross-chain interface. */ interface ICatalystV1VaultSuccessFailure { /** @notice Release the escrowed tokens into the vault. */ function onSendAssetSuccess( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, address escrowToken, uint32 blockNumberMod ) external; /** @notice Returned the escrowed tokens to the user */ function onSendAssetFailure( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, address escrowToken, uint32 blockNumberMod ) external; /** @notice Release the escrowed tokens into the vault. */ function onSendLiquiditySuccess( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, uint32 blockNumberMod ) external; /** @notice Returned the escrowed tokens to the user */ function onSendLiquidityFailure( bytes32 channelId, bytes calldata toAccount, uint256 U, uint256 escrowAmount, uint32 blockNumberMod ) external; }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title Administrative actions defined by Catalyst v1 Vaults * @notice Contains all functions which can only be called by privileged users. */ interface ICatalystV1VaultAdministration { function setFeeAdministrator(address administrator) external; function setVaultFee(uint64 fee) external; function setGovernanceFee(uint64 fee) external; /** * @notice Initializes the vault pricing parameters. * @param assets The list of assets the vault will support. * @param weights The weights of the tokens. * @param amp Vault amplification. * @param depositor The account to which the initial vault tokens are minted to. */ function initializeSwapCurves( address[] calldata assets, uint256[] calldata weights, uint64 amp, address depositor ) external; /** * @notice Creates a connection to the vault toVault on the channel _channelId. * @dev if _vaultReceiving is an EVM vault, it can be computes as: * Vyper: convert(_vaultAddress, bytes32) * Solidity: abi.encode(_vaultAddress) * Brownie: brownie.convert.to_bytes(_vaultAddress, type_str="bytes32") * setupMaster == ZERO_ADDRESS * @param channelId The _channelId of the target vault. * @param toVault The bytes32 representation of the target vault * @param state Boolean indicating if the connection should be open or closed. */ function setConnection( bytes32 channelId, bytes calldata toVault, bool state ) external; /** * @notice Gives up short term ownership of the vault. This makes the vault unstoppable. */ function finishSetup() external; }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title Derived Vault state * @notice Contains all vault state which is derived from vault storage */ interface ICatalystV1VaultDerived { /** @notice Returns the current cross-chain unit capacity. */ function getUnitCapacity() external view returns (uint256); function calcSendAsset(address from, uint256 amount) external view returns (uint256); /** * @notice Computes the output of ReceiveAsset, without executing one. * @param to The address of the token to buy. * @param U The number of units used to buy to. * @return uint256 Number of purchased tokens. */ function calcReceiveAsset(address to, uint256 U) external view returns (uint256); /** * @notice Computes the output of localSwap, without executing one. * @param from The address of the token to sell. * @param to The address of the token to buy. * @param amount The amount of from token to sell for to token. * @return Output denominated in to token. */ function calcLocalSwap(address from, address to, uint256 amount) external view returns (uint256); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title Events emitted by Catalyst v1 Vaults * @notice Contains all events emitted by the vault * @dev When using events to match transations, the combination of: channelId, fromVault, toAccount, toAsset, units, and block number is semi-guranteed to be unique. * If more than 2**32 blocks exist, then all instances are guaranteed to be non-overlapping */ interface ICatalystV1VaultEvents { /** * @notice Describes an atomic swap between the 2 tokens: _fromAsset and _toAsset. * @param account The user / exchange who facilitated the trade (msg.sender) * @param fromAsset The asset which was sold in exchange for _toAsset * @param toAsset The asset which was purchased with _fromAsset * @param fromAmount The number of _fromAsset sold * @param toAmount The number of tokens provided to toAccount */ event LocalSwap( address indexed account, address fromAsset, address toAsset, uint256 fromAmount, uint256 toAmount ); /** * @notice Describes the creation of an external swap: Cross-chain swap. * @param channelId The target chain identifier * @param toVault The target vault. * @param toAccount The recipient of the trade. The person who bought the trade is not present. * @param fromAsset The asset which was sold in exchange for _toAsset. * @param toAssetIndex The token index of the asset to purchase on _toChain. * @param fromAmount The number of _fromAsset sold. * @param minOut The minimum output to be accepted of fromAsset. * @param units The calculated number of units bought. Will be sold to buy _toAsset * @param underwriteIncentiveX16 The incentive out of 2**16 - 1 provided to the underwriter. * @param fee The number of tokens paid to the vault in fees. */ event SendAsset( bytes32 channelId, bytes toVault, bytes toAccount, address fromAsset, uint8 toAssetIndex, uint256 fromAmount, uint256 minOut, uint256 units, uint256 fee, uint16 underwriteIncentiveX16 ); /** * @notice Describes the arrival of an external swap: Cross-chain swap. * If toAccount is used to match trades, remember to convert it into 64 + 1 bytes. * @param channelId The target chain identifier * @param fromVault The source vault. * @param toAccount The recipient of the trade. * @param toAsset The asset which was purchased with _fromAsset * @param units The number of units sent from the other chain. * @param toAmount The number of tokens provided to toAccount * @param fromAmount The amount spent to get units on the source side. * @param fromAsset The provided asset on the source side. * @param sourceBlockNumberMod The block number of the sending transaction mod 2**32 - 1 */ event ReceiveAsset( bytes32 channelId, bytes fromVault, address toAccount, address toAsset, uint256 units, uint256 toAmount, uint256 fromAmount, bytes fromAsset, uint32 sourceBlockNumberMod ); /** * @notice Describes the creation of a liquidity swap * @param channelId The target chain identifier * @param toVault The target vault. * @param toAccount The recipient of the liquidity. The person who bought the trade is not present. * @param fromAmount The number of _fromAsset sold * @param minOut An array containing a list of minimum outputs [minVaultTokens, minReferenceAssets] * @param units The calculated number of liquidity units bought. */ event SendLiquidity( bytes32 channelId, bytes toVault, bytes toAccount, uint256 fromAmount, uint256[2] minOut, uint256 units ); /** * @notice Describes the arrival of a liquidity swap * @param channelId The target chain identifier * @param fromVault The source vault. * @param toAccount The recipient of the liquidity. * @param units The number of liquidity units sent from the other chain. * @param toAmount The number of vault tokens provided to toAccount * @param fromAmount The amount spent to get units on the source side. * @param sourceBlockNumberMod The block number of the sending transaction mod 2**32 - 1 */ event ReceiveLiquidity( bytes32 channelId, bytes fromVault, address toAccount, uint256 units, uint256 toAmount, uint256 fromAmount, uint256 sourceBlockNumberMod ); /** * @notice Emitted on liquidity deposits. * @param toAccount The depositor. Is credited with _mints vault tokens. * @param mint The number of minted vault tokens credited to toAccount * @param assets An array of the number of deposited assets. */ event VaultDeposit(address indexed toAccount, uint256 mint, uint256[] assets); /** * @notice Emitted on liquidity withdrawal. * @param toAccount The withdrawer. Is debited _burns vault tokens. * @param burn The number of burned vault tokens. * @param assets An array of the token amounts returned */ event VaultWithdraw(address indexed toAccount, uint256 burn, uint256[] assets); /** @notice Called upon successful asset swap. */ event SendAssetSuccess( bytes32 channelId, bytes toAccount, uint256 units, uint256 escrowAmount, address escrowToken, uint32 blockNumberMod ); /** @notice Called upon failed asset swap. */ event SendAssetFailure( bytes32 channelId, bytes toAccount, uint256 units, uint256 escrowAmount, address escrowToken, uint32 blockNumberMod ); /** @notice Called upon successful liquidity swap. */ event SendLiquiditySuccess( bytes32 channelId, bytes toAccount, uint256 units, uint256 escrowAmount, uint32 blockNumberMod ); /** @notice Called upon failed liquidity swap. */ event SendLiquidityFailure( bytes32 channelId, bytes toAccount, uint256 units, uint256 escrowAmount, uint32 blockNumberMod ); /** @notice Vault setup has been finalised. */ event FinishSetup(); /** * @notice Emitted on fee administrator adjustment * @param administrator The new vault fee administrator */ event SetFeeAdministrator( address administrator ); /** * @notice Emitted on vault fee adjustment * @param fee The new vault fee */ event SetVaultFee( uint64 fee ); /** * @notice Emitted on governance fee adjustment * @param fee The new governance fee */ event SetGovernanceFee( uint64 fee ); /** * @notice Emitted on weights modification * @param targetTime Time at which the weights adjustment must complete. * @param targetWeights The desired new weights. */ event SetWeights( uint248 targetTime, uint256[] targetWeights ); /** * @notice Amplification has been modification * @param targetTime Time at which the amplification adjustment must complete. * @param targetAmplification The desired new amplification. */ event SetAmplification( uint48 targetTime, uint256 targetAmplification ); /** * @notice A connection has been modified * @param channelId Target chain identifier. * @param toVault Bytes32 representation of the target vault. * @param newState Boolean indicating if the connection should be open or closed. */ event SetConnection( bytes32 channelId, bytes toVault, bool newState ); //-- Underwriting Events --// /** * @notice A swap has been underwritten. */ event SwapUnderwritten( bytes32 indexed identifier, address toAsset, uint256 U, uint256 purchasedTokens ); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title Immutable vault state * @notice Contains all vault state which doesn't change once set. */ interface ICatalystV1VaultImmutables { function _chainInterface() external view returns (address); function FACTORY() external view returns (address); function MATHLIB() external view returns (address); /** * @notice To indicate which token is desired on the target vault,the _toAsset is an integer * from 0 to MAX_ASSETS indicating which asset the vault should purchase with units. */ function _tokenIndexing(uint256 tokenIndex) external view returns (address); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import { ICatalystV1Structs } from "./ICatalystV1VaultState.sol"; interface ICatalystV1VaultPermissionless { /** * @notice Setup a vault. * @param name_ Name of the Vault token. * @param symbol_ Symbol for the Vault token. * @param chainInterface Cross chain interface used for cross-chain swaps. (Can be address(0) to disable cross-chain swaps.) * @param vaultFee Vault fee. * @param governanceFee Governance fee share. * @param feeAdministrator Account that can modify the fees. * @param setupMaster Short-term owner of the vault (until finishSetup is called). */ function setup( string calldata name_, string calldata symbol_, address chainInterface, uint64 vaultFee, uint64 governanceFee, address feeAdministrator, address setupMaster ) external; //--- Balance Changes ---// /** * @notice Deposits a user configurable amount of tokens. * @dev Requires approvals for all deposited tokens within the vault. * Volatile: It is advised that the deposit matches the vault's %token distribution. * Amplified: It is advised that the deposit is as close to 1,1,... as possible. * Otherwise between 1,1,... and the vault's %token distribution. * @param tokenAmounts Array of the tokens amounts to be deposited. * @param minOut Minimum number of vault tokens to be minted. */ function depositMixed(uint256[] calldata tokenAmounts, uint256 minOut) external returns(uint256); /** * @notice Burns baseAmount and releases the symmetrical share * of tokens to the burner. This doesn't change the vault price. * @param baseAmount Number of vault tokens to burn. */ function withdrawAll(uint256 baseAmount, uint256[] calldata minOut) external returns(uint256[] memory); /** * @notice Burns vaultTokens and release a token distribution which can be set by the user. * @dev Requires approvals for all tokens within the vault. * Volatile: It is advised that the deposit matches the vault's %token distribution. * Amplified: It is advised that the deposit matches the vault's %token distribution. * Otherwise it should be weighted towards the tokens the vault has more of. * @param vaultTokens Number of vault tokens to withdraw. * @param withdrawRatio Percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD. * @param minOut Minimum number of tokens minted. */ function withdrawMixed( uint256 vaultTokens, uint256[] calldata withdrawRatio, uint256[] calldata minOut ) external returns(uint256[] memory); //--- Swaps ---// /** * @notice A swap between 2 assets which both are inside the vault. Is atomic. * @param fromAsset Asset the user wants to sell. * @param toAsset Asset the user wants to buy. * @param amount Amount of fromAsset the user wants to sell. * @param minOut Minimum output of _toAsset the user wants. */ function localSwap( address fromAsset, address toAsset, uint256 amount, uint256 minOut ) external returns (uint256); /** * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault. * @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as: * Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode(<vaultAddress>)) * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param fromAsset Asset the user wants to sell. * @param toAssetIndex Index of the asset the user wants to buy in the target vault. * @param amount Number of fromAsset to sell to the vault. * @param minOut Minimum number of returned tokens to the toAccount on the target chain. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max)) * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed. * @return uint256 The number of units minted. */ function sendAsset( ICatalystV1Structs.RouteDescription calldata routeDescription, address fromAsset, uint8 toAssetIndex, uint256 amount, uint256 minOut, address fallbackUser, uint16 underwriteIncentiveX16, bytes calldata calldata_ ) external payable returns (uint256); /** * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units. * @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as: * Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode(<vaultAddress>)) * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param fromAsset Asset the user wants to sell. * @param toAssetIndex Index of the asset the user wants to buy in the target vault. * @param amount Number of fromAsset to sell to the vault. * @param minOut Minimum number of returned tokens to the toAccount on the target chain. * @param minU Minimum and exact number of units sent. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with evm being: abi.encodePacket(bytes20(<address>), <data>) * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max)) * @return uint256 Number of units minted. */ function sendAssetFixedUnit( ICatalystV1Structs.RouteDescription calldata routeDescription, address fromAsset, uint8 toAssetIndex, uint256 amount, uint256 minOut, uint256 minU, address fallbackUser, uint16 underwriteIncentiveX16, bytes calldata calldata_ ) external payable returns (uint256); /** * @notice Completes a cross-chain swap by converting units to the desired token (toAsset) * @dev Can only be called by the chainInterface. * @param channelId The incoming connection identifier. * @param fromVault The source vault. * @param toAssetIndex Index of the asset to be purchased with Units. * @param toAccount The recipient. * @param U Number of units to convert into toAsset. * @param minOut Minimum number of tokens bought. Reverts if less. * @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain. * @param fromAsset Used to match cross-chain swap events. The input asset on the sending chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the host side. */ function receiveAsset( bytes32 channelId, bytes calldata fromVault, uint256 toAssetIndex, address toAccount, uint256 U, uint256 minOut, uint256 fromAmount, bytes calldata fromAsset, uint32 blockNumberMod ) external returns(uint256 purchasedTokens); /** * @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units. * @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted * directly into units through the following equation: * U = ln(PT/(PT-pt)) * \sum W_i * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param vaultTokens Number of vault tokens to exchange. * @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed. * @return uint256 Number of units minted. */ function sendLiquidity( ICatalystV1Structs.RouteDescription calldata routeDescription, uint256 vaultTokens, uint256[2] calldata minOut, address fallbackUser, bytes calldata calldata_ ) external payable returns (uint256); /** * @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing. * @dev Called exclusively by the chainInterface. * @param fromVault Source vault * @param toAccount Recipient of the vault tokens * @param U Number of units to convert into vault tokens. * @param minVaultTokens Minimum number of vault tokens to mint on target vault. Otherwise: Reject * @param minReferenceAsset Minimum number of reference asset the vaults tokens are worth. Otherwise: Reject * @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the host side. */ function receiveLiquidity( bytes32 channelId, bytes calldata fromVault, address toAccount, uint256 U, uint256 minVaultTokens, uint256 minReferenceAsset, uint256 fromAmount, uint32 blockNumberMod ) external returns(uint256 purchasedVaultTokens); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** @title Extensions to vaults which supports underwriting. */ interface ICatalystV1Underwriting { function underwriteAsset( bytes32 identifier, address toAsset, uint256 U, uint256 minOut ) external returns (uint256); function releaseUnderwriteAsset( address refundTo, bytes32 identifier, uint256 escrowAmount, address escrowToken, bytes32 sourceIdentifier, bytes calldata fromVault ) external; function deleteUnderwriteAsset( bytes32 identifier, uint256 U, uint256 escrowAmount, address escrowToken ) external; }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title Events emitted by Catalyst v1 Factory * @notice Contains all events emitted by the Factory */ interface ICatalystV1FactoryEvents { /** * @notice Describes the deployment of a new vault as a proxy of the given vault template. * @dev Should be used for vault discovery and pathing. * @param vaultTemplate The address of the template used by the transparent proxy. * @param chainInterface The address of the CCI used by the transparent proxy. * @param vaultAddress The minimal transparent proxy address for the vault. * @param assets List of the assets the vault supports. * @param k Set to 10**18 if the vault is volatile, otherwise the vault is an amplified vault. */ event VaultDeployed( address indexed vaultTemplate, address indexed chainInterface, address indexed deployer, address vaultAddress, address[] assets, uint256 k ); /** * @notice Describes governance fee changes. * @dev Only applies to new vaults, has no impact on existing vaults. * @param fee The new governance fee. */ event SetDefaultGovernanceFee( uint256 fee ); /** * @notice Sets a new destination for governance fees. */ event SetGovernanceFeeDestination( address newFeeDestination ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Minimal proxy library. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol) /// @author Minimal proxy by 0age (https://github.com/0age) /// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie /// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args) /// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy) /// /// @dev Minimal proxy: /// Although the sw0nt pattern saves 5 gas over the erc-1167 pattern during runtime, /// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern, /// which saves 4 gas over the erc-1167 pattern during runtime, and has the smallest bytecode. /// /// @dev Minimal proxy (PUSH0 variant): /// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai. /// It is optimized first for minimal runtime gas, then for minimal bytecode. /// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as /// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai. /// Please use with caution. /// /// @dev Clones with immutable args (CWIA): /// The implementation of CWIA here implements a `receive()` method that emits the /// `ReceiveETH(uint256)` event. This skips the `DELEGATECALL` when there is no calldata, /// enabling us to accept hard gas-capped `sends` & `transfers` for maximum backwards /// composability. The minimal proxy implementation does not offer this feature. /// /// @dev Minimal ERC1967 proxy: /// An minimal ERC1967 proxy, intended to be upgraded with UUPS. /// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic. /// /// @dev ERC1967I proxy: /// An variant of the minimal ERC1967 proxy, with a special code path that activates /// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the /// `implementation` address. The returned implementation is guaranteed to be valid if the /// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`. library LibClone { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The keccak256 of the deployed code for the ERC1967 proxy. bytes32 internal constant ERC1967_CODE_HASH = 0xaaa52c8cc8a0e3fd27ce756cc6b4e70c51423e9b597b11f32d3e49f8b1fc890d; /// @dev The keccak256 of the deployed code for the ERC1967I proxy. bytes32 internal constant ERC1967I_CODE_HASH = 0xce700223c0d4cea4583409accfc45adac4a093b3519998a9cbbe1504dadba6f7; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Unable to deploy the clone. error DeploymentFailed(); /// @dev The salt must start with either the zero address or `by`. error SaltDoesNotStartWith(); /// @dev The ETH transfer has failed. error ETHTransferFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a clone of `implementation`. function clone(address implementation) internal returns (address instance) { instance = clone(0, implementation); } /// @dev Deploys a clone of `implementation`. /// Deposits `value` ETH during deployment. function clone(uint256 value, address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * --------------------------------------------------------------------------+ * CREATION (9 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * --------------------------------------------------------------------------| * RUNTIME (44 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * | * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 3d | RETURNDATASIZE | 0 0 0 | | * 3d | RETURNDATASIZE | 0 0 0 0 | | * | * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 0 0 | | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | | * 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | | * 37 | CALLDATACOPY | 0 0 0 0 | [0..cds): calldata | * | * ::: delegate call to the implementation contract :::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata | * 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata | * 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata | * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata | * | * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata | * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata | * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata | * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | * | * 60 0x2a | PUSH1 0x2a | 0x2a success 0 rds | [0..rds): returndata | * 57 | JUMPI | 0 rds | [0..rds): returndata | * | * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * fd | REVERT | | [0..rds): returndata | * | * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * --------------------------------------------------------------------------+ */ mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) instance := create(value, 0x0c, 0x35) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Deploys a deterministic clone of `implementation` with `salt`. function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { instance = cloneDeterministic(0, implementation, salt); } /// @dev Deploys a deterministic clone of `implementation` with `salt`. /// Deposits `value` ETH during deployment. function cloneDeterministic(uint256 value, address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) instance := create2(value, 0x0c, 0x35, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the initialization code of the clone of `implementation`. function initCode(address implementation) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) mstore(add(result, 0x40), 0x5af43d3d93803e602a57fd5bf30000000000000000000000) mstore(add(result, 0x28), implementation) mstore(add(result, 0x14), 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) mstore(result, 0x35) // Store the length. mstore(0x40, add(result, 0x60)) // Allocate memory. } } /// @dev Returns the initialization code hash of the clone of `implementation`. /// Used for mining vanity addresses with create2crunch. function initCodeHash(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) hash := keccak256(0x0c, 0x35) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the address of the deterministic clone of `implementation`, /// with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress(address implementation, bytes32 salt, address deployer) internal pure returns (address predicted) { bytes32 hash = initCodeHash(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL PROXY OPERATIONS (PUSH0 VARIANT) */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a PUSH0 clone of `implementation`. function clone_PUSH0(address implementation) internal returns (address instance) { instance = clone_PUSH0(0, implementation); } /// @dev Deploys a PUSH0 clone of `implementation`. /// Deposits `value` ETH during deployment. function clone_PUSH0(uint256 value, address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * --------------------------------------------------------------------------+ * CREATION (9 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 5f | PUSH0 | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 5f | PUSH0 | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * --------------------------------------------------------------------------| * RUNTIME (45 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * | * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | * 5f | PUSH0 | 0 | | * 5f | PUSH0 | 0 0 | | * | * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 | | * 5f | PUSH0 | 0 cds 0 0 | | * 5f | PUSH0 | 0 0 cds 0 0 | | * 37 | CALLDATACOPY | 0 0 | [0..cds): calldata | * | * ::: delegate call to the implementation contract :::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 | [0..cds): calldata | * 5f | PUSH0 | 0 cds 0 0 | [0..cds): calldata | * 73 addr | PUSH20 addr | addr 0 cds 0 0 | [0..cds): calldata | * 5a | GAS | gas addr 0 cds 0 0 | [0..cds): calldata | * f4 | DELEGATECALL | success | [0..cds): calldata | * | * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success | [0..cds): calldata | * 5f | PUSH0 | 0 rds success | [0..cds): calldata | * 5f | PUSH0 | 0 0 rds success | [0..cds): calldata | * 3e | RETURNDATACOPY | success | [0..rds): returndata | * | * 60 0x29 | PUSH1 0x29 | 0x29 success | [0..rds): returndata | * 57 | JUMPI | | [0..rds): returndata | * | * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [0..rds): returndata | * 5f | PUSH0 | 0 rds | [0..rds): returndata | * fd | REVERT | | [0..rds): returndata | * | * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [0..rds): returndata | * 3d | RETURNDATASIZE | rds | [0..rds): returndata | * 5f | PUSH0 | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * --------------------------------------------------------------------------+ */ mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 instance := create(value, 0x0e, 0x36) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`. function cloneDeterministic_PUSH0(address implementation, bytes32 salt) internal returns (address instance) { instance = cloneDeterministic_PUSH0(0, implementation, salt); } /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`. /// Deposits `value` ETH during deployment. function cloneDeterministic_PUSH0(uint256 value, address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 instance := create2(value, 0x0e, 0x36, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the initialization code of the PUSH0 clone of `implementation`. function initCode_PUSH0(address implementation) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) mstore(add(result, 0x40), 0x5af43d5f5f3e6029573d5ffd5b3d5ff300000000000000000000) // 16 mstore(add(result, 0x26), implementation) // 20 mstore(add(result, 0x12), 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 mstore(result, 0x36) // Store the length. mstore(0x40, add(result, 0x60)) // Allocate memory. } } /// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`. /// Used for mining vanity addresses with create2crunch. function initCodeHash_PUSH0(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 hash := keccak256(0x0e, 0x36) mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the address of the deterministic PUSH0 clone of `implementation`, /// with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress_PUSH0( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHash_PUSH0(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CLONES WITH IMMUTABLE ARGS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: This implementation of CWIA differs from the original implementation. // If the calldata is empty, it will emit a `ReceiveETH(uint256)` event and skip the `DELEGATECALL`. /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `data`. function clone(address implementation, bytes memory data) internal returns (address instance) { instance = clone(0, implementation, data); } /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `data`. /// Deposits `value` ETH during deployment. function clone(uint256 value, address implementation, bytes memory data) internal returns (address instance) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) // The `creationSize` is `extraLength + 108` // The `runSize` is `creationSize - 10`. /** * ---------------------------------------------------------------------------------------------------+ * CREATION (10 bytes) | * ---------------------------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------------------------| * 61 runSize | PUSH2 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------------------------| * RUNTIME (98 bytes + extraLength) | * ---------------------------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------------------------| * | * ::: if no calldata, emit event & return w/o `DELEGATECALL` ::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 60 0x2c | PUSH1 0x2c | 0x2c cds | | * 57 | JUMPI | | | * 34 | CALLVALUE | cv | | * 3d | RETURNDATASIZE | 0 cv | | * 52 | MSTORE | | [0..0x20): callvalue | * 7f sig | PUSH32 0x9e.. | sig | [0..0x20): callvalue | * 59 | MSIZE | 0x20 sig | [0..0x20): callvalue | * 3d | RETURNDATASIZE | 0 0x20 sig | [0..0x20): callvalue | * a1 | LOG1 | | [0..0x20): callvalue | * 00 | STOP | | [0..0x20): callvalue | * 5b | JUMPDEST | | | * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..cds): calldata | * | * ::: keep some values in stack :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 0 0 | [0..cds): calldata | * 61 extra | PUSH2 extra | e 0 0 0 0 | [0..cds): calldata | * | * ::: copy extra data to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 80 | DUP1 | e e 0 0 0 0 | [0..cds): calldata | * 60 0x62 | PUSH1 0x62 | 0x62 e e 0 0 0 0 | [0..cds): calldata | * 36 | CALLDATASIZE | cds 0x62 e e 0 0 0 0 | [0..cds): calldata | * 39 | CODECOPY | e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * | * ::: delegate call to the implementation contract ::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 01 | ADD | cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 3d | RETURNDATASIZE | 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 73 addr | PUSH20 addr | addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 5a | GAS | gas addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * | * ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData | * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData | * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | * | * 60 0x60 | PUSH1 0x60 | 0x60 success 0 rds | [0..rds): returndata | * 57 | JUMPI | 0 rds | [0..rds): returndata | * | * ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * fd | REVERT | | [0..rds): returndata | * | * ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * ---------------------------------------------------------------------------------------------------+ */ mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data. mstore(sub(data, 0x0d), implementation) // Write the address of the implementation. // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( // Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e. // The actual EVM limit may be smaller and may change over time. sub(data, add(0x59, lt(extraLength, 0xff9e))), or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) instance := create(value, sub(data, 0x4c), add(extraLength, 0x6c)) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Deploys a deterministic clone of `implementation` /// with immutable arguments encoded in `data` and `salt`. function cloneDeterministic(address implementation, bytes memory data, bytes32 salt) internal returns (address instance) { instance = cloneDeterministic(0, implementation, data, salt); } /// @dev Deploys a deterministic clone of `implementation` /// with immutable arguments encoded in `data` and `salt`. function cloneDeterministic( uint256 value, address implementation, bytes memory data, bytes32 salt ) internal returns (address instance) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data. mstore(sub(data, 0x0d), implementation) // Write the address of the implementation. // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( // Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e. // The actual EVM limit may be smaller and may change over time. sub(data, add(0x59, lt(extraLength, 0xff9e))), or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) instance := create2(value, sub(data, 0x4c), add(extraLength, 0x6c), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Returns the initialization code hash of the clone of `implementation` /// using immutable arguments encoded in `data`. function initCode(address implementation, bytes memory data) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let dataLength := mload(data) // Do a out-of-gas revert if `dataLength` is too big. 0xffff - 0x02 - 0x62 = 0xff9b. // The actual EVM limit may be smaller and may change over time. returndatacopy(returndatasize(), returndatasize(), gt(dataLength, 0xff9b)) let o := add(result, 0x8c) let end := add(o, dataLength) // Copy the `data` into `result`. for { let d := sub(add(data, 0x20), o) } 1 {} { mstore(o, mload(add(o, d))) o := add(o, 0x20) if iszero(lt(o, end)) { break } } // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) mstore(add(result, 0x6c), 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data. mstore(add(result, 0x5f), implementation) // Write the address of the implementation. // Write the rest of the bytecode. mstore( add(result, 0x4b), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( add(result, 0x32), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( add(result, 0x12), or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f) ) mstore(end, shl(0xf0, extraLength)) mstore(add(end, 0x02), 0) // Zeroize the slot after the result. mstore(result, add(extraLength, 0x6c)) // Store the length. mstore(0x40, add(0x22, end)) // Allocate memory. } } /// @dev Returns the initialization code hash of the clone of `implementation` /// using immutable arguments encoded in `data`. /// Used for mining vanity addresses with create2crunch. function initCodeHash(address implementation, bytes memory data) internal pure returns (bytes32 hash) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // Do a out-of-gas revert if `dataLength` is too big. 0xffff - 0x02 - 0x62 = 0xff9b. // The actual EVM limit may be smaller and may change over time. returndatacopy(returndatasize(), returndatasize(), gt(dataLength, 0xff9b)) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data. mstore(sub(data, 0x0d), implementation) // Write the address of the implementation. // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( sub(data, 0x5a), or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) hash := keccak256(sub(data, 0x4c), add(extraLength, 0x6c)) // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Returns the address of the deterministic clone of /// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress( address implementation, bytes memory data, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHash(implementation, data); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL ERC1967 PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: The ERC1967 proxy here is intended to be upgraded with UUPS. // This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic. /// @dev Deploys a minimal ERC1967 proxy with `implementation`. function deployERC1967(address implementation) internal returns (address instance) { instance = deployERC1967(0, implementation); } /// @dev Deploys a minimal ERC1967 proxy with `implementation`. /// Deposits `value` ETH during deployment. function deployERC1967(uint256 value, address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * ---------------------------------------------------------------------------------+ * CREATION (34 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code | * 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code | * 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code | * 55 | SSTORE | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------| * RUNTIME (61 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..calldatasize): calldata | * | * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata | * 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata | * 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata | * 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata | * f4 | DELEGATECALL | succ | [0..calldatasize): calldata | * | * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata | * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata | * 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata | * 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata | * | * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: | * 60 0x38 | PUSH1 0x38 | dest succ | [0..returndatasize): returndata | * 57 | JUMPI | | [0..returndatasize): returndata | * | * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * fd | REVERT | | [0..returndatasize): returndata | * | * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [0..returndatasize): returndata | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * f3 | RETURN | | [0..returndatasize): returndata | * ---------------------------------------------------------------------------------+ */ let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x20, 0x6009) mstore(0x1e, implementation) mstore(0x0a, 0x603d3d8160223d3973) instance := create(value, 0x21, 0x5f) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`. function deployDeterministicERC1967(address implementation, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967(0, implementation, salt); } /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967(uint256 value, address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x20, 0x6009) mstore(0x1e, implementation) mstore(0x0a, 0x603d3d8160223d3973) instance := create2(value, 0x21, 0x5f, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967(address implementation, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967(0, implementation, salt); } /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967(uint256 value, address implementation, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x20, 0x6009) mstore(0x1e, implementation) mstore(0x0a, 0x603d3d8160223d3973) // Compute and store the bytecode hash. mstore(add(m, 0x35), keccak256(0x21, 0x5f)) mstore(m, shl(88, address())) mstore8(m, 0xff) // Write the prefix. mstore(add(m, 0x15), salt) instance := keccak256(m, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, 0x21, 0x5f, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`. function initCodeERC1967(address implementation) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) mstore( add(result, 0x60), 0x3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f300 ) mstore( add(result, 0x40), 0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc ) mstore(add(result, 0x20), or(shl(24, implementation), 0x600951)) mstore(add(result, 0x09), 0x603d3d8160223d3973) mstore(result, 0x5f) // Store the length. mstore(0x40, add(result, 0x80)) // Allocate memory. } } /// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`. /// Used for mining vanity addresses with create2crunch. function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x20, 0x6009) mstore(0x1e, implementation) mstore(0x0a, 0x603d3d8160223d3973) hash := keccak256(0x21, 0x5f) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the address of the deterministic ERC1967 proxy of `implementation`, /// with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC1967I PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: This proxy has a special code path that activates if `calldatasize() == 1`. // This code path skips the delegatecall and directly returns the `implementation` address. // The returned implementation is guaranteed to be valid if the keccak256 of the // proxy's code is equal to `ERC1967I_CODE_HASH`. /// @dev Deploys a minimal ERC1967I proxy with `implementation`. function deployERC1967I(address implementation) internal returns (address instance) { instance = deployERC1967I(0, implementation); } /// @dev Deploys a ERC1967I proxy with `implementation`. /// Deposits `value` ETH during deployment. function deployERC1967I(uint256 value, address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * ---------------------------------------------------------------------------------+ * CREATION (34 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code | * 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code | * 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code | * 55 | SSTORE | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------| * RUNTIME (82 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * | * ::: check calldatasize ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 58 | PC | 1 cds | | * 14 | EQ | eqs | | * 60 0x43 | PUSH1 0x43 | dest eqs | | * 57 | JUMPI | | | * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..calldatasize): calldata | * | * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata | * 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata | * 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata | * 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata | * f4 | DELEGATECALL | succ | [0..calldatasize): calldata | * | * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata | * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata | * 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata | * 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata | * | * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: | * 60 0x3E | PUSH1 0x3E | dest succ | [0..returndatasize): returndata | * 57 | JUMPI | | [0..returndatasize): returndata | * | * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * fd | REVERT | | [0..returndatasize): returndata | * | * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [0..returndatasize): returndata | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * f3 | RETURN | | [0..returndatasize): returndata | * | * ::: implementation , return :::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | | * 60 0x20 | PUSH1 0x20 | 32 | | * 60 0x0F | PUSH1 0x0F | o 32 | | * 3d | RETURNDATASIZE | 0 o 32 | | * 39 | CODECOPY | | [0..32): implementation slot | * 3d | RETURNDATASIZE | 0 | [0..32): implementation slot | * 51 | MLOAD | slot | [0..32): implementation slot | * 54 | SLOAD | impl | [0..32): implementation slot | * 3d | RETURNDATASIZE | 0 impl | [0..32): implementation slot | * 52 | MSTORE | | [0..32): implementation address | * 59 | MSIZE | 32 | [0..32): implementation address | * 3d | RETURNDATASIZE | 0 32 | [0..32): implementation address | * f3 | RETURN | | [0..32): implementation address | * | * ---------------------------------------------------------------------------------+ */ let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation)))) instance := create(value, 0x0c, 0x74) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`. function deployDeterministicERC1967I(address implementation, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967I(0, implementation, salt); } /// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967I(uint256 value, address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation)))) instance := create2(value, 0x0c, 0x74, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967I(address implementation, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967I(0, implementation, salt); } /// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967I(uint256 value, address implementation, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation)))) // Compute and store the bytecode hash. mstore(add(m, 0x35), keccak256(0x0c, 0x74)) mstore(m, shl(88, address())) mstore8(m, 0xff) // Write the prefix. mstore(add(m, 0x15), salt) instance := keccak256(m, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, 0x0c, 0x74, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`. function initCodeERC1967I(address implementation) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) mstore( add(result, 0x74), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3 ) mstore( add(result, 0x54), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4 ) mstore(add(result, 0x34), 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(add(result, 0x1d), implementation) mstore(add(result, 0x09), 0x60523d8160223d3973) mstore(add(result, 0x94), 0) mstore(result, 0x74) // Store the length. mstore(0x40, add(result, 0xa0)) // Allocate memory. } } /// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`. /// Used for mining vanity addresses with create2crunch. function initCodeHashERC1967I(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation)))) hash := keccak256(0x0c, 0x74) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the address of the deterministic ERC1967I proxy of `implementation`, /// with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967I( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967I(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OTHER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the address when a contract with initialization code hash, /// `hash`, is deployed with `salt`, by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, hash) mstore(0x01, shl(96, deployer)) mstore(0x15, salt) predicted := keccak256(0x00, 0x55) mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Requires that `salt` starts with either the zero address or `by`. function checkStartsWith(bytes32 salt, address by) internal pure { /// @solidity memory-safe-assembly assembly { // If the salt does not start with the zero address or `by`. if iszero(or(iszero(shr(96, salt)), eq(shr(96, shl(96, by)), shr(96, salt)))) { mstore(0x00, 0x0c4549ef) // `SaltDoesNotStartWith()`. revert(0x1c, 0x04) } } } }
{ "remappings": [ "forge-std/=lib/forge-std/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/", "GeneralisedIncentives/=lib/GeneralisedIncentives/", "@lazyledger/protobuf3-solidity-lib/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/", "@openzeppelin-upgradeable/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/", "@openzeppelin/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "base64/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/base64/", "clones-with-immutable-args/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/clones-with-immutable-args/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts-upgradeable/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/GeneralisedIncentives/lib/openzeppelin-contracts/contracts/", "optimism/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/", "proto/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/proto/", "protobuf3-solidity-lib/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/contracts/", "safe-contracts/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts/", "solady/=lib/solady/src/", "solmate/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/solmate/src/", "vibc-core-smart-contracts/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/" ], "optimizer": { "enabled": true, "runs": 10000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": {} }
[{"inputs":[{"internalType":"address","name":"factory_","type":"address"},{"internalType":"address","name":"mathlib_","type":"address"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"EscrowAlreadyExists","type":"error"},{"inputs":[],"name":"ExceedsSecurityLimit","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"NotEnoughGas","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"ReturnInsufficient","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"UnusedUnitsAfterWithdrawal","type":"error"},{"inputs":[],"name":"VaultNotConnected","type":"error"},{"inputs":[],"name":"WithdrawRatioNotZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[],"name":"FinishSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"fromAsset","type":"address"},{"indexed":false,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"}],"name":"LocalSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"fromVault","type":"bytes"},{"indexed":false,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"fromAsset","type":"bytes"},{"indexed":false,"internalType":"uint32","name":"sourceBlockNumberMod","type":"uint32"}],"name":"ReceiveAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"fromVault","type":"bytes"},{"indexed":false,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sourceBlockNumberMod","type":"uint256"}],"name":"ReceiveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toVault","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"address","name":"fromAsset","type":"address"},{"indexed":false,"internalType":"uint8","name":"toAssetIndex","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"}],"name":"SendAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"escrowToken","type":"address"},{"indexed":false,"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"SendAssetFailure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"escrowToken","type":"address"},{"indexed":false,"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"SendAssetSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toVault","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256[2]","name":"minOut","type":"uint256[2]"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"}],"name":"SendLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"SendLiquidityFailure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"SendLiquiditySuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"targetTime","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"targetAmplification","type":"uint256"}],"name":"SetAmplification","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toVault","type":"bytes"},{"indexed":false,"internalType":"bool","name":"newState","type":"bool"}],"name":"SetConnection","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"administrator","type":"address"}],"name":"SetFeeAdministrator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"fee","type":"uint64"}],"name":"SetGovernanceFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"fee","type":"uint64"}],"name":"SetVaultFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint248","name":"targetTime","type":"uint248"},{"indexed":false,"internalType":"uint256[]","name":"targetWeights","type":"uint256[]"}],"name":"SetWeights","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"U","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"purchasedTokens","type":"uint256"}],"name":"SwapUnderwritten","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"mint","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"assets","type":"uint256[]"}],"name":"VaultDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"burn","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"assets","type":"uint256[]"}],"name":"VaultWithdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MATHLIB","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_adjustmentTarget","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_chainInterface","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"_escrowLookup","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_escrowedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_escrowedVaultTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_feeAdministrator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_governanceFeeShare","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_lastModificationTime","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_maxUnitCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_oneMinusAmp","outputs":[{"internalType":"int64","name":"","type":"int64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_setupMaster","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_targetAmplification","outputs":[{"internalType":"int64","name":"","type":"int64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_tokenIndexing","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_underwriteEscrowMatchBalance0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_unitTracker","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"_vaultConnection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_vaultFee","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_weight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calcLocalSwap","outputs":[{"internalType":"uint256","name":"output","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"}],"name":"calcReceiveAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calcSendAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"computeBalance0","outputs":[{"internalType":"uint256","name":"walpha_0","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"address","name":"escrowToken","type":"address"}],"name":"deleteUnderwriteAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"internalType":"uint256","name":"minOut","type":"uint256"}],"name":"depositMixed","outputs":[{"internalType":"uint256","name":"vaultTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factoryOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finishSetup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getUnitCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceFeeDestination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"},{"internalType":"uint64","name":"amp","type":"uint64"},{"internalType":"address","name":"depositor","type":"address"}],"name":"initializeSwapCurves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"}],"name":"localSwap","outputs":[{"internalType":"uint256","name":"out","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"address","name":"escrowToken","type":"address"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"onSendAssetFailure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"address","name":"escrowToken","type":"address"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"onSendAssetSuccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"onSendLiquidityFailure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"onSendLiquiditySuccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ready","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"fromVault","type":"bytes"},{"internalType":"uint256","name":"toAssetIndex","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"bytes","name":"fromAsset","type":"bytes"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"receiveAsset","outputs":[{"internalType":"uint256","name":"purchasedTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"fromVault","type":"bytes"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minVaultTokens","type":"uint256"},{"internalType":"uint256","name":"minReferenceAsset","type":"uint256"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"receiveLiquidity","outputs":[{"internalType":"uint256","name":"purchasedVaultTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"address","name":"escrowToken","type":"address"},{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes","name":"fromVault","type":"bytes"}],"name":"releaseUnderwriteAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint8","name":"toAssetIndex","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"fallbackUser","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendAsset","outputs":[{"internalType":"uint256","name":"U","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint8","name":"toAssetIndex","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"minU","type":"uint256"},{"internalType":"address","name":"fallbackUser","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendAssetFixedUnit","outputs":[{"internalType":"uint256","name":"U","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"uint256","name":"vaultTokens","type":"uint256"},{"internalType":"uint256[2]","name":"minOut","type":"uint256[2]"},{"internalType":"address","name":"fallbackUser","type":"address"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendLiquidity","outputs":[{"internalType":"uint256","name":"U","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setConnection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"administrator","type":"address"}],"name":"setFeeAdministrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"fee","type":"uint64"}],"name":"setGovernanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"fee","type":"uint64"}],"name":"setVaultFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"address","name":"chainInterface","type":"address"},{"internalType":"uint64","name":"vaultFee","type":"uint64"},{"internalType":"uint64","name":"governanceFee","type":"uint64"},{"internalType":"address","name":"feeAdministrator","type":"address"},{"internalType":"address","name":"setupMaster","type":"address"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"}],"name":"underwriteAsset","outputs":[{"internalType":"uint256","name":"purchasedTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateMaxUnitCapacity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultTokens","type":"uint256"},{"internalType":"uint256[]","name":"minOut","type":"uint256[]"}],"name":"withdrawAll","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultTokens","type":"uint256"},{"internalType":"uint256[]","name":"withdrawRatio","type":"uint256[]"},{"internalType":"uint256[]","name":"minOut","type":"uint256[]"}],"name":"withdrawMixed","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"}]
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.