Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
411107 | 9 days ago | Contract Creation | 0 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
ContractOwner
Compiler Version
v0.8.13+commit.abaa5c0e
ZkSolc Version
v1.5.8
Optimization Enabled:
Yes with Mode 3
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/engine/ISpotEngine.sol"; import "./interfaces/engine/IPerpEngine.sol"; import "./interfaces/engine/IProductEngine.sol"; import "./interfaces/IEndpoint.sol"; import {SpotEngine} from "./SpotEngine.sol"; import "./PerpEngine.sol"; import "./Endpoint.sol"; import "./Verifier.sol"; import "./WithdrawPool.sol"; contract ContractOwner is EIP712Upgradeable, OwnableUpgradeable { /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize( address multisig, address _deployer, address _spotEngine, address _perpEngine, address _endpoint, address _clearinghouse, address _verifier ) external initializer { require(_deployer == msg.sender, "expected deployed to initialize"); __Ownable_init(); transferOwnership(multisig); deployer = _deployer; spotEngine = SpotEngine(_spotEngine); perpEngine = PerpEngine(_perpEngine); endpoint = Endpoint(_endpoint); clearinghouse = IClearinghouse(_clearinghouse); verifier = Verifier(_verifier); } address deployer; SpotEngine spotEngine; PerpEngine perpEngine; Endpoint endpoint; IClearinghouse clearinghouse; Verifier verifier; LegacySpotAddProductCall[] spotAddProductCalls; // deprecated PerpAddProductCall[] perpAddProductCalls; // deprecated bytes[] internal updateProductTxs; // using `bytes[]` in case we will change the layout of the calls. bytes[] internal rawSpotAddProductCalls; bytes[] internal rawPerpAddProductCalls; modifier onlyDeployer() { require(msg.sender == deployer, "sender must be deployer"); _; } struct LegacySpotAddProductCall { uint32 productId; address book; int128 sizeIncrement; int128 minSize; int128 lpSpreadX18; ISpotEngine.Config config; RiskHelper.RiskStore riskStore; } struct SpotAddProductCall { uint32 productId; uint32 quoteId; address book; int128 sizeIncrement; int128 minSize; int128 lpSpreadX18; ISpotEngine.Config config; RiskHelper.RiskStore riskStore; } struct PerpAddProductCall { uint32 productId; address book; int128 sizeIncrement; int128 minSize; int128 lpSpreadX18; RiskHelper.RiskStore riskStore; } function submitSpotAddProductCall( uint32 productId, uint32 quoteId, address book, int128 sizeIncrement, int128 minSize, int128 lpSpreadX18, ISpotEngine.Config calldata config, RiskHelper.RiskStore calldata riskStore ) external onlyDeployer { uint32[] memory pendingIds = pendingSpotAddProductIds(); for (uint256 i = 0; i < pendingIds.length; i++) { require( productId != pendingIds[i], "trying to add a spot product twice." ); } rawSpotAddProductCalls.push( abi.encode( SpotAddProductCall( productId, quoteId, book, sizeIncrement, minSize, lpSpreadX18, config, riskStore ) ) ); } function submitPerpAddProductCall( uint32 productId, address book, int128 sizeIncrement, int128 minSize, int128 lpSpreadX18, RiskHelper.RiskStore calldata riskStore ) external onlyDeployer { uint32[] memory pendingIds = pendingPerpAddProductIds(); for (uint256 i = 0; i < pendingIds.length; i++) { require( productId != pendingIds[i], "trying to add a perp product twice." ); } rawPerpAddProductCalls.push( abi.encode( PerpAddProductCall( productId, book, sizeIncrement, minSize, lpSpreadX18, riskStore ) ) ); } function clearSpotAddProductCalls() external onlyDeployer { delete rawSpotAddProductCalls; } function clearPerpAddProductCalls() external onlyDeployer { delete rawPerpAddProductCalls; } function addProducts(uint32[] memory spotIds, uint32[] memory perpIds) external onlyOwner { for (uint256 i = 0; i < rawSpotAddProductCalls.length; i++) { SpotAddProductCall memory call = abi.decode( rawSpotAddProductCalls[i], (SpotAddProductCall) ); require(spotIds[i] == call.productId, "spot id doesn't match."); spotEngine.addProduct( call.productId, call.quoteId, call.book, call.sizeIncrement, call.minSize, call.lpSpreadX18, call.config, call.riskStore ); } delete rawSpotAddProductCalls; for (uint256 i = 0; i < rawPerpAddProductCalls.length; i++) { PerpAddProductCall memory call = abi.decode( rawPerpAddProductCalls[i], (PerpAddProductCall) ); require(perpIds[i] == call.productId, "perp id doesn't match."); perpEngine.addProduct( call.productId, call.book, call.sizeIncrement, call.minSize, call.lpSpreadX18, call.riskStore ); } delete rawPerpAddProductCalls; } function pendingSpotAddProductIds() public view returns (uint32[] memory) { uint32[] memory productIds = new uint32[]( rawSpotAddProductCalls.length ); for (uint256 i = 0; i < rawSpotAddProductCalls.length; i++) { SpotAddProductCall memory call = abi.decode( rawSpotAddProductCalls[i], (SpotAddProductCall) ); productIds[i] = call.productId; } return productIds; } function pendingPerpAddProductIds() public view returns (uint32[] memory) { uint32[] memory productIds = new uint32[]( rawPerpAddProductCalls.length ); for (uint256 i = 0; i < rawPerpAddProductCalls.length; i++) { PerpAddProductCall memory call = abi.decode( rawPerpAddProductCalls[i], (PerpAddProductCall) ); productIds[i] = call.productId; } return productIds; } function hasPendingAddProductCalls() public view returns (bool) { return rawPerpAddProductCalls.length > 0 || rawSpotAddProductCalls.length > 0; } function submitUpdateProductTx(bytes calldata slowModeTx) external onlyDeployer { updateProductTxs.push(slowModeTx); } function clearUpdateProductTxs() external onlyDeployer { delete updateProductTxs; } function batchSubmitUpdateProductTxs(bytes[] calldata slowModeTxs) external onlyDeployer { for (uint256 i = 0; i < slowModeTxs.length; i++) { bytes memory txn = slowModeTxs[i]; updateProductTxs.push(txn); } } function updateProducts() external onlyOwner { for (uint256 i = 0; i < updateProductTxs.length; i++) { bytes memory txn = updateProductTxs[i]; endpoint.submitSlowModeTransaction(txn); } delete updateProductTxs; } function withdrawInsurance(uint128 amount, address sendTo) external onlyOwner { IEndpoint.WithdrawInsurance memory _txn = IEndpoint.WithdrawInsurance( amount, sendTo ); bytes memory txn = abi.encodePacked(uint8(27), abi.encode(_txn)); endpoint.submitSlowModeTransaction(txn); } function hasPendingUpdateProductTxs() public view returns (bool) { return updateProductTxs.length > 0; } function addEngine( address engine, address offchainExchange, IProductEngine.EngineType engineType ) external onlyOwner { clearinghouse.addEngine(engine, offchainExchange, engineType); } function assignPubKey( uint256 i, uint256 x, uint256 y ) public onlyOwner { verifier.assignPubKey(i, x, y); } function deletePubkey(uint256 index) public onlyOwner { verifier.deletePubkey(index); } function spotUpdateRisk( uint32 productId, RiskHelper.RiskStore memory riskStore ) external onlyOwner { spotEngine.updateRisk(productId, riskStore); } function perpUpdateRisk( uint32 productId, RiskHelper.RiskStore memory riskStore ) external onlyOwner { perpEngine.updateRisk(productId, riskStore); } function setWithdrawPool(address _withdrawPool) external onlyOwner { clearinghouse.setWithdrawPool(_withdrawPool); } function removeWithdrawPoolLiquidity( uint32 productId, uint128 amount, address sendTo ) external onlyOwner { WithdrawPool withdrawPool = WithdrawPool( clearinghouse.getWithdrawPool() ); withdrawPool.removeLiquidity(productId, amount, sendTo); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./common/Constants.sol"; import "./common/Errors.sol"; import "./interfaces/engine/ISpotEngine.sol"; import "./interfaces/clearinghouse/IClearinghouse.sol"; import "./libraries/MathHelper.sol"; import "./libraries/MathSD21x18.sol"; import "./libraries/RiskHelper.sol"; import "./BaseEngine.sol"; import "./SpotEngineState.sol"; import "./SpotEngineLP.sol"; contract SpotEngine is SpotEngineLP { using MathSD21x18 for int128; function initialize( address _clearinghouse, address _offchainExchange, address _quote, address _endpoint, address _admin ) external { _initialize(_clearinghouse, _offchainExchange, _endpoint, _admin); configs[QUOTE_PRODUCT_ID] = Config({ token: _quote, interestInflectionUtilX18: 8e17, // .8 interestFloorX18: 1e16, // .01 interestSmallCapX18: 4e16, // .04 interestLargeCapX18: ONE // 1 }); _risk().value[QUOTE_PRODUCT_ID] = RiskHelper.RiskStore({ longWeightInitial: 1e9, shortWeightInitial: 1e9, longWeightMaintenance: 1e9, shortWeightMaintenance: 1e9, priceX18: ONE }); states[QUOTE_PRODUCT_ID] = State({ cumulativeDepositsMultiplierX18: ONE, cumulativeBorrowsMultiplierX18: ONE, totalDepositsNormalized: 0, totalBorrowsNormalized: 0 }); productIds.push(QUOTE_PRODUCT_ID); emit AddProduct(QUOTE_PRODUCT_ID); } /** * View */ function getEngineType() external pure returns (EngineType) { return EngineType.SPOT; } function getConfig(uint32 productId) external view returns (Config memory) { return configs[productId]; } /** * Actions */ /// @notice adds a new product with default parameters function addProduct( uint32 productId, uint32 quoteId, address book, int128 sizeIncrement, int128 minSize, int128 lpSpreadX18, Config calldata config, RiskHelper.RiskStore calldata riskStore ) public onlyOwner { require(productId != QUOTE_PRODUCT_ID); _addProductForId( productId, quoteId, book, sizeIncrement, minSize, lpSpreadX18, riskStore ); configs[productId] = config; states[productId] = State({ cumulativeDepositsMultiplierX18: ONE, cumulativeBorrowsMultiplierX18: ONE, totalDepositsNormalized: 0, totalBorrowsNormalized: 0 }); lpStates[productId] = LpState({ supply: 0, quote: Balance({amount: 0, lastCumulativeMultiplierX18: ONE}), base: Balance({amount: 0, lastCumulativeMultiplierX18: ONE}) }); } function updateProduct(bytes calldata rawTxn) external onlyEndpoint { UpdateProductTx memory txn = abi.decode(rawTxn, (UpdateProductTx)); RiskHelper.RiskStore memory riskStore = txn.riskStore; if (txn.productId != QUOTE_PRODUCT_ID) { require( riskStore.longWeightInitial <= riskStore.longWeightMaintenance && riskStore.shortWeightInitial >= riskStore.shortWeightMaintenance && configs[txn.productId].token == txn.config.token, ERR_BAD_PRODUCT_CONFIG ); RiskHelper.RiskStore memory r = _risk().value[txn.productId]; r.longWeightInitial = riskStore.longWeightInitial; r.shortWeightInitial = riskStore.shortWeightInitial; r.longWeightMaintenance = riskStore.longWeightMaintenance; r.shortWeightMaintenance = riskStore.shortWeightMaintenance; _risk().value[txn.productId] = r; _exchange().updateMarket( txn.productId, type(uint32).max, address(0), txn.sizeIncrement, txn.minSize, txn.lpSpreadX18 ); } configs[txn.productId] = txn.config; } function updateQuoteFromInsurance(bytes32 subaccount, int128 insurance) external returns (int128) { _assertInternal(); State memory state = states[QUOTE_PRODUCT_ID]; BalanceNormalized memory balanceNormalized = balances[QUOTE_PRODUCT_ID][ subaccount ].balance; int128 balanceAmount = balanceNormalizedToBalance( state, balanceNormalized ).amount; if (balanceAmount < 0) { int128 topUpAmount = MathHelper.max( MathHelper.min(insurance, -balanceAmount), 0 ); insurance -= topUpAmount; _updateBalanceNormalized(state, balanceNormalized, topUpAmount); } states[QUOTE_PRODUCT_ID] = state; balances[QUOTE_PRODUCT_ID][subaccount].balance = balanceNormalized; return insurance; } function updateBalance( uint32 productId, bytes32 subaccount, int128 amountDelta, int128 quoteDelta ) external { require(productId != QUOTE_PRODUCT_ID, ERR_INVALID_PRODUCT); _assertInternal(); State memory state = states[productId]; State memory quoteState = states[QUOTE_PRODUCT_ID]; BalanceNormalized memory balance = balances[productId][subaccount] .balance; BalanceNormalized memory quoteBalance = balances[QUOTE_PRODUCT_ID][ subaccount ].balance; _updateBalanceNormalized(state, balance, amountDelta); _updateBalanceNormalized(quoteState, quoteBalance, quoteDelta); balances[productId][subaccount].balance = balance; balances[QUOTE_PRODUCT_ID][subaccount].balance = quoteBalance; states[productId] = state; states[QUOTE_PRODUCT_ID] = quoteState; _balanceUpdate(productId, subaccount); _balanceUpdate(QUOTE_PRODUCT_ID, subaccount); } function updateBalance( uint32 productId, bytes32 subaccount, int128 amountDelta ) external { _assertInternal(); State memory state = states[productId]; BalanceNormalized memory balance = balances[productId][subaccount] .balance; _updateBalanceNormalized(state, balance, amountDelta); balances[productId][subaccount].balance = balance; states[productId] = state; _balanceUpdate(productId, subaccount); } // only check on withdraw -- ensure that users can't withdraw // funds that are in the Vertex contract but not officially // 'deposited' into the Vertex system and counted in balances // (i.e. if a user transfers tokens to the clearinghouse // without going through the standard deposit) function assertUtilization(uint32 productId) external view { (State memory _state, ) = getStateAndBalance(productId, X_ACCOUNT); int128 totalDeposits = _state.totalDepositsNormalized.mul( _state.cumulativeDepositsMultiplierX18 ); int128 totalBorrows = _state.totalBorrowsNormalized.mul( _state.cumulativeBorrowsMultiplierX18 ); require(totalDeposits >= totalBorrows, ERR_MAX_UTILIZATION); } function socializeSubaccount(bytes32 subaccount) external { require(msg.sender == address(_clearinghouse), ERR_UNAUTHORIZED); uint32[] memory _productIds = getProductIds(); for (uint128 i = 0; i < _productIds.length; ++i) { uint32 productId = _productIds[i]; State memory state = states[productId]; Balance memory balance = balanceNormalizedToBalance( state, balances[productId][subaccount].balance ); if (balance.amount < 0) { int128 totalDeposited = state.totalDepositsNormalized.mul( state.cumulativeDepositsMultiplierX18 ); state.cumulativeDepositsMultiplierX18 = (totalDeposited + balance.amount).div(state.totalDepositsNormalized); require(state.cumulativeDepositsMultiplierX18 > 0); state.totalBorrowsNormalized += balance.amount.div( state.cumulativeBorrowsMultiplierX18 ); balances[productId][subaccount].balance.amountNormalized = 0; if (productId == QUOTE_PRODUCT_ID) { for (uint32 j = 0; j < _productIds.length; ++j) { uint32 baseProductId = _productIds[j]; if (baseProductId == QUOTE_PRODUCT_ID) { continue; } LpState memory lpState = lpStates[baseProductId]; _updateBalanceWithoutDelta(state, lpState.quote); lpStates[baseProductId] = lpState; _productUpdate(baseProductId); } } else { LpState memory lpState = lpStates[productId]; _updateBalanceWithoutDelta(state, lpState.base); lpStates[productId] = lpState; } states[productId] = state; _balanceUpdate(productId, subaccount); } } } function manualAssert( int128[] calldata totalDeposits, int128[] calldata totalBorrows ) external view { for (uint128 i = 0; i < totalDeposits.length; ++i) { uint32 productId = productIds[i]; State memory state = states[productId]; require( state.totalDepositsNormalized.mul( state.cumulativeDepositsMultiplierX18 ) == totalDeposits[i], ERR_DSYNC ); require( state.totalBorrowsNormalized.mul( state.cumulativeBorrowsMultiplierX18 ) == totalBorrows[i], ERR_DSYNC ); } } function getToken(uint32 productId) external view returns (address) { return address(configs[productId].token); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "./common/Errors.sol"; import "./libraries/MathHelper.sol"; import "./interfaces/IVerifier.sol"; contract Verifier is EIP712Upgradeable, OwnableUpgradeable, IVerifier { Point[8] internal pubkeys; Point[256] internal aggregatePubkey; bool[256] internal isAggregatePubkeyLatest; uint256 internal nSigner; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize(Point[8] memory initialSet) external initializer { __Ownable_init(); for (uint256 i = 0; i < 8; ++i) { if (!isPointNone(initialSet[i])) { _assignPubkey(i, initialSet[i].x, initialSet[i].y); } } } function revertGasInfo(uint256 i, uint256 gasUsed) external pure { revert( string.concat( "G ", MathHelper.uint2str(uint128(i)), " ", MathHelper.uint2str(uint128(gasUsed)) ) ); } function assignPubKey( uint256 i, uint256 x, uint256 y ) public onlyOwner { _assignPubkey(i, x, y); } function _assignPubkey( uint256 i, uint256 x, uint256 y ) internal { require(i < 8); if (isPointNone(pubkeys[i])) { nSigner += 1; } pubkeys[i] = Point(x, y); for (uint256 s = (1 << i); s < 256; s = (s + 1) | (1 << i)) { isAggregatePubkeyLatest[s] = false; } } function deletePubkey(uint256 index) public onlyOwner { if (!isPointNone(pubkeys[index])) { nSigner -= 1; delete pubkeys[index]; } } function getPubkey(uint8 index) public view returns (Point memory) { return pubkeys[index]; } function getPubkeyAddress(uint8 index) public view returns (address) { Point memory p = getPubkey(index); return address(uint160(uint256(keccak256(abi.encode(p.x, p.y))))); } function getAggregatePubkey(uint8 signerBitmask) internal returns (Point memory) { if (signerBitmask == 0 || isAggregatePubkeyLatest[signerBitmask]) return aggregatePubkey[signerBitmask]; Point memory res; for (uint256 i = 0; i < 8; ++i) { if ((signerBitmask >> i) % 2 == 1) { require(!isPointNone(pubkeys[i])); res = pointAdd( getAggregatePubkey(signerBitmask ^ uint8(1 << i)), pubkeys[i] ); break; } } aggregatePubkey[signerBitmask] = res; isAggregatePubkeyLatest[signerBitmask] = true; return res; } // determine if 2/3 of the signers are included in this signing mask // and if the keys are present function checkQuorum(uint8 signerBitmask) internal view returns (bool) { uint256 nSigned = 0; for (uint256 i = 0; i < 8; ++i) { bool signed = ((signerBitmask >> i) & 1) == 1; if (signed) { if (isPointNone(pubkeys[i])) { return false; } nSigned += 1; } } return nSigned * 2 > nSigner; } function requireValidSignature( bytes32 message, bytes32 e, bytes32 s, uint8 signerBitmask ) public { require(checkQuorum(signerBitmask)); Point memory pubkey = getAggregatePubkey(signerBitmask); require( verify( pubkey.y % 2 == 0 ? 27 : 28, bytes32(pubkey.x), message, e, s ), "Verification failed" ); } /// SCHNORR IMPLEMENTATION BELOW // secp256k1 group order uint256 public constant Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; // parity := public key y-coord parity (27 or 28) // px := public key x-coord // message := 32-byte message // e := schnorr signature challenge // s := schnorr signature function verify( uint8 parity, bytes32 px, bytes32 message, bytes32 e, bytes32 s ) internal pure returns (bool) { // ecrecover = (m, v, r, s); bytes32 sp = bytes32(Q - mulmod(uint256(s), uint256(px), Q)); bytes32 ep = bytes32(Q - mulmod(uint256(e), uint256(px), Q)); require(sp != 0); // the ecrecover precompile implementation checks that the `r` and `s` // inputs are non-zero (in this case, `px` and `ep`), thus we don't need to // check if they're zero. address R = ecrecover(sp, parity, px, ep); require(R != address(0), "ecrecover failed"); return e == keccak256(abi.encodePacked(R, uint8(parity), px, message)); } uint256 public constant _P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; struct Point { uint256 x; uint256 y; } function pow( uint256 a, uint256 b, uint256 mod ) internal pure returns (uint256) { // a ^ b % mod uint256 res = 1; while (b > 0) { if (b % 2 == 1) { res = mulmod(res, a, mod); } a = mulmod(a, a, mod); b /= 2; } return res; } function isPointNone(Point memory u) internal pure returns (bool) { return u.x == 0 && u.y == 0; } function pointAdd(Point memory u, Point memory v) internal pure returns (Point memory) { if (isPointNone(u)) return v; if (isPointNone(v)) return u; uint256 lam = 0; if (u.x == v.x) { if (u.y != v.y) return Point(0, 0); lam = mulmod(3, u.x, _P); lam = mulmod(lam, u.x, _P); lam = mulmod(lam, pow(mulmod(2, v.y, _P), _P - 2, _P), _P); } else { lam = mulmod( addmod(v.y, _P - u.y, _P), pow(addmod(v.x, _P - u.x, _P), _P - 2, _P), _P ); } uint256 x3 = mulmod(lam, lam, _P); x3 = addmod(x3, _P - u.x, _P); x3 = addmod(x3, _P - v.x, _P); uint256 y3 = addmod(u.x, _P - x3, _P); y3 = mulmod(y3, lam, _P); y3 = addmod(y3, _P - u.y, _P); return Point(x3, y3); } function checkIndividualSignature( bytes32 digest, bytes memory signature, uint8 signerIndex ) public view returns (bool) { address expectedAddress = getPubkeyAddress(signerIndex); address recovered = ECDSA.recover(digest, signature); return expectedAddress == recovered; } function requireValidTxSignatures( bytes calldata txn, uint64 idx, bytes[] calldata signatures ) public { bytes32 data = keccak256( abi.encodePacked(uint256(block.chainid), uint256(idx), txn) ); bytes32 hashedMsg = keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", data) ); uint256 nSignatures = 0; for (uint256 i = 0; i < signatures.length; i++) { if (signatures[i].length > 0) { nSignatures += 1; require( checkIndividualSignature( hashedMsg, signatures[i], uint8(i) ), "invalid signature" ); } } require(nSignatures == nSigner, "not enough signatures"); } function validateSignature( bytes32 sender, address linkedSigner, bytes32 digest, bytes memory signature ) public pure { address recovered = ECDSA.recover(digest, signature); require( (recovered != address(0)) && ((recovered == address(uint160(bytes20(sender)))) || (recovered == linkedSigner)), ERR_INVALID_SIGNATURE ); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol"; import "./interfaces/IEndpoint.sol"; import "./interfaces/IOffchainExchange.sol"; import "./interfaces/clearinghouse/IClearinghouse.sol"; import "./EndpointGated.sol"; import "./common/Errors.sol"; import "./libraries/ERC20Helper.sol"; import "./libraries/MathHelper.sol"; import "./libraries/Logger.sol"; import "./interfaces/engine/ISpotEngine.sol"; import "./interfaces/engine/IPerpEngine.sol"; import "./interfaces/IERC20Base.sol"; import "./interfaces/IVerifier.sol"; interface ISanctionsList { function isSanctioned(address addr) external view returns (bool); } contract Endpoint is IEndpoint, EIP712Upgradeable, OwnableUpgradeable { using ERC20Helper for IERC20Base; IERC20Base private quote; // deprecated IClearinghouse public clearinghouse; ISpotEngine private spotEngine; IPerpEngine private perpEngine; ISanctionsList private sanctions; address internal sequencer; int128 private sequencerFees; mapping(bytes32 => uint64) internal subaccountIds; mapping(uint64 => bytes32) internal subaccounts; uint64 internal numSubaccounts; // healthGroup -> (spotPriceX18, perpPriceX18) mapping(uint32 => Prices) private pricesX18; // deprecated mapping(uint32 => address) private books; // deprecated mapping(address => uint64) internal nonces; uint64 public nSubmissions; SlowModeConfig internal slowModeConfig; mapping(uint64 => SlowModeTx) internal slowModeTxs; struct Times { uint128 perpTime; uint128 spotTime; } Times internal times; mapping(uint32 => int128) internal sequencerFee; mapping(bytes32 => address) internal linkedSigners; int128 private slowModeFees; // invitee -> referralCode mapping(address => string) public referralCodes; // address -> whether can call `BurnLpAndTransfer`. mapping(address => bool) internal transferableWallets; mapping(uint32 => int128) internal priceX18; address internal offchainExchange; string internal constant LIQUIDATE_SUBACCOUNT_SIGNATURE = "LiquidateSubaccount(bytes32 sender,bytes32 liquidatee,uint32 productId,bool isEncodedSpread,int128 amount,uint64 nonce)"; string internal constant TRANSFER_QUOTE_SIGNATURE = "TransferQuote(bytes32 sender,bytes32 recipient,uint128 amount,uint64 nonce)"; string internal constant WITHDRAW_COLLATERAL_SIGNATURE = "WithdrawCollateral(bytes32 sender,uint32 productId,uint128 amount,uint64 nonce)"; string internal constant MINT_LP_SIGNATURE = "MintLp(bytes32 sender,uint32 productId,uint128 amountBase,uint128 quoteAmountLow,uint128 quoteAmountHigh,uint64 nonce)"; string internal constant BURN_LP_SIGNATURE = "BurnLp(bytes32 sender,uint32 productId,uint128 amount,uint64 nonce)"; string internal constant LINK_SIGNER_SIGNATURE = "LinkSigner(bytes32 sender,bytes32 signer,uint64 nonce)"; IVerifier private verifier; function _updatePrice(uint32 productId, int128 _priceX18) internal { require(_priceX18 > 0, ERR_INVALID_PRICE); address engine = clearinghouse.getEngineByProduct(productId); if (engine != address(0)) { priceX18[productId] = _priceX18; IProductEngine(engine).updatePrice(productId, _priceX18); } } function initialize( address _sanctions, address _sequencer, address _offchainExchange, IClearinghouse _clearinghouse, address _verifier, int128[] memory initialPrices ) external initializer { __Ownable_init(); __EIP712_init("Vertex", "0.0.1"); sequencer = _sequencer; clearinghouse = _clearinghouse; offchainExchange = _offchainExchange; verifier = IVerifier(_verifier); sanctions = ISanctionsList(_sanctions); spotEngine = ISpotEngine( clearinghouse.getEngineByType(IProductEngine.EngineType.SPOT) ); perpEngine = IPerpEngine( clearinghouse.getEngineByType(IProductEngine.EngineType.PERP) ); quote = IERC20Base(clearinghouse.getQuote()); slowModeConfig = SlowModeConfig({timeout: 0, txCount: 0, txUpTo: 0}); for (uint32 i = 0; i < initialPrices.length; i++) { priceX18[i] = initialPrices[i]; } } // NOTE: we want DepositCollateral to be the first action anybody takes on Vertex // so we can record the existence of their subaccount on-chain // unfortunately, there are some edge cases where an empty account can place an order // or do an AMM swap that passes health checks without going through a deposit, so // we block those functions unless there has been a deposit first function _recordSubaccount(bytes32 subaccount) internal { if (subaccountIds[subaccount] == 0) { subaccountIds[subaccount] = ++numSubaccounts; subaccounts[numSubaccounts] = subaccount; } } function requireSubaccount(bytes32 subaccount) private view { require( subaccount == X_ACCOUNT || (subaccountIds[subaccount] != 0), ERR_REQUIRES_DEPOSIT ); } function validateSubmissionIdx(uint64 idx) private view { require(idx == nSubmissions, ERR_INVALID_SUBMISSION_INDEX); } function validateNonce(bytes32 sender, uint64 nonce) internal virtual { require( nonce == nonces[address(uint160(bytes20(sender)))]++, ERR_WRONG_NONCE ); } function chargeFee(bytes32 sender, int128 fee) internal { chargeFee(sender, fee, QUOTE_PRODUCT_ID); } function chargeFee( bytes32 sender, int128 fee, uint32 productId ) internal { spotEngine.updateBalance(productId, sender, -fee); sequencerFee[productId] += fee; } function chargeSlowModeFee(IERC20Base token, address from) internal virtual { require(address(token) != address(0)); token.safeTransferFrom( from, address(this), clearinghouse.getSlowModeFee() ); } function getLinkedSigner(bytes32 subaccount) public view virtual returns (address) { return RiskHelper.isIsolatedSubaccount(subaccount) ? linkedSigners[ IOffchainExchange(offchainExchange).getParentSubaccount( subaccount ) ] : linkedSigners[subaccount]; } function validateSignature( bytes32 sender, bytes32 digest, bytes memory signature ) internal virtual { verifier.validateSignature( sender, getLinkedSigner(sender), digest, signature ); } function safeTransferFrom( IERC20Base token, address from, uint256 amount ) internal virtual { token.safeTransferFrom(from, address(this), amount); } function safeTransferTo( IERC20Base token, address to, uint256 amount ) internal virtual { token.safeTransfer(to, amount); } function handleDepositTransfer( IERC20Base token, address from, uint256 amount ) internal { require(address(token) != address(0)); safeTransferFrom(token, from, amount); safeTransferTo(token, address(clearinghouse), amount); } function validateSender(bytes32 txSender, address sender) internal view { require( address(uint160(bytes20(txSender))) == sender || sender == address(this), ERR_SLOW_MODE_WRONG_SENDER ); } function setReferralCode(address sender, string memory referralCode) internal { if (bytes(referralCodes[sender]).length == 0) { referralCodes[sender] = referralCode; } } function depositCollateral( bytes12 subaccountName, uint32 productId, uint128 amount ) external { depositCollateralWithReferral( bytes32(abi.encodePacked(msg.sender, subaccountName)), productId, amount, DEFAULT_REFERRAL_CODE ); } function depositCollateralWithReferral( bytes12 subaccountName, uint32 productId, uint128 amount, string calldata referralCode ) external { depositCollateralWithReferral( bytes32(abi.encodePacked(msg.sender, subaccountName)), productId, amount, referralCode ); } function depositCollateralWithReferral( bytes32 subaccount, uint32 productId, uint128 amount, string memory referralCode ) public { require(bytes(referralCode).length != 0); require( !RiskHelper.isIsolatedSubaccount(subaccount) && productId != 145, ERR_UNAUTHORIZED ); address sender = address(bytes20(subaccount)); // depositor / depositee need to be unsanctioned requireUnsanctioned(msg.sender); requireUnsanctioned(sender); // no referral code allowed for remote deposit setReferralCode( sender, sender == msg.sender ? referralCode : DEFAULT_REFERRAL_CODE ); if (subaccount != X_ACCOUNT && (subaccountIds[subaccount] == 0)) { clearinghouse.requireMinDeposit(productId, amount); } handleDepositTransfer( IERC20Base(spotEngine.getToken(productId)), msg.sender, uint256(amount) ); // copy from submitSlowModeTransaction SlowModeConfig memory _slowModeConfig = slowModeConfig; slowModeTxs[_slowModeConfig.txCount++] = SlowModeTx({ executableAt: uint64(block.timestamp) + 259200, // hardcoded to three days sender: sender, tx: abi.encodePacked( uint8(TransactionType.DepositCollateral), abi.encode( DepositCollateral({ sender: subaccount, productId: productId, amount: amount }) ) ) }); slowModeConfig = _slowModeConfig; } function requireUnsanctioned(address sender) internal view virtual { require(!sanctions.isSanctioned(sender), ERR_WALLET_SANCTIONED); } function submitSlowModeTransaction(bytes calldata transaction) external { TransactionType txType = TransactionType(uint8(transaction[0])); // special case for DepositCollateral because upon // slow mode submission we must take custody of the // actual funds address sender = msg.sender; if (txType == TransactionType.DepositCollateral) { revert(); } else if (txType == TransactionType.DepositInsurance) { DepositInsurance memory txn = abi.decode( transaction[1:], (DepositInsurance) ); handleDepositTransfer(_getQuote(), sender, uint256(txn.amount)); } else if (txType == TransactionType.UpdateProduct) { require(sender == owner()); } else if (txType == TransactionType.BurnLpAndTransfer) { require(transferableWallets[sender], ERR_WALLET_NOT_TRANSFERABLE); BurnLpAndTransfer memory txn = abi.decode( transaction[1:], (BurnLpAndTransfer) ); setReferralCode( address(uint160(bytes20(txn.recipient))), DEFAULT_REFERRAL_CODE ); } else if (txType == TransactionType.WithdrawInsurance) { require(sender == owner()); } else { chargeSlowModeFee(_getQuote(), sender); slowModeFees += SLOW_MODE_FEE; } SlowModeConfig memory _slowModeConfig = slowModeConfig; requireUnsanctioned(sender); slowModeTxs[_slowModeConfig.txCount++] = SlowModeTx({ executableAt: uint64(block.timestamp) + 259200, // hardcoded to three days sender: sender, tx: transaction }); // TODO: to save on costs we could potentially just emit something // for now, we can just create a separate loop in the engine that queries the remote // sequencer for slow mode transactions, and ignore the possibility of a reorgy attack slowModeConfig = _slowModeConfig; } function _executeSlowModeTransaction( SlowModeConfig memory _slowModeConfig, bool fromSequencer ) internal { require( _slowModeConfig.txUpTo < _slowModeConfig.txCount, ERR_NO_SLOW_MODE_TXS_REMAINING ); SlowModeTx memory txn = slowModeTxs[_slowModeConfig.txUpTo]; delete slowModeTxs[_slowModeConfig.txUpTo++]; require( fromSequencer || (txn.executableAt <= block.timestamp), ERR_SLOW_TX_TOO_RECENT ); if (block.chainid == 31337) { // for testing purposes, we don't fail silently when the chainId is hardhat's default. this.processSlowModeTransaction(txn.sender, txn.tx); } else { uint256 gasRemaining = gasleft(); try this.processSlowModeTransaction(txn.sender, txn.tx) {} catch { // we need to differentiate between a revert and an out of gas // the issue is that in evm every inner call only 63/64 of the // remaining gas in the outer frame is forwarded. as a result // the amount of gas left for execution is (63/64)**len(stack) // and you can get an out of gas while spending an arbitrarily // low amount of gas in the final frame. we use a heuristic // here that isn't perfect but covers our cases. // having gasleft() <= gasRemaining / 2 buys us 44 nested calls // before we miss out of gas errors; 1/2 ~= (63/64)**44 // this is good enough for our purposes if (gasleft() <= 250000 || gasleft() <= gasRemaining / 2) { assembly { invalid() } } // try return funds now removed } } } function executeSlowModeTransaction() external { SlowModeConfig memory _slowModeConfig = slowModeConfig; _executeSlowModeTransaction(_slowModeConfig, false); slowModeConfig = _slowModeConfig; } // TODO: these do not need senders or nonces // we can save some gas by creating new structs function processSlowModeTransaction( address sender, bytes calldata transaction ) public { require(msg.sender == address(this)); TransactionType txType = TransactionType(uint8(transaction[0])); if (txType == TransactionType.LiquidateSubaccount) { LiquidateSubaccount memory txn = abi.decode( transaction[1:], (LiquidateSubaccount) ); validateSender(txn.sender, sender); requireSubaccount(txn.sender); clearinghouse.liquidateSubaccount(txn); } else if (txType == TransactionType.DepositCollateral) { DepositCollateral memory txn = abi.decode( transaction[1:], (DepositCollateral) ); validateSender(txn.sender, sender); _recordSubaccount(txn.sender); clearinghouse.depositCollateral(txn); } else if (txType == TransactionType.WithdrawCollateral) { WithdrawCollateral memory txn = abi.decode( transaction[1:], (WithdrawCollateral) ); validateSender(txn.sender, sender); clearinghouse.withdrawCollateral( txn.sender, txn.productId, txn.amount, address(0), nSubmissions ); } else if (txType == TransactionType.SettlePnl) { clearinghouse.settlePnl(transaction); } else if (txType == TransactionType.DepositInsurance) { clearinghouse.depositInsurance(transaction); } else if (txType == TransactionType.MintLp) { MintLp memory txn = abi.decode(transaction[1:], (MintLp)); require( clearinghouse.getEngineByProduct(txn.productId) == clearinghouse.getEngineByType( IProductEngine.EngineType.SPOT ), ERR_INVALID_PRODUCT ); validateSender(txn.sender, sender); clearinghouse.mintLp(txn); } else if (txType == TransactionType.BurnLp) { BurnLp memory txn = abi.decode(transaction[1:], (BurnLp)); validateSender(txn.sender, sender); clearinghouse.burnLp(txn); } else if (txType == TransactionType.SwapAMM) { SwapAMM memory txn = abi.decode(transaction[1:], (SwapAMM)); validateSender(txn.sender, sender); requireSubaccount(txn.sender); IOffchainExchange(offchainExchange).swapAMM(txn); } else if (txType == TransactionType.UpdateProduct) { UpdateProduct memory txn = abi.decode( transaction[1:], (UpdateProduct) ); IProductEngine(txn.engine).updateProduct(txn.tx); } else if (txType == TransactionType.LinkSigner) { LinkSigner memory txn = abi.decode(transaction[1:], (LinkSigner)); validateSender(txn.sender, sender); requireSubaccount(txn.sender); linkedSigners[txn.sender] = address(uint160(bytes20(txn.signer))); } else if (txType == TransactionType.BurnLpAndTransfer) { BurnLpAndTransfer memory txn = abi.decode( transaction[1:], (BurnLpAndTransfer) ); validateSender(txn.sender, sender); _recordSubaccount(txn.recipient); clearinghouse.burnLpAndTransfer(txn); } else if (txType == TransactionType.WithdrawInsurance) { clearinghouse.withdrawInsurance(transaction, nSubmissions); } else { revert(); } } function processTransaction(bytes calldata transaction) internal { TransactionType txType = TransactionType(uint8(transaction[0])); if (txType == TransactionType.LiquidateSubaccount) { SignedLiquidateSubaccount memory signedTx = abi.decode( transaction[1:], (SignedLiquidateSubaccount) ); validateNonce(signedTx.tx.sender, signedTx.tx.nonce); validateSignature( signedTx.tx.sender, _hashTypedDataV4( keccak256( abi.encode( keccak256(bytes(LIQUIDATE_SUBACCOUNT_SIGNATURE)), signedTx.tx.sender, signedTx.tx.liquidatee, signedTx.tx.productId, signedTx.tx.isEncodedSpread, signedTx.tx.amount, signedTx.tx.nonce ) ) ), signedTx.signature ); requireSubaccount(signedTx.tx.sender); chargeFee(signedTx.tx.sender, LIQUIDATION_FEE); clearinghouse.liquidateSubaccount(signedTx.tx); } else if (txType == TransactionType.WithdrawCollateral) { SignedWithdrawCollateral memory signedTx = abi.decode( transaction[1:], (SignedWithdrawCollateral) ); validateNonce(signedTx.tx.sender, signedTx.tx.nonce); validateSignature( signedTx.tx.sender, _hashTypedDataV4( keccak256( abi.encode( keccak256(bytes(WITHDRAW_COLLATERAL_SIGNATURE)), signedTx.tx.sender, signedTx.tx.productId, signedTx.tx.amount, signedTx.tx.nonce ) ) ), signedTx.signature ); chargeFee( signedTx.tx.sender, clearinghouse.getWithdrawFee(signedTx.tx.productId), signedTx.tx.productId ); clearinghouse.withdrawCollateral( signedTx.tx.sender, signedTx.tx.productId, signedTx.tx.amount, address(0), nSubmissions ); } else if (txType == TransactionType.SpotTick) { SpotTick memory txn = abi.decode(transaction[1:], (SpotTick)); Times memory t = times; uint128 dt = t.spotTime == 0 ? 0 : txn.time - t.spotTime; spotEngine.updateStates(dt); t.spotTime = txn.time; times = t; } else if (txType == TransactionType.PerpTick) { PerpTick memory txn = abi.decode(transaction[1:], (PerpTick)); Times memory t = times; uint128 dt = t.perpTime == 0 ? 0 : txn.time - t.perpTime; perpEngine.updateStates(dt, txn.avgPriceDiffs); t.perpTime = txn.time; times = t; } else if (txType == TransactionType.UpdatePrice) { UpdatePrice memory txn = abi.decode(transaction[1:], (UpdatePrice)); _updatePrice(txn.productId, txn.priceX18); } else if (txType == TransactionType.SettlePnl) { clearinghouse.settlePnl(transaction); } else if ( txType == TransactionType.MatchOrders || txType == TransactionType.MatchOrdersRFQ ) { MatchOrders memory txn = abi.decode(transaction[1:], (MatchOrders)); requireSubaccount(txn.taker.order.sender); requireSubaccount(txn.maker.order.sender); MatchOrdersWithSigner memory txnWithSigner = MatchOrdersWithSigner({ matchOrders: txn, takerLinkedSigner: getLinkedSigner(txn.taker.order.sender), makerLinkedSigner: getLinkedSigner(txn.maker.order.sender) }); IOffchainExchange(offchainExchange).matchOrders(txnWithSigner); } else if (txType == TransactionType.MatchOrderAMM) { MatchOrderAMM memory txn = abi.decode( transaction[1:], (MatchOrderAMM) ); requireSubaccount(txn.taker.order.sender); IOffchainExchange(offchainExchange).matchOrderAMM( txn, getLinkedSigner(txn.taker.order.sender) ); } else if (txType == TransactionType.ExecuteSlowMode) { SlowModeConfig memory _slowModeConfig = slowModeConfig; _executeSlowModeTransaction(_slowModeConfig, true); slowModeConfig = _slowModeConfig; } else if (txType == TransactionType.MintLp) { SignedMintLp memory signedTx = abi.decode( transaction[1:], (SignedMintLp) ); validateNonce(signedTx.tx.sender, signedTx.tx.nonce); validateSignature( signedTx.tx.sender, _hashTypedDataV4( keccak256( abi.encode( keccak256(bytes(MINT_LP_SIGNATURE)), signedTx.tx.sender, signedTx.tx.productId, signedTx.tx.amountBase, signedTx.tx.quoteAmountLow, signedTx.tx.quoteAmountHigh, signedTx.tx.nonce ) ) ), signedTx.signature ); chargeFee(signedTx.tx.sender, HEALTHCHECK_FEE); clearinghouse.mintLp(signedTx.tx); } else if (txType == TransactionType.BurnLp) { SignedBurnLp memory signedTx = abi.decode( transaction[1:], (SignedBurnLp) ); validateNonce(signedTx.tx.sender, signedTx.tx.nonce); validateSignature( signedTx.tx.sender, _hashTypedDataV4( keccak256( abi.encode( keccak256(bytes(BURN_LP_SIGNATURE)), signedTx.tx.sender, signedTx.tx.productId, signedTx.tx.amount, signedTx.tx.nonce ) ) ), signedTx.signature ); chargeFee(signedTx.tx.sender, HEALTHCHECK_FEE); clearinghouse.burnLp(signedTx.tx); } else if (txType == TransactionType.DumpFees) { IOffchainExchange(offchainExchange).dumpFees(); } else if (txType == TransactionType.ClaimSequencerFees) { ClaimSequencerFees memory txn = abi.decode( transaction[1:], (ClaimSequencerFees) ); uint32[] memory spotIds = spotEngine.getProductIds(); int128[] memory fees = new int128[](spotIds.length); for (uint256 i = 0; i < spotIds.length; i++) { fees[i] = sequencerFee[spotIds[i]]; sequencerFee[spotIds[i]] = 0; } requireSubaccount(txn.subaccount); clearinghouse.claimSequencerFees(txn, fees); } else if (txType == TransactionType.ManualAssert) { clearinghouse.manualAssert(transaction); } else if (txType == TransactionType.LinkSigner) { SignedLinkSigner memory signedTx = abi.decode( transaction[1:], (SignedLinkSigner) ); validateNonce(signedTx.tx.sender, signedTx.tx.nonce); validateSignature( signedTx.tx.sender, _hashTypedDataV4( keccak256( abi.encode( keccak256(bytes(LINK_SIGNER_SIGNATURE)), signedTx.tx.sender, signedTx.tx.signer, signedTx.tx.nonce ) ) ), signedTx.signature ); linkedSigners[signedTx.tx.sender] = address( uint160(bytes20(signedTx.tx.signer)) ); } else if (txType == TransactionType.UpdateFeeRates) { UpdateFeeRates memory txn = abi.decode( transaction[1:], (UpdateFeeRates) ); IOffchainExchange(offchainExchange).updateFeeRates( txn.user, txn.productId, txn.makerRateX18, txn.takerRateX18 ); } else if (txType == TransactionType.TransferQuote) { SignedTransferQuote memory signedTx = abi.decode( transaction[1:], (SignedTransferQuote) ); _recordSubaccount(signedTx.tx.recipient); validateSignature( signedTx.tx.sender, _hashTypedDataV4( keccak256( abi.encode( keccak256(bytes(TRANSFER_QUOTE_SIGNATURE)), signedTx.tx.sender, signedTx.tx.recipient, signedTx.tx.amount, signedTx.tx.nonce ) ) ), signedTx.signature ); validateNonce(signedTx.tx.sender, signedTx.tx.nonce); chargeFee(signedTx.tx.sender, HEALTHCHECK_FEE); clearinghouse.transferQuote(signedTx.tx); } else if (txType == TransactionType.RebalanceXWithdraw) { RebalanceXWithdraw memory txn = abi.decode( transaction[1:], (RebalanceXWithdraw) ); clearinghouse.withdrawCollateral( X_ACCOUNT, txn.productId, txn.amount, txn.sendTo, nSubmissions ); } else if (txType == TransactionType.UpdateMinDepositRate) { UpdateMinDepositRate memory txn = abi.decode( transaction[1:], (UpdateMinDepositRate) ); spotEngine.updateMinDepositRate( txn.productId, txn.minDepositRateX18 ); } else if (txType == TransactionType.AssertCode) { clearinghouse.assertCode(transaction); } else if (txType == TransactionType.CreateIsolatedSubaccount) { CreateIsolatedSubaccount memory txn = abi.decode( transaction[1:], (CreateIsolatedSubaccount) ); bytes32 newIsolatedSubaccount = IOffchainExchange(offchainExchange) .createIsolatedSubaccount( txn, getLinkedSigner(txn.order.sender) ); _recordSubaccount(newIsolatedSubaccount); } else { revert(); } } function submitTransactionsChecked( uint64 idx, bytes[] calldata transactions, bytes32 e, bytes32 s, uint8 signerBitmask ) external { validateSubmissionIdx(idx); require(msg.sender == sequencer); // TODO: if one of these transactions fails this means the sequencer is in an error state // we should probably record this, and engage some sort of recovery mode bytes32 digest = keccak256(abi.encode(idx)); for (uint256 i = 0; i < transactions.length; ++i) { digest = keccak256(abi.encodePacked(digest, transactions[i])); } verifier.requireValidSignature(digest, e, s, signerBitmask); for (uint256 i = 0; i < transactions.length; i++) { bytes calldata transaction = transactions[i]; processTransaction(transaction); nSubmissions += 1; } } function submitTransactionsCheckedWithGasLimit( uint64 idx, bytes[] calldata transactions, uint256 gasLimit ) external { uint256 gasUsed = gasleft(); validateSubmissionIdx(idx); for (uint256 i = 0; i < transactions.length; i++) { bytes calldata transaction = transactions[i]; processTransaction(transaction); if (gasUsed - gasleft() > gasLimit) { verifier.revertGasInfo(i, gasUsed); } } verifier.revertGasInfo(transactions.length, gasUsed - gasleft()); } function getSubaccountId(bytes32 subaccount) external view returns (uint64) { return subaccountIds[subaccount]; } function _getQuote() internal view returns (IERC20Base) { return IERC20Base(spotEngine.getToken(QUOTE_PRODUCT_ID)); } function getPriceX18(uint32 productId) public view returns (int128 _priceX18) { _priceX18 = priceX18[productId]; require(_priceX18 != 0, ERR_INVALID_PRODUCT); } function getTime() external view returns (uint128) { Times memory t = times; uint128 _time = t.spotTime > t.perpTime ? t.spotTime : t.perpTime; require(_time != 0, ERR_INVALID_TIME); return _time; } function getOffchainExchange() external view returns (address) { return offchainExchange; } function getSequencer() external view returns (address) { return sequencer; } function getSlowModeTx(uint64 idx) external view returns ( SlowModeTx memory, uint64, uint64 ) { return ( slowModeTxs[idx], slowModeConfig.txUpTo, slowModeConfig.txCount ); } function getNonce(address sender) external view returns (uint64) { return nonces[sender]; } function updateSanctions(address _sanctions) external onlyOwner { sanctions = ISanctionsList(_sanctions); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol"; import "./libraries/MathHelper.sol"; import "./interfaces/IEndpoint.sol"; import "./Verifier.sol"; import "./interfaces/engine/ISpotEngine.sol"; import "./interfaces/IERC20Base.sol"; import "./libraries/ERC20Helper.sol"; import "./common/Constants.sol"; contract WithdrawPool is EIP712Upgradeable, OwnableUpgradeable { using ERC20Helper for IERC20Base; using MathSD21x18 for int128; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize(address _clearinghouse, address _verifier) external initializer { __Ownable_init(); clearinghouse = _clearinghouse; verifier = _verifier; } address internal clearinghouse; address internal verifier; // submitted withdrawal idxs mapping(uint64 => bool) public markedIdxs; // collected withdrawal fees in native token decimals mapping(uint32 => int128) public fees; uint64 public minIdx; function submitFastWithdrawal( uint64 idx, bytes calldata transaction, bytes[] calldata signatures ) public { require(!markedIdxs[idx], "Withdrawal already submitted"); require(idx > minIdx, "idx too small"); markedIdxs[idx] = true; Verifier v = Verifier(verifier); v.requireValidTxSignatures(transaction, idx, signatures); IEndpoint.SignedWithdrawCollateral memory signedTx = abi.decode( transaction[1:], (IEndpoint.SignedWithdrawCollateral) ); IERC20Base token = getToken(signedTx.tx.productId); address sendTo = address(uint160(bytes20(signedTx.tx.sender))); uint128 transferAmount = signedTx.tx.amount; require(transferAmount <= INT128_MAX, ERR_CONVERSION_OVERFLOW); int128 fee = fastWithdrawalFeeAmount( token, signedTx.tx.productId, transferAmount ); require(transferAmount > uint128(fee), "Fee larger than balance"); transferAmount -= uint128(fee); fees[signedTx.tx.productId] += fee; handleWithdrawTransfer(token, sendTo, transferAmount); } function submitWithdrawal( IERC20Base token, address sendTo, uint128 amount, uint64 idx ) public { require(msg.sender == clearinghouse); if (markedIdxs[idx]) { return; } markedIdxs[idx] = true; // set minIdx to most recent withdrawal submitted by sequencer minIdx = idx; handleWithdrawTransfer(token, sendTo, amount); } function fastWithdrawalFeeAmount( IERC20Base token, uint32 productId, uint128 amount ) public view returns (int128) { uint8 decimals = token.decimals(); require(decimals <= MAX_DECIMALS); int256 multiplier = int256(10**(MAX_DECIMALS - uint8(decimals))); int128 amountX18 = int128(amount) * int128(multiplier); int128 proportionalFeeX18 = FAST_WITHDRAWAL_FEE_RATE.mul(amountX18); int128 minFeeX18 = 5 * IClearinghouse(clearinghouse).getWithdrawFee(productId); int128 feeX18 = MathHelper.max(proportionalFeeX18, minFeeX18); return feeX18 / int128(multiplier); } function removeLiquidity( uint32 productId, uint128 amount, address sendTo ) external onlyOwner { handleWithdrawTransfer(getToken(productId), sendTo, amount); } function checkMarkedIdxs(uint64[] calldata idxs) public view returns (bool[] memory) { bool[] memory marked = new bool[](idxs.length); for (uint256 i = 0; i < idxs.length; i++) { marked[i] = markedIdxs[idxs[i]]; } return marked; } function checkProductBalances(uint32[] calldata productIds) public view returns (uint256[] memory) { uint256[] memory balances = new uint256[](productIds.length); for (uint256 i = 0; i < productIds.length; i++) { IERC20Base token = getToken(productIds[i]); balances[i] = token.balanceOf(address(this)); } return balances; } function handleWithdrawTransfer( IERC20Base token, address to, uint128 amount ) internal virtual { token.safeTransfer(to, uint256(amount)); } function safeTransferFrom( IERC20Base token, address from, uint256 amount ) internal virtual { token.safeTransferFrom(from, address(this), amount); } function getToken(uint32 productId) internal view returns (IERC20Base) { IERC20Base token = IERC20Base(spotEngine().getConfig(productId).token); require(address(token) != address(0)); return token; } function spotEngine() internal view returns (ISpotEngine) { return ISpotEngine( IClearinghouse(clearinghouse).getEngineByType( IProductEngine.EngineType.SPOT ) ); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "hardhat/console.sol"; import "./common/Constants.sol"; import "./common/Errors.sol"; import "./interfaces/engine/IProductEngine.sol"; import "./interfaces/engine/IPerpEngine.sol"; import "./interfaces/clearinghouse/IClearinghouse.sol"; import "./libraries/MathHelper.sol"; import "./libraries/MathSD21x18.sol"; import "./BaseEngine.sol"; import "./PerpEngineLp.sol"; contract PerpEngine is PerpEngineLp { using MathSD21x18 for int128; function initialize( address _clearinghouse, address _offchainExchange, address, address _endpoint, address _admin ) external { _initialize(_clearinghouse, _offchainExchange, _endpoint, _admin); } function getEngineType() external pure returns (EngineType) { return EngineType.PERP; } /** * Actions */ /// @notice adds a new product with default parameters function addProduct( uint32 productId, address book, int128 sizeIncrement, int128 minSize, int128 lpSpreadX18, RiskHelper.RiskStore calldata riskStore ) public onlyOwner { _addProductForId( productId, QUOTE_PRODUCT_ID, book, sizeIncrement, minSize, lpSpreadX18, riskStore ); states[productId] = State({ cumulativeFundingLongX18: 0, cumulativeFundingShortX18: 0, availableSettle: 0, openInterest: 0 }); lpStates[productId] = LpState({ supply: 0, lastCumulativeFundingX18: 0, cumulativeFundingPerLpX18: 0, base: 0, quote: 0 }); } /// @notice changes the configs of a product, if a new book is provided /// also clears the book function updateProduct(bytes calldata rawTxn) external onlyEndpoint { UpdateProductTx memory txn = abi.decode(rawTxn, (UpdateProductTx)); RiskHelper.RiskStore memory riskStore = txn.riskStore; require( riskStore.longWeightInitial <= riskStore.longWeightMaintenance && riskStore.shortWeightInitial >= riskStore.shortWeightMaintenance, ERR_BAD_PRODUCT_CONFIG ); RiskHelper.RiskStore memory r = _risk().value[txn.productId]; r.longWeightInitial = riskStore.longWeightInitial; r.shortWeightInitial = riskStore.shortWeightInitial; r.longWeightMaintenance = riskStore.longWeightMaintenance; r.shortWeightMaintenance = riskStore.shortWeightMaintenance; _risk().value[txn.productId] = r; _exchange().updateMarket( txn.productId, type(uint32).max, address(0), txn.sizeIncrement, txn.minSize, txn.lpSpreadX18 ); } function updateBalance( uint32 productId, bytes32 subaccount, int128 amountDelta, int128 vQuoteDelta ) external { // Only a market book can apply deltas _assertInternal(); State memory state = states[productId]; Balance memory balance = balances[productId][subaccount]; _updateBalance(state, balance, amountDelta, vQuoteDelta); states[productId] = state; balances[productId][subaccount] = balance; _balanceUpdate(productId, subaccount); } function settlePnl(bytes32 subaccount, uint256 productIds) external returns (int128) { _assertInternal(); int128 totalSettled = 0; while (productIds != 0) { uint32 productId = uint32(productIds & ((1 << 32) - 1)); // otherwise it means the product is a spot. if (productId % 2 == 0) { ( int128 canSettle, LpState memory lpState, LpBalance memory lpBalance, State memory state, Balance memory balance ) = getSettlementState(productId, subaccount); state.availableSettle -= canSettle; balance.vQuoteBalance -= canSettle; totalSettled += canSettle; lpStates[productId] = lpState; states[productId] = state; lpBalances[productId][subaccount] = lpBalance; balances[productId][subaccount] = balance; _balanceUpdate(productId, subaccount); } productIds >>= 32; } return totalSettled; } function calculatePositionPnl( LpState memory lpState, LpBalance memory lpBalance, Balance memory balance, uint32 productId ) internal view returns (int128 positionPnl) { int128 priceX18 = _risk(productId).priceX18; (int128 ammBase, int128 ammQuote) = MathHelper.ammEquilibrium( lpState.base, lpState.quote, priceX18 ); if (lpBalance.amount == 0) { positionPnl = priceX18.mul(balance.amount) + balance.vQuoteBalance; } else { positionPnl = priceX18.mul( balance.amount + ammBase.mul(lpBalance.amount).div(lpState.supply) ) + balance.vQuoteBalance + ammQuote.mul(lpBalance.amount).div(lpState.supply); } } function getPositionPnl(uint32 productId, bytes32 subaccount) external view returns (int128) { ( LpState memory lpState, LpBalance memory lpBalance, , Balance memory balance ) = getStatesAndBalances(productId, subaccount); return calculatePositionPnl(lpState, lpBalance, balance, productId); } function getSettlementState(uint32 productId, bytes32 subaccount) public view returns ( int128 availableSettle, LpState memory lpState, LpBalance memory lpBalance, State memory state, Balance memory balance ) { (lpState, lpBalance, state, balance) = getStatesAndBalances( productId, subaccount ); availableSettle = MathHelper.min( calculatePositionPnl(lpState, lpBalance, balance, productId), state.availableSettle ); } function socializeSubaccount(bytes32 subaccount, int128 insurance) external returns (int128) { require(msg.sender == address(_clearinghouse), ERR_UNAUTHORIZED); uint32[] memory _productIds = getProductIds(); for (uint128 i = 0; i < _productIds.length; ++i) { uint32 productId = _productIds[i]; (State memory state, Balance memory balance) = getStateAndBalance( productId, subaccount ); if (balance.vQuoteBalance < 0) { int128 insuranceCover = MathHelper.min( insurance, -balance.vQuoteBalance ); insurance -= insuranceCover; balance.vQuoteBalance += insuranceCover; state.availableSettle += insuranceCover; // actually socialize if still not enough if (balance.vQuoteBalance < 0) { // socialize across all other participants int128 fundingPerShare = -balance.vQuoteBalance.div( state.openInterest ) / 2; state.cumulativeFundingLongX18 += fundingPerShare; state.cumulativeFundingShortX18 -= fundingPerShare; LpState memory lpState = lpStates[productId]; Balance memory tmp = Balance({ amount: lpState.base, vQuoteBalance: 0, lastCumulativeFundingX18: lpState .lastCumulativeFundingX18 }); _updateBalance(state, tmp, 0, 0); if (lpState.supply != 0) { lpState.cumulativeFundingPerLpX18 += tmp .vQuoteBalance .div(lpState.supply); } lpState.lastCumulativeFundingX18 = state .cumulativeFundingLongX18; lpStates[productId] = lpState; balance.vQuoteBalance = 0; } states[productId] = state; balances[productId][subaccount] = balance; _balanceUpdate(productId, subaccount); } } return insurance; } function manualAssert(int128[] calldata openInterests) external view { for (uint128 i = 0; i < openInterests.length; ++i) { uint32 productId = productIds[i]; require( states[productId].openInterest == openInterests[i], ERR_DSYNC ); } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./clearinghouse/IClearinghouse.sol"; interface IEndpoint { event SubmitTransactions(); // events that we parse transactions into enum TransactionType { LiquidateSubaccount, DepositCollateral, WithdrawCollateral, SpotTick, UpdatePrice, SettlePnl, MatchOrders, DepositInsurance, ExecuteSlowMode, MintLp, BurnLp, SwapAMM, MatchOrderAMM, DumpFees, ClaimSequencerFees, PerpTick, ManualAssert, Rebate, // deprecated UpdateProduct, LinkSigner, UpdateFeeRates, BurnLpAndTransfer, MatchOrdersRFQ, TransferQuote, RebalanceXWithdraw, UpdateMinDepositRate, AssertCode, WithdrawInsurance, CreateIsolatedSubaccount } struct UpdateProduct { address engine; bytes tx; } /// requires signature from sender enum LiquidationMode { SPREAD, SPOT, PERP } struct LegacyLiquidateSubaccount { bytes32 sender; bytes32 liquidatee; uint8 mode; uint32 healthGroup; int128 amount; uint64 nonce; } struct LiquidateSubaccount { bytes32 sender; bytes32 liquidatee; uint32 productId; bool isEncodedSpread; int128 amount; uint64 nonce; } struct LegacySignedLiquidateSubaccount { LegacyLiquidateSubaccount tx; bytes signature; } struct SignedLiquidateSubaccount { LiquidateSubaccount tx; bytes signature; } struct DepositCollateral { bytes32 sender; uint32 productId; uint128 amount; } struct SignedDepositCollateral { DepositCollateral tx; bytes signature; } struct WithdrawCollateral { bytes32 sender; uint32 productId; uint128 amount; uint64 nonce; } struct SignedWithdrawCollateral { WithdrawCollateral tx; bytes signature; } struct MintLp { bytes32 sender; uint32 productId; uint128 amountBase; uint128 quoteAmountLow; uint128 quoteAmountHigh; uint64 nonce; } struct SignedMintLp { MintLp tx; bytes signature; } struct BurnLp { bytes32 sender; uint32 productId; uint128 amount; uint64 nonce; } struct SignedBurnLp { BurnLp tx; bytes signature; } struct LinkSigner { bytes32 sender; bytes32 signer; uint64 nonce; } struct SignedLinkSigner { LinkSigner tx; bytes signature; } /// callable by endpoint; no signature verifications needed struct PerpTick { uint128 time; int128[] avgPriceDiffs; } struct LegacySpotTick { uint128 time; } struct SpotTick { uint128 time; // utilization ratio across all chains int128[] utilizationRatiosX18; } struct ManualAssert { int128[] openInterests; int128[] totalDeposits; int128[] totalBorrows; } struct AssertCode { string[] contractNames; bytes32[] codeHashes; } struct WithdrawInsurance { uint128 amount; address sendTo; } struct Rebate { bytes32[] subaccounts; int128[] amounts; } struct UpdateFeeRates { address user; uint32 productId; // the absolute value of fee rates can't be larger than 100%, // so their X18 values are in the range [-1e18, 1e18], which // can be stored by using int64. int64 makerRateX18; int64 takerRateX18; } struct ClaimSequencerFees { bytes32 subaccount; } struct RebalanceXWithdraw { uint32 productId; uint128 amount; address sendTo; } struct UpdateMinDepositRate { uint32 productId; int128 minDepositRateX18; } struct UpdatePrice { uint32 productId; int128 priceX18; } struct SettlePnl { bytes32[] subaccounts; uint256[] productIds; } /// matching struct Order { bytes32 sender; int128 priceX18; int128 amount; uint64 expiration; uint64 nonce; } struct SignedOrder { Order order; bytes signature; } struct LegacyMatchOrders { uint32 productId; bool amm; SignedOrder taker; SignedOrder maker; } struct MatchOrders { uint32 productId; SignedOrder taker; SignedOrder maker; } struct MatchOrdersWithSigner { MatchOrders matchOrders; address takerLinkedSigner; address makerLinkedSigner; } // just swap against AMM -- theres no maker order struct MatchOrderAMM { uint32 productId; int128 baseDelta; int128 quoteDelta; SignedOrder taker; } struct SwapAMM { bytes32 sender; uint32 productId; int128 amount; int128 priceX18; } struct DepositInsurance { uint128 amount; } struct SlowModeTx { uint64 executableAt; address sender; bytes tx; } struct SlowModeConfig { uint64 timeout; uint64 txCount; uint64 txUpTo; } // legacy :( struct Prices { int128 spotPriceX18; int128 perpPriceX18; } struct BurnLpAndTransfer { bytes32 sender; uint32 productId; uint128 amount; bytes32 recipient; } struct TransferQuote { bytes32 sender; bytes32 recipient; uint128 amount; uint64 nonce; } struct SignedTransferQuote { TransferQuote tx; bytes signature; } struct IsolatedOrder { bytes32 sender; int128 priceX18; int128 amount; uint64 expiration; uint64 nonce; int128 margin; } struct CreateIsolatedSubaccount { IsolatedOrder order; uint32 productId; bytes signature; } function depositCollateral( bytes12 subaccountName, uint32 productId, uint128 amount ) external; function depositCollateralWithReferral( bytes12 subaccountName, uint32 productId, uint128 amount, string calldata referralCode ) external; function depositCollateralWithReferral( bytes32 subaccount, uint32 productId, uint128 amount, string calldata referralCode ) external; function submitSlowModeTransaction(bytes calldata transaction) external; function getTime() external view returns (uint128); function getSequencer() external view returns (address); function getNonce(address sender) external view returns (uint64); function getOffchainExchange() external view returns (address); function getPriceX18(uint32 productId) external view returns (int128); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./IProductEngine.sol"; import "../../libraries/RiskHelper.sol"; interface IPerpEngine is IProductEngine { event FundingPayment( uint32 productId, uint128 dt, int128 openInterest, int128 payment ); struct State { int128 cumulativeFundingLongX18; int128 cumulativeFundingShortX18; int128 availableSettle; int128 openInterest; } struct Balance { int128 amount; int128 vQuoteBalance; int128 lastCumulativeFundingX18; } struct LpState { int128 supply; // TODO: this should be removed; we can just get it from State.cumulativeFundingLongX18 int128 lastCumulativeFundingX18; int128 cumulativeFundingPerLpX18; int128 base; int128 quote; } struct LpBalance { int128 amount; // NOTE: funding payments should be rolled // into Balance.vQuoteBalance; int128 lastCumulativeFundingX18; } struct UpdateProductTx { uint32 productId; int128 sizeIncrement; int128 minSize; int128 lpSpreadX18; RiskHelper.RiskStore riskStore; } function getStateAndBalance(uint32 productId, bytes32 subaccount) external view returns (State memory, Balance memory); function getBalance(uint32 productId, bytes32 subaccount) external view returns (Balance memory); function getStatesAndBalances(uint32 productId, bytes32 subaccount) external view returns ( LpState memory, LpBalance memory, State memory, Balance memory ); /// @dev Returns amount settled and emits SettlePnl events for each product function settlePnl(bytes32 subaccount, uint256 productIds) external returns (int128); function getSettlementState(uint32 productId, bytes32 subaccount) external view returns ( int128 availableSettle, LpState memory lpState, LpBalance memory lpBalance, State memory state, Balance memory balance ); function updateBalance( uint32 productId, bytes32 subaccount, int128 amountDelta, int128 vQuoteDelta ) external; function updateStates(uint128 dt, int128[] calldata avgPriceDiffs) external; function manualAssert(int128[] calldata openInterests) external view; function getPositionPnl(uint32 productId, bytes32 subaccount) external view returns (int128); function socializeSubaccount(bytes32 subaccount, int128 insurance) external returns (int128); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./IProductEngine.sol"; import "../../libraries/RiskHelper.sol"; interface ISpotEngine is IProductEngine { event SpotBalance( bytes32 indexed subaccount, uint32 indexed productId, int128 amount, int128 lastCumulativeMultiplierX18 ); event InterestPayment( uint32 productId, uint128 dt, int128 depositRateMultiplierX18, int128 borrowRateMultiplierX18, int128 feeAmount ); struct Config { address token; int128 interestInflectionUtilX18; int128 interestFloorX18; int128 interestSmallCapX18; int128 interestLargeCapX18; } struct State { int128 cumulativeDepositsMultiplierX18; int128 cumulativeBorrowsMultiplierX18; int128 totalDepositsNormalized; int128 totalBorrowsNormalized; } struct Balance { int128 amount; int128 lastCumulativeMultiplierX18; } struct BalanceNormalized { int128 amountNormalized; } struct LpState { int128 supply; Balance quote; Balance base; } struct LpBalance { int128 amount; } struct Balances { BalanceNormalized balance; LpBalance lpBalance; } struct UpdateProductTx { uint32 productId; int128 sizeIncrement; int128 minSize; int128 lpSpreadX18; Config config; RiskHelper.RiskStore riskStore; } function getStateAndBalance(uint32 productId, bytes32 subaccount) external view returns (State memory, Balance memory); function getBalance(uint32 productId, bytes32 subaccount) external view returns (Balance memory); function getStatesAndBalances(uint32 productId, bytes32 subaccount) external view returns ( LpState memory, LpBalance memory, State memory, Balance memory ); function getConfig(uint32 productId) external view returns (Config memory); function getToken(uint32 productId) external view returns (address); function updateBalance( uint32 productId, bytes32 subaccount, int128 amountDelta ) external; function updateBalance( uint32 productId, bytes32 subaccount, int128 amountDelta, int128 quoteDelta ) external; function updateQuoteFromInsurance(bytes32 subaccount, int128 insurance) external returns (int128); function updateStates(uint128 dt) external; function updateMinDepositRate(uint32 productId, int128 minDepositRateX18) external; function manualAssert( int128[] calldata totalDeposits, int128[] calldata totalBorrows ) external view; function socializeSubaccount(bytes32 subaccount) external; function assertUtilization(uint32 productId) external view; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../clearinghouse/IClearinghouse.sol"; import "../../libraries/RiskHelper.sol"; interface IProductEngine { event AddProduct(uint32 productId); enum EngineType { SPOT, PERP } enum HealthType { INITIAL, MAINTENANCE, PNL } struct ProductDelta { uint32 productId; bytes32 subaccount; int128 amountDelta; int128 vQuoteDelta; } struct CoreRisk { int128 amount; int128 price; int128 longWeight; } /// @notice Initializes the engine function initialize( address _clearinghouse, address _offchainExchange, address _quote, address _endpoint, address _admin ) external; function getHealthContribution( bytes32 subaccount, IProductEngine.HealthType healthType ) external view returns (int128); function getCoreRisk( bytes32 subaccount, uint32 productId, IProductEngine.HealthType healthType ) external view returns (IProductEngine.CoreRisk memory); function updateProduct(bytes calldata txn) external; function swapLp( uint32 productId, int128 baseDelta, int128 quoteDelta ) external returns (int128, int128); function mintLp( uint32 productId, bytes32 subaccount, int128 amountBase, int128 quoteAmountLow, int128 quoteAmountHigh ) external; function burnLp( uint32 productId, bytes32 subaccount, // passing 0 here means to burn all int128 amountLp ) external returns (int128, int128); function decomposeLps(bytes32 liquidatee, bytes32 liquidator) external returns (int128); /// @notice return clearinghouse addr function getClearinghouse() external view returns (address); /// @notice return productIds associated with engine function getProductIds() external view returns (uint32[] memory); function getRisk(uint32 productId) external view returns (RiskHelper.Risk memory); /// @notice return the type of engine function getEngineType() external pure returns (IProductEngine.EngineType); function updatePrice(uint32 productId, int128 priceX18) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./MathSD21x18.sol"; import "../interfaces/engine/IProductEngine.sol"; import "../common/Constants.sol"; import "../common/Errors.sol"; import "./MathHelper.sol"; /// @title RiskHelper /// @dev Provides basic math functions library RiskHelper { using MathSD21x18 for int128; struct RiskStore { // these weights are all // between 0 and 2 // these integers are the real // weights times 1e9 int32 longWeightInitial; int32 shortWeightInitial; int32 longWeightMaintenance; int32 shortWeightMaintenance; int128 priceX18; } struct Risk { int128 longWeightInitialX18; int128 shortWeightInitialX18; int128 longWeightMaintenanceX18; int128 shortWeightMaintenanceX18; int128 priceX18; } function _getSpreadHealthRebateAmount( Risk memory perpRisk, int128 basisAmount, int128 priceSumX18, IProductEngine.HealthType healthType ) internal pure returns (int128) { // 5x more leverage than the standard perp // by refunding 4/5 of the health penalty int128 rebateRateX18 = ((ONE - _getWeightX18(perpRisk, 1, healthType)) * 4) / 5; return rebateRateX18.mul(priceSumX18).mul(basisAmount); } function _getLpRawValue( int128 baseAmount, int128 quoteAmount, int128 priceX18 ) internal pure returns (int128) { // naive way: value an LP token by value of the raw components 2 * arithmetic mean of base value and quote value // price manipulation proof way: use the geometric mean return 2 * int128( MathHelper.sqrt256( int256(baseAmount.mul(priceX18)) * quoteAmount ) ); } function _getWeightX18( Risk memory risk, int128 amount, IProductEngine.HealthType healthType ) internal pure returns (int128) { // (1 + imf * sqrt(amount)) if (healthType == IProductEngine.HealthType.PNL) { return ONE; } int128 weight; if (amount >= 0) { weight = healthType == IProductEngine.HealthType.INITIAL ? risk.longWeightInitialX18 : risk.longWeightMaintenanceX18; } else { weight = healthType == IProductEngine.HealthType.INITIAL ? risk.shortWeightInitialX18 : risk.shortWeightMaintenanceX18; } return weight; } function isIsolatedSubaccount(bytes32 subaccount) internal pure returns (bool) { return uint256(subaccount) & 0xFFFFFF == 6910831; } function getIsolatedProductId(bytes32 subaccount) internal pure returns (uint32) { if (!isIsolatedSubaccount(subaccount)) { return 0; } return uint32((uint256(subaccount) >> 32) & 0xFFFF); } function getIsolatedId(bytes32 subaccount) internal pure returns (uint8) { if (!isIsolatedSubaccount(subaccount)) { return 0; } return uint8((uint256(subaccount) >> 24) & 0xFF); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "hardhat/console.sol"; import "./common/Constants.sol"; import "./common/Errors.sol"; import "./libraries/MathHelper.sol"; import "./libraries/MathSD21x18.sol"; import "./interfaces/clearinghouse/IClearinghouse.sol"; import "./interfaces/engine/IProductEngine.sol"; import "./interfaces/IOffchainExchange.sol"; import "./interfaces/IEndpoint.sol"; import "./EndpointGated.sol"; import "./libraries/Logger.sol"; abstract contract BaseEngine is IProductEngine, EndpointGated { using MathSD21x18 for int128; IClearinghouse internal _clearinghouse; address internal _fees; // deprecated uint32[] internal productIds; mapping(uint32 => address) internal markets; // deprecated // Whether an address can apply deltas - all orderbooks and clearinghouse is whitelisted mapping(address => bool) internal canApplyDeltas; bytes32 internal constant RISK_STORAGE = keccak256("vertex.protocol.risk"); event BalanceUpdate(uint32 productId, bytes32 subaccount); event ProductUpdate(uint32 productId); function _productUpdate(uint32 productId) internal virtual {} struct Uint256Slot { uint256 value; } struct RiskStoreMappingSlot { mapping(uint32 => RiskHelper.RiskStore) value; } function _risk() internal pure returns (RiskStoreMappingSlot storage r) { bytes32 slot = RISK_STORAGE; assembly { r.slot := slot } } function _risk(uint32 productId, RiskStoreMappingSlot storage rmap) internal view returns (RiskHelper.Risk memory r) { RiskHelper.RiskStore memory s = rmap.value[productId]; r.longWeightInitialX18 = int128(s.longWeightInitial) * 1e9; r.shortWeightInitialX18 = int128(s.shortWeightInitial) * 1e9; r.longWeightMaintenanceX18 = int128(s.longWeightMaintenance) * 1e9; r.shortWeightMaintenanceX18 = int128(s.shortWeightMaintenance) * 1e9; r.priceX18 = s.priceX18; } function _risk(uint32 productId) internal view returns (RiskHelper.Risk memory) { return _risk(productId, _risk()); } function getRisk(uint32 productId) external view returns (RiskHelper.Risk memory) { return _risk(productId); } function _getInLpBalance(uint32 productId, bytes32 subaccount) internal view virtual returns ( // baseAmount, quoteAmount, quoteDeltaAmount (funding) int128, int128, int128 ); function _getBalance(uint32 productId, bytes32 subaccount) internal view virtual returns (int128, int128); function getHealthContribution( bytes32 subaccount, IProductEngine.HealthType healthType ) public view returns (int128 health) { uint32[] memory _productIds = getProductIds(); RiskStoreMappingSlot storage r = _risk(); for (uint32 i = 0; i < _productIds.length; i++) { uint32 productId = _productIds[i]; RiskHelper.Risk memory risk = _risk(productId, r); { (int128 amount, int128 quoteAmount) = _getBalance( productId, subaccount ); int128 weight = RiskHelper._getWeightX18( risk, amount, healthType ); health += quoteAmount; if (amount != 0) { // anything with a short weight of 2 is a spot that // should not count towards health and exists out of the risk system // if we're getting a weight of 2 it means this is attempting to short // the spot, so we should error out if (weight == 2 * ONE) { return type(int128).min; } health += amount.mul(weight).mul(risk.priceX18); } } { ( int128 baseAmount, int128 quoteAmount, int128 quoteDeltaAmount ) = _getInLpBalance(productId, subaccount); if (baseAmount != 0) { int128 lpValue = RiskHelper._getLpRawValue( baseAmount, quoteAmount, risk.priceX18 ); health += lpValue.mul( RiskHelper._getWeightX18(risk, 1, healthType) ) + quoteDeltaAmount; } } } } function getCoreRisk( bytes32 subaccount, uint32 productId, IProductEngine.HealthType healthType ) external view returns (IProductEngine.CoreRisk memory) { RiskHelper.Risk memory risk = _risk(productId); (int128 amount, ) = _getBalance(productId, subaccount); return IProductEngine.CoreRisk( amount, risk.priceX18, RiskHelper._getWeightX18(risk, 1, healthType) ); } function _balanceUpdate(uint32 productId, bytes32 subaccount) internal virtual {} function _assertInternal() internal view virtual { require(canApplyDeltas[msg.sender], ERR_UNAUTHORIZED); } function _initialize( address _clearinghouseAddr, address _offchainExchangeAddr, address _endpointAddr, address _admin ) internal initializer { __Ownable_init(); setEndpoint(_endpointAddr); transferOwnership(_admin); _clearinghouse = IClearinghouse(_clearinghouseAddr); canApplyDeltas[_endpointAddr] = true; canApplyDeltas[_clearinghouseAddr] = true; canApplyDeltas[_offchainExchangeAddr] = true; } function getClearinghouse() external view returns (address) { return address(_clearinghouse); } function getProductIds() public view returns (uint32[] memory) { return productIds; } function _addProductForId( uint32 productId, uint32 quoteId, address virtualBook, int128 sizeIncrement, int128 minSize, int128 lpSpreadX18, RiskHelper.RiskStore memory riskStore ) internal { require(virtualBook != address(0)); require( riskStore.longWeightInitial <= riskStore.longWeightMaintenance && riskStore.longWeightMaintenance <= 10**9 && riskStore.shortWeightInitial >= riskStore.shortWeightMaintenance && riskStore.shortWeightMaintenance >= 10**9, ERR_BAD_PRODUCT_CONFIG ); _risk().value[productId] = riskStore; // register product with clearinghouse _clearinghouse.registerProduct(productId); productIds.push(productId); // product ids are in ascending order for (uint256 i = productIds.length - 1; i > 0; i--) { if (productIds[i] < productIds[i - 1]) { uint32 t = productIds[i]; productIds[i] = productIds[i - 1]; productIds[i - 1] = t; } else { break; } } _exchange().updateMarket( productId, quoteId, virtualBook, sizeIncrement, minSize, lpSpreadX18 ); emit AddProduct(productId); } function _exchange() internal view returns (IOffchainExchange) { return IOffchainExchange(IEndpoint(getEndpoint()).getOffchainExchange()); } function updatePrice(uint32 productId, int128 priceX18) external onlyEndpoint { _risk().value[productId].priceX18 = priceX18; } function updateRisk(uint32 productId, RiskHelper.RiskStore memory riskStore) external onlyOwner { require( riskStore.longWeightInitial <= riskStore.longWeightMaintenance && riskStore.shortWeightInitial >= riskStore.shortWeightMaintenance, ERR_BAD_PRODUCT_CONFIG ); _risk().value[productId] = riskStore; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./interfaces/engine/ISpotEngine.sol"; import "./libraries/Logger.sol"; import "./BaseEngine.sol"; abstract contract SpotEngineState is ISpotEngine, BaseEngine { using MathSD21x18 for int128; mapping(uint32 => Config) internal configs; mapping(uint32 => State) internal states; mapping(uint32 => mapping(bytes32 => Balances)) internal balances; mapping(uint32 => LpState) internal lpStates; mapping(uint32 => int128) internal withdrawFees; uint64 public migrationFlag; // deprecated mapping(uint32 => int128) internal minDepositRatesX18; function _updateBalanceWithoutDelta( State memory state, Balance memory balance ) internal pure { if (balance.amount == 0) { balance.lastCumulativeMultiplierX18 = state .cumulativeDepositsMultiplierX18; return; } // Current cumulative multiplier associated with product int128 cumulativeMultiplierX18; if (balance.amount > 0) { cumulativeMultiplierX18 = state.cumulativeDepositsMultiplierX18; } else { cumulativeMultiplierX18 = state.cumulativeBorrowsMultiplierX18; } if (balance.lastCumulativeMultiplierX18 == cumulativeMultiplierX18) { return; } balance.amount = balance.amount.mul(cumulativeMultiplierX18).div( balance.lastCumulativeMultiplierX18 ); balance.lastCumulativeMultiplierX18 = cumulativeMultiplierX18; } function _updateBalance( State memory state, Balance memory balance, int128 balanceDelta ) internal pure { if (balance.amount == 0 && balance.lastCumulativeMultiplierX18 == 0) { balance.lastCumulativeMultiplierX18 = ONE; } if (balance.amount > 0) { state.totalDepositsNormalized -= balance.amount.div( balance.lastCumulativeMultiplierX18 ); } else { state.totalBorrowsNormalized += balance.amount.div( balance.lastCumulativeMultiplierX18 ); } // Current cumulative multiplier associated with product int128 cumulativeMultiplierX18; if (balance.amount > 0) { cumulativeMultiplierX18 = state.cumulativeDepositsMultiplierX18; } else { cumulativeMultiplierX18 = state.cumulativeBorrowsMultiplierX18; } // Apply balance delta and interest rate balance.amount = balance.amount.mul( cumulativeMultiplierX18.div(balance.lastCumulativeMultiplierX18) ) + balanceDelta; if (balance.amount > 0) { cumulativeMultiplierX18 = state.cumulativeDepositsMultiplierX18; } else { cumulativeMultiplierX18 = state.cumulativeBorrowsMultiplierX18; } balance.lastCumulativeMultiplierX18 = cumulativeMultiplierX18; // Update the product given balanceDelta if (balance.amount > 0) { state.totalDepositsNormalized += balance.amount.div( balance.lastCumulativeMultiplierX18 ); } else { state.totalBorrowsNormalized -= balance.amount.div( balance.lastCumulativeMultiplierX18 ); } } function _updateBalanceNormalizedNoTotals( State memory state, BalanceNormalized memory balance, int128 balanceDelta ) internal pure { // dont count X balances in total deposits / borrows // Current cumulative multiplier associated with product int128 cumulativeMultiplierX18; if (balance.amountNormalized > 0) { cumulativeMultiplierX18 = state.cumulativeDepositsMultiplierX18; } else { cumulativeMultiplierX18 = state.cumulativeBorrowsMultiplierX18; } int128 newAmount = balance.amountNormalized.mul( cumulativeMultiplierX18 ) + balanceDelta; if (newAmount > 0) { cumulativeMultiplierX18 = state.cumulativeDepositsMultiplierX18; } else { cumulativeMultiplierX18 = state.cumulativeBorrowsMultiplierX18; } balance.amountNormalized = newAmount.div(cumulativeMultiplierX18); } function _updateBalanceNormalized( State memory state, BalanceNormalized memory balance, int128 balanceDelta ) internal pure { if (balance.amountNormalized > 0) { state.totalDepositsNormalized -= balance.amountNormalized; } else { state.totalBorrowsNormalized += balance.amountNormalized; } _updateBalanceNormalizedNoTotals(state, balance, balanceDelta); // Update the product given balanceDelta if (balance.amountNormalized > 0) { state.totalDepositsNormalized += balance.amountNormalized; } else { state.totalBorrowsNormalized -= balance.amountNormalized; } } function _updateState( uint32 productId, State memory state, uint128 dt ) internal { int128 borrowRateMultiplierX18; int128 totalDeposits = state.totalDepositsNormalized.mul( state.cumulativeDepositsMultiplierX18 ); int128 totalBorrows = state.totalBorrowsNormalized.mul( state.cumulativeBorrowsMultiplierX18 ); int128 utilizationRatioX18 = totalBorrows.div(totalDeposits); { Config memory config = configs[productId]; // annualized borrower rate int128 borrowerRateX18 = config.interestFloorX18; if (utilizationRatioX18 == 0) { // setting borrowerRateX18 to 0 here has the property that // adding a product at the beginning of time and not using it until time T // results in the same state as adding the product at time T borrowerRateX18 = 0; } else if (utilizationRatioX18 < config.interestInflectionUtilX18) { borrowerRateX18 += config .interestSmallCapX18 .mul(utilizationRatioX18) .div(config.interestInflectionUtilX18); } else { borrowerRateX18 += config.interestSmallCapX18 + config.interestLargeCapX18.mul( ( (utilizationRatioX18 - config.interestInflectionUtilX18).div( ONE - config.interestInflectionUtilX18 ) ) ); } // convert to per second borrowerRateX18 = borrowerRateX18.div( MathSD21x18.fromInt(31536000) ); borrowRateMultiplierX18 = (ONE + borrowerRateX18).pow(int128(dt)); } // if we don't take fees into account, the liquidity, which is // (deposits - borrows) should remain the same after updating state. // For simplicity, we use `tb`, `cbm`, `td`, and `cdm` for // `totalBorrowsNormalized`, `cumulativeBorrowsMultiplier`, // `totalDepositsNormalized`, and `cumulativeDepositsMultiplier` // before the updating, the liquidity is (td * cdm - tb * cbm) // after the updating, the liquidity is // (td * cdm * depositRateMultiplier - tb * cbm * borrowRateMultiplier) // so we can get // depositRateMultiplier = utilization * (borrowRateMultiplier - 1) + 1 int128 totalDepositRateX18 = utilizationRatioX18.mul( borrowRateMultiplierX18 - ONE ); // deduct protocol fees int128 realizedDepositRateX18 = totalDepositRateX18.mul( ONE - INTEREST_FEE_FRACTION ); // pass fees balance change int128 feesAmt = totalDeposits.mul( totalDepositRateX18 - realizedDepositRateX18 ); state.cumulativeBorrowsMultiplierX18 = state .cumulativeBorrowsMultiplierX18 .mul(borrowRateMultiplierX18); int128 depositRateMultiplierX18 = ONE + realizedDepositRateX18; state.cumulativeDepositsMultiplierX18 = state .cumulativeDepositsMultiplierX18 .mul(depositRateMultiplierX18); if (feesAmt != 0) { BalanceNormalized memory feesAccBalance = balances[productId][ FEES_ACCOUNT ].balance; _updateBalanceNormalized(state, feesAccBalance, feesAmt); balances[productId][FEES_ACCOUNT].balance = feesAccBalance; _balanceUpdate(productId, FEES_ACCOUNT); } // apply the min deposit rate if (minDepositRatesX18[productId] != 0) { int128 minDepositRatePerSecondX18 = minDepositRatesX18[productId] .div(MathSD21x18.fromInt(31536000)); int128 minDepositRateMultiplierX18 = (ONE + minDepositRatePerSecondX18).pow(int128(dt)); state.cumulativeBorrowsMultiplierX18 = state .cumulativeBorrowsMultiplierX18 .mul(minDepositRateMultiplierX18); state.cumulativeDepositsMultiplierX18 = state .cumulativeDepositsMultiplierX18 .mul(minDepositRateMultiplierX18); depositRateMultiplierX18 = depositRateMultiplierX18.mul( minDepositRateMultiplierX18 ); borrowRateMultiplierX18 = borrowRateMultiplierX18.mul( minDepositRateMultiplierX18 ); } emit InterestPayment( productId, dt, depositRateMultiplierX18, borrowRateMultiplierX18, feesAmt ); } function balanceNormalizedToBalance( State memory state, BalanceNormalized memory balance ) internal pure returns (Balance memory) { int128 cumulativeMultiplierX18; if (balance.amountNormalized > 0) { cumulativeMultiplierX18 = state.cumulativeDepositsMultiplierX18; } else { cumulativeMultiplierX18 = state.cumulativeBorrowsMultiplierX18; } return Balance( balance.amountNormalized.mul(cumulativeMultiplierX18), cumulativeMultiplierX18 ); } function _balanceUpdate(uint32 productId, bytes32 subaccount) internal virtual override { Balance memory balance = getBalance(productId, subaccount); emit SpotBalance( subaccount, productId, balance.amount, balance.lastCumulativeMultiplierX18 ); } // TODO: maybe combine the next two functions // probably also need some protection where quote state must // be fetched through getQuoteState function getStateAndBalance(uint32 productId, bytes32 subaccount) public view returns (State memory, Balance memory) { State memory state = states[productId]; BalanceNormalized memory balance = balances[productId][subaccount] .balance; return (state, balanceNormalizedToBalance(state, balance)); } function getBalance(uint32 productId, bytes32 subaccount) public view returns (Balance memory) { State memory state = states[productId]; BalanceNormalized memory balance = balances[productId][subaccount] .balance; return balanceNormalizedToBalance(state, balance); } function _getBalance(uint32 productId, bytes32 subaccount) internal view override returns (int128, int128) { return (getBalance(productId, subaccount).amount, 0); } function _getInLpBalance(uint32 productId, bytes32 subaccount) internal view virtual override returns ( // baseAmount, quoteAmount, deltaQuoteAmount (funding) int128, int128, int128 ) { LpBalance memory lpBalance = balances[productId][subaccount].lpBalance; if (lpBalance.amount == 0) { return (0, 0, 0); } LpState memory lpState = lpStates[productId]; int128 ratio = lpBalance.amount.div(lpState.supply); int128 baseAmount = lpState.base.amount.mul(ratio); int128 quoteAmount = lpState.quote.amount.mul(ratio); return (baseAmount, quoteAmount, 0); } function getStatesAndBalances(uint32 productId, bytes32 subaccount) external view returns ( LpState memory, LpBalance memory, State memory, Balance memory ) { LpState memory lpState = lpStates[productId]; State memory state = states[productId]; Balances memory bal = balances[productId][subaccount]; LpBalance memory lpBalance = bal.lpBalance; BalanceNormalized memory balance = bal.balance; return ( lpState, lpBalance, state, balanceNormalizedToBalance(state, balance) ); } function updateStates(uint128 dt) external onlyEndpoint { State memory quoteState; require(dt < 7 * SECONDS_PER_DAY, ERR_INVALID_TIME); for (uint32 i = 0; i < productIds.length; i++) { uint32 productId = productIds[i]; State memory state = states[productId]; if (productId == QUOTE_PRODUCT_ID) { quoteState = state; } if (state.totalDepositsNormalized == 0) { continue; } LpState memory lpState = lpStates[productId]; _updateState(productId, state, dt); _updateBalanceWithoutDelta(state, lpState.base); _updateBalanceWithoutDelta(quoteState, lpState.quote); lpStates[productId] = lpState; states[productId] = state; _productUpdate(productId); } } function updateMinDepositRate(uint32 productId, int128 minDepositRateX18) external onlyEndpoint { // deposit rate can't be larger than 100% so that when the rate is incorrectly // set, we still can rescue it without having too much damage. require( minDepositRateX18 >= 0 && minDepositRateX18 <= ONE, ERR_BAD_PRODUCT_CONFIG ); minDepositRatesX18[productId] = minDepositRateX18; } function getMinDepositRate(uint32 productId) external view returns (int128) { return minDepositRatesX18[productId]; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./SpotEngineState.sol"; import "./libraries/Logger.sol"; abstract contract SpotEngineLP is SpotEngineState { using MathSD21x18 for int128; function mintLp( uint32 productId, bytes32 subaccount, int128 amountBase, int128 quoteAmountLow, int128 quoteAmountHigh ) external { _assertInternal(); require( amountBase > 0 && quoteAmountLow > 0 && quoteAmountHigh > 0, ERR_INVALID_LP_AMOUNT ); require( _exchange().getMarketInfo(productId).quoteId == QUOTE_PRODUCT_ID, ERR_INVALID_PRODUCT ); LpState memory lpState = lpStates[productId]; State memory base = states[productId]; State memory quote = states[QUOTE_PRODUCT_ID]; int128 amountQuote = (lpState.base.amount == 0) ? amountBase.mul(_risk(productId).priceX18) : amountBase.mul(lpState.quote.amount.div(lpState.base.amount)); require(amountQuote >= quoteAmountLow, ERR_SLIPPAGE_TOO_HIGH); require(amountQuote <= quoteAmountHigh, ERR_SLIPPAGE_TOO_HIGH); int128 toMint; if (lpState.supply == 0) { toMint = amountBase + amountQuote; } else { toMint = amountBase.div(lpState.base.amount).mul(lpState.supply); } _updateBalance(base, lpState.base, amountBase); _updateBalance(quote, lpState.quote, amountQuote); lpState.supply += toMint; balances[productId][subaccount].lpBalance.amount += toMint; lpStates[productId] = lpState; BalanceNormalized memory baseBalance = balances[productId][subaccount] .balance; BalanceNormalized memory quoteBalance = balances[QUOTE_PRODUCT_ID][ subaccount ].balance; _updateBalanceNormalized(base, baseBalance, -amountBase); _updateBalanceNormalized(quote, quoteBalance, -amountQuote); balances[productId][subaccount].balance = baseBalance; balances[QUOTE_PRODUCT_ID][subaccount].balance = quoteBalance; states[productId] = base; states[QUOTE_PRODUCT_ID] = quote; _balanceUpdate(productId, subaccount); _balanceUpdate(QUOTE_PRODUCT_ID, subaccount); } function burnLp( uint32 productId, bytes32 subaccount, int128 amountLp ) public returns (int128 amountBase, int128 amountQuote) { _assertInternal(); require(amountLp > 0, ERR_INVALID_LP_AMOUNT); LpState memory lpState = lpStates[productId]; LpBalance memory lpBalance = balances[productId][subaccount].lpBalance; State memory base = states[productId]; State memory quote = states[QUOTE_PRODUCT_ID]; if (amountLp == type(int128).max) { amountLp = lpBalance.amount; } if (amountLp == 0) { return (0, 0); } require(lpBalance.amount >= amountLp, ERR_INSUFFICIENT_LP); lpBalance.amount -= amountLp; amountBase = int128( (int256(amountLp) * lpState.base.amount) / lpState.supply ); amountQuote = int128( (int256(amountLp) * lpState.quote.amount) / lpState.supply ); _updateBalance(base, lpState.base, -amountBase); _updateBalance(quote, lpState.quote, -amountQuote); lpState.supply -= amountLp; lpStates[productId] = lpState; balances[productId][subaccount].lpBalance = lpBalance; BalanceNormalized memory baseBalance = balances[productId][subaccount] .balance; BalanceNormalized memory quoteBalance = balances[QUOTE_PRODUCT_ID][ subaccount ].balance; _updateBalanceNormalized(base, baseBalance, amountBase); _updateBalanceNormalized(quote, quoteBalance, amountQuote); balances[productId][subaccount].balance = baseBalance; balances[QUOTE_PRODUCT_ID][subaccount].balance = quoteBalance; states[productId] = base; states[QUOTE_PRODUCT_ID] = quote; _balanceUpdate(productId, subaccount); _balanceUpdate(QUOTE_PRODUCT_ID, subaccount); } function swapLp( uint32 productId, int128 baseDelta, int128 quoteDelta ) external returns (int128, int128) { _assertInternal(); LpState memory lpState = lpStates[productId]; require( MathHelper.isSwapValid( baseDelta, quoteDelta, lpState.base.amount, lpState.quote.amount ), ERR_INVALID_MAKER ); int128 baseDepositsMultiplierX18 = states[productId] .cumulativeDepositsMultiplierX18; int128 quoteDepositsMultiplierX18 = states[QUOTE_PRODUCT_ID] .cumulativeDepositsMultiplierX18; lpState.base.amount += baseDelta; lpState.quote.amount += quoteDelta; lpStates[productId] = lpState; states[productId].totalDepositsNormalized += baseDelta.div( baseDepositsMultiplierX18 ); states[QUOTE_PRODUCT_ID].totalDepositsNormalized += quoteDelta.div( quoteDepositsMultiplierX18 ); _productUpdate(productId); return (baseDelta, quoteDelta); } function decomposeLps(bytes32 liquidatee, bytes32 liquidator) external returns (int128 liquidationFees) { uint32[] memory _productIds = getProductIds(); for (uint128 i = 0; i < _productIds.length; ++i) { uint32 productId = _productIds[i]; (, int128 amountQuote) = burnLp( productId, liquidatee, type(int128).max ); if (amountQuote != 0) { int128 rewards = amountQuote.mul( (ONE - RiskHelper._getWeightX18( _risk(productId), amountQuote, IProductEngine.HealthType.MAINTENANCE )) / 50 ); int128 fees = rewards.mul(LIQUIDATION_FEE_FRACTION); rewards -= fees; liquidationFees += fees; State memory quote = states[QUOTE_PRODUCT_ID]; BalanceNormalized memory liquidateeQuote = balances[ QUOTE_PRODUCT_ID ][liquidatee].balance; BalanceNormalized memory liquidatorQuote = balances[ QUOTE_PRODUCT_ID ][liquidator].balance; _updateBalanceNormalized( quote, liquidateeQuote, -rewards - fees ); _updateBalanceNormalized(quote, liquidatorQuote, rewards); balances[QUOTE_PRODUCT_ID][liquidatee] .balance = liquidateeQuote; balances[QUOTE_PRODUCT_ID][liquidator] .balance = liquidatorQuote; states[QUOTE_PRODUCT_ID] = quote; _balanceUpdate(QUOTE_PRODUCT_ID, liquidator); _balanceUpdate(QUOTE_PRODUCT_ID, liquidatee); } } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IEndpoint.sol"; import "./interfaces/IEndpointGated.sol"; import "./libraries/MathSD21x18.sol"; import "./common/Constants.sol"; import "hardhat/console.sol"; abstract contract EndpointGated is OwnableUpgradeable, IEndpointGated { address private endpoint; function setEndpoint(address _endpoint) internal onlyOwner { endpoint = _endpoint; } function getEndpoint() public view returns (address) { return endpoint; } function getOracleTime() internal view returns (uint128) { return IEndpoint(endpoint).getTime(); } modifier onlyEndpoint() { require( msg.sender == endpoint, "SequencerGated: caller is not the endpoint" ); _; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @dev Each clearinghouse has a unique quote product uint32 constant QUOTE_PRODUCT_ID = 0; /// @dev Fees account bytes32 constant FEES_ACCOUNT = bytes32(0); bytes32 constant X_ACCOUNT = 0x0000000000000000000000000000000000000000000000000000000000000001; string constant DEFAULT_REFERRAL_CODE = "-1"; uint128 constant MINIMUM_LIQUIDITY = 10**3; int128 constant ONE = 10**18; uint8 constant MAX_DECIMALS = 18; int128 constant TAKER_SEQUENCER_FEE = 0; // $0.00 int128 constant SLOW_MODE_FEE = 1000000; // $1 int128 constant FAST_WITHDRAWAL_FEE_RATE = 1_000_000_000_000_000; // 0.1% int128 constant LIQUIDATION_FEE = 1e18; // $1 int128 constant HEALTHCHECK_FEE = 1e18; // $1 uint128 constant INT128_MAX = uint128(type(int128).max); uint64 constant SECONDS_PER_DAY = 3600 * 24; uint32 constant VRTX_PRODUCT_ID = 41; int128 constant LIQUIDATION_FEE_FRACTION = 500_000_000_000_000_000; // 50% int128 constant INTEREST_FEE_FRACTION = 200_000_000_000_000_000; // 20% int256 constant MIN_DEPOSIT_AMOUNT = 5 * ONE; uint32 constant MAX_ISOLATED_SUBACCOUNTS_PER_ADDRESS = 10;
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./PerpEngineState.sol"; import "./libraries/Logger.sol"; abstract contract PerpEngineLp is PerpEngineState { using MathSD21x18 for int128; function mintLp( uint32 productId, bytes32 subaccount, int128 amountBase, int128 quoteAmountLow, int128 quoteAmountHigh ) external { _assertInternal(); int128 sizeIncrement = _exchange().getSizeIncrement(productId); require( amountBase > 0 && quoteAmountLow > 0 && quoteAmountHigh > 0 && amountBase % sizeIncrement == 0, ERR_INVALID_LP_AMOUNT ); ( LpState memory lpState, LpBalance memory lpBalance, State memory state, Balance memory balance ) = getStatesAndBalances(productId, subaccount); int128 amountQuote = (lpState.base == 0) ? amountBase.mul(_risk(productId).priceX18) : amountBase.mul(lpState.quote.div(lpState.base)); require(amountQuote >= quoteAmountLow, ERR_SLIPPAGE_TOO_HIGH); require(amountQuote <= quoteAmountHigh, ERR_SLIPPAGE_TOO_HIGH); int128 toMint; if (lpState.supply == 0) { toMint = amountBase + amountQuote; } else { toMint = amountBase.div(lpState.base).mul(lpState.supply); } state.openInterest += amountBase; lpState.base += amountBase; lpState.quote += amountQuote; lpBalance.amount += toMint; _updateBalance(state, balance, -amountBase, -amountQuote); lpState.supply += toMint; lpBalances[productId][subaccount] = lpBalance; states[productId] = state; lpStates[productId] = lpState; balances[productId][subaccount] = balance; _balanceUpdate(productId, subaccount); } function burnLp( uint32 productId, bytes32 subaccount, int128 amountLp ) public returns (int128 amountBase, int128 amountQuote) { _assertInternal(); require(amountLp > 0, ERR_INVALID_LP_AMOUNT); int128 sizeIncrement = _exchange().getSizeIncrement(productId); ( LpState memory lpState, LpBalance memory lpBalance, State memory state, Balance memory balance ) = getStatesAndBalances(productId, subaccount); if (amountLp == type(int128).max) { amountLp = lpBalance.amount; } if (amountLp == 0) { return (0, 0); } require(lpBalance.amount >= amountLp, ERR_INSUFFICIENT_LP); lpBalance.amount -= amountLp; amountBase = MathHelper.floor( int128((int256(amountLp) * lpState.base) / lpState.supply), sizeIncrement ); amountQuote = int128( (int256(amountLp) * lpState.quote) / lpState.supply ); state.openInterest -= amountBase; _updateBalance(state, balance, amountBase, amountQuote); lpState.base -= amountBase; lpState.quote -= amountQuote; lpState.supply -= amountLp; lpStates[productId] = lpState; lpBalances[productId][subaccount] = lpBalance; states[productId] = state; balances[productId][subaccount] = balance; _balanceUpdate(productId, subaccount); } function swapLp( uint32 productId, int128 baseDelta, int128 quoteDelta ) external returns (int128, int128) { _assertInternal(); LpState memory lpState = lpStates[productId]; require( MathHelper.isSwapValid( baseDelta, quoteDelta, lpState.base, lpState.quote ), ERR_INVALID_MAKER ); states[productId].openInterest += baseDelta; lpState.base += baseDelta; lpState.quote += quoteDelta; lpStates[productId] = lpState; _productUpdate(productId); return (baseDelta, quoteDelta); } function decomposeLps(bytes32 liquidatee, bytes32 liquidator) external returns (int128 liquidationFees) { uint32[] memory _productIds = getProductIds(); for (uint128 i = 0; i < _productIds.length; ++i) { uint32 productId = _productIds[i]; (, int128 amountQuote) = burnLp( productId, liquidatee, type(int128).max ); if (amountQuote != 0) { int128 rewards = amountQuote.mul( (ONE - RiskHelper._getWeightX18( _risk(productId), amountQuote, IProductEngine.HealthType.MAINTENANCE )) / 50 ); int128 fees = rewards.mul(LIQUIDATION_FEE_FRACTION); rewards -= fees; liquidationFees += fees; // transfer some of the burned proceeds to liquidator State memory state = states[productId]; Balance memory liquidateeBalance = balances[productId][ liquidatee ]; Balance memory liquidatorBalance = balances[productId][ liquidator ]; _updateBalance(state, liquidateeBalance, 0, -rewards - fees); _updateBalance(state, liquidatorBalance, 0, rewards); states[productId] = state; balances[productId][liquidatee] = liquidateeBalance; balances[productId][liquidator] = liquidatorBalance; _balanceUpdate(productId, liquidator); _balanceUpdate(productId, liquidatee); } } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./IClearinghouseEventEmitter.sol"; import "../engine/IProductEngine.sol"; import "../IEndpoint.sol"; import "../IEndpointGated.sol"; import "../../libraries/RiskHelper.sol"; interface IClearinghouse is IClearinghouseEventEmitter, IEndpointGated { function addEngine( address engine, address offchainExchange, IProductEngine.EngineType engineType ) external; function registerProduct(uint32 productId) external; function transferQuote(IEndpoint.TransferQuote calldata tx) external; function depositCollateral(IEndpoint.DepositCollateral calldata tx) external; function withdrawCollateral( bytes32 sender, uint32 productId, uint128 amount, address sendTo, uint64 idx ) external; function mintLp(IEndpoint.MintLp calldata tx) external; function burnLp(IEndpoint.BurnLp calldata tx) external; function liquidateSubaccount(IEndpoint.LiquidateSubaccount calldata tx) external; function depositInsurance(bytes calldata transaction) external; function withdrawInsurance(bytes calldata transaction, uint64 idx) external; function settlePnl(bytes calldata transaction) external; function claimSequencerFees( IEndpoint.ClaimSequencerFees calldata tx, int128[] calldata fees ) external; /// @notice Retrieve quote ERC20 address function getQuote() external view returns (address); /// @notice Returns the registered engine address by type function getEngineByType(IProductEngine.EngineType engineType) external view returns (address); /// @notice Returns the engine associated with a product ID function getEngineByProduct(uint32 productId) external view returns (address); /// @notice Returns health for the subaccount across all engines function getHealth(bytes32 subaccount, IProductEngine.HealthType healthType) external view returns (int128); /// @notice Returns the amount of insurance remaining in this clearinghouse function getInsurance() external view returns (int128); function getSpreads() external view returns (uint256); function upgradeClearinghouseLiq(address _clearinghouseLiq) external; function getClearinghouseLiq() external view returns (address); function burnLpAndTransfer(IEndpoint.BurnLpAndTransfer calldata txn) external; function requireMinDeposit(uint32 productId, uint128 amount) external; function assertCode(bytes calldata tx) external; function manualAssert(bytes calldata tx) external; function getWithdrawPool() external view returns (address); function getSlowModeFee() external view returns (uint256); function getWithdrawFee(uint32 productId) external view returns (int128); function setWithdrawPool(address _withdrawPool) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "prb-math/contracts/PRBMathSD59x18.sol"; library MathSD21x18 { using PRBMathSD59x18 for int256; int128 private constant ONE_X18 = 1000000000000000000; int128 private constant MIN_X18 = -0x80000000000000000000000000000000; int128 private constant MAX_X18 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; string private constant ERR_OVERFLOW = "OF"; string private constant ERR_DIV_BY_ZERO = "DBZ"; function fromInt(int128 x) internal pure returns (int128) { unchecked { int256 result = int256(x) * ONE_X18; require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW); return int128(result); } } function mulDiv( int128 x, int128 y, int128 z ) internal pure returns (int128) { unchecked { require(z != 0, ERR_DIV_BY_ZERO); int256 result = (int256(x) * y) / z; require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW); return int128(result); } } function toInt(int128 x) internal pure returns (int128) { unchecked { return int128(x / ONE_X18); } } function add(int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) + y; require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW); return int128(result); } } function sub(int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) - y; require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW); return int128(result); } } function mul(int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = (int256(x) * y) / ONE_X18; require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW); return int128(result); } } function div(int128 x, int128 y) internal pure returns (int128) { unchecked { require(y != 0, ERR_DIV_BY_ZERO); int256 result = (int256(x) * ONE_X18) / y; require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW); return int128(result); } } function abs(int128 x) internal pure returns (int128) { unchecked { require(x != MIN_X18, ERR_OVERFLOW); return x < 0 ? -x : x; } } function sqrt(int128 x) internal pure returns (int128) { unchecked { int256 result = int256(x).sqrt(); require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW); return int128(result); } } // note that y is not X18 function pow(int128 x, int128 y) internal pure returns (int128) { unchecked { require(y >= 0, ERR_OVERFLOW); int128 result = ONE_X18; for (int128 i = 1; i <= y; i *= 2) { if (i & y != 0) { result = mul(result, x); } x = mul(x, x); } return result; } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; // Trying to take an action on vertex when string constant ERR_REQUIRES_DEPOSIT = "RS"; // ERC20 Transfer failed string constant ERR_TRANSFER_FAILED = "TF"; // Unauthorized string constant ERR_UNAUTHORIZED = "U"; // Invalid product string constant ERR_INVALID_PRODUCT = "IP"; // Subaccount health too low string constant ERR_SUBACCT_HEALTH = "SH"; // Not liquidatable string constant ERR_NOT_LIQUIDATABLE = "NL"; // Liquidator health too low string constant ERR_NOT_LIQUIDATABLE_INITIAL = "NLI"; // Liquidatee has positive initial health string constant ERR_LIQUIDATED_TOO_MUCH = "LTM"; // Trying to liquidate quote, or string constant ERR_INVALID_LIQUIDATION_PARAMS = "NILP"; // Trying to liquidate perp but the amount is not divisible by sizeIncrement string constant ERR_INVALID_LIQUIDATION_AMOUNT = "NILA"; // Tried to liquidate too little, too much or signs are different string constant ERR_NOT_LIQUIDATABLE_AMT = "NLA"; // Tried to liquidate liabilities before perps string constant ERR_NOT_LIQUIDATABLE_LIABILITIES = "NLL"; // Tried to finalize subaccount that cannot be finalized string constant ERR_NOT_FINALIZABLE_SUBACCOUNT = "NFS"; // Not enough quote to settle string constant ERR_CANNOT_SETTLE = "CS"; // Not enough insurance to settle string constant ERR_NO_INSURANCE = "NI"; // Above reserve ratio string constant ERR_RESERVE_RATIO = "RR"; // Invalid socialize amount string constant ERR_INVALID_SOCIALIZE_AMT = "ISA"; // Socializing product with no open interest string constant ERR_NO_OPEN_INTEREST = "NOI"; // FOK not filled, this isn't rly an error so this is jank string constant ERR_FOK_NOT_FILLED = "ENF"; // bad product config via weights string constant ERR_BAD_PRODUCT_CONFIG = "BPC"; // subacct name too long string constant ERR_LONG_NAME = "LN"; // already registered in health group string constant ERR_ALREADY_REGISTERED = "AR"; // invalid health group provided string constant ERR_INVALID_HEALTH_GROUP = "IHG"; string constant ERR_GETTING_ZERO_HEALTH_GROUP = "GZHG"; // trying to burn more LP than owned string constant ERR_INSUFFICIENT_LP = "ILP"; // taker order subaccount fails risk or is invalid string constant ERR_INVALID_TAKER = "IT"; // maker order subaccount fails risk or is invalid string constant ERR_INVALID_MAKER = "IM"; string constant ERR_INVALID_SIGNATURE = "IS"; string constant ERR_ORDERS_CANNOT_BE_MATCHED = "OCBM"; string constant ERR_INVALID_LP_AMOUNT = "ILA"; string constant ERR_SLIPPAGE_TOO_HIGH = "STH"; string constant ERR_SUBACCOUNT_NOT_FOUND = "SNF"; string constant ERR_INVALID_PRICE = "IPR"; string constant ERR_INVALID_TIME = "ITI"; // states on node and engine are not same string constant ERR_DSYNC = "DSYNC"; string constant ERR_INVALID_SWAP_PARAMS = "ISP"; string constant ERR_CONVERSION_OVERFLOW = "CO"; string constant ERR_ONLY_CLEARINGHOUSE_CAN_SET_BOOK = "OCCSB"; // we match on containing these strings in sequencer string constant ERR_INVALID_SUBMISSION_INDEX = "IX"; string constant ERR_NO_SLOW_MODE_TXS_REMAINING = "no slow mode transactions remaining"; string constant ERR_INVALID_COUNT = "IC"; string constant ERR_SLOW_TX_TOO_RECENT = "STTR"; string constant ERR_WALLET_NOT_TRANSFERABLE = "WNT"; string constant ERR_WALLET_SANCTIONED = "WS"; string constant ERR_SLOW_MODE_WRONG_SENDER = "SMWS"; string constant ERR_WRONG_NONCE = "WN"; // initially wanted to call this // ERR_FULL_UTILIZATION but the shortened // error string may make people mad on the frontend string constant ERR_MAX_UTILIZATION = "MU"; string constant ERR_INVALID_RISK_GROUP = "IRG"; string constant ERR_VERIFY_SCHNORR = "VSR"; string constant ERR_DEPOSIT_TOO_SMALL = "DTS"; string constant ERR_CODE_NOT_MATCH = "CNM";
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./MathSD21x18.sol"; /// @title MathHelper /// @dev Provides basic math functions library MathHelper { using MathSD21x18 for int128; /// @notice Returns market id for two given product ids function max(int128 a, int128 b) internal pure returns (int128) { return a > b ? a : b; } function min(int128 a, int128 b) internal pure returns (int128) { return a < b ? a : b; } function abs(int128 val) internal pure returns (int128) { return val < 0 ? -val : val; } // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) function sqrt(int128 y) internal pure returns (int128 z) { require(y >= 0, "ds-math-sqrt-non-positive"); if (y > 3) { z = y; int128 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } function sqrt256(int256 y) internal pure returns (int256 z) { require(y >= 0, "ds-math-sqrt-non-positive"); if (y > 3) { z = y; int256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } function int2str(int128 value) internal pure returns (string memory) { if (value == 0) { return "0"; } bool negative = value < 0; uint128 absval = uint128(negative ? -value : value); string memory out = uint2str(absval); if (negative) { out = string.concat("-", out); } return out; } function uint2str(uint128 value) internal pure returns (string memory) { if (value == 0) { return "0"; } uint128 temp = value; uint128 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint128(value % 10))); value /= 10; } return string(buffer); } // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/math/SignedSafeMath.sol#L86 function add(int128 x, int128 y) internal pure returns (int128) { int128 z = x + y; require((y >= 0 && z >= x) || (y < 0 && z < x), "ds-math-add-overflow"); return z; } // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/math/SignedSafeMath.sol#L69 function sub(int128 x, int128 y) internal pure returns (int128) { int128 z = x - y; require( (y >= 0 && z <= x) || (y < 0 && z > x), "ds-math-sub-underflow" ); return z; } function mul(int128 x, int128 y) internal pure returns (int128 z) { require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow"); } function floor(int128 x, int128 y) internal pure returns (int128 z) { require(y > 0, "ds-math-floor-neg-mod"); int128 r = x % y; if (r == 0) { z = x; } else { z = (x >= 0 ? x - r : x - r - y); } } function ceil(int128 x, int128 y) internal pure returns (int128 z) { require(y > 0, "ds-math-ceil-neg-mod"); int128 r = x % y; if (r == 0) { z = x; } else { z = (x >= 0 ? x + y - r : x - r); } } // we don't need to floor base with sizeIncrement in this function // because this function is only used by `view` functions, which means // the returned values will not be written into storage. function ammEquilibrium( int128 base, int128 quote, int128 priceX18 ) internal pure returns (int128, int128) { if (base == 0 || quote == 0) { return (0, 0); } int256 k = int256(base) * quote; // base * price * base == k // base = sqrt(k / price); base = int128(MathHelper.sqrt256((k * 1e18) / priceX18)); quote = (base == 0) ? int128(0) : int128(k / base); return (base, quote); } function isSwapValid( int128 baseDelta, int128 quoteDelta, int128 base, int128 quote ) internal pure returns (bool) { if ( base == 0 || quote == 0 || base + baseDelta <= 0 || quote + quoteDelta <= 0 ) { return false; } int256 kPrev = int256(base) * quote; int256 kNew = int256(base + baseDelta) * (quote + quoteDelta); return kNew > kPrev; } function swap( int128 amountSwap, int128 base, int128 quote, int128 priceX18, int128 sizeIncrement, int128 lpSpreadX18 ) internal pure returns (int128, int128) { // (amountSwap % sizeIncrement) is guaranteed to be 0 if (base == 0 || quote == 0) { return (0, 0); } int128 currentPriceX18 = quote.div(base); int128 keepRateX18 = 1e18 - lpSpreadX18; // selling if (amountSwap > 0) { priceX18 = priceX18.div(keepRateX18); if (priceX18 >= currentPriceX18) { return (0, 0); } } else { priceX18 = priceX18.mul(keepRateX18); if (priceX18 <= currentPriceX18) { return (0, 0); } } int256 k = int256(base) * quote; int128 baseAtPrice = int128( (MathHelper.sqrt256(k) * 1e9) / MathHelper.sqrt(priceX18) ); // base -> base + amountSwap int128 baseSwapped; if ( (amountSwap > 0 && base + amountSwap > baseAtPrice) || (amountSwap < 0 && base + amountSwap < baseAtPrice) ) { // we hit price limits before we exhaust amountSwap if (baseAtPrice >= base) { baseSwapped = MathHelper.floor( baseAtPrice - base, sizeIncrement ); } else { baseSwapped = MathHelper.ceil( baseAtPrice - base, sizeIncrement ); } } else { // just swap it all // amountSwap is already guaranteed to adhere to sizeIncrement baseSwapped = amountSwap; } int128 quoteSwapped = int128(k / (base + baseSwapped) - quote); if (amountSwap > 0) { quoteSwapped = quoteSwapped.mul(keepRateX18); } else { quoteSwapped = quoteSwapped.div(keepRateX18); } return (baseSwapped, quoteSwapped); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; interface IVerifier { function requireValidSignature( bytes32 message, bytes32 e, bytes32 s, uint8 signerBitmask ) external; function revertGasInfo(uint256 i, uint256 gasUsed) external pure; function validateSignature( bytes32 sender, address linkedSigner, bytes32 digest, bytes memory signature ) external pure; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./clearinghouse/IClearinghouse.sol"; interface IOffchainExchange { event FillOrder( uint32 indexed productId, // original order information bytes32 indexed digest, bytes32 indexed subaccount, int128 priceX18, int128 amount, uint64 expiration, uint64 nonce, // whether this order is taking or making bool isTaker, // amount paid in fees (in quote) int128 feeAmount, // change in this subaccount's base balance from this fill int128 baseDelta, // change in this subaccount's quote balance from this fill int128 quoteDelta ); event CloseIsolatedSubaccount( bytes32 indexed isolatedSubaccount, bytes32 indexed parentSubaccount ); struct FeeRates { int64 makerRateX18; int64 takerRateX18; uint8 isNonDefault; // 1: non-default, 0: default } struct LpParams { int128 lpSpreadX18; } struct MarketInfoStore { int64 minSize; int64 sizeIncrement; int128 collectedFees; } struct MarketInfo { uint32 quoteId; int128 minSize; int128 sizeIncrement; int128 collectedFees; } function initialize(address _clearinghouse, address _endpoint) external; function updateFeeRates( address user, uint32 productId, int64 makerRateX18, int64 takerRateX18 ) external; function updateMarket( uint32 productId, uint32 quoteId, address virtualBook, int128 sizeIncrement, int128 minSize, int128 lpSpreadX18 ) external; function getMinSize(uint32 productId) external view returns (int128); function getDigest(uint32 productId, IEndpoint.Order memory order) external view returns (bytes32); function getSizeIncrement(uint32 productId) external view returns (int128); function getMarketInfo(uint32 productId) external view returns (MarketInfo memory); function getLpParams(uint32 productId) external view returns (LpParams memory); function swapAMM(IEndpoint.SwapAMM calldata tx) external; function matchOrderAMM( IEndpoint.MatchOrderAMM calldata tx, address takerLinkedSigner ) external; function matchOrders(IEndpoint.MatchOrdersWithSigner calldata tx) external; function dumpFees() external; function createIsolatedSubaccount( IEndpoint.CreateIsolatedSubaccount memory tx, address linkedSigner ) external returns (bytes32); function isIsolatedSubaccountActive(bytes32 parent, bytes32 subaccount) external view returns (bool); function getParentSubaccount(bytes32 subaccount) external view returns (bytes32); function tryCloseIsolatedSubaccount(bytes32 subaccount) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "../interfaces/IERC20Base.sol"; import "../common/Errors.sol"; import "hardhat/console.sol"; // @dev Adapted from https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/TransferHelper.sol library ERC20Helper { function safeTransfer( IERC20Base self, address to, uint256 amount ) internal { (bool success, bytes memory data) = address(self).call( abi.encodeWithSelector(IERC20Base.transfer.selector, to, amount) ); require( success && (data.length == 0 || abi.decode(data, (bool))), ERR_TRANSFER_FAILED ); } function safeTransferFrom( IERC20Base self, address from, address to, uint256 amount ) internal { (bool success, bytes memory data) = address(self).call( abi.encodeWithSelector( IERC20Base.transferFrom.selector, from, to, amount ) ); require( success && (data.length == 0 || abi.decode(data, (bool))), ERR_TRANSFER_FAILED ); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; import "@openzeppelin/contracts/utils/Strings.sol"; import "./MathHelper.sol"; library Logger { event VertexEVMLog(string message); function log(string memory message) internal { emit VertexEVMLog(message); } function log(int128 value) internal { log(MathHelper.int2str(value)); } function log(string memory message, int128 value) internal { log(string.concat(message, " ", MathHelper.int2str(value))); } function log(string memory message, uint128 value) internal { log(string.concat(message, " ", MathHelper.uint2str(value))); } // function log(string memory message, uint32 value) internal { // log(message, uint128(value)); // } function log(string memory message, address value) internal { log( string.concat(message, " ", Strings.toHexString(uint160(value), 20)) ); } function log(string memory messages, bytes32 value) internal { log(string.concat(messages, " ", string(abi.encodePacked(value)))); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; interface IERC20Base { function decimals() external view returns (uint8); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); function increaseAllowance(address spender, uint256 addedValue) external returns (bool); function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); function balanceOf(address account) external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.0; import "./ECDSAUpgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding * they need in their contracts using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * _Available since v3.4._ * * @custom:storage-size 52 */ abstract contract EIP712Upgradeable is Initializable { /* solhint-disable var-name-mixedcase */ bytes32 private _HASHED_NAME; bytes32 private _HASHED_VERSION; bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); /* solhint-enable var-name-mixedcase */ /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ function __EIP712_init(string memory name, string memory version) internal onlyInitializing { __EIP712_init_unchained(name, version); } function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing { bytes32 hashedName = keccak256(bytes(name)); bytes32 hashedVersion = keccak256(bytes(version)); _HASHED_NAME = hashedName; _HASHED_VERSION = hashedVersion; } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash()); } function _buildDomainSeparator( bytes32 typeHash, bytes32 nameHash, bytes32 versionHash ) private view returns (bytes32) { return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash); } /** * @dev The hash of the name parameter for the EIP712 domain. * * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs * are a concern. */ function _EIP712NameHash() internal virtual view returns (bytes32) { return _HASHED_NAME; } /** * @dev The hash of the version parameter for the EIP712 domain. * * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs * are a concern. */ function _EIP712VersionHash() internal virtual view returns (bytes32) { return _HASHED_VERSION; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; library console { address constant CONSOLE_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67; function _sendLogPayloadImplementation(bytes memory payload) internal view { address consoleAddress = CONSOLE_ADDRESS; /// @solidity memory-safe-assembly assembly { pop( staticcall( gas(), consoleAddress, add(payload, 32), mload(payload), 0, 0 ) ) } } function _castToPure( function(bytes memory) internal view fnIn ) internal pure returns (function(bytes memory) pure fnOut) { assembly { fnOut := fnIn } } function _sendLogPayload(bytes memory payload) internal pure { _castToPure(_sendLogPayloadImplementation)(payload); } function log() internal pure { _sendLogPayload(abi.encodeWithSignature("log()")); } function logInt(int256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); } function logUint(uint256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } function logString(string memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function logBool(bool p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function logAddress(address p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function logBytes(bytes memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); } function logBytes1(bytes1 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); } function logBytes2(bytes2 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); } function logBytes3(bytes3 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); } function logBytes4(bytes4 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); } function logBytes5(bytes5 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); } function logBytes6(bytes6 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); } function logBytes7(bytes7 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); } function logBytes8(bytes8 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); } function logBytes9(bytes9 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); } function logBytes10(bytes10 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); } function logBytes11(bytes11 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); } function logBytes12(bytes12 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); } function logBytes13(bytes13 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); } function logBytes14(bytes14 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); } function logBytes15(bytes15 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); } function logBytes16(bytes16 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); } function logBytes17(bytes17 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); } function logBytes18(bytes18 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); } function logBytes19(bytes19 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); } function logBytes20(bytes20 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); } function logBytes21(bytes21 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); } function logBytes22(bytes22 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); } function logBytes23(bytes23 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); } function logBytes24(bytes24 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); } function logBytes25(bytes25 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); } function logBytes26(bytes26 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); } function logBytes27(bytes27 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); } function logBytes28(bytes28 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); } function logBytes29(bytes29 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); } function logBytes30(bytes30 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); } function logBytes31(bytes31 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); } function logBytes32(bytes32 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); } function log(uint256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } function log(string memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function log(bool p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function log(address p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function log(uint256 p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); } function log(uint256 p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); } function log(uint256 p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); } function log(uint256 p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); } function log(string memory p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); } function log(string memory p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); } function log(string memory p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); } function log(string memory p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); } function log(bool p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); } function log(bool p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); } function log(bool p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); } function log(bool p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); } function log(address p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); } function log(address p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); } function log(address p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); } function log(address p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } function log(uint256 p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); } function log(uint256 p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); } function log(uint256 p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); } function log(uint256 p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); } function log(uint256 p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); } function log(uint256 p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); } function log(uint256 p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); } function log(uint256 p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); } function log(uint256 p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); } function log(uint256 p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); } function log(uint256 p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); } function log(uint256 p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); } function log(uint256 p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); } function log(string memory p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); } function log(string memory p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); } function log(string memory p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); } function log(string memory p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); } function log(string memory p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); } function log(string memory p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); } function log(string memory p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); } function log(string memory p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); } function log(string memory p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); } function log(string memory p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); } function log(string memory p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); } function log(string memory p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); } function log(string memory p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); } function log(string memory p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); } function log(string memory p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); } function log(string memory p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); } function log(bool p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); } function log(bool p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); } function log(bool p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); } function log(bool p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); } function log(bool p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); } function log(bool p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); } function log(bool p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); } function log(bool p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); } function log(bool p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); } function log(bool p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); } function log(bool p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); } function log(bool p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); } function log(bool p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); } function log(bool p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); } function log(bool p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); } function log(bool p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); } function log(address p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); } function log(address p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); } function log(address p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); } function log(address p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); } function log(address p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); } function log(address p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); } function log(address p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); } function log(address p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); } function log(address p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); } function log(address p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); } function log(address p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); } function log(address p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); } function log(address p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); } function log(address p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); } function log(address p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); } function log(address p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.13; import "./IEndpoint.sol"; interface IEndpointGated { function getEndpoint() external view returns (address endpoint); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/draft-EIP712.sol) pragma solidity ^0.8.0; // EIP-712 is Final as of 2022-08-11. This file is deprecated. import "./EIP712Upgradeable.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "./interfaces/engine/IPerpEngine.sol"; import "./BaseEngine.sol"; int128 constant EMA_TIME_CONSTANT_X18 = 998334721450938752; int128 constant ONE_DAY_X18 = 86400_000000000000000000; // 24 hours // we will want to config this later, but for now this is global and a percentage int128 constant MAX_DAILY_FUNDING_RATE = 20000000000000000; // 0.02 abstract contract PerpEngineState is IPerpEngine, BaseEngine { using MathSD21x18 for int128; mapping(uint32 => State) public states; mapping(uint32 => mapping(bytes32 => Balance)) public balances; mapping(uint32 => LpState) public lpStates; mapping(uint32 => mapping(bytes32 => LpBalance)) public lpBalances; function _updateBalance( State memory state, Balance memory balance, int128 balanceDelta, int128 vQuoteDelta ) internal pure { // pre update state.openInterest -= (balance.amount > 0) ? balance.amount : int128(0); int128 cumulativeFundingAmountX18 = (balance.amount > 0) ? state.cumulativeFundingLongX18 : state.cumulativeFundingShortX18; int128 diffX18 = cumulativeFundingAmountX18 - balance.lastCumulativeFundingX18; int128 deltaQuote = vQuoteDelta - diffX18.mul(balance.amount); // apply delta balance.amount += balanceDelta; // apply vquote balance.vQuoteBalance += deltaQuote; // post update if (balance.amount > 0) { state.openInterest += balance.amount; balance.lastCumulativeFundingX18 = state.cumulativeFundingLongX18; } else { balance.lastCumulativeFundingX18 = state.cumulativeFundingShortX18; } } function _applyLpBalanceFunding( LpState memory lpState, LpBalance memory lpBalance, Balance memory balance ) internal pure { int128 vQuoteDelta = (lpState.cumulativeFundingPerLpX18 - lpBalance.lastCumulativeFundingX18).mul(lpBalance.amount); balance.vQuoteBalance += vQuoteDelta; lpBalance.lastCumulativeFundingX18 = lpState.cumulativeFundingPerLpX18; } function getStateAndBalance(uint32 productId, bytes32 subaccount) public view returns (State memory, Balance memory) { State memory state = states[productId]; Balance memory balance = balances[productId][subaccount]; _updateBalance(state, balance, 0, 0); return (state, balance); } function getBalance(uint32 productId, bytes32 subaccount) public view returns (Balance memory) { State memory state = states[productId]; Balance memory balance = balances[productId][subaccount]; _updateBalance(state, balance, 0, 0); return balance; } function _getBalance(uint32 productId, bytes32 subaccount) internal view virtual override returns (int128, int128) { State memory state = states[productId]; Balance memory balance = balances[productId][subaccount]; _updateBalance(state, balance, 0, 0); return (balance.amount, balance.vQuoteBalance); } function _getInLpBalance(uint32 productId, bytes32 subaccount) internal view virtual override returns ( // baseAmount, quoteAmount, deltaQuoteAmount (funding) int128, int128, int128 ) { LpBalance memory lpBalance = lpBalances[productId][subaccount]; if (lpBalance.amount == 0) { return (0, 0, 0); } LpState memory lpState = lpStates[productId]; int128 ratio = lpBalance.amount.div(lpState.supply); int128 baseAmount = lpState.base.mul(ratio); int128 quoteAmount = lpState.quote.mul(ratio); int128 quoteDeltaAmount = lpState .cumulativeFundingPerLpX18 .sub(lpBalance.lastCumulativeFundingX18) .mul(lpBalance.amount); return (baseAmount, quoteAmount, quoteDeltaAmount); } function getStatesAndBalances(uint32 productId, bytes32 subaccount) public view returns ( LpState memory, LpBalance memory, State memory, Balance memory ) { LpState memory lpState = lpStates[productId]; State memory state = states[productId]; LpBalance memory lpBalance = lpBalances[productId][subaccount]; Balance memory balance = balances[productId][subaccount]; _updateBalance(state, balance, 0, 0); _applyLpBalanceFunding(lpState, lpBalance, balance); return (lpState, lpBalance, state, balance); } function updateStates(uint128 dt, int128[] calldata avgPriceDiffs) external onlyEndpoint { int128 dtX18 = int128(dt).fromInt(); for (uint32 i = 0; i < avgPriceDiffs.length; i++) { uint32 productId = productIds[i]; State memory state = states[productId]; if (state.openInterest == 0) { continue; } require(dt < 7 * SECONDS_PER_DAY, ERR_INVALID_TIME); LpState memory lpState = lpStates[productId]; { int128 indexPriceX18 = _risk(productId).priceX18; // cap this price diff int128 priceDiffX18 = avgPriceDiffs[i]; int128 maxPriceDiff = MAX_DAILY_FUNDING_RATE.mul(indexPriceX18); if (priceDiffX18.abs() > maxPriceDiff) { // Proper sign priceDiffX18 = (priceDiffX18 > 0) ? maxPriceDiff : -maxPriceDiff; } int128 paymentAmount = priceDiffX18.mul(dtX18).div(ONE_DAY_X18); state.cumulativeFundingLongX18 += paymentAmount; state.cumulativeFundingShortX18 += paymentAmount; emit FundingPayment( productId, dt, state.openInterest, paymentAmount ); } { Balance memory balance = Balance({ amount: lpState.base, vQuoteBalance: 0, lastCumulativeFundingX18: lpState.lastCumulativeFundingX18 }); _updateBalance(state, balance, 0, 0); if (lpState.supply != 0) { lpState.cumulativeFundingPerLpX18 += balance .vQuoteBalance .div(lpState.supply); } lpState.lastCumulativeFundingX18 = state .cumulativeFundingLongX18; } lpStates[productId] = lpState; states[productId] = state; _productUpdate(productId); } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; interface IClearinghouseEventEmitter { /// @notice Emitted during initialization event ClearinghouseInitialized(address endpoint, address quote); /// @notice Emitted when collateral is modified for a subaccount event ModifyCollateral( int128 amount, bytes32 indexed subaccount, uint32 productId ); event Liquidation( bytes32 indexed liquidatorSubaccount, bytes32 indexed liquidateeSubaccount, uint32 productId, bool isEncodedSpread, int128 amount, int128 amountQuote ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: Unlicense pragma solidity >=0.8.4; import "./PRBMath.sol"; /// @title PRBMathSD59x18 /// @author Paul Razvan Berg /// @notice Smart contract library for advanced fixed-point math that works with int256 numbers considered to have 18 /// trailing decimals. We call this number representation signed 59.18-decimal fixed-point, since the numbers can have /// a sign and there can be up to 59 digits in the integer part and up to 18 decimals in the fractional part. The numbers /// are bound by the minimum and the maximum values permitted by the Solidity type int256. library PRBMathSD59x18 { /// @dev log2(e) as a signed 59.18-decimal fixed-point number. int256 internal constant LOG2_E = 1_442695040888963407; /// @dev Half the SCALE number. int256 internal constant HALF_SCALE = 5e17; /// @dev The maximum value a signed 59.18-decimal fixed-point number can have. int256 internal constant MAX_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_792003956564819967; /// @dev The maximum whole value a signed 59.18-decimal fixed-point number can have. int256 internal constant MAX_WHOLE_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_000000000000000000; /// @dev The minimum value a signed 59.18-decimal fixed-point number can have. int256 internal constant MIN_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_792003956564819968; /// @dev The minimum whole value a signed 59.18-decimal fixed-point number can have. int256 internal constant MIN_WHOLE_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_000000000000000000; /// @dev How many trailing decimals can be represented. int256 internal constant SCALE = 1e18; /// INTERNAL FUNCTIONS /// /// @notice Calculate the absolute value of x. /// /// @dev Requirements: /// - x must be greater than MIN_SD59x18. /// /// @param x The number to calculate the absolute value for. /// @param result The absolute value of x. function abs(int256 x) internal pure returns (int256 result) { unchecked { if (x == MIN_SD59x18) { revert PRBMathSD59x18__AbsInputTooSmall(); } result = x < 0 ? -x : x; } } /// @notice Calculates the arithmetic average of x and y, rounding down. /// @param x The first operand as a signed 59.18-decimal fixed-point number. /// @param y The second operand as a signed 59.18-decimal fixed-point number. /// @return result The arithmetic average as a signed 59.18-decimal fixed-point number. function avg(int256 x, int256 y) internal pure returns (int256 result) { // The operations can never overflow. unchecked { int256 sum = (x >> 1) + (y >> 1); if (sum < 0) { // If at least one of x and y is odd, we add 1 to the result. This is because shifting negative numbers to the // right rounds down to infinity. assembly { result := add(sum, and(or(x, y), 1)) } } else { // If both x and y are odd, we add 1 to the result. This is because if both numbers are odd, the 0.5 // remainder gets truncated twice. result = sum + (x & y & 1); } } } /// @notice Yields the least greatest signed 59.18 decimal fixed-point number greater than or equal to x. /// /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts. /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. /// /// Requirements: /// - x must be less than or equal to MAX_WHOLE_SD59x18. /// /// @param x The signed 59.18-decimal fixed-point number to ceil. /// @param result The least integer greater than or equal to x, as a signed 58.18-decimal fixed-point number. function ceil(int256 x) internal pure returns (int256 result) { if (x > MAX_WHOLE_SD59x18) { revert PRBMathSD59x18__CeilOverflow(x); } unchecked { int256 remainder = x % SCALE; if (remainder == 0) { result = x; } else { // Solidity uses C fmod style, which returns a modulus with the same sign as x. result = x - remainder; if (x > 0) { result += SCALE; } } } } /// @notice Divides two signed 59.18-decimal fixed-point numbers, returning a new signed 59.18-decimal fixed-point number. /// /// @dev Variant of "mulDiv" that works with signed numbers. Works by computing the signs and the absolute values separately. /// /// Requirements: /// - All from "PRBMath.mulDiv". /// - None of the inputs can be MIN_SD59x18. /// - The denominator cannot be zero. /// - The result must fit within int256. /// /// Caveats: /// - All from "PRBMath.mulDiv". /// /// @param x The numerator as a signed 59.18-decimal fixed-point number. /// @param y The denominator as a signed 59.18-decimal fixed-point number. /// @param result The quotient as a signed 59.18-decimal fixed-point number. function div(int256 x, int256 y) internal pure returns (int256 result) { if (x == MIN_SD59x18 || y == MIN_SD59x18) { revert PRBMathSD59x18__DivInputTooSmall(); } // Get hold of the absolute values of x and y. uint256 ax; uint256 ay; unchecked { ax = x < 0 ? uint256(-x) : uint256(x); ay = y < 0 ? uint256(-y) : uint256(y); } // Compute the absolute value of (x*SCALE)÷y. The result must fit within int256. uint256 rAbs = PRBMath.mulDiv(ax, uint256(SCALE), ay); if (rAbs > uint256(MAX_SD59x18)) { revert PRBMathSD59x18__DivOverflow(rAbs); } // Get the signs of x and y. uint256 sx; uint256 sy; assembly { sx := sgt(x, sub(0, 1)) sy := sgt(y, sub(0, 1)) } // XOR over sx and sy. This is basically checking whether the inputs have the same sign. If yes, the result // should be positive. Otherwise, it should be negative. result = sx ^ sy == 1 ? -int256(rAbs) : int256(rAbs); } /// @notice Returns Euler's number as a signed 59.18-decimal fixed-point number. /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant). function e() internal pure returns (int256 result) { result = 2_718281828459045235; } /// @notice Calculates the natural exponent of x. /// /// @dev Based on the insight that e^x = 2^(x * log2(e)). /// /// Requirements: /// - All from "log2". /// - x must be less than 133.084258667509499441. /// /// Caveats: /// - All from "exp2". /// - For any x less than -41.446531673892822322, the result is zero. /// /// @param x The exponent as a signed 59.18-decimal fixed-point number. /// @return result The result as a signed 59.18-decimal fixed-point number. function exp(int256 x) internal pure returns (int256 result) { // Without this check, the value passed to "exp2" would be less than -59.794705707972522261. if (x < -41_446531673892822322) { return 0; } // Without this check, the value passed to "exp2" would be greater than 192. if (x >= 133_084258667509499441) { revert PRBMathSD59x18__ExpInputTooBig(x); } // Do the fixed-point multiplication inline to save gas. unchecked { int256 doubleScaleProduct = x * LOG2_E; result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE); } } /// @notice Calculates the binary exponent of x using the binary fraction method. /// /// @dev See https://ethereum.stackexchange.com/q/79903/24693. /// /// Requirements: /// - x must be 192 or less. /// - The result must fit within MAX_SD59x18. /// /// Caveats: /// - For any x less than -59.794705707972522261, the result is zero. /// /// @param x The exponent as a signed 59.18-decimal fixed-point number. /// @return result The result as a signed 59.18-decimal fixed-point number. function exp2(int256 x) internal pure returns (int256 result) { // This works because 2^(-x) = 1/2^x. if (x < 0) { // 2^59.794705707972522262 is the maximum number whose inverse does not truncate down to zero. if (x < -59_794705707972522261) { return 0; } // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE. unchecked { result = 1e36 / exp2(-x); } } else { // 2^192 doesn't fit within the 192.64-bit format used internally in this function. if (x >= 192e18) { revert PRBMathSD59x18__Exp2InputTooBig(x); } unchecked { // Convert x to the 192.64-bit fixed-point format. uint256 x192x64 = (uint256(x) << 64) / uint256(SCALE); // Safe to convert the result to int256 directly because the maximum input allowed is 192. result = int256(PRBMath.exp2(x192x64)); } } } /// @notice Yields the greatest signed 59.18 decimal fixed-point number less than or equal to x. /// /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts. /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. /// /// Requirements: /// - x must be greater than or equal to MIN_WHOLE_SD59x18. /// /// @param x The signed 59.18-decimal fixed-point number to floor. /// @param result The greatest integer less than or equal to x, as a signed 58.18-decimal fixed-point number. function floor(int256 x) internal pure returns (int256 result) { if (x < MIN_WHOLE_SD59x18) { revert PRBMathSD59x18__FloorUnderflow(x); } unchecked { int256 remainder = x % SCALE; if (remainder == 0) { result = x; } else { // Solidity uses C fmod style, which returns a modulus with the same sign as x. result = x - remainder; if (x < 0) { result -= SCALE; } } } } /// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right /// of the radix point for negative numbers. /// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part /// @param x The signed 59.18-decimal fixed-point number to get the fractional part of. /// @param result The fractional part of x as a signed 59.18-decimal fixed-point number. function frac(int256 x) internal pure returns (int256 result) { unchecked { result = x % SCALE; } } /// @notice Converts a number from basic integer form to signed 59.18-decimal fixed-point representation. /// /// @dev Requirements: /// - x must be greater than or equal to MIN_SD59x18 divided by SCALE. /// - x must be less than or equal to MAX_SD59x18 divided by SCALE. /// /// @param x The basic integer to convert. /// @param result The same number in signed 59.18-decimal fixed-point representation. function fromInt(int256 x) internal pure returns (int256 result) { unchecked { if (x < MIN_SD59x18 / SCALE) { revert PRBMathSD59x18__FromIntUnderflow(x); } if (x > MAX_SD59x18 / SCALE) { revert PRBMathSD59x18__FromIntOverflow(x); } result = x * SCALE; } } /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down. /// /// @dev Requirements: /// - x * y must fit within MAX_SD59x18, lest it overflows. /// - x * y cannot be negative. /// /// @param x The first operand as a signed 59.18-decimal fixed-point number. /// @param y The second operand as a signed 59.18-decimal fixed-point number. /// @return result The result as a signed 59.18-decimal fixed-point number. function gm(int256 x, int256 y) internal pure returns (int256 result) { if (x == 0) { return 0; } unchecked { // Checking for overflow this way is faster than letting Solidity do it. int256 xy = x * y; if (xy / x != y) { revert PRBMathSD59x18__GmOverflow(x, y); } // The product cannot be negative. if (xy < 0) { revert PRBMathSD59x18__GmNegativeProduct(x, y); } // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE // during multiplication. See the comments within the "sqrt" function. result = int256(PRBMath.sqrt(uint256(xy))); } } /// @notice Calculates 1 / x, rounding toward zero. /// /// @dev Requirements: /// - x cannot be zero. /// /// @param x The signed 59.18-decimal fixed-point number for which to calculate the inverse. /// @return result The inverse as a signed 59.18-decimal fixed-point number. function inv(int256 x) internal pure returns (int256 result) { unchecked { // 1e36 is SCALE * SCALE. result = 1e36 / x; } } /// @notice Calculates the natural logarithm of x. /// /// @dev Based on the insight that ln(x) = log2(x) / log2(e). /// /// Requirements: /// - All from "log2". /// /// Caveats: /// - All from "log2". /// - This doesn't return exactly 1 for 2718281828459045235, for that we would need more fine-grained precision. /// /// @param x The signed 59.18-decimal fixed-point number for which to calculate the natural logarithm. /// @return result The natural logarithm as a signed 59.18-decimal fixed-point number. function ln(int256 x) internal pure returns (int256 result) { // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x) // can return is 195205294292027477728. unchecked { result = (log2(x) * SCALE) / LOG2_E; } } /// @notice Calculates the common logarithm of x. /// /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common /// logarithm based on the insight that log10(x) = log2(x) / log2(10). /// /// Requirements: /// - All from "log2". /// /// Caveats: /// - All from "log2". /// /// @param x The signed 59.18-decimal fixed-point number for which to calculate the common logarithm. /// @return result The common logarithm as a signed 59.18-decimal fixed-point number. function log10(int256 x) internal pure returns (int256 result) { if (x <= 0) { revert PRBMathSD59x18__LogInputTooSmall(x); } // Note that the "mul" in this block is the assembly mul operation, not the "mul" function defined in this contract. // prettier-ignore assembly { switch x case 1 { result := mul(SCALE, sub(0, 18)) } case 10 { result := mul(SCALE, sub(1, 18)) } case 100 { result := mul(SCALE, sub(2, 18)) } case 1000 { result := mul(SCALE, sub(3, 18)) } case 10000 { result := mul(SCALE, sub(4, 18)) } case 100000 { result := mul(SCALE, sub(5, 18)) } case 1000000 { result := mul(SCALE, sub(6, 18)) } case 10000000 { result := mul(SCALE, sub(7, 18)) } case 100000000 { result := mul(SCALE, sub(8, 18)) } case 1000000000 { result := mul(SCALE, sub(9, 18)) } case 10000000000 { result := mul(SCALE, sub(10, 18)) } case 100000000000 { result := mul(SCALE, sub(11, 18)) } case 1000000000000 { result := mul(SCALE, sub(12, 18)) } case 10000000000000 { result := mul(SCALE, sub(13, 18)) } case 100000000000000 { result := mul(SCALE, sub(14, 18)) } case 1000000000000000 { result := mul(SCALE, sub(15, 18)) } case 10000000000000000 { result := mul(SCALE, sub(16, 18)) } case 100000000000000000 { result := mul(SCALE, sub(17, 18)) } case 1000000000000000000 { result := 0 } case 10000000000000000000 { result := SCALE } case 100000000000000000000 { result := mul(SCALE, 2) } case 1000000000000000000000 { result := mul(SCALE, 3) } case 10000000000000000000000 { result := mul(SCALE, 4) } case 100000000000000000000000 { result := mul(SCALE, 5) } case 1000000000000000000000000 { result := mul(SCALE, 6) } case 10000000000000000000000000 { result := mul(SCALE, 7) } case 100000000000000000000000000 { result := mul(SCALE, 8) } case 1000000000000000000000000000 { result := mul(SCALE, 9) } case 10000000000000000000000000000 { result := mul(SCALE, 10) } case 100000000000000000000000000000 { result := mul(SCALE, 11) } case 1000000000000000000000000000000 { result := mul(SCALE, 12) } case 10000000000000000000000000000000 { result := mul(SCALE, 13) } case 100000000000000000000000000000000 { result := mul(SCALE, 14) } case 1000000000000000000000000000000000 { result := mul(SCALE, 15) } case 10000000000000000000000000000000000 { result := mul(SCALE, 16) } case 100000000000000000000000000000000000 { result := mul(SCALE, 17) } case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) } case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) } case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) } case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) } case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) } case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) } case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) } case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) } case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) } case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) } case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) } case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) } case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) } case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) } case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) } case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) } case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) } case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) } case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) } case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) } case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) } case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) } case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) } case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) } case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) } case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) } case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) } case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) } case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) } case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) } case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) } case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) } case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) } case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) } case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) } case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) } case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) } case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) } case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) } case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) } case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) } default { result := MAX_SD59x18 } } if (result == MAX_SD59x18) { // Do the fixed-point division inline to save gas. The denominator is log2(10). unchecked { result = (log2(x) * SCALE) / 3_321928094887362347; } } } /// @notice Calculates the binary logarithm of x. /// /// @dev Based on the iterative approximation algorithm. /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation /// /// Requirements: /// - x must be greater than zero. /// /// Caveats: /// - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation. /// /// @param x The signed 59.18-decimal fixed-point number for which to calculate the binary logarithm. /// @return result The binary logarithm as a signed 59.18-decimal fixed-point number. function log2(int256 x) internal pure returns (int256 result) { if (x <= 0) { revert PRBMathSD59x18__LogInputTooSmall(x); } unchecked { // This works because log2(x) = -log2(1/x). int256 sign; if (x >= SCALE) { sign = 1; } else { sign = -1; // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE. assembly { x := div(1000000000000000000000000000000000000, x) } } // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n). uint256 n = PRBMath.mostSignificantBit(uint256(x / SCALE)); // The integer part of the logarithm as a signed 59.18-decimal fixed-point number. The operation can't overflow // because n is maximum 255, SCALE is 1e18 and sign is either 1 or -1. result = int256(n) * SCALE; // This is y = x * 2^(-n). int256 y = x >> n; // If y = 1, the fractional part is zero. if (y == SCALE) { return result * sign; } // Calculate the fractional part via the iterative approximation. // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster. for (int256 delta = int256(HALF_SCALE); delta > 0; delta >>= 1) { y = (y * y) / SCALE; // Is y^2 > 2 and so in the range [2,4)? if (y >= 2 * SCALE) { // Add the 2^(-m) factor to the logarithm. result += delta; // Corresponds to z/2 on Wikipedia. y >>= 1; } } result *= sign; } } /// @notice Multiplies two signed 59.18-decimal fixed-point numbers together, returning a new signed 59.18-decimal /// fixed-point number. /// /// @dev Variant of "mulDiv" that works with signed numbers and employs constant folding, i.e. the denominator is /// always 1e18. /// /// Requirements: /// - All from "PRBMath.mulDivFixedPoint". /// - None of the inputs can be MIN_SD59x18 /// - The result must fit within MAX_SD59x18. /// /// Caveats: /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works. /// /// @param x The multiplicand as a signed 59.18-decimal fixed-point number. /// @param y The multiplier as a signed 59.18-decimal fixed-point number. /// @return result The product as a signed 59.18-decimal fixed-point number. function mul(int256 x, int256 y) internal pure returns (int256 result) { if (x == MIN_SD59x18 || y == MIN_SD59x18) { revert PRBMathSD59x18__MulInputTooSmall(); } unchecked { uint256 ax; uint256 ay; ax = x < 0 ? uint256(-x) : uint256(x); ay = y < 0 ? uint256(-y) : uint256(y); uint256 rAbs = PRBMath.mulDivFixedPoint(ax, ay); if (rAbs > uint256(MAX_SD59x18)) { revert PRBMathSD59x18__MulOverflow(rAbs); } uint256 sx; uint256 sy; assembly { sx := sgt(x, sub(0, 1)) sy := sgt(y, sub(0, 1)) } result = sx ^ sy == 1 ? -int256(rAbs) : int256(rAbs); } } /// @notice Returns PI as a signed 59.18-decimal fixed-point number. function pi() internal pure returns (int256 result) { result = 3_141592653589793238; } /// @notice Raises x to the power of y. /// /// @dev Based on the insight that x^y = 2^(log2(x) * y). /// /// Requirements: /// - All from "exp2", "log2" and "mul". /// - z cannot be zero. /// /// Caveats: /// - All from "exp2", "log2" and "mul". /// - Assumes 0^0 is 1. /// /// @param x Number to raise to given power y, as a signed 59.18-decimal fixed-point number. /// @param y Exponent to raise x to, as a signed 59.18-decimal fixed-point number. /// @return result x raised to power y, as a signed 59.18-decimal fixed-point number. function pow(int256 x, int256 y) internal pure returns (int256 result) { if (x == 0) { result = y == 0 ? SCALE : int256(0); } else { result = exp2(mul(log2(x), y)); } } /// @notice Raises x (signed 59.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the /// famous algorithm "exponentiation by squaring". /// /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring /// /// Requirements: /// - All from "abs" and "PRBMath.mulDivFixedPoint". /// - The result must fit within MAX_SD59x18. /// /// Caveats: /// - All from "PRBMath.mulDivFixedPoint". /// - Assumes 0^0 is 1. /// /// @param x The base as a signed 59.18-decimal fixed-point number. /// @param y The exponent as an uint256. /// @return result The result as a signed 59.18-decimal fixed-point number. function powu(int256 x, uint256 y) internal pure returns (int256 result) { uint256 xAbs = uint256(abs(x)); // Calculate the first iteration of the loop in advance. uint256 rAbs = y & 1 > 0 ? xAbs : uint256(SCALE); // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster. uint256 yAux = y; for (yAux >>= 1; yAux > 0; yAux >>= 1) { xAbs = PRBMath.mulDivFixedPoint(xAbs, xAbs); // Equivalent to "y % 2 == 1" but faster. if (yAux & 1 > 0) { rAbs = PRBMath.mulDivFixedPoint(rAbs, xAbs); } } // The result must fit within the 59.18-decimal fixed-point representation. if (rAbs > uint256(MAX_SD59x18)) { revert PRBMathSD59x18__PowuOverflow(rAbs); } // Is the base negative and the exponent an odd number? bool isNegative = x < 0 && y & 1 == 1; result = isNegative ? -int256(rAbs) : int256(rAbs); } /// @notice Returns 1 as a signed 59.18-decimal fixed-point number. function scale() internal pure returns (int256 result) { result = SCALE; } /// @notice Calculates the square root of x, rounding down. /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. /// /// Requirements: /// - x cannot be negative. /// - x must be less than MAX_SD59x18 / SCALE. /// /// @param x The signed 59.18-decimal fixed-point number for which to calculate the square root. /// @return result The result as a signed 59.18-decimal fixed-point . function sqrt(int256 x) internal pure returns (int256 result) { unchecked { if (x < 0) { revert PRBMathSD59x18__SqrtNegativeInput(x); } if (x > MAX_SD59x18 / SCALE) { revert PRBMathSD59x18__SqrtOverflow(x); } // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two signed // 59.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root). result = int256(PRBMath.sqrt(uint256(x * SCALE))); } } /// @notice Converts a signed 59.18-decimal fixed-point number to basic integer form, rounding down in the process. /// @param x The signed 59.18-decimal fixed-point number to convert. /// @return result The same number in basic integer form. function toInt(int256 x) internal pure returns (int256 result) { unchecked { result = x / SCALE; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. 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 executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Internal function that returns the initialized version. Returns `_initialized` */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Internal function that returns the initialized version. Returns `_initializing` */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../StringsUpgradeable.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSAUpgradeable { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: Unlicense pragma solidity >=0.8.4; /// @notice Emitted when the result overflows uint256. error PRBMath__MulDivFixedPointOverflow(uint256 prod1); /// @notice Emitted when the result overflows uint256. error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator); /// @notice Emitted when one of the inputs is type(int256).min. error PRBMath__MulDivSignedInputTooSmall(); /// @notice Emitted when the intermediary absolute result overflows int256. error PRBMath__MulDivSignedOverflow(uint256 rAbs); /// @notice Emitted when the input is MIN_SD59x18. error PRBMathSD59x18__AbsInputTooSmall(); /// @notice Emitted when ceiling a number overflows SD59x18. error PRBMathSD59x18__CeilOverflow(int256 x); /// @notice Emitted when one of the inputs is MIN_SD59x18. error PRBMathSD59x18__DivInputTooSmall(); /// @notice Emitted when one of the intermediary unsigned results overflows SD59x18. error PRBMathSD59x18__DivOverflow(uint256 rAbs); /// @notice Emitted when the input is greater than 133.084258667509499441. error PRBMathSD59x18__ExpInputTooBig(int256 x); /// @notice Emitted when the input is greater than 192. error PRBMathSD59x18__Exp2InputTooBig(int256 x); /// @notice Emitted when flooring a number underflows SD59x18. error PRBMathSD59x18__FloorUnderflow(int256 x); /// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18. error PRBMathSD59x18__FromIntOverflow(int256 x); /// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18. error PRBMathSD59x18__FromIntUnderflow(int256 x); /// @notice Emitted when the product of the inputs is negative. error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y); /// @notice Emitted when multiplying the inputs overflows SD59x18. error PRBMathSD59x18__GmOverflow(int256 x, int256 y); /// @notice Emitted when the input is less than or equal to zero. error PRBMathSD59x18__LogInputTooSmall(int256 x); /// @notice Emitted when one of the inputs is MIN_SD59x18. error PRBMathSD59x18__MulInputTooSmall(); /// @notice Emitted when the intermediary absolute result overflows SD59x18. error PRBMathSD59x18__MulOverflow(uint256 rAbs); /// @notice Emitted when the intermediary absolute result overflows SD59x18. error PRBMathSD59x18__PowuOverflow(uint256 rAbs); /// @notice Emitted when the input is negative. error PRBMathSD59x18__SqrtNegativeInput(int256 x); /// @notice Emitted when the calculating the square root overflows SD59x18. error PRBMathSD59x18__SqrtOverflow(int256 x); /// @notice Emitted when addition overflows UD60x18. error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y); /// @notice Emitted when ceiling a number overflows UD60x18. error PRBMathUD60x18__CeilOverflow(uint256 x); /// @notice Emitted when the input is greater than 133.084258667509499441. error PRBMathUD60x18__ExpInputTooBig(uint256 x); /// @notice Emitted when the input is greater than 192. error PRBMathUD60x18__Exp2InputTooBig(uint256 x); /// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18. error PRBMathUD60x18__FromUintOverflow(uint256 x); /// @notice Emitted when multiplying the inputs overflows UD60x18. error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y); /// @notice Emitted when the input is less than 1. error PRBMathUD60x18__LogInputTooSmall(uint256 x); /// @notice Emitted when the calculating the square root overflows UD60x18. error PRBMathUD60x18__SqrtOverflow(uint256 x); /// @notice Emitted when subtraction underflows UD60x18. error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y); /// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library /// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point /// representation. When it does not, it is explicitly mentioned in the NatSpec documentation. library PRBMath { /// STRUCTS /// struct SD59x18 { int256 value; } struct UD60x18 { uint256 value; } /// STORAGE /// /// @dev How many trailing decimals can be represented. uint256 internal constant SCALE = 1e18; /// @dev Largest power of two divisor of SCALE. uint256 internal constant SCALE_LPOTD = 262144; /// @dev SCALE inverted mod 2^256. uint256 internal constant SCALE_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281; /// FUNCTIONS /// /// @notice Calculates the binary exponent of x using the binary fraction method. /// @dev Has to use 192.64-bit fixed-point numbers. /// See https://ethereum.stackexchange.com/a/96594/24693. /// @param x The exponent as an unsigned 192.64-bit fixed-point number. /// @return result The result as an unsigned 60.18-decimal fixed-point number. function exp2(uint256 x) internal pure returns (uint256 result) { unchecked { // Start from 0.5 in the 192.64-bit fixed-point format. result = 0x800000000000000000000000000000000000000000000000; // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows // because the initial result is 2^191 and all magic factors are less than 2^65. if (x & 0x8000000000000000 > 0) { result = (result * 0x16A09E667F3BCC909) >> 64; } if (x & 0x4000000000000000 > 0) { result = (result * 0x1306FE0A31B7152DF) >> 64; } if (x & 0x2000000000000000 > 0) { result = (result * 0x1172B83C7D517ADCE) >> 64; } if (x & 0x1000000000000000 > 0) { result = (result * 0x10B5586CF9890F62A) >> 64; } if (x & 0x800000000000000 > 0) { result = (result * 0x1059B0D31585743AE) >> 64; } if (x & 0x400000000000000 > 0) { result = (result * 0x102C9A3E778060EE7) >> 64; } if (x & 0x200000000000000 > 0) { result = (result * 0x10163DA9FB33356D8) >> 64; } if (x & 0x100000000000000 > 0) { result = (result * 0x100B1AFA5ABCBED61) >> 64; } if (x & 0x80000000000000 > 0) { result = (result * 0x10058C86DA1C09EA2) >> 64; } if (x & 0x40000000000000 > 0) { result = (result * 0x1002C605E2E8CEC50) >> 64; } if (x & 0x20000000000000 > 0) { result = (result * 0x100162F3904051FA1) >> 64; } if (x & 0x10000000000000 > 0) { result = (result * 0x1000B175EFFDC76BA) >> 64; } if (x & 0x8000000000000 > 0) { result = (result * 0x100058BA01FB9F96D) >> 64; } if (x & 0x4000000000000 > 0) { result = (result * 0x10002C5CC37DA9492) >> 64; } if (x & 0x2000000000000 > 0) { result = (result * 0x1000162E525EE0547) >> 64; } if (x & 0x1000000000000 > 0) { result = (result * 0x10000B17255775C04) >> 64; } if (x & 0x800000000000 > 0) { result = (result * 0x1000058B91B5BC9AE) >> 64; } if (x & 0x400000000000 > 0) { result = (result * 0x100002C5C89D5EC6D) >> 64; } if (x & 0x200000000000 > 0) { result = (result * 0x10000162E43F4F831) >> 64; } if (x & 0x100000000000 > 0) { result = (result * 0x100000B1721BCFC9A) >> 64; } if (x & 0x80000000000 > 0) { result = (result * 0x10000058B90CF1E6E) >> 64; } if (x & 0x40000000000 > 0) { result = (result * 0x1000002C5C863B73F) >> 64; } if (x & 0x20000000000 > 0) { result = (result * 0x100000162E430E5A2) >> 64; } if (x & 0x10000000000 > 0) { result = (result * 0x1000000B172183551) >> 64; } if (x & 0x8000000000 > 0) { result = (result * 0x100000058B90C0B49) >> 64; } if (x & 0x4000000000 > 0) { result = (result * 0x10000002C5C8601CC) >> 64; } if (x & 0x2000000000 > 0) { result = (result * 0x1000000162E42FFF0) >> 64; } if (x & 0x1000000000 > 0) { result = (result * 0x10000000B17217FBB) >> 64; } if (x & 0x800000000 > 0) { result = (result * 0x1000000058B90BFCE) >> 64; } if (x & 0x400000000 > 0) { result = (result * 0x100000002C5C85FE3) >> 64; } if (x & 0x200000000 > 0) { result = (result * 0x10000000162E42FF1) >> 64; } if (x & 0x100000000 > 0) { result = (result * 0x100000000B17217F8) >> 64; } if (x & 0x80000000 > 0) { result = (result * 0x10000000058B90BFC) >> 64; } if (x & 0x40000000 > 0) { result = (result * 0x1000000002C5C85FE) >> 64; } if (x & 0x20000000 > 0) { result = (result * 0x100000000162E42FF) >> 64; } if (x & 0x10000000 > 0) { result = (result * 0x1000000000B17217F) >> 64; } if (x & 0x8000000 > 0) { result = (result * 0x100000000058B90C0) >> 64; } if (x & 0x4000000 > 0) { result = (result * 0x10000000002C5C860) >> 64; } if (x & 0x2000000 > 0) { result = (result * 0x1000000000162E430) >> 64; } if (x & 0x1000000 > 0) { result = (result * 0x10000000000B17218) >> 64; } if (x & 0x800000 > 0) { result = (result * 0x1000000000058B90C) >> 64; } if (x & 0x400000 > 0) { result = (result * 0x100000000002C5C86) >> 64; } if (x & 0x200000 > 0) { result = (result * 0x10000000000162E43) >> 64; } if (x & 0x100000 > 0) { result = (result * 0x100000000000B1721) >> 64; } if (x & 0x80000 > 0) { result = (result * 0x10000000000058B91) >> 64; } if (x & 0x40000 > 0) { result = (result * 0x1000000000002C5C8) >> 64; } if (x & 0x20000 > 0) { result = (result * 0x100000000000162E4) >> 64; } if (x & 0x10000 > 0) { result = (result * 0x1000000000000B172) >> 64; } if (x & 0x8000 > 0) { result = (result * 0x100000000000058B9) >> 64; } if (x & 0x4000 > 0) { result = (result * 0x10000000000002C5D) >> 64; } if (x & 0x2000 > 0) { result = (result * 0x1000000000000162E) >> 64; } if (x & 0x1000 > 0) { result = (result * 0x10000000000000B17) >> 64; } if (x & 0x800 > 0) { result = (result * 0x1000000000000058C) >> 64; } if (x & 0x400 > 0) { result = (result * 0x100000000000002C6) >> 64; } if (x & 0x200 > 0) { result = (result * 0x10000000000000163) >> 64; } if (x & 0x100 > 0) { result = (result * 0x100000000000000B1) >> 64; } if (x & 0x80 > 0) { result = (result * 0x10000000000000059) >> 64; } if (x & 0x40 > 0) { result = (result * 0x1000000000000002C) >> 64; } if (x & 0x20 > 0) { result = (result * 0x10000000000000016) >> 64; } if (x & 0x10 > 0) { result = (result * 0x1000000000000000B) >> 64; } if (x & 0x8 > 0) { result = (result * 0x10000000000000006) >> 64; } if (x & 0x4 > 0) { result = (result * 0x10000000000000003) >> 64; } if (x & 0x2 > 0) { result = (result * 0x10000000000000001) >> 64; } if (x & 0x1 > 0) { result = (result * 0x10000000000000001) >> 64; } // We're doing two things at the same time: // // 1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for // the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191 // rather than 192. // 2. Convert the result to the unsigned 60.18-decimal fixed-point format. // // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n". result *= SCALE; result >>= (191 - (x >> 64)); } } /// @notice Finds the zero-based index of the first one in the binary representation of x. /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set /// @param x The uint256 number for which to find the index of the most significant bit. /// @return msb The index of the most significant bit as an uint256. function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) { if (x >= 2**128) { x >>= 128; msb += 128; } if (x >= 2**64) { x >>= 64; msb += 64; } if (x >= 2**32) { x >>= 32; msb += 32; } if (x >= 2**16) { x >>= 16; msb += 16; } if (x >= 2**8) { x >>= 8; msb += 8; } if (x >= 2**4) { x >>= 4; msb += 4; } if (x >= 2**2) { x >>= 2; msb += 2; } if (x >= 2**1) { // No need to shift x any more. msb += 1; } } /// @notice Calculates floor(x*y÷denominator) with full precision. /// /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv. /// /// Requirements: /// - The denominator cannot be zero. /// - The result must fit within uint256. /// /// Caveats: /// - This function does not work with fixed-point numbers. /// /// @param x The multiplicand as an uint256. /// @param y The multiplier as an uint256. /// @param denominator The divisor as an uint256. /// @return result The result as an uint256. function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { unchecked { result = prod0 / denominator; } return result; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (prod1 >= denominator) { revert PRBMath__MulDivOverflow(prod1, denominator); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. unchecked { // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 lpotdod = denominator & (~denominator + 1); assembly { // Divide denominator by lpotdod. denominator := div(denominator, lpotdod) // Divide [prod1 prod0] by lpotdod. prod0 := div(prod0, lpotdod) // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one. lpotdod := add(div(sub(0, lpotdod), lpotdod), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * lpotdod; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /// @notice Calculates floor(x*y÷1e18) with full precision. /// /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of /// being rounded to 1e-18. See "Listing 6" and text above it at https://accu.org/index.php/journals/1717. /// /// Requirements: /// - The result must fit within uint256. /// /// Caveats: /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works. /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations: /// 1. x * y = type(uint256).max * SCALE /// 2. (x * y) % SCALE >= SCALE / 2 /// /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number. /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number. /// @return result The result as an unsigned 60.18-decimal fixed-point number. function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) { uint256 prod0; uint256 prod1; assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } if (prod1 >= SCALE) { revert PRBMath__MulDivFixedPointOverflow(prod1); } uint256 remainder; uint256 roundUpUnit; assembly { remainder := mulmod(x, y, SCALE) roundUpUnit := gt(remainder, 499999999999999999) } if (prod1 == 0) { unchecked { result = (prod0 / SCALE) + roundUpUnit; return result; } } assembly { result := add( mul( or( div(sub(prod0, remainder), SCALE_LPOTD), mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1)) ), SCALE_INVERSE ), roundUpUnit ) } } /// @notice Calculates floor(x*y÷denominator) with full precision. /// /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately. /// /// Requirements: /// - None of the inputs can be type(int256).min. /// - The result must fit within int256. /// /// @param x The multiplicand as an int256. /// @param y The multiplier as an int256. /// @param denominator The divisor as an int256. /// @return result The result as an int256. function mulDivSigned( int256 x, int256 y, int256 denominator ) internal pure returns (int256 result) { if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) { revert PRBMath__MulDivSignedInputTooSmall(); } // Get hold of the absolute values of x, y and the denominator. uint256 ax; uint256 ay; uint256 ad; unchecked { ax = x < 0 ? uint256(-x) : uint256(x); ay = y < 0 ? uint256(-y) : uint256(y); ad = denominator < 0 ? uint256(-denominator) : uint256(denominator); } // Compute the absolute value of (x*y)÷denominator. The result must fit within int256. uint256 rAbs = mulDiv(ax, ay, ad); if (rAbs > uint256(type(int256).max)) { revert PRBMath__MulDivSignedOverflow(rAbs); } // Get the signs of x, y and the denominator. uint256 sx; uint256 sy; uint256 sd; assembly { sx := sgt(x, sub(0, 1)) sy := sgt(y, sub(0, 1)) sd := sgt(denominator, sub(0, 1)) } // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs. // If yes, the result should be negative. result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs); } /// @notice Calculates the square root of x, rounding down. /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. /// /// Caveats: /// - This function does not work with fixed-point numbers. /// /// @param x The uint256 number for which to calculate the square root. /// @return result The result as an uint256. function sqrt(uint256 x) internal pure returns (uint256 result) { if (x == 0) { return 0; } // Set the initial guess to the least power of two that is greater than or equal to sqrt(x). uint256 xAux = uint256(x); result = 1; if (xAux >= 0x100000000000000000000000000000000) { xAux >>= 128; result <<= 64; } if (xAux >= 0x10000000000000000) { xAux >>= 64; result <<= 32; } if (xAux >= 0x100000000) { xAux >>= 32; result <<= 16; } if (xAux >= 0x10000) { xAux >>= 16; result <<= 8; } if (xAux >= 0x100) { xAux >>= 8; result <<= 4; } if (xAux >= 0x10) { xAux >>= 4; result <<= 2; } if (xAux >= 0x8) { result <<= 1; } // The operations can never overflow because the result is max 2^127 when it enters this block. unchecked { result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; // Seven iterations should be enough uint256 roundedDownResult = x / result; return result >= roundedDownResult ? roundedDownResult : result; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/MathUpgradeable.sol"; /** * @dev String operations. */ library StringsUpgradeable { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = MathUpgradeable.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, MathUpgradeable.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library MathUpgradeable { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
{ "optimizer": { "enabled": true, "mode": "3", "fallback_to_optimizing_for_size": true }, "outputSelection": { "*": { "*": [ "abi" ] } }, "detectMissingLibraries": false, "forceEVMLA": false, "enableEraVMExtensions": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"engine","type":"address"},{"internalType":"address","name":"offchainExchange","type":"address"},{"internalType":"enum IProductEngine.EngineType","name":"engineType","type":"uint8"}],"name":"addEngine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"spotIds","type":"uint32[]"},{"internalType":"uint32[]","name":"perpIds","type":"uint32[]"}],"name":"addProducts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"assignPubKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"slowModeTxs","type":"bytes[]"}],"name":"batchSubmitUpdateProductTxs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearPerpAddProductCalls","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearSpotAddProductCalls","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearUpdateProductTxs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"deletePubkey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"hasPendingAddProductCalls","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasPendingUpdateProductTxs","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"multisig","type":"address"},{"internalType":"address","name":"_deployer","type":"address"},{"internalType":"address","name":"_spotEngine","type":"address"},{"internalType":"address","name":"_perpEngine","type":"address"},{"internalType":"address","name":"_endpoint","type":"address"},{"internalType":"address","name":"_clearinghouse","type":"address"},{"internalType":"address","name":"_verifier","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingPerpAddProductIds","outputs":[{"internalType":"uint32[]","name":"","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingSpotAddProductIds","outputs":[{"internalType":"uint32[]","name":"","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"},{"components":[{"internalType":"int32","name":"longWeightInitial","type":"int32"},{"internalType":"int32","name":"shortWeightInitial","type":"int32"},{"internalType":"int32","name":"longWeightMaintenance","type":"int32"},{"internalType":"int32","name":"shortWeightMaintenance","type":"int32"},{"internalType":"int128","name":"priceX18","type":"int128"}],"internalType":"struct RiskHelper.RiskStore","name":"riskStore","type":"tuple"}],"name":"perpUpdateRisk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"address","name":"sendTo","type":"address"}],"name":"removeWithdrawPoolLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_withdrawPool","type":"address"}],"name":"setWithdrawPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"},{"components":[{"internalType":"int32","name":"longWeightInitial","type":"int32"},{"internalType":"int32","name":"shortWeightInitial","type":"int32"},{"internalType":"int32","name":"longWeightMaintenance","type":"int32"},{"internalType":"int32","name":"shortWeightMaintenance","type":"int32"},{"internalType":"int128","name":"priceX18","type":"int128"}],"internalType":"struct RiskHelper.RiskStore","name":"riskStore","type":"tuple"}],"name":"spotUpdateRisk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"address","name":"book","type":"address"},{"internalType":"int128","name":"sizeIncrement","type":"int128"},{"internalType":"int128","name":"minSize","type":"int128"},{"internalType":"int128","name":"lpSpreadX18","type":"int128"},{"components":[{"internalType":"int32","name":"longWeightInitial","type":"int32"},{"internalType":"int32","name":"shortWeightInitial","type":"int32"},{"internalType":"int32","name":"longWeightMaintenance","type":"int32"},{"internalType":"int32","name":"shortWeightMaintenance","type":"int32"},{"internalType":"int128","name":"priceX18","type":"int128"}],"internalType":"struct RiskHelper.RiskStore","name":"riskStore","type":"tuple"}],"name":"submitPerpAddProductCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"uint32","name":"quoteId","type":"uint32"},{"internalType":"address","name":"book","type":"address"},{"internalType":"int128","name":"sizeIncrement","type":"int128"},{"internalType":"int128","name":"minSize","type":"int128"},{"internalType":"int128","name":"lpSpreadX18","type":"int128"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"int128","name":"interestInflectionUtilX18","type":"int128"},{"internalType":"int128","name":"interestFloorX18","type":"int128"},{"internalType":"int128","name":"interestSmallCapX18","type":"int128"},{"internalType":"int128","name":"interestLargeCapX18","type":"int128"}],"internalType":"struct ISpotEngine.Config","name":"config","type":"tuple"},{"components":[{"internalType":"int32","name":"longWeightInitial","type":"int32"},{"internalType":"int32","name":"shortWeightInitial","type":"int32"},{"internalType":"int32","name":"longWeightMaintenance","type":"int32"},{"internalType":"int32","name":"shortWeightMaintenance","type":"int32"},{"internalType":"int128","name":"priceX18","type":"int128"}],"internalType":"struct RiskHelper.RiskStore","name":"riskStore","type":"tuple"}],"name":"submitSpotAddProductCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"slowModeTx","type":"bytes"}],"name":"submitUpdateProductTx","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateProducts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"address","name":"sendTo","type":"address"}],"name":"withdrawInsurance","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
9c4d535b0000000000000000000000000000000000000000000000000000000000000000010005a74cacbc340094472b0a6f38a84c59666a401d1cc5a98f94866dec0fe700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x0002000000000002001600000000000200010000000103550000006003100270000005300030019d0000008005000039000000400050043f0000000100200190000000380000c13d0000053002300197000000040020008c0000097c0000413d000000000321034f000000000401043b000000e004400270000005380040009c000000610000a13d000005390040009c000000d70000a13d0000053a0040009c000003150000a13d0000053b0040009c000003500000213d0000053e0040009c000008c90000613d0000053f0040009c0000097c0000c13d000000240020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000401100370000000000101043b000005590010009c0000097c0000213d0000006702000039000000000202041a00000559022001970000000003000411000000000032004b0000097e0000c13d000000000001004b000009960000c13d0000053101000041000000800010043f0000002001000039000000840010043f0000002601000039000000a40010043f0000056d01000041000000c40010043f0000056e01000041000000e40010043f0000053401000041000014be000104300000000001000416000000000001004b0000097c0000c13d000000000100041a0000ff0000100190000000550000c13d000000ff0210018f000000ff0020008c000000500000613d000000ff011001bf000000000010041b000000ff01000039000000800010043f0000000001000414000005300010009c0000053001008041000000c00110021000000535011001c70000800d020000390000000103000039000005360400004114bc14b20000040f00000001002001900000097c0000613d0000002001000039000001000010044300000120000004430000053701000041000014bd0001042e0000053101000041000000800010043f0000002001000039000000840010043f0000002701000039000000a40010043f0000053201000041000000c40010043f0000053301000041000000e40010043f0000053401000041000014be000104300000054a0040009c000000b60000213d000005520040009c000000e80000213d000005560040009c000006a20000613d000005570040009c000005210000613d000005580040009c0000097c0000c13d000000e40020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000402100370000000000202043b001100000002001d000005590020009c0000097c0000213d0000002402100370000000000202043b001000000002001d000005590020009c0000097c0000213d0000004402100370000000000202043b000f00000002001d000005590020009c0000097c0000213d0000006402100370000000000202043b000e00000002001d000005590020009c0000097c0000213d0000008402100370000000000202043b000d00000002001d000005590020009c0000097c0000213d000000a402100370000000000202043b000c00000002001d000005590020009c0000097c0000213d000000c401100370000000000101043b000b00000001001d000005590010009c0000097c0000213d0000000001000415000000160110008a0009000500100218000000000100041a000a00000001001d0008ff000010019400000b4a0000c13d0000000001000415000000150110008a00090005001002180000000a01000029000000ff0010019000000b4a0000c13d000001000100008a0000000a0110017f00000001021001bf0000059e0120019700000100021001bf000a00000000001d000000000020041b000000400100043d0000000003000411000000100030006b00000c620000c13d0000ff000020019000000d770000c13d00000064021000390000059b03000041000000000032043500000044021000390000059c03000041000000000032043500000024021000390000002b0300003900000dbe0000013d0000054b0040009c000001730000213d0000054f0040009c000006af0000613d000005500040009c000005c20000613d000005510040009c0000097c0000c13d0000000001000416000000000001004b0000097c0000c13d0000006701000039000000000201041a00000559032001970000000005000411000000000053004b0000097e0000c13d0000058b02200197000000000021041b0000000001000414000005300010009c0000053001008041000000c0011002100000058c011001c70000800d0200003900000003030000390000058d04000041000000000600001914bc14b20000040f00000001002001900000097c0000613d0000058e01000041000014bd0001042e000005430040009c000002cd0000213d000005470040009c000007df0000613d000005480040009c000007a80000613d000005490040009c0000097c0000c13d0000000001000416000000000001004b0000097c0000c13d0000006701000039000000000101041a0000055901100197000000800010043f0000056c01000041000014bd0001042e000005530040009c000006fe0000613d000005540040009c000006280000613d000005550040009c0000097c0000c13d000000640020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000402100370000000000202043b001100000002001d000005300020009c0000097c0000213d0000002402100370000000000202043b001000000002001d0000058f0020009c0000097c0000213d0000004401100370000000000101043b000f00000001001d000005590010009c0000097c0000213d0000006701000039000000000101041a00000559011001970000000002000411000000000021004b0000097e0000c13d0000009d01000039000000000101041a0000059302000041000000800020043f00000000030004140000055902100197000005300030009c0000053003008041000000c00130021000000594011001c714bc14b70000040f00000060031002700000053003300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000080057001bf000001220000613d0000008008000039000000000901034f000000009a09043c0000000008a80436000000000058004b0000011e0000c13d000000000006004b0000012f0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000009e20000613d0000001f01400039000000600110018f00000080011001bf000000400010043f000000200030008c0000097c0000413d000000800100043d000e00000001001d000005590010009c0000097c0000213d000005720100004100000000001004430000000e0100002900000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000097c0000613d000000400300043d00000044013000390000000f02000029000000000021043500000024013000390000001002000029000000000021043500000595010000410000000000130435000000040130003900000011020000290000000000210435000005300030009c001100000003001d0000053001000041000000000103401900100040001002180000000001000414000005300010009c0000053001008041000000c00110021000000010011001af00000578011001c70000000e0200002914bc14b20000040f0000000100200190000006f70000c13d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b0000016e0000c13d00000b360000013d0000054c0040009c000007960000613d0000054d0040009c0000062d0000613d0000054e0040009c0000097c0000c13d000001440020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000402100370000000000202043b000c00000002001d000005300020009c0000097c0000213d0000002402100370000000000202043b000b00000002001d000005590020009c0000097c0000213d0000004402100370000000000502043b0000055a005001980000055b0200004100000000020060190000055c04500197000000000242019f000a00000005001d000000000052004b0000097c0000c13d0000006402100370000000000502043b0000055a005001980000055b0200004100000000020060190000055c04500197000000000242019f000900000005001d000000000052004b0000097c0000c13d0000008401100370000000000401043b0000055a004001980000055b0100004100000000010060190000055c02400197000000000121019f000800000004001d000000000041004b0000097c0000c13d0000009901000039000000000101041a00000559011001970000000002000411000000000012004b000009870000c13d000000a301000039000000000401041a0000055d0040009c000009ba0000213d00000005024002100000003f012000390000055e011001970000055f0010009c000009ba0000213d0000008001100039000000400010043f000d00000004001d000000800040043f0000001f0120018f000000000002004b000001c20000613d000000a002200039000000a004000039000000003503043c0000000004540436000000000024004b000001be0000c13d000000000001004b0000000d08000029000000000008004b000000a30700003900000b770000c13d0000000c010000290000053005100197000000800100043d000000000001004b000001d60000613d00000000020000190000000503200210000000a00330003900000000030304330000053003300197000000000035004b00000d9e0000613d0000000102200039000000000012004b000001cd0000413d000000400300043d000005820030009c000000a30c000039000009ba0000213d000000c001300039000000400010043f00000080013000390000000802000029000000000021043500000060023000390000000904000029000000000042043500000040043000390000000a06000029000000000064043500000000055304360000000b0600002900000559066001970000000000650435000000a40600008a0000000006600031000005620060009c0000097c0000213d000000a00060008c0000097c0000413d000000400600043d000005640060009c000009ba0000213d000000a007600039000000400070043f0000000107000367000000a408700370000000000808043b000005650080019800000566090000410000000009006019000005670a8001970000000009a9019f000000000089004b0000097c0000c13d0000000008860436000000c409700370000000000909043b0000056500900198000005660a000041000000000a006019000005670b900197000000000aba019f00000000009a004b0000097c0000c13d0000000000980435000000e408700370000000000808043b000005650080019800000566090000410000000009006019000005670a8001970000000009a9019f000000000089004b0000097c0000c13d000000400960003900000000008904350000010408700370000000000808043b000005650080019800000566090000410000000009006019000005670a8001970000000009a9019f000000000089004b0000097c0000c13d000000600960003900000000008904350000012407700370000000000707043b0000055a007001980000055b0800004100000000080060190000055c09700197000000000898019f000000000078004b0000097c0000c13d00000080086000390000000000780435000000a007300039000000000067043500000000030304330000053003300197000000400600043d0000002008600039001000000008001d0000000000380435000000000305043300000559033001970000004005600039000000000035043500000000030404330000055c043001970000055a003001980000055b030000410000000003006019000000000343019f0000006004600039000000000034043500000000020204330000055c032001970000055a002001980000055b020000410000000002006019000000000232019f0000008003600039000000000023043500000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f000000a0026000390000000000120435000000000107043300000000320104340000056704200197000005650020019800000566020000410000000002006019000000000242019f000000c004600039000000000024043500000000020304330000056703200197000005650020019800000566020000410000000002006019000000000232019f000000e0036000390000000000230435000000400210003900000000020204330000056703200197000005650020019800000566020000410000000002006019000000000232019f00000100036000390000000000230435000000600210003900000000020204330000056703200197000005650020019800000566020000410000000002006019000000000232019f00000120036000390000000000230435000000800110003900000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f0000014002600039000000000012043500000140010000390000000000160435001100000006001d000005840060009c000009ba0000213d00000011010000290000016001100039000000400010043f00000000010c041a0000055d0010009c000009ba0000213d000000010210003900000000002c041b0000000000c0043f00000011020000290000000002020433000f00000002001d0000055d0020009c000009ba0000213d0000057a0110009a000e00000001001d000000000101041a000000010010019000000001021002700000007f0220618f000d00000002001d0000001f0020008c00000000020000390000000102002039000000000121013f00000001001001900000034a0000c13d0000000d01000029000000200010008c000002b90000413d0000000e01000029000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d0000000f030000290000001f023000390000000502200270000000200030008c0000000002004019000000000301043b0000000d010000290000001f01100039000000050110027000000000011300190000000002230019000000000012004b000002b90000813d000000000002041b0000000102200039000000000012004b000002b50000413d0000000f01000029000000200010008c000011d00000413d0000000e01000029000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000200200008a0000000f02200180000000000101043b000011d70000c13d0000002003000039000011e40000013d000005440040009c000008780000613d000005450040009c000007ce0000613d000005460040009c0000097c0000c13d000000240020008c0000097c0000413d0000000001000416000000000001004b0000097c0000c13d0000006701000039000000000101041a00000559011001970000000002000411000000000021004b0000097e0000c13d0000009e01000039000000000101041a000005720200004100000000002004430000055901100197001100000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000097c0000613d000000400300043d0000057901000041000000000013043500000004010000390000000101100367000000000101043b00000004023000390000000000120435000005300030009c001000000003001d00000530010000410000000001034019000f0040001002180000000001000414000005300010009c0000053001008041000000c0011002100000000f011001af00000575011001c7000000110200002914bc14b20000040f0000000100200190000009b70000c13d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b000003100000c13d00000b360000013d000005400040009c0000095e0000613d000005410040009c000009240000613d000005420040009c0000097c0000c13d000000240020008c0000097c0000413d0000000003000416000000000003004b0000097c0000c13d0000000403100370000000000303043b0000055d0030009c0000097c0000213d0000002304300039000000000024004b0000097c0000813d001000040030003d0000001001100360000000000101043b001100000001001d0000055d0010009c0000097c0000213d0000002403300039000f00000003001d0000001101300029000000000021004b0000097c0000213d0000009901000039000000000101041a00000559011001970000000002000411000000000012004b000009870000c13d000000a102000039000000000102041a0000055d0010009c000009ba0000213d0000000103100039000000000032041b000000000020043f000005710310009a000000000103041a000000010010019000000001041002700000007f0440618f0000001f0040008c00000000020000390000000102002039000000000121013f000000010010019000000af60000613d0000058801000041000000000010043f0000002201000039000000040010043f0000057501000041000014be000104300000053c0040009c000009190000613d0000053d0040009c0000097c0000c13d000002040020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000402100370000000000202043b000c00000002001d000005300020009c0000097c0000213d0000002402100370000000000202043b000b00000002001d000005300020009c0000097c0000213d0000004402100370000000000202043b000a00000002001d000005590020009c0000097c0000213d0000006402100370000000000502043b0000055a005001980000055b0200004100000000020060190000055c04500197000000000242019f000900000005001d000000000052004b0000097c0000c13d0000008402100370000000000502043b0000055a005001980000055b0200004100000000020060190000055c04500197000000000242019f000800000005001d000000000052004b0000097c0000c13d000000a401100370000000000401043b0000055a004001980000055b0100004100000000010060190000055c02400197000000000121019f000700000004001d000000000041004b0000097c0000c13d0000009901000039000000000101041a00000559011001970000000002000411000000000012004b000009870000c13d000000a201000039000000000401041a0000055d0040009c000009ba0000213d00000005024002100000003f012000390000055e011001970000055f0010009c000009ba0000213d0000008001100039000000400010043f000d00000004001d000000800040043f0000001f0120018f000000000002004b000003a20000613d000000a002200039000000a004000039000000003503043c0000000004540436000000000024004b0000039e0000c13d000000000001004b0000000d08000029000000000008004b000000a20700003900000c6d0000c13d000000800100043d000000000001004b0000000c04000029000003b50000613d00000000020000190000000503200210000000a0033000390000000003030433000000000343013f000005300030019800000db50000613d0000000102200039000000000012004b000003ac0000413d000000400400043d000005630040009c000000a20e000039000009ba0000213d0000010001400039000000400010043f000000a001400039000000070200002900000000002104350000008002400039000000080300002900000000003204350000006003400039000000090500002900000000005304350000000a050000290000055906500197000000400540003900000000006504350000000c06000029000005300660019700000000066404360000000b07000029000005300770019700000000007604350000000009000031000005620090009c0000097c0000213d000001640090008c0000097c0000413d000000400a00043d0000056400a0009c000009ba0000213d000000a007a00039000000400070043f0000000107000367000000c408700370000000000808043b000005590080009c0000097c0000213d00000000088a0436000000e40b700370000000000b0b043b0000055a00b001980000055b0c000041000000000c0060190000055c0db00197000000000cdc019f0000000000bc004b0000097c0000c13d0000000000b804350000010408700370000000000808043b0000055a008001980000055b0b000041000000000b0060190000055c0c800197000000000bcb019f00000000008b004b0000097c0000c13d000000400ba0003900000000008b04350000012408700370000000000808043b0000055a008001980000055b0b000041000000000b0060190000055c0c800197000000000bcb019f00000000008b004b0000097c0000c13d000000600ba0003900000000008b04350000014408700370000000000808043b0000055a008001980000055b0b000041000000000b0060190000055c0c800197000000000bcb019f00000000008b004b0000097c0000c13d000000800ba0003900000000008b0435000000c0084000390000000000a80435000002040090008c0000097c0000413d000000400900043d000005640090009c000009ba0000213d000000a00a9000390000004000a0043f000001640a700370000000000a0a043b0000056500a00198000005660b000041000000000b006019000005670ca00197000000000bcb019f0000000000ab004b0000097c0000c13d000000000aa90436000001840b700370000000000b0b043b0000056500b00198000005660c000041000000000c006019000005670db00197000000000cdc019f0000000000bc004b0000097c0000c13d0000000000ba0435000001a40a700370000000000a0a043b0000056500a00198000005660b000041000000000b006019000005670ca00197000000000bcb019f0000000000ab004b0000097c0000c13d000000400b9000390000000000ab0435000001c40a700370000000000a0a043b0000056500a00198000005660b000041000000000b006019000005670ca00197000000000bcb019f0000000000ab004b0000097c0000c13d000000600b9000390000000000ab0435000001e407700370000000000707043b0000055a007001980000055b0a000041000000000a0060190000055c0b700197000000000aba019f00000000007a004b0000097c0000c13d000000800a90003900000000007a0435000000e007400039000000000097043500000000040404330000053004400197000000400900043d000000200a90003900100000000a001d00000000004a04350000000004060433000005300440019700000040069000390000000000460435000000000405043300000559044001970000006005900039000000000045043500000000030304330000055c043001970000055a003001980000055b030000410000000003006019000000000343019f0000008004900039000000000034043500000000020204330000055c032001970000055a002001980000055b020000410000000002006019000000000232019f000000a003900039000000000023043500000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f000000c0029000390000000000120435000000000108043300000000320104340000055902200197000000e004900039000000000024043500000000020304330000055c032001970000055a002001980000055b020000410000000002006019000000000232019f00000100039000390000000000230435000000400210003900000000020204330000055c032001970000055a002001980000055b020000410000000002006019000000000232019f00000120039000390000000000230435000000600210003900000000020204330000055c032001970000055a002001980000055b020000410000000002006019000000000232019f00000140039000390000000000230435000000800110003900000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f00000160029000390000000000120435000000000107043300000000320104340000056704200197000005650020019800000566020000410000000002006019000000000242019f0000018004900039000000000024043500000000020304330000056703200197000005650020019800000566020000410000000002006019000000000232019f000001a0039000390000000000230435000000400210003900000000020204330000056703200197000005650020019800000566020000410000000002006019000000000232019f000001c0039000390000000000230435000000600210003900000000020204330000056703200197000005650020019800000566020000410000000002006019000000000232019f000001e0039000390000000000230435000000800110003900000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f0000020002900039000000000012043500000200010000390000000000190435001100000009001d0000056b0090009c000009ba0000213d00000011010000290000022001100039000000400010043f00000000010e041a0000055d0010009c000009ba0000213d000000010210003900000000002e041b0000000000e0043f00000011020000290000000002020433000f00000002001d0000055d0020009c000009ba0000213d000005600110009a000e00000001001d000000000101041a000000010010019000000001021002700000007f0220618f000d00000002001d0000001f0020008c00000000020000390000000102002039000000000121013f00000001001001900000034a0000c13d0000000d01000029000000200010008c000005020000413d0000000e01000029000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d0000000f030000290000001f023000390000000502200270000000200030008c0000000002004019000000000301043b0000000d010000290000001f01100039000000050110027000000000011300190000000002230019000000000012004b000005020000813d000000000002041b0000000102200039000000000012004b000004fe0000413d0000000f01000029000000200010008c000011d00000413d0000000e01000029000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000200200008a0000000f02200180000000000101043b000002cb0000613d000000010320008a000000050330027000000000043100190000002003000039000000010440003900000011053000290000000005050433000000000051041b00000020033000390000000101100039000000000041004b000005190000c13d000011e40000013d000000c40020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000402100370000000000202043b001100000002001d000005300020009c0000097c0000213d0000012002000039000000400020043f0000002402100370000000000202043b0000056500200198000005660300004100000000030060190000056704200197000000000343019f000000000023004b0000097c0000c13d000000800020043f0000004402100370000000000202043b0000056500200198000005660300004100000000030060190000056704200197000000000343019f000000000023004b0000097c0000c13d000000a00020043f0000006402100370000000000202043b0000056500200198000005660300004100000000030060190000056704200197000000000343019f000000000023004b0000097c0000c13d000000c00020043f0000008402100370000000000202043b0000056500200198000005660300004100000000030060190000056704200197000000000343019f000000000023004b0000097c0000c13d000000e00020043f000000a401100370000000000101043b0000055a001001980000055b0200004100000000020060190000055c03100197000000000232019f000000000012004b0000097c0000c13d000001000010043f0000006701000039000000000101041a00000559011001970000000002000411000000000021004b00000aed0000c13d0000009b01000039000000000101041a000005720200004100000000002004430000055901100197001000000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000097c0000613d000000400300043d0000057e010000410000000000130435000000040130003900000011020000290000000000210435000000800100043d0000056702100197000005650010019800000566010000410000000001006019000000000121019f00000024023000390000000000120435000000a00100043d0000056702100197000005650010019800000566010000410000000001006019000000000121019f00000044023000390000000000120435000000c00100043d0000056702100197000005650010019800000566010000410000000001006019000000000121019f00000064023000390000000000120435000000e00100043d0000056702100197000005650010019800000566010000410000000001006019000000000121019f00000084023000390000000000120435000001000100043d0000055c021001970000055a001001980000055b010000410000000001006019000000000121019f000000a4023000390000000000120435000005300030009c001100000003001d00000530010000410000000001034019000f0040001002180000000001000414000005300010009c0000053001008041000000c0011002100000000f011001af0000057f011001c7000000100200002914bc14b20000040f0000000100200190000008730000c13d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b000005bd0000c13d00000b360000013d000000440020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000402100370000000000202043b0000058f0020009c0000097c0000213d0000002401100370000000000101043b000005590010009c0000097c0000213d0000006703000039000000000303041a00000559033001970000000004000411000000000043004b0000097e0000c13d000000800020043f000000a00010043f000000e00020043f000001000010043f0000004003000039000000c00030043f0000059003000041000001400030043f000001410020043f000001610010043f0000004101000039000001200010043f000001a001000039000000400010043f0000009c01000039000000000101041a000005720200004100000000002004430000055901100197001100000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000097c0000613d000000400600043d000005910100004100000000001604350000000401600039000000200200003900000000002104350000002402600039000001200100043d0000000000120435000000000001004b0000060d0000613d0000004402600039000000000300001900000000042300190000014005300039000000000505043300000000005404350000002003300039000000000013004b000006030000413d0000060d0000a13d000000000221001900000000000204350000001f011000390000059f011001970000004401100039000005300010009c000005300100804100000060011002100000000002000414000005300020009c0000053002008041000000c002200210000000000112019f000005300060009c00000530020000410000000002064019001000400020021800000010011001af0000001102000029001100000006001d14bc14b20000040f000000010020019000000ae00000613d00000011020000290000055d0020009c000009ba0000213d000000400020043f0000001001000029000014bd0001042e0000000001000416000000000001004b0000097c0000c13d14bc12800000040f0000079a0000013d0000000003000416000000000003004b0000097c0000c13d001400400000003d000000440020008c0000097c0000413d001300040000003d0000000403100370000000000303043b0000055d0030009c0000097c0000213d0000002304300039000000000024004b0000097c0000813d0000000404300039000000000441034f000000000504043b0000055d0050009c000009ba0000213d00000005045002100000003f064000390000055e066001970000055f0060009c000009ba0000213d0000008006600039000000400060043f000000800050043f00000024033000390000000004340019000000000024004b0000097c0000213d000000000005004b000006580000613d0000008005000039000000000631034f000000000606043b000005300060009c0000097c0000213d000000200550003900000000006504350000002003300039000000000043004b0000064f0000413d0000002403100370000000000403043b0000055d0040009c0000097c0000213d0000002303400039000000000023004b00000000050000190000057d050080410000057d03300197000000000003004b00000000060000190000057d060040410000057d0030009c000000000605c019000000000006004b0000097c0000c13d0000000403400039000000000331034f000000000503043b0000055d0050009c000009ba0000213d00000005065002100000003f036000390000055e07300197000000400300043d0000000007730019000000000037004b000000000800003900000001080040390000055d0070009c000009ba0000213d0000000100800190000009ba0000c13d000000400070043f000000000053043500000024044000390000000006460019000000000026004b0000097c0000213d000000000005004b0000068b0000613d0000000002030019000000000541034f000000000505043b000005300050009c0000097c0000213d000000200220003900000000005204350000002004400039000000000064004b000006820000413d001200000003001d0000006701000039000000000101041a00000559011001970000000002000411000000000021004b00000da50000c13d000000a201000039000000000101041a000000000001004b00000dc90000c13d000000a201000039000000000001041b000f00140000002d000000a301000039000000000101041a000000000001004b0000100c0000c13d000000a301000039000000000001041b0000000f010000290000000001010433000007db0000013d0000000001000416000000000001004b0000097c0000c13d000000a301000039000000000101041a000000000001004b000009910000c13d000000a201000039000000000101041a000000000001004b0000000001000039000000010100c039000009920000013d000000640020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000402100370000000000202043b001100000002001d000005590020009c0000097c0000213d0000002402100370000000000202043b001000000002001d000005590020009c0000097c0000213d0000004401100370000000000101043b000f00000001001d000000010010008c0000097c0000213d0000006701000039000000000101041a00000559011001970000000002000411000000000021004b0000097e0000c13d0000009d01000039000000000101041a000005720200004100000000002004430000055901100197000e00000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000097c0000613d000000400300043d00000044013000390000000f02000029000000000021043500000024013000390000001002000029000000000021043500000592010000410000000000130435000000040130003900000011020000290000000000210435000005300030009c001100000003001d0000053001000041000000000103401900100040001002180000000001000414000005300010009c0000053001008041000000c00110021000000010011001af00000578011001c70000000e0200002914bc14b20000040f000000010020019000000b2a0000613d00000011010000290000055d0010009c000009ba0000213d0000001101000029000000400010043f0000001001000029000014bd0001042e0000000001000416000000000001004b0000097c0000c13d0000006701000039000000000101041a00000559011001970000000002000411000000000021004b0000097e0000c13d000000a102000039000000000102041a000000000001004b000007d90000613d0000000003000019000000000020043f000f00000003001d000005710130009a000000000201041a000000010320019000000001072002700000007f0770618f0000001f0070008c00000000040000390000000104002039000000000043004b0000034a0000c13d0000000006750436000000000003004b001100000005001d001000000006001d000007360000613d000e00000007001d000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d0000000e05000029000000000005004b0000073a0000613d000000000201043b000000000100001900000010060000290000000003610019000000000402041a000000000043043500000001022000390000002001100039000000000051004b0000072e0000413d0000073c0000013d000005a001200197000000000016043500000040015000390000073e0000013d000000000100001900000010060000290000000001610019000000110500002900000000015100490000001f011000390000059f021001970000000001520019000000000021004b000000000200003900000001020040390000055d0010009c000009ba0000213d0000000100200190000009ba0000c13d000000400010043f0000009c01000039000000000101041a000005720200004100000000002004430000055901100197000e00000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000097c0000613d000000400700043d000005910100004100000000001704350000000401700039000000200200003900000000002104350000002402700039000000110100002900000000010104330000000000120435000000000001004b000007760000613d00000044027000390000000003000019000000100600002900000000042300190000000005360019000000000505043300000000005404350000002003300039000000000013004b0000076c0000413d000007760000a13d000000000221001900000000000204350000001f011000390000059f011001970000004401100039000005300010009c00000530010080410000006001100210000005300070009c000005300200004100000000020740190000004002200210000000000121019f0000000002000414000005300020009c0000053002008041000000c002200210000000000112019f0000000e02000029001100000007001d14bc14b20000040f0000000100200190000009d50000613d00000011050000290000055d0050009c000009ba0000213d000000400050043f0000000f030000290000000103300039000000a102000039000000000102041a000000000013004b0000070c0000413d000007d90000013d0000000001000416000000000001004b0000097c0000c13d14bc13b70000040f0000000002010019000000400100043d001100000001001d14bc11f20000040f00000011020000290000000001210049000005300010009c00000530010080410000006001100210000005300020009c00000530020080410000004002200210000000000121019f000014bd0001042e000000240020008c0000097c0000413d0000000003000416000000000003004b0000097c0000c13d0000000403100370000000000303043b000c00000003001d0000055d0030009c0000097c0000213d0000000c030000290000002303300039000000000023004b0000097c0000813d0000000c030000290000000403300039000000000131034f000000000101043b000b00000001001d0000055d0010009c0000097c0000213d0000000c01000029001100240010003d0000000b0100002900000005011002100000001101100029000000000021004b0000097c0000213d0000009901000039000000000101041a00000559011001970000000002000411000000000012004b000009870000c13d0000000b0000006b000009fc0000c13d0000008001000039000007db0000013d0000000001000416000000000001004b0000097c0000c13d0000009901000039000000000101041a00000559011001970000000002000411000000000012004b0000000001000039000000010100603914bc121c0000040f14bc12300000040f000000400100043d000005300010009c00000530010080410000004001100210000014bd0001042e000000c40020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000402100370000000000202043b001100000002001d000005300020009c0000097c0000213d0000012002000039000000400020043f0000002402100370000000000202043b0000056500200198000005660300004100000000030060190000056704200197000000000343019f000000000023004b0000097c0000c13d000000800020043f0000004402100370000000000202043b0000056500200198000005660300004100000000030060190000056704200197000000000343019f000000000023004b0000097c0000c13d000000a00020043f0000006402100370000000000202043b0000056500200198000005660300004100000000030060190000056704200197000000000343019f000000000023004b0000097c0000c13d000000c00020043f0000008402100370000000000202043b0000056500200198000005660300004100000000030060190000056704200197000000000343019f000000000023004b0000097c0000c13d000000e00020043f000000a401100370000000000101043b0000055a001001980000055b0200004100000000020060190000055c03100197000000000232019f000000000012004b0000097c0000c13d000001000010043f0000006701000039000000000101041a00000559011001970000000002000411000000000021004b00000aed0000c13d0000009a01000039000000000101041a000005720200004100000000002004430000055901100197001000000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000097c0000613d000000400300043d0000057e010000410000000000130435000000040130003900000011020000290000000000210435000000800100043d0000056702100197000005650010019800000566010000410000000001006019000000000121019f00000024023000390000000000120435000000a00100043d0000056702100197000005650010019800000566010000410000000001006019000000000121019f00000044023000390000000000120435000000c00100043d0000056702100197000005650010019800000566010000410000000001006019000000000121019f00000064023000390000000000120435000000e00100043d0000056702100197000005650010019800000566010000410000000001006019000000000121019f00000084023000390000000000120435000001000100043d0000055c021001970000055a001001980000055b010000410000000001006019000000000121019f000000a4023000390000000000120435000005300030009c001100000003001d00000530010000410000000001034019000f0040001002180000000001000414000005300010009c0000053001008041000000c0011002100000000f011001af0000057f011001c7000000100200002914bc14b20000040f000000010020019000000d6a0000613d00000011010000290000055d0010009c000009ba0000213d0000001101000029000009c00000013d0000000001000416000000000001004b0000097c0000c13d0000009901000039000000000101041a00000559011001970000000002000411000000000012004b000009870000c13d000000a303000039000000000203041a000000000003041b0000008001000039000000000002004b000007db0000613d000000000030043f0000057a0320009a0000057b0030009c000007db0000413d0000057c04000041000f00000003001d000008a00000013d000000000030043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000000101043b0000001104000029000000000004041b0000000f03000029000000000001041b0000000104400039000000000034004b000007da0000813d000000000104041a000000010010019000000001051002700000007f0550618f0000001f0050008c00000000020000390000000102002039000000000121013f00000001001001900000034a0000c13d000000000005004b0000089d0000613d0000001f0050008c00000000010400190000089c0000a13d001000000005001d000000000040043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c70000801002000039001100000004001d14bc14b70000040f00000001002001900000097c0000613d0000001103000029000000000201043b00000010010000290000001f01100039000000050110027000000000011200190000000102200039000000000012004b0000088e0000813d000000000002041b0000000102200039000000000012004b000008c40000413d0000088e0000013d0000000001000416000000000001004b0000097c0000c13d0000009901000039000000000101041a00000559011001970000000002000411000000000012004b000009870000c13d000000a202000039000000000102041a000000000002041b000000000001004b000009d10000613d000000000020043f000005600310009a0000056f0030009c000009d10000413d0000057004000041000f00000003001d000008f00000013d000000000030043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000000101043b0000001104000029000000000004041b0000000f03000029000000000001041b0000000104400039000000000034004b000009d00000813d000000000104041a000000010010019000000001051002700000007f0550618f0000001f0050008c00000000020000390000000102002039000000000121013f00000001001001900000034a0000c13d000000000005004b000008ed0000613d0000001f0050008c0000000001040019000008ec0000a13d001000000005001d000000000040043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c70000801002000039001100000004001d14bc14b70000040f00000001002001900000097c0000613d0000001103000029000000000201043b00000010010000290000001f01100039000000050110027000000000011200190000000102200039000000000012004b000008de0000813d000000000002041b0000000102200039000000000012004b000009140000413d000008de0000013d0000000001000416000000000001004b0000097c0000c13d000000a101000039000000000101041a000000000001004b0000000001000039000000010100c039000000800010043f0000056c01000041000014bd0001042e000000240020008c0000097c0000413d0000000002000416000000000002004b0000097c0000c13d0000000401100370000000000101043b001100000001001d000005590010009c0000097c0000213d0000006701000039000000000101041a00000559011001970000000002000411000000000021004b0000097e0000c13d0000009d01000039000000000101041a000005720200004100000000002004430000055901100197001000000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000097c0000613d000000400300043d00000574010000410000000000130435000000040130003900000011020000290000000000210435000005300030009c001100000003001d00000530010000410000000001034019000f0040001002180000000001000414000005300010009c0000053001008041000000c0011002100000000f011001af00000575011001c7000000100200002914bc14b20000040f000000010020019000000ad30000613d0000001101000029000009b80000013d000000640020008c0000097c0000413d0000000001000416000000000001004b0000097c0000c13d0000006701000039000000000101041a00000559011001970000000002000411000000000021004b0000097e0000c13d0000009e01000039000000000101041a000005720200004100000000002004430000055901100197001100000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b000009980000c13d0000000001000019000014be000104300000053101000041000000800010043f0000002001000039000000840010043f000000a40010043f0000059601000041000000c40010043f0000058101000041000014be000104300000053101000041000000800010043f0000002001000039000000840010043f0000001701000039000000a40010043f0000058001000041000000c40010043f0000058101000041000014be000104300000000101000039000000010110018f000000800010043f0000056c01000041000014bd0001042e14bc12020000040f000007da0000013d000000400400043d0000057701000041000000000014043500000001010003670000000402100370000000000202043b000000040340003900000000002304350000002402100370000000000202043b000000240340003900000000002304350000004401100370000000000101043b00000044024000390000000000120435000005300040009c001000000004001d00000530010000410000000001044019000f0040001002180000000001000414000005300010009c0000053001008041000000c0011002100000000f011001af00000578011001c7000000110200002914bc14b20000040f0000000100200190000009c30000613d00000010010000290000055d0010009c000009c00000a13d0000058801000041000000000010043f0000004101000039000000040010043f0000057501000041000014be00010430000000400010043f0000000f01000029000014bd0001042e00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b000009cb0000c13d00000b360000013d000000400500043d000005300050009c00000530050080410000004001500210000014bd0001042e00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b000009dd0000c13d00000b360000013d0000001f0530018f0000057606300198000000400200043d0000000004620019000009ed0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000009e90000c13d000000000005004b000009fa0000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f0000000000140435000000600130021000000b450000013d000000200900008a000000a10a000039000000000b00001900000a060000013d000000010180021000000001011001bf000000000017041b000000010bb000390000000b00b0006c000007da0000813d0000000501b0021000000011011000290000000102000367000000000112034f000000000101043b00000000030000310000000c0430006a000000430440008a0000057d054001970000057d06100197000000000756013f000000000056004b00000000050000190000057d05004041000000000041004b00000000040000190000057d040080410000057d0070009c000000000504c019000000000005004b0000097c0000c13d0000001104100029000000000142034f000000000101043b0000055d0010009c0000097c0000213d000000000513004900000020044000390000057d065001970000057d07400197000000000867013f000000000067004b00000000060000190000057d06004041000000000054004b00000000050000190000057d050020410000057d0080009c000000000605c019000000000006004b0000097c0000c13d0000001f05100039000000000595016f0000003f05500039000000000595016f000000400800043d0000000005580019000000000085004b000000000600003900000001060040390000055d0050009c000009ba0000213d0000000100600190000009ba0000c13d000000400050043f000000000c1804360000000005410019000000000035004b0000097c0000213d000000000342034f000000000491017000000000024c001900000a4b0000613d000000000503034f00000000060c0019000000005705043c0000000006760436000000000026004b00000a470000c13d0000001f0510019000000a580000613d000000000343034f0000000304500210000000000502043300000000054501cf000000000545022f000000000303043b0000010004400089000000000343022f00000000034301cf000000000353019f000000000032043500000000011c0019000000000001043500000000010a041a0000055d0010009c000009ba0000213d000000010210003900000000002a041b0000000000a0043f00000000040804330000055d0040009c000009ba0000213d000005710710009a000000000107041a000000010010019000000001031002700000007f0330618f0000001f0030008c00000000020000390000000102002039000000000121013f00000001001001900000034a0000c13d000000200030008c00100000000b001d000d00000008001d000f00000007001d000e00000004001d00000a960000413d000900000003001d000000000070043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c70000801002000039000a0000000c001d14bc14b70000040f0000000a0c000029000000100b000029000000a10a000039000000200900008a00000001002001900000097c0000613d0000000e040000290000001f024000390000000502200270000000200040008c0000000002004019000000000301043b00000009010000290000001f01100039000000050110027000000000011300190000000002230019000000000012004b0000000f0700002900000a960000813d000000000002041b0000000102200039000000000012004b00000a920000413d000000200040008c00000aba0000413d000000000070043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f0000000d06000029000000100b000029000000a10a000039000000200900008a00000001002001900000097c0000613d0000000e080000290000000002980170000000000101043b00000ac60000613d000000010320008a00000005033002700000000003310019000000010430003900000020030000390000000f0700002900000000056300190000000005050433000000000051041b00000020033000390000000101100039000000000041004b00000ab00000c13d000000000082004b00000a000000813d00000aca0000013d000000000004004b00000abe0000613d00000000010c043300000abf0000013d00000000010000190000000302400210000005a10220027f000005a102200167000000000121016f0000000102400210000000000121019f00000a020000013d00000020030000390000000f07000029000000000082004b00000a000000813d0000000302800210000000f80220018f000005a10220027f000005a10220016700000000036300190000000003030433000000000223016f000000000021041b00000a000000013d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b00000adb0000c13d00000b360000013d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b00000ae80000c13d00000b360000013d0000053101000041000001200010043f0000002001000039000001240010043f000001440010043f0000059601000041000001640010043f0000059d01000041000014be00010430000e00000003001d000d00000004001d000000200040008c00000b160000413d0000000e01000029000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d00000011030000290000001f023000390000000502200270000000200030008c0000000002004019000000000301043b0000000d010000290000001f01100039000000050110027000000000011300190000000002230019000000000012004b00000b160000813d000000000002041b0000000102200039000000000012004b00000b120000413d00000011010000290000001f0010008c00000b690000a13d0000000e01000029000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000200200008a0000001102200180000000000101043b00000c370000c13d000000000300001900000c420000013d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b00000b320000c13d0000053006600197000000000004004b00000b440000613d000000000151034f0000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000006001600210000005300020009c00000530020080410000004002200210000000000112019f000014be0001043000000572010000410000000000100443000000000100041000000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b00000c550000c13d0000000a01000029000000ff0110018f000000010010008c00000009010000290000000501100270000000000100003f000000010100603f00000c580000c13d000000000200041a000005a00120019700000001021001bf000000080000006b000000a30000613d000a00010000003d000000a60000013d000000110000006b000000000100001900000b700000613d000000100100002900000020011000390000000101100367000000000101043b00000011040000290000000302400210000005a10220027f000005a102200167000000000221016f000000010140021000000c510000013d000000200900008a000000000a000019000000000070043f0000057a01a0009a000000000201041a000000010320019000000001062002700000007f0660618f0000001f0060008c00000000040000390000000104002039000000000442013f00000001004001900000034a0000c13d000000400b00043d00000000056b0436000000000003004b00000bab0000613d000e00000006001d000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003900110000000a001d00100000000b001d000f00000005001d14bc14b70000040f0000000f05000029000000100b000029000000110a000029000000200900008a0000000d08000029000000a30700003900000001002001900000097c0000613d0000000e06000029000000000006004b00000baf0000613d000000000201043b00000000010000190000000003510019000000000402041a000000000043043500000001022000390000002001100039000000000061004b00000ba20000413d000000000151001900000bb00000013d0000004001b00039000005a002200197000000000025043500000bb00000013d00000000015000190000000001b100490000001f01100039000000000291016f0000000001b20019000000000021004b000000000200003900000001020040390000055d0010009c000009ba0000213d0000000100200190000009ba0000c13d000000400010043f00000000020b0433000005620020009c0000097c0000213d000001400020008c0000097c0000413d000005820010009c000009ba0000213d000000c002100039000000400020043f0000000002050433000005300020009c0000097c0000213d00000000022104360000004003b000390000000003030433000005590030009c0000097c0000213d00000000003204350000006002b0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000097c0000c13d000000400310003900000000002304350000008002b0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000097c0000c13d00000060031000390000000000230435000000a002b0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000097c0000c13d00000080031000390000000000230435000000400200043d000005640020009c000009ba0000213d000000a003200039000000400030043f000000c003b0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000097c0000c13d0000000003320436000000e004b0003900000000040404330000056500400198000005660500004100000000050060190000056706400197000000000565019f000000000045004b0000097c0000c13d00000000004304350000010003b0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000097c0000c13d000000400420003900000000003404350000012003b0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000097c0000c13d000000600420003900000000003404350000014003b0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b0000097c0000c13d00000080042000390000000000340435000000a0031000390000000000230435000000800200043d0000000000a2004b000011a50000a13d000000000101043300000530011001970000000502a00210000000a0022000390000000000120435000000010aa0003900000000008a004b00000b790000413d000001c70000013d000000010400036700000000030000190000000f060000290000000005630019000000000554034f000000000505043b000000000051041b00000001011000390000002003300039000000000023004b00000c3a0000413d000000110020006c00000c4e0000813d00000011020000290000000302200210000000f80220018f000005a10220027f000005a1022001670000000f033000290000000103300367000000000303043b000000000223016f000000000021041b000000010100003900000011020000290000000102200210000000000112019f0000000e02000029000000000012041b000007da0000013d00000009010000290000000501100270000000000100003f000000400100043d00000064021000390000059703000041000000000032043500000044021000390000059803000041000000000032043500000024021000390000002e0300003900000dbe0000013d00000044021000390000059903000041000000000032043500000024021000390000001f030000390000000000320435000005310200004100000000002104350000000402100039000000200300003900000daf0000013d000000200900008a000000000a000019000000000070043f0000056001a0009a000000000201041a000000010320019000000001062002700000007f0660618f0000001f0060008c00000000040000390000000104002039000000000442013f00000001004001900000034a0000c13d000000400b00043d00000000056b0436000000000003004b00000ca10000613d000e00000006001d000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003900110000000a001d00100000000b001d000f00000005001d14bc14b70000040f0000000f05000029000000100b000029000000110a000029000000200900008a0000000d08000029000000a20700003900000001002001900000097c0000613d0000000e06000029000000000006004b00000ca50000613d000000000201043b00000000010000190000000003510019000000000402041a000000000043043500000001022000390000002001100039000000000061004b00000c980000413d000000000151001900000ca60000013d0000004001b00039000005a002200197000000000025043500000ca60000013d00000000015000190000000001b100490000001f01100039000000000291016f0000000001b20019000000000021004b000000000200003900000001020040390000055d0010009c000009ba0000213d0000000100200190000009ba0000c13d000000400010043f00000000020b0433000005620020009c0000097c0000213d000002000020008c0000097c0000413d000005630010009c000009ba0000213d0000010002100039000000400020043f0000000002050433000005300020009c0000097c0000213d00000000022104360000004003b000390000000003030433000005300030009c0000097c0000213d00000000003204350000006002b000390000000002020433000005590020009c0000097c0000213d000000400310003900000000002304350000008002b0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000097c0000c13d00000060031000390000000000230435000000a002b0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000097c0000c13d00000080031000390000000000230435000000c002b0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000097c0000c13d000000a0031000390000000000230435000000400200043d000005640020009c000009ba0000213d000000a003200039000000400030043f000000e003b000390000000003030433000005590030009c0000097c0000213d00000000033204360000010004b0003900000000040404330000055a004001980000055b0500004100000000050060190000055c06400197000000000565019f000000000045004b0000097c0000c13d00000000004304350000012003b0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b0000097c0000c13d000000400420003900000000003404350000014003b0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b0000097c0000c13d000000600420003900000000003404350000016003b0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b0000097c0000c13d00000080042000390000000000340435000000c0031000390000000000230435000000400200043d000005640020009c000009ba0000213d000000a003200039000000400030043f0000018003b0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000097c0000c13d0000000003320436000001a004b0003900000000040404330000056500400198000005660500004100000000050060190000056706400197000000000565019f000000000045004b0000097c0000c13d0000000000430435000001c003b0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000097c0000c13d00000040042000390000000000340435000001e003b0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000097c0000c13d000000600420003900000000003404350000020003b0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b0000097c0000c13d00000080042000390000000000340435000000e0031000390000000000230435000000800200043d0000000000a2004b000011a50000a13d000000000101043300000530011001970000000502a00210000000a0022000390000000000120435000000010aa0003900000000008a004b00000c6f0000413d000003a70000013d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b00000d720000c13d00000b360000013d0000006704000039000000000204041a0000058b032001970000001006000029000000000363019f000000000034041b000005300010009c000005300100804100000040011002100000000003000414000005300030009c0000053003008041000000c003300210000000000113019f00000559052001970000059a011001c70000800d0200003900000003030000390000058d0400004114bc14b20000040f00000001002001900000097c0000613d0000006701000039000000000201041a0000055901200197000000100010006c00000da50000c13d000000400100043d000000110000006b00000fc20000c13d00000064021000390000056e03000041000000000032043500000044021000390000056d0300004100000000003204350000002402100039000000260300003900000dbe0000013d000000400100043d0000006402100039000005680300004100000000003204350000004402100039000005830300004100000dbb0000013d000000400100043d0000004402100039000005960300004100000000003204350000053102000041000000000021043500000024021000390000002003000039000000000032043500000004021000390000000000320435000005300010009c0000053001008041000000400110021000000578011001c7000014be00010430000000400100043d00000064021000390000056803000041000000000032043500000044021000390000056903000041000000000032043500000024021000390000002303000039000000000032043500000531020000410000000000210435000000040210003900000020030000390000000000320435000005300010009c000005300100804100000040011002100000056a011001c7000014be00010430000000400c00043d0000000005000019000000a202000039000000000020043f000005600150009a000000000201041a000000010320019000000001062002700000007f0660618f0000001f0060008c00000000040000390000000104002039000000000442013f00000001004001900000034a0000c13d001100000005001d00000000056c0436000000000003004b00000df90000613d000f00000006001d000e00000005001d000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003900100000000c001d14bc14b70000040f000000100c00002900000001002001900000097c0000613d0000000f06000029000000000006004b00000dfd0000613d000000000201043b00000000010000190000000e050000290000000003510019000000000402041a000000000043043500000001022000390000002001100039000000000061004b00000df00000413d000000000151001900000dff0000013d0000004001c00039000005a002200197000000000025043500000dff0000013d0000000e0500002900000000015000190000000001c100490000001f011000390000059f021001970000000001c20019000000000021004b000000000200003900000001020040390000055d0010009c000009ba0000213d0000000100200190000009ba0000c13d000000400010043f00000000020c0433000005620020009c0000097c0000213d000002000020008c0000097c0000413d000005630010009c000009ba0000213d0000010002100039000000400020043f0000000002050433000005300020009c0000097c0000213d00000000022104360000004003c000390000000003030433000005300030009c0000097c0000213d00000000003204350000006003c000390000000004030433000005590040009c0000097c0000213d000000400310003900000000004304350000008004c0003900000000050404330000055a005001980000055b0400004100000000040060190000055c06500197000000000464019f000000000054004b0000097c0000c13d00000060041000390000000000540435000000a005c0003900000000060504330000055a006001980000055b0500004100000000050060190000055c07600197000000000575019f000000000065004b0000097c0000c13d00000080051000390000000000650435000000c006c0003900000000070604330000055a007001980000055b0600004100000000060060190000055c08700197000000000686019f000000000076004b0000097c0000c13d000000a0061000390000000000760435000000400800043d000005640080009c000009ba0000213d000000a007800039000000400070043f000000e007c000390000000007070433000005590070009c0000097c0000213d00000000077804360000010009c0003900000000090904330000055a009001980000055b0a000041000000000a0060190000055c0b900197000000000aba019f00000000009a004b0000097c0000c13d00000000009704350000012007c0003900000000070704330000055a007001980000055b0900004100000000090060190000055c0a7001970000000009a9019f000000000079004b0000097c0000c13d000000400980003900000000007904350000014007c0003900000000070704330000055a007001980000055b0900004100000000090060190000055c0a7001970000000009a9019f000000000079004b0000097c0000c13d000000600980003900000000007904350000016007c0003900000000070704330000055a007001980000055b0900004100000000090060190000055c0a7001970000000009a9019f000000000079004b0000097c0000c13d00000080098000390000000000790435000000c0071000390000000000870435000000400b00043d0000056400b0009c000009ba0000213d000000a008b00039000000400080043f0000018008c000390000000008080433000005650080019800000566090000410000000009006019000005670a8001970000000009a9019f000000000089004b0000097c0000c13d000000000d8b0436000001a008c000390000000008080433000005650080019800000566090000410000000009006019000005670a8001970000000009a9019f000000000089004b0000097c0000c13d00000000008d0435000001c008c000390000000008080433000005650080019800000566090000410000000009006019000005670a8001970000000009a9019f000000000089004b0000097c0000c13d0000004009b00039001000000009001d0000000000890435000001e008c000390000000008080433000005650080019800000566090000410000000009006019000005670a8001970000000009a9019f000000000089004b0000097c0000c13d0000006009b00039000f00000009001d00000000008904350000020008c0003900000000080804330000055a008001980000055b0900004100000000090060190000055c0a8001970000000009a9019f000000000089004b0000097c0000c13d000000800ab0003900000000008a0435000000e0081000390000000000b80435000000800800043d0000001109000029000000000098004b000011a50000a13d000c0000000a001d000d0000000d001d000e0000000b001d0000000508900210000000a0088000390000000008080433000b05300080019b000000000101043300000530011001970000000b0010006b000011ab0000c13d0000000001070433000900000001001d0000000001020433000800000001001d0000000001030433000700000001001d0000000001040433000600000001001d0000000001050433000500000001001d0000000001060433000400000001001d0000009a01000039000000000101041a000005720200004100000000002004430000055901100197000a00000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000000e090000290000000d0a0000290000097c0000613d00000004020000290000055c012001970000055a0020019800000005030000290000055c023001970000055a0330019700000006050000290000055c045001970000055a055001970000000706000029000005590660019700000008070000290000053007700197000000400b00043d0000004408b0003900000000006804350000002406b000390000000000760435000005860600004100000000006b04350000000406b000390000000b0700002900000000007604350000055b060000410000000006006019000000000116019f000000a406b000390000000000160435000000000003004b0000055b010000410000000001006019000000000121019f0000008402b000390000000000120435000000000005004b0000055b010000410000000001006019000000000141019f0000006402b000390000000000120435000000090400002900000000210404340000055901100197000000c403b00039000000000013043500000000010204330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f000000e402b000390000000000120435000000400140003900000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f0000010402b000390000000000120435000000600140003900000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f0000012402b000390000000000120435000000800140003900000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f0000014402b00039000000000012043500000000010904330000056702100197000005650010019800000566010000410000000001006019000000000121019f0000016402b00039000000000012043500000000010a04330000056702100197000005650010019800000566010000410000000001006019000000000121019f0000018402b000390000000000120435000000100100002900000000010104330000056702100197000005650010019800000566010000410000000001006019000000000121019f000001a402b0003900000000001204350000000f0100002900000000010104330000056702100197000005650010019800000566010000410000000001006019000000000121019f000001c402b0003900000000001204350000000c0100002900000000010104330000055c021001970000055a001001980000055b010000410000000001006019000000000121019f000001e402b0003900000000001204350000053000b0009c00100000000b001d000005300100004100000000010b401900000040011002100000000002000414000005300020009c0000053002008041000000c002200210000000000121019f00000587011001c70000000a0200002914bc14b20000040f0000000100200190000011af0000613d000000100c0000290000055d00c0009c000009ba0000213d0000004000c0043f00000011050000290000000105500039000000a202000039000000000102041a000000000015004b00000dcc0000413d000000000002041b000000000001004b000006980000613d000000000020043f000005600310009a0000056f0030009c000006980000413d0000057004000041000f00000003001d00000f9a0000013d0000001101000029000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000000101043b0000001104000029000000000004041b0000000f03000029000000000001041b0000000104400039000000000034004b000006980000813d000000000104041a000000010010019000000001051002700000007f0550618f0000001f0050008c00000000020000390000000102002039000000000121013f00000001001001900000034a0000c13d000000000005004b00000f970000613d0000001f0050008c000000000104001900000f960000a13d001000000005001d001100000004001d000000000040043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000000201043b00000010010000290000001f01100039000000050110027000000000011200190000000102200039000000000012004b00000f870000813d000000000002041b0000000102200039000000000012004b00000fbd0000413d00000f870000013d0000058b022001970000001106000029000000000262019f0000006703000039000000000023041b000005300010009c000005300100804100000040011002100000000002000414000005300020009c0000053002008041000000c002200210000000000112019f0000059a011001c70000800d0200003900000003030000390000058d04000041000000100500002914bc14b20000040f00000001002001900000097c0000613d0000009901000039000000000201041a0000058b0220019700000010022001af000000000021041b0000009a01000039000000000201041a0000058b022001970000000f022001af000000000021041b0000009b01000039000000000201041a0000058b022001970000000e022001af000000000021041b0000009c01000039000000000201041a0000058b022001970000000d022001af000000000021041b0000009d01000039000000000201041a0000058b022001970000000c022001af000000000021041b0000009e01000039000000000201041a0000058b022001970000000b022001af000000000021041b0000000a0000006b000007da0000c13d000000000200041a0000059e01200197000000000010041b0000000103000039000000400100043d0000000000310435000005300010009c000005300100804100000040011002100000000002000414000005300020009c0000053002008041000000c002200210000000000112019f00000561011001c70000800d02000039000005360400004114bc14b20000040f0000000100200190000007da0000c13d0000097c0000013d000000400800043d0000001201000029000400000001001d000300200010003d0000001301000029000200000001001d0000014001100039000005300010009c000005300100804100010060001002180000000005000019000000a302000039000000000020043f0000057a0150009a000000000201041a000000010320019000000001062002700000007f0660618f0000001f0060008c00000000040000390000000104002039000000000442013f00000001004001900000034a0000c13d001100000005001d0000000005680436000000000003004b000010450000613d000d00000006001d000e00000005001d000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c70000801002000039001000000008001d14bc14b70000040f000000100800002900000001002001900000097c0000613d0000000d06000029000000000006004b000010490000613d000000000201043b00000000010000190000000e050000290000000003510019000000000402041a000000000043043500000001022000390000002001100039000000000061004b0000103c0000413d00000000015100190000104b0000013d0000004001800039000005a00220019700000000002504350000104b0000013d0000000e05000029000000000150001900000000018100490000001f011000390000059f021001970000000001820019000000000021004b000000000200003900000001020040390000055d0010009c000009ba0000213d0000000100200190000009ba0000c13d000000400010043f0000000002080433000005620020009c0000097c0000213d000001400020008c0000097c0000413d000005820010009c000009ba0000213d000000c002100039000000400020043f0000000002050433000005300020009c0000097c0000213d000000000221043600000040038000390000000003030433000005590030009c0000097c0000213d0000000000320435000000600380003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b0000097c0000c13d00000040041000390000000000340435000000800380003900000000040304330000055a004001980000055b0300004100000000030060190000055c05400197000000000353019f000000000043004b0000097c0000c13d00000060031000390000000000430435000000a00480003900000000050404330000055a005001980000055b0400004100000000040060190000055c06500197000000000464019f000000000054004b0000097c0000c13d00000080041000390000000000540435000000400900043d000005640090009c000009ba0000213d000000a005900039000000400050043f000000c00580003900000000050504330000056500500198000005660600004100000000060060190000056707500197000000000676019f000000000056004b0000097c0000c13d000000000a590436000000e00580003900000000050504330000056500500198000005660600004100000000060060190000056707500197000000000676019f000000000056004b0000097c0000c13d00000000005a0435000001000580003900000000050504330000056500500198000005660600004100000000060060190000056707500197000000000676019f000000000056004b0000097c0000c13d0000004006900039001000000006001d0000000000560435000001200580003900000000050504330000056500500198000005660600004100000000060060190000056707500197000000000676019f000000000056004b0000097c0000c13d0000006006900039000e00000006001d0000000000560435000001400580003900000000050504330000055a005001980000055b0600004100000000060060190000055c07500197000000000676019f000000000056004b0000097c0000c13d00000080079000390000000000570435000000a0051000390000000000950435000000040500002900000000050504330000001106000029000000000065004b000011a50000a13d000b00000007001d000c0000000a001d000d00000009001d000000050560021000000003055000290000000005050433000a05300050019b000000000501043300000530055001970000000a0050006b000011bc0000c13d0000000f011000290000000001010433000800000001001d0000000001020433000700000001001d0000000001030433000600000001001d0000000001040433000500000001001d0000009b01000039000000000101041a000005720200004100000000002004430000055901100197000900000001001d00000004001004430000000001000414000005300010009c0000053001008041000000c00110021000000573011001c7000080020200003914bc14b70000040f0000000100200190000011690000613d000000000101043b000000000001004b0000000d090000290000000c0a0000290000097c0000613d00000005020000290000055c012001970000055a0020019800000006030000290000055c023001970000055a033001970000000704000029000005590440019700000008060000290000055c056001970000055a066001970000000f07000029000000000b0704330000058a0700004100000000007b04350000055b070000410000000007006019000000000717019f0000000201b0002900000080081000390000000000780435000000000003004b0000055b030000410000000003006019000000000223019f00000060031000390000000000230435000000000006004b0000055b020000410000000002006019000000000252019f00000040031000390000000000230435000000200210003900000000004204350000000a02000029000000000021043500000000020904330000056703200197000005650020019800000566020000410000000002006019000000000232019f000000a003100039000000000023043500000000020a04330000056703200197000005650020019800000566020000410000000002006019000000000232019f000000c0031000390000000000230435000000100200002900000000020204330000056703200197000005650020019800000566020000410000000002006019000000000232019f000000e00310003900000000002304350000000e0200002900000000020204330000056703200197000005650020019800000566020000410000000002006019000000000232019f000001000310003900000000002304350000000b0200002900000000020204330000055c032001970000055a002001980000055b020000410000000002006019000000000232019f000001200110003900000000002104350000053000b0009c00100000000b001d000005300100004100000000010b4019000000400110021000000001011001af0000000002000414000005300020009c0000053002008041000000c002200210000000000112019f000000090200002914bc14b20000040f0000000100200190000011c30000613d00000010080000290000055d0080009c000009ba0000213d000000400080043f00000011050000290000000105500039000000a302000039000000000102041a000000000015004b000010180000413d000000000002041b000000000001004b0000069f0000613d000000000020043f0000057a0310009a0000057b0030009c0000069f0000413d0000057c04000041000e00000003001d0000117d0000013d000000000001042f0000001101000029000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000000101043b0000001104000029000000000004041b0000000e03000029000000000001041b0000000104400039000000000034004b0000069f0000813d000000000104041a000000010010019000000001051002700000007f0550618f0000001f0050008c00000000020000390000000102002039000000000121013f00000001001001900000034a0000c13d000000000005004b0000117a0000613d0000001f0050008c0000000001040019000011790000a13d001000000005001d001100000004001d000000000040043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000097c0000613d000000000201043b00000010010000290000001f01100039000000050110027000000000011200190000000102200039000000000012004b0000116a0000813d000000000002041b0000000102200039000000000012004b000011a00000413d0000116a0000013d0000058801000041000000000010043f0000003201000039000000040010043f0000057501000041000014be00010430000000400100043d00000044021000390000058503000041000011bf0000013d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b000011b70000c13d00000b360000013d000000400100043d0000004402100039000005890300004100000000003204350000002402100039000000160300003900000c670000013d00000060061002700000001f0460018f0000057605600198000000400200043d000000000352001900000b360000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b000011cb0000c13d00000b360000013d0000000f0000006b0000000001000019000011d50000613d000000100100002900000000010104330000000f0400002900000b710000013d000000010320008a0000000503300270000000000431001900000020030000390000000104400039000000110600002900000000056300190000000005050433000000000051041b00000020033000390000000101100039000000000041004b000011dd0000c13d0000000f0020006c000011ef0000813d0000000f020000290000000302200210000000f80220018f000005a10220027f000005a10220016700000011033000290000000003030433000000000223016f000000000021041b00000001010000390000000f0200002900000c500000013d00000020030000390000000004310436000000000302043300000000003404350000004001100039000000000003004b000012010000613d000000000400001900000020022000390000000005020433000005300550019700000000015104360000000104400039000000000034004b000011fa0000413d000000000001042d00000559061001970000006701000039000000000201041a0000058b03200197000000000363019f000000000031041b000000400100043d000005300010009c000005300100804100000040011002100000000003000414000005300030009c0000053003008041000000c003300210000000000113019f00000559052001970000059a011001c70000800d0200003900000003030000390000058d0400004114bc14b20000040f00000001002001900000121a0000613d000000000001042d0000000001000019000014be00010430000000000001004b0000121f0000613d000000000001042d000000400100043d00000044021000390000058003000041000000000032043500000024021000390000001703000039000000000032043500000531020000410000000000210435000000040210003900000020030000390000000000320435000005300010009c0000053001008041000000400110021000000578011001c7000014be000104300003000000000002000000a102000039000000000102041a000000000002041b000000000001004b000012770000613d000000000020043f000005710310009a000005a20030009c000012770000413d000005a304000041000100000003001d000012410000013d000000000001041b0000000104400039000000000034004b000012770000813d000000000104041a000000010210019000000001051002700000007f0550618f0000001f0050008c00000000010000390000000101002039000000000012004b000012780000c13d000000000005004b0000123e0000613d0000001f0050008c00000000010400190000123d0000a13d000200000005001d000000000040043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c70000801002000039000300000004001d14bc14b70000040f00000001002001900000127e0000613d0000000303000029000000000201043b00000002010000290000001f01100039000000050110027000000000011200190000000102200039000000000012004b000012680000813d000000000002041b0000000102200039000000000012004b000012640000413d000000000030043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003914bc14b70000040f00000001002001900000127e0000613d000000000101043b0000000304000029000000000004041b00000001030000290000123d0000013d000000000001042d0000058801000041000000000010043f0000002201000039000000040010043f0000057501000041000014be000104300000000001000019000014be000104300007000000000002000000a207000039000000000807041a000005a40080009c000013a50000813d00000005018002100000003f021000390000055e02200197000000400900043d0000000002290019000000000092004b000000000300003900000001030040390000055d0020009c000013a50000213d0000000100300190000013a50000c13d000000400020043f000000000a8904360000001f0210018f000000000001004b0000129e0000613d00000000011a00190000000003000031000000010330036700000000040a0019000000003503043c0000000004540436000000000014004b0000129a0000c13d000000000002004b000000000008004b000013a10000613d000000200b00008a000000000c000019000300000008001d000200000009001d00010000000a001d000000000070043f0000056001c0009a000000000201041a000000010320019000000001062002700000007f0660618f0000001f0060008c00000000040000390000000104002039000000000043004b000013ab0000c13d000000400d00043d00000000056d0436000000000003004b000012d90000613d000400000006001d000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003900070000000c001d00060000000d001d000500000005001d14bc14b70000040f0000000505000029000000060d000029000000070c000029000000200b00008a000000010a00002900000002090000290000000308000029000000a2070000390000000100200190000013a30000613d0000000406000029000000000006004b000012dd0000613d000000000201043b00000000010000190000000003510019000000000402041a000000000043043500000001022000390000002001100039000000000061004b000012d00000413d0000000001510019000012de0000013d0000004001d00039000005a0022001970000000000250435000012de0000013d00000000015000190000000001d100490000001f011000390000000002b1016f0000000001d20019000000000021004b000000000200003900000001020040390000055d0010009c000013a50000213d0000000100200190000013a50000c13d000000400010043f00000000020d0433000005620020009c000013a30000213d000002000020008c000013a30000413d000005630010009c000013a50000213d0000010002100039000000400020043f0000000002050433000005300020009c000013a30000213d00000000022104360000004003d000390000000003030433000005300030009c000013a30000213d00000000003204350000006002d000390000000002020433000005590020009c000013a30000213d000000400310003900000000002304350000008002d0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b000013a30000c13d00000060031000390000000000230435000000a002d0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b000013a30000c13d00000080031000390000000000230435000000c002d0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b000013a30000c13d000000a0031000390000000000230435000000400200043d000005640020009c000013a50000213d000000a003200039000000400030043f000000e003d000390000000003030433000005590030009c000013a30000213d00000000033204360000010004d0003900000000040404330000055a004001980000055b0500004100000000050060190000055c06400197000000000565019f000000000045004b000013a30000c13d00000000004304350000012003d0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b000013a30000c13d000000400420003900000000003404350000014003d0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b000013a30000c13d000000600420003900000000003404350000016003d0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b000013a30000c13d00000080042000390000000000340435000000c0031000390000000000230435000000400200043d000005640020009c000013a50000213d000000a003200039000000400030043f0000018003d0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b000013a30000c13d0000000003320436000001a004d0003900000000040404330000056500400198000005660500004100000000050060190000056706400197000000000565019f000000000045004b000013a30000c13d0000000000430435000001c003d0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b000013a30000c13d00000040042000390000000000340435000001e003d0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b000013a30000c13d000000600420003900000000003404350000020003d0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b000013a30000c13d00000080042000390000000000340435000000e003100039000000000023043500000000020904330000000000c2004b000013b10000a13d0000000502c002100000000002a20019000000000101043300000530011001970000000000120435000000010cc0003900000000008c004b000012a60000413d0000000001090019000000000001042d0000000001000019000014be000104300000058801000041000000000010043f0000004101000039000000040010043f0000057501000041000014be000104300000058801000041000000000010043f0000002201000039000000040010043f0000057501000041000014be000104300000058801000041000000000010043f0000003201000039000000040010043f0000057501000041000014be000104300007000000000002000000a307000039000000000807041a000005a40080009c0000149f0000813d00000005018002100000003f021000390000055e02200197000000400900043d0000000002290019000000000092004b000000000300003900000001030040390000055d0020009c0000149f0000213d00000001003001900000149f0000c13d000000400020043f000000000a8904360000001f0210018f000000000001004b000013d50000613d00000000011a00190000000003000031000000010330036700000000040a0019000000003503043c0000000004540436000000000014004b000013d10000c13d000000000002004b000000000008004b0000149b0000613d000000200b00008a000000000c000019000300000008001d000200000009001d00010000000a001d000000000070043f0000057a01c0009a000000000201041a000000010320019000000001062002700000007f0660618f0000001f0060008c00000000040000390000000104002039000000000043004b000014a50000c13d000000400d00043d00000000056d0436000000000003004b000014100000613d000400000006001d000000000010043f0000000001000414000005300010009c0000053001008041000000c00110021000000561011001c7000080100200003900070000000c001d00060000000d001d000500000005001d14bc14b70000040f0000000505000029000000060d000029000000070c000029000000200b00008a000000010a00002900000002090000290000000308000029000000a30700003900000001002001900000149d0000613d0000000406000029000000000006004b000014140000613d000000000201043b00000000010000190000000003510019000000000402041a000000000043043500000001022000390000002001100039000000000061004b000014070000413d0000000001510019000014150000013d0000004001d00039000005a0022001970000000000250435000014150000013d00000000015000190000000001d100490000001f011000390000000002b1016f0000000001d20019000000000021004b000000000200003900000001020040390000055d0010009c0000149f0000213d00000001002001900000149f0000c13d000000400010043f00000000020d0433000005620020009c0000149d0000213d000001400020008c0000149d0000413d000005820010009c0000149f0000213d000000c002100039000000400020043f0000000002050433000005300020009c0000149d0000213d00000000022104360000004003d000390000000003030433000005590030009c0000149d0000213d00000000003204350000006002d0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000149d0000c13d000000400310003900000000002304350000008002d0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000149d0000c13d00000060031000390000000000230435000000a002d0003900000000020204330000055a002001980000055b0300004100000000030060190000055c04200197000000000343019f000000000023004b0000149d0000c13d00000080031000390000000000230435000000400200043d000005640020009c0000149f0000213d000000a003200039000000400030043f000000c003d0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000149d0000c13d0000000003320436000000e004d0003900000000040404330000056500400198000005660500004100000000050060190000056706400197000000000565019f000000000045004b0000149d0000c13d00000000004304350000010003d0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000149d0000c13d000000400420003900000000003404350000012003d0003900000000030304330000056500300198000005660400004100000000040060190000056705300197000000000454019f000000000034004b0000149d0000c13d000000600420003900000000003404350000014003d0003900000000030304330000055a003001980000055b0400004100000000040060190000055c05300197000000000454019f000000000034004b0000149d0000c13d00000080042000390000000000340435000000a003100039000000000023043500000000020904330000000000c2004b000014ab0000a13d0000000502c002100000000002a20019000000000101043300000530011001970000000000120435000000010cc0003900000000008c004b000013dd0000413d0000000001090019000000000001042d0000000001000019000014be000104300000058801000041000000000010043f0000004101000039000000040010043f0000057501000041000014be000104300000058801000041000000000010043f0000002201000039000000040010043f0000057501000041000014be000104300000058801000041000000000010043f0000003201000039000000040010043f0000057501000041000014be00010430000000000001042f000014b5002104210000000102000039000000000001042d0000000002000019000000000001042d000014ba002104230000000102000039000000000001042d0000000002000019000000000001042d000014bc00000432000014bd0001042e000014be00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff08c379a000000000000000000000000000000000000000000000000000000000496e697469616c697a61626c653a20636f6e747261637420697320696e697469616c697a696e6700000000000000000000000000000000000000000000000000000000000000000000000000000000000000008400000080000000000000000002000000000000000000000000000000000000200000008000000000000000007f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024980000000200000000000000000000000000000040000001000000000000000000000000000000000000000000000000000000000000000000000000008ab3daad00000000000000000000000000000000000000000000000000000000be13bac300000000000000000000000000000000000000000000000000000000e77bdc6d00000000000000000000000000000000000000000000000000000000fb1cb04800000000000000000000000000000000000000000000000000000000fb1cb04900000000000000000000000000000000000000000000000000000000ffcf786a00000000000000000000000000000000000000000000000000000000e77bdc6e00000000000000000000000000000000000000000000000000000000f2fde38b00000000000000000000000000000000000000000000000000000000be13bac400000000000000000000000000000000000000000000000000000000c227db9600000000000000000000000000000000000000000000000000000000d4a5ea8500000000000000000000000000000000000000000000000000000000905d8a4400000000000000000000000000000000000000000000000000000000905d8a4500000000000000000000000000000000000000000000000000000000b496fbb400000000000000000000000000000000000000000000000000000000bbef84b4000000000000000000000000000000000000000000000000000000008ab3daae000000000000000000000000000000000000000000000000000000008ca46725000000000000000000000000000000000000000000000000000000008da5cb5b0000000000000000000000000000000000000000000000000000000056e49ef200000000000000000000000000000000000000000000000000000000788d424700000000000000000000000000000000000000000000000000000000788d4248000000000000000000000000000000000000000000000000000000007b674c7a0000000000000000000000000000000000000000000000000000000082d12cf80000000000000000000000000000000000000000000000000000000056e49ef3000000000000000000000000000000000000000000000000000000006c6c654e00000000000000000000000000000000000000000000000000000000715018a6000000000000000000000000000000000000000000000000000000003d141d37000000000000000000000000000000000000000000000000000000003d141d380000000000000000000000000000000000000000000000000000000050e82790000000000000000000000000000000000000000000000000000000005312b91f000000000000000000000000000000000000000000000000000000000577559f000000000000000000000000000000000000000000000000000000003392c5850000000000000000000000000000000000000000000000000000000035876476000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000080000000000000000000000000000000ffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000007fffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000ffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000000000000000000ffffffffffffff7f550b0a72166cff30523ba7a8aa0c892a058b82a43a9e2a42628ef21e06e40bd302000000000000000000000000000000000000200000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000fffffffffffffeff000000000000000000000000000000000000000000000000ffffffffffffff5f0000000000000000000000000000000000000000000000000000000080000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000007fffffff63652e0000000000000000000000000000000000000000000000000000000000747279696e6720746f2061646420612073706f742070726f64756374207477690000000000000000000000000000000000000084000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffddf00000000000000000000000000000000000000200000008000000000000000004f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573730000000000000000000000000000000000000000000000000000aaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42eaaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42d5523c84745a9ba19d0bab97fd24ddea6c56b8d633403a5682fec9a57709b67881806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b830200000200000000000000000000000000000024000000000000000000000000c227db9600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffe0be13bac4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000bbef84b4000000000000000000000000000000000000000000000000000000009f7a6e773001d680bb221d60d2d79a9cb9de0d9deafb63514cfb3345a99574e960859188cffe297f44dde29f2d2865634621f26215049caeb304ccba566a8b1860859188cffe297f44dde29f2d2865634621f26215049caeb304ccba566a8b178000000000000000000000000000000000000000000000000000000000000000c55607b50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000073656e646572206d757374206265206465706c6f7965720000000000000000000000000000000000000000000000000000000064000000800000000000000000000000000000000000000000000000000000000000000000ffffffffffffff3f747279696e6720746f20616464206120706572702070726f6475637420747769000000000000000000000000000000000000000000000000fffffffffffffe9f73706f7420696420646f65736e2774206d617463682e00000000000000000000d386c1e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002040000000000000000000000004e487b71000000000000000000000000000000000000000000000000000000007065727020696420646f65736e2774206d617463682e0000000000000000000003a1870000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000002000000000000000000000000000000000000000000008000000000000000008be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff1b00000000000000000000000000000000000000000000000000000000000000e604ed9e0000000000000000000000000000000000000000000000000000000056e49ef300000000000000000000000000000000000000000000000000000000fba56008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000800000000000000000fd8c52cd000000000000000000000000000000000000000000000000000000004f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572647920696e697469616c697a6564000000000000000000000000000000000000496e697469616c697a61626c653a20636f6e747261637420697320616c7265616578706563746564206465706c6f79656420746f20696e697469616c697a650002000000000000000000000000000000000000000000000000000000000000006e697469616c697a696e67000000000000000000000000000000000000000000496e697469616c697a61626c653a20636f6e7472616374206973206e6f7420690000000000000000000000000000000000000064000001200000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f649879aadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f64987800000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000ea703cf9612deb418509b3032b39e663d571f29d1581ca79fd7eb1e600e3b744
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.