More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
14533937 | 1016 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
PrimitiveEngine
Compiler Version
v0.8.6+commit.11564f7e
Optimization Enabled:
Yes with 1000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.6; import "./libraries/Margin.sol"; import "./libraries/ReplicationMath.sol"; import "./libraries/Reserve.sol"; import "./libraries/SafeCast.sol"; import "./libraries/Transfers.sol"; import "./libraries/Units.sol"; import "./interfaces/callback/IPrimitiveCreateCallback.sol"; import "./interfaces/callback/IPrimitiveDepositCallback.sol"; import "./interfaces/callback/IPrimitiveLiquidityCallback.sol"; import "./interfaces/callback/IPrimitiveSwapCallback.sol"; import "./interfaces/IERC20.sol"; import "./interfaces/IPrimitiveEngine.sol"; import "./interfaces/IPrimitiveFactory.sol"; /// @title Primitive Engine /// @author Primitive /// @notice Replicating Market Maker /// @dev RMM-01 contract PrimitiveEngine is IPrimitiveEngine { using ReplicationMath for int128; using Units for uint256; using SafeCast for uint256; using Reserve for mapping(bytes32 => Reserve.Data); using Reserve for Reserve.Data; using Margin for mapping(address => Margin.Data); using Margin for Margin.Data; using Transfers for IERC20; /// @dev Parameters of each pool /// @param strike Strike price of pool with stable token decimals /// @param sigma Implied volatility, with 1e4 decimals such that 10000 = 100% /// @param maturity Timestamp of pool expiration, in seconds /// @param lastTimestamp Timestamp of the pool's last update, in seconds /// @param gamma Multiplied against deltaIn amounts to apply swap fee, gamma = 1 - fee %, scaled up by 1e4 struct Calibration { uint128 strike; uint32 sigma; uint32 maturity; uint32 lastTimestamp; uint32 gamma; } /// @inheritdoc IPrimitiveEngineView uint256 public constant override PRECISION = 10**18; /// @inheritdoc IPrimitiveEngineView uint256 public constant override BUFFER = 120 seconds; /// @inheritdoc IPrimitiveEngineView uint256 public immutable override MIN_LIQUIDITY; /// @inheritdoc IPrimitiveEngineView uint256 public immutable override scaleFactorRisky; /// @inheritdoc IPrimitiveEngineView uint256 public immutable override scaleFactorStable; /// @inheritdoc IPrimitiveEngineView address public immutable override factory; /// @inheritdoc IPrimitiveEngineView address public immutable override risky; /// @inheritdoc IPrimitiveEngineView address public immutable override stable; /// @dev Reentrancy guard initialized to state uint256 private locked = 1; /// @inheritdoc IPrimitiveEngineView mapping(bytes32 => Calibration) public override calibrations; /// @inheritdoc IPrimitiveEngineView mapping(address => Margin.Data) public override margins; /// @inheritdoc IPrimitiveEngineView mapping(bytes32 => Reserve.Data) public override reserves; /// @inheritdoc IPrimitiveEngineView mapping(address => mapping(bytes32 => uint256)) public override liquidity; modifier lock() { if (locked != 1) revert LockedError(); locked = 2; _; locked = 1; } /// @notice Deploys an Engine with two tokens, a 'Risky' and 'Stable' constructor() { (factory, risky, stable, scaleFactorRisky, scaleFactorStable, MIN_LIQUIDITY) = IPrimitiveFactory(msg.sender) .args(); } /// @return Risky token balance of this contract function balanceRisky() private view returns (uint256) { (bool success, bytes memory data) = risky.staticcall( abi.encodeWithSelector(IERC20.balanceOf.selector, address(this)) ); if (!success || data.length != 32) revert BalanceError(); return abi.decode(data, (uint256)); } /// @return Stable token balance of this contract function balanceStable() private view returns (uint256) { (bool success, bytes memory data) = stable.staticcall( abi.encodeWithSelector(IERC20.balanceOf.selector, address(this)) ); if (!success || data.length != 32) revert BalanceError(); return abi.decode(data, (uint256)); } /// @notice Revert if expected amount does not exceed current balance function checkRiskyBalance(uint256 expectedRisky) private view { uint256 actualRisky = balanceRisky(); if (actualRisky < expectedRisky) revert RiskyBalanceError(expectedRisky, actualRisky); } /// @notice Revert if expected amount does not exceed current balance function checkStableBalance(uint256 expectedStable) private view { uint256 actualStable = balanceStable(); if (actualStable < expectedStable) revert StableBalanceError(expectedStable, actualStable); } /// @return blockTimestamp casted as a uint32 function _blockTimestamp() internal view virtual returns (uint32 blockTimestamp) { // solhint-disable-next-line blockTimestamp = uint32(block.timestamp); } /// @inheritdoc IPrimitiveEngineActions function updateLastTimestamp(bytes32 poolId) external override lock returns (uint32 lastTimestamp) { lastTimestamp = _updateLastTimestamp(poolId); } /// @notice Sets the lastTimestamp of `poolId` to `block.timestamp`, max value is `maturity` /// @return lastTimestamp of the pool, used in calculating the time until expiry function _updateLastTimestamp(bytes32 poolId) internal virtual returns (uint32 lastTimestamp) { Calibration storage cal = calibrations[poolId]; if (cal.lastTimestamp == 0) revert UninitializedError(); lastTimestamp = _blockTimestamp(); uint32 maturity = cal.maturity; if (lastTimestamp > maturity) lastTimestamp = maturity; // if expired, set to the maturity cal.lastTimestamp = lastTimestamp; // set state emit UpdateLastTimestamp(poolId); } /// @inheritdoc IPrimitiveEngineActions function create( uint128 strike, uint32 sigma, uint32 maturity, uint32 gamma, uint256 riskyPerLp, uint256 delLiquidity, bytes calldata data ) external override lock returns ( bytes32 poolId, uint256 delRisky, uint256 delStable ) { (uint256 factor0, uint256 factor1) = (scaleFactorRisky, scaleFactorStable); poolId = keccak256(abi.encodePacked(address(this), strike, sigma, maturity, gamma)); if (calibrations[poolId].lastTimestamp != 0) revert PoolDuplicateError(); if (sigma > 1e7 || sigma < 1) revert SigmaError(sigma); if (strike == 0) revert StrikeError(strike); if (delLiquidity <= MIN_LIQUIDITY) revert MinLiquidityError(delLiquidity); if (riskyPerLp > PRECISION / factor0 || riskyPerLp == 0) revert RiskyPerLpError(riskyPerLp); if (gamma > Units.PERCENTAGE || gamma < 9000) revert GammaError(gamma); Calibration memory cal = Calibration({ strike: strike, sigma: sigma, maturity: maturity, lastTimestamp: _blockTimestamp(), gamma: gamma }); if (cal.lastTimestamp > cal.maturity) revert PoolExpiredError(); uint32 tau = cal.maturity - cal.lastTimestamp; // time until expiry delStable = ReplicationMath.getStableGivenRisky(0, factor0, factor1, riskyPerLp, cal.strike, cal.sigma, tau); delRisky = (riskyPerLp * delLiquidity) / PRECISION; // riskyDecimals * 1e18 decimals / 1e18 = riskyDecimals delStable = (delStable * delLiquidity) / PRECISION; if (delRisky == 0 || delStable == 0) revert CalibrationError(delRisky, delStable); calibrations[poolId] = cal; // state update uint256 amount = delLiquidity - MIN_LIQUIDITY; liquidity[msg.sender][poolId] += amount; // burn min liquidity, at cost of msg.sender reserves[poolId].allocate(delRisky, delStable, delLiquidity, cal.lastTimestamp); // state update (uint256 balRisky, uint256 balStable) = (balanceRisky(), balanceStable()); IPrimitiveCreateCallback(msg.sender).createCallback(delRisky, delStable, data); checkRiskyBalance(balRisky + delRisky); checkStableBalance(balStable + delStable); emit Create(msg.sender, cal.strike, cal.sigma, cal.maturity, cal.gamma, delRisky, delStable, amount); } // ===== Margin ===== /// @inheritdoc IPrimitiveEngineActions function deposit( address recipient, uint256 delRisky, uint256 delStable, bytes calldata data ) external override lock { if (delRisky == 0 && delStable == 0) revert ZeroDeltasError(); margins[recipient].deposit(delRisky, delStable); // state update uint256 balRisky; uint256 balStable; if (delRisky != 0) balRisky = balanceRisky(); if (delStable != 0) balStable = balanceStable(); IPrimitiveDepositCallback(msg.sender).depositCallback(delRisky, delStable, data); // agnostic payment if (delRisky != 0) checkRiskyBalance(balRisky + delRisky); if (delStable != 0) checkStableBalance(balStable + delStable); emit Deposit(msg.sender, recipient, delRisky, delStable); } /// @inheritdoc IPrimitiveEngineActions function withdraw( address recipient, uint256 delRisky, uint256 delStable ) external override lock { if (delRisky == 0 && delStable == 0) revert ZeroDeltasError(); margins.withdraw(delRisky, delStable); // state update if (delRisky != 0) IERC20(risky).safeTransfer(recipient, delRisky); if (delStable != 0) IERC20(stable).safeTransfer(recipient, delStable); emit Withdraw(msg.sender, recipient, delRisky, delStable); } // ===== Liquidity ===== /// @inheritdoc IPrimitiveEngineActions function allocate( bytes32 poolId, address recipient, uint256 delRisky, uint256 delStable, bool fromMargin, bytes calldata data ) external override lock returns (uint256 delLiquidity) { if (delRisky == 0 || delStable == 0) revert ZeroDeltasError(); Reserve.Data storage reserve = reserves[poolId]; if (reserve.blockTimestamp == 0) revert UninitializedError(); uint32 timestamp = _blockTimestamp(); uint256 liquidity0 = (delRisky * reserve.liquidity) / uint256(reserve.reserveRisky); uint256 liquidity1 = (delStable * reserve.liquidity) / uint256(reserve.reserveStable); delLiquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1; if (delLiquidity == 0) revert ZeroLiquidityError(); liquidity[recipient][poolId] += delLiquidity; // increase position liquidity reserve.allocate(delRisky, delStable, delLiquidity, timestamp); // increase reserves and liquidity if (fromMargin) { margins.withdraw(delRisky, delStable); // removes tokens from `msg.sender` margin account } else { (uint256 balRisky, uint256 balStable) = (balanceRisky(), balanceStable()); IPrimitiveLiquidityCallback(msg.sender).allocateCallback(delRisky, delStable, data); // agnostic payment checkRiskyBalance(balRisky + delRisky); checkStableBalance(balStable + delStable); } emit Allocate(msg.sender, recipient, poolId, delRisky, delStable, delLiquidity); } /// @inheritdoc IPrimitiveEngineActions function remove(bytes32 poolId, uint256 delLiquidity) external override lock returns (uint256 delRisky, uint256 delStable) { if (delLiquidity == 0) revert ZeroLiquidityError(); Reserve.Data storage reserve = reserves[poolId]; if (reserve.blockTimestamp == 0) revert UninitializedError(); (delRisky, delStable) = reserve.getAmounts(delLiquidity); liquidity[msg.sender][poolId] -= delLiquidity; // state update reserve.remove(delRisky, delStable, delLiquidity, _blockTimestamp()); margins[msg.sender].deposit(delRisky, delStable); emit Remove(msg.sender, poolId, delRisky, delStable, delLiquidity); } struct SwapDetails { address recipient; bool riskyForStable; bool fromMargin; bool toMargin; uint32 timestamp; bytes32 poolId; uint256 deltaIn; uint256 deltaOut; } /// @inheritdoc IPrimitiveEngineActions function swap( address recipient, bytes32 poolId, bool riskyForStable, uint256 deltaIn, uint256 deltaOut, bool fromMargin, bool toMargin, bytes calldata data ) external override lock { if (deltaIn == 0) revert DeltaInError(); if (deltaOut == 0) revert DeltaOutError(); SwapDetails memory details = SwapDetails({ recipient: recipient, poolId: poolId, deltaIn: deltaIn, deltaOut: deltaOut, riskyForStable: riskyForStable, fromMargin: fromMargin, toMargin: toMargin, timestamp: _blockTimestamp() }); uint32 lastTimestamp = _updateLastTimestamp(details.poolId); // updates lastTimestamp of `poolId` if (details.timestamp > lastTimestamp + BUFFER) revert PoolExpiredError(); // 120s buffer to allow final swaps int128 invariantX64 = invariantOf(details.poolId); // stored in memory to perform the invariant check { // swap scope, avoids stack too deep errors Calibration memory cal = calibrations[details.poolId]; Reserve.Data storage reserve = reserves[details.poolId]; uint32 tau = cal.maturity - cal.lastTimestamp; uint256 deltaInWithFee = (details.deltaIn * cal.gamma) / Units.PERCENTAGE; // amount * (1 - fee %) uint256 adjustedRisky; uint256 adjustedStable; if (details.riskyForStable) { adjustedRisky = uint256(reserve.reserveRisky) + deltaInWithFee; adjustedStable = uint256(reserve.reserveStable) - deltaOut; } else { adjustedRisky = uint256(reserve.reserveRisky) - deltaOut; adjustedStable = uint256(reserve.reserveStable) + deltaInWithFee; } adjustedRisky = (adjustedRisky * PRECISION) / reserve.liquidity; adjustedStable = (adjustedStable * PRECISION) / reserve.liquidity; int128 invariantAfter = ReplicationMath.calcInvariant( scaleFactorRisky, scaleFactorStable, adjustedRisky, adjustedStable, cal.strike, cal.sigma, tau ); if (invariantX64 > invariantAfter) revert InvariantError(invariantX64, invariantAfter); reserve.swap(details.riskyForStable, details.deltaIn, details.deltaOut, details.timestamp); // state update } if (details.riskyForStable) { if (details.toMargin) { margins[details.recipient].deposit(0, details.deltaOut); } else { IERC20(stable).safeTransfer(details.recipient, details.deltaOut); // optimistic transfer out } if (details.fromMargin) { margins.withdraw(details.deltaIn, 0); // pay for swap } else { uint256 balRisky = balanceRisky(); IPrimitiveSwapCallback(msg.sender).swapCallback(details.deltaIn, 0, data); // agnostic transfer in checkRiskyBalance(balRisky + details.deltaIn); } } else { if (details.toMargin) { margins[details.recipient].deposit(details.deltaOut, 0); } else { IERC20(risky).safeTransfer(details.recipient, details.deltaOut); // optimistic transfer out } if (details.fromMargin) { margins.withdraw(0, details.deltaIn); // pay for swap } else { uint256 balStable = balanceStable(); IPrimitiveSwapCallback(msg.sender).swapCallback(0, details.deltaIn, data); // agnostic transfer in checkStableBalance(balStable + details.deltaIn); } } emit Swap( msg.sender, details.recipient, details.poolId, details.riskyForStable, details.deltaIn, details.deltaOut ); } // ===== View ===== /// @inheritdoc IPrimitiveEngineView function invariantOf(bytes32 poolId) public view override returns (int128 invariant) { Calibration memory cal = calibrations[poolId]; uint32 tau = cal.maturity - cal.lastTimestamp; // cal maturity can never be less than lastTimestamp (uint256 riskyPerLiquidity, uint256 stablePerLiquidity) = reserves[poolId].getAmounts(PRECISION); // 1e18 liquidity invariant = ReplicationMath.calcInvariant( scaleFactorRisky, scaleFactorStable, riskyPerLiquidity, stablePerLiquidity, cal.strike, cal.sigma, tau ); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "./SafeCast.sol"; /// @title Margin Library /// @author Primitive /// @dev Uses a data struct with two uint128s to optimize for one storage slot library Margin { using SafeCast for uint256; struct Data { uint128 balanceRisky; // Balance of the risky token, aka underlying asset uint128 balanceStable; // Balance of the stable token, aka "quote" asset } /// @notice Adds to risky and stable token balances /// @param margin Margin data of an account in storage to manipulate /// @param delRisky Amount of risky tokens to add to margin /// @param delStable Amount of stable tokens to add to margin function deposit( Data storage margin, uint256 delRisky, uint256 delStable ) internal { if (delRisky != 0) margin.balanceRisky += delRisky.toUint128(); if (delStable != 0) margin.balanceStable += delStable.toUint128(); } /// @notice Removes risky and stable token balance from `msg.sender`'s internal margin account /// @param margins Margin data mapping, uses `msg.sender`'s margin account /// @param delRisky Amount of risky tokens to subtract from margin /// @param delStable Amount of stable tokens to subtract from margin /// @return margin Data storage of a margin account function withdraw( mapping(address => Data) storage margins, uint256 delRisky, uint256 delStable ) internal returns (Data storage margin) { margin = margins[msg.sender]; if (delRisky != 0) margin.balanceRisky -= delRisky.toUint128(); if (delStable != 0) margin.balanceStable -= delStable.toUint128(); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.4; import "./ABDKMath64x64.sol"; import "./CumulativeNormalDistribution.sol"; import "./Units.sol"; /// @title Replication Math /// @author Primitive /// @notice Alex Evans, Guillermo Angeris, and Tarun Chitra. Replicating Market Makers. /// https://stanford.edu/~guillean/papers/rmms.pdf library ReplicationMath { using ABDKMath64x64 for int128; using ABDKMath64x64 for uint256; using CumulativeNormalDistribution for int128; using Units for int128; using Units for uint256; int128 internal constant ONE_INT = 0x10000000000000000; /// @notice Normalizes volatility with respect to square root of time until expiry /// @param sigma Unsigned 256-bit percentage as an integer with precision of 1e4, 10000 = 100% /// @param tau Time until expiry in seconds as an unsigned 256-bit integer /// @return vol Signed fixed point 64.64 number equal to sigma * sqrt(tau) function getProportionalVolatility(uint256 sigma, uint256 tau) internal pure returns (int128 vol) { int128 sqrtTauX64 = tau.toYears().sqrt(); int128 sigmaX64 = sigma.percentageToX64(); vol = sigmaX64.mul(sqrtTauX64); } /// @notice Uses riskyPerLiquidity and invariant to calculate stablePerLiquidity /// @dev Converts unsigned 256-bit values to fixed point 64.64 numbers w/ decimals of precision /// @param invariantLastX64 Signed 64.64 fixed point number. Calculated w/ same `tau` as the parameter `tau` /// @param scaleFactorRisky Unsigned 256-bit integer scaling factor for `risky`, 10^(18 - risky.decimals()) /// @param scaleFactorStable Unsigned 256-bit integer scaling factor for `stable`, 10^(18 - stable.decimals()) /// @param riskyPerLiquidity Unsigned 256-bit integer of Pool's risky reserves *per liquidity*, 0 <= x <= 1 /// @param strike Unsigned 256-bit integer value with precision equal to 10^(18 - scaleFactorStable) /// @param sigma Volatility of the Pool as an unsigned 256-bit integer w/ precision of 1e4, 10000 = 100% /// @param tau Time until expiry in seconds as an unsigned 256-bit integer /// @return stablePerLiquidity = K*CDF(CDF^-1(1 - riskyPerLiquidity) - sigma*sqrt(tau)) + invariantLastX64 as uint function getStableGivenRisky( int128 invariantLastX64, uint256 scaleFactorRisky, uint256 scaleFactorStable, uint256 riskyPerLiquidity, uint256 strike, uint256 sigma, uint256 tau ) internal pure returns (uint256 stablePerLiquidity) { int128 strikeX64 = strike.scaleToX64(scaleFactorStable); int128 riskyX64 = riskyPerLiquidity.scaleToX64(scaleFactorRisky); // mul by 2^64, div by precision int128 oneMinusRiskyX64 = ONE_INT.sub(riskyX64); if (tau != 0) { int128 volX64 = getProportionalVolatility(sigma, tau); int128 phi = oneMinusRiskyX64.getInverseCDF(); int128 input = phi.sub(volX64); int128 stableX64 = strikeX64.mul(input.getCDF()).add(invariantLastX64); stablePerLiquidity = stableX64.scaleFromX64(scaleFactorStable); } else { stablePerLiquidity = (strikeX64.mul(oneMinusRiskyX64).add(invariantLastX64)).scaleFromX64( scaleFactorStable ); } } /// @notice Calculates the invariant of a curve /// @dev Per unit of replication, aka per unit of liquidity /// @param scaleFactorRisky Unsigned 256-bit integer scaling factor for `risky`, 10^(18 - risky.decimals()) /// @param scaleFactorStable Unsigned 256-bit integer scaling factor for `stable`, 10^(18 - stable.decimals()) /// @param riskyPerLiquidity Unsigned 256-bit integer of Pool's risky reserves *per liquidity*, 0 <= x <= 1 /// @param stablePerLiquidity Unsigned 256-bit integer of Pool's stable reserves *per liquidity*, 0 <= x <= strike /// @return invariantX64 = stablePerLiquidity - K * CDF(CDF^-1(1 - riskyPerLiquidity) - sigma * sqrt(tau)) function calcInvariant( uint256 scaleFactorRisky, uint256 scaleFactorStable, uint256 riskyPerLiquidity, uint256 stablePerLiquidity, uint256 strike, uint256 sigma, uint256 tau ) internal pure returns (int128 invariantX64) { uint256 output = getStableGivenRisky( 0, scaleFactorRisky, scaleFactorStable, riskyPerLiquidity, strike, sigma, tau ); int128 outputX64 = output.scaleToX64(scaleFactorStable); int128 stableX64 = stablePerLiquidity.scaleToX64(scaleFactorStable); invariantX64 = stableX64.sub(outputX64); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "./SafeCast.sol"; /// @title Reserves Library /// @author Primitive /// @dev Data structure library for an Engine's Reserves library Reserve { using SafeCast for uint256; /// @notice Stores global state of a pool /// @param reserveRisky Risky token reserve /// @param reserveStable Stable token reserve /// @param liquidity Total supply of liquidity /// @param blockTimestamp Last timestamp of which updated the accumulators /// @param cumulativeRisky Cumulative sum of the risky reserves /// @param cumulativeStable Cumulative sum of stable reserves /// @param cumulativeLiquidity Cumulative sum of total liquidity supply struct Data { uint128 reserveRisky; uint128 reserveStable; uint128 liquidity; uint32 blockTimestamp; uint256 cumulativeRisky; uint256 cumulativeStable; uint256 cumulativeLiquidity; } /// @notice Adds to the cumulative reserves /// @dev Overflow is desired on the cumulative values /// @param res Reserve storage to update /// @param blockTimestamp Checkpoint timestamp of update function update(Data storage res, uint32 blockTimestamp) internal { uint32 deltaTime = blockTimestamp - res.blockTimestamp; // overflow is desired if (deltaTime != 0) { unchecked { res.cumulativeRisky += uint256(res.reserveRisky) * deltaTime; res.cumulativeStable += uint256(res.reserveStable) * deltaTime; res.cumulativeLiquidity += uint256(res.liquidity) * deltaTime; } res.blockTimestamp = blockTimestamp; } } /// @notice Increases one reserve value and decreases the other /// @param reserve Reserve state to update /// @param riskyForStable Direction of swap /// @param deltaIn Amount of tokens paid, increases one reserve by /// @param deltaOut Amount of tokens sent out, decreases the other reserve by /// @param blockTimestamp Timestamp used to update cumulative reserves function swap( Data storage reserve, bool riskyForStable, uint256 deltaIn, uint256 deltaOut, uint32 blockTimestamp ) internal { update(reserve, blockTimestamp); if (riskyForStable) { reserve.reserveRisky += deltaIn.toUint128(); reserve.reserveStable -= deltaOut.toUint128(); } else { reserve.reserveRisky -= deltaOut.toUint128(); reserve.reserveStable += deltaIn.toUint128(); } } /// @notice Add to both reserves and total supply of liquidity /// @param reserve Reserve storage to manipulate /// @param delRisky Amount of risky tokens to add to the reserve /// @param delStable Amount of stable tokens to add to the reserve /// @param delLiquidity Amount of liquidity created with the provided tokens /// @param blockTimestamp Timestamp used to update cumulative reserves function allocate( Data storage reserve, uint256 delRisky, uint256 delStable, uint256 delLiquidity, uint32 blockTimestamp ) internal { update(reserve, blockTimestamp); reserve.reserveRisky += delRisky.toUint128(); reserve.reserveStable += delStable.toUint128(); reserve.liquidity += delLiquidity.toUint128(); } /// @notice Remove from both reserves and total supply of liquidity /// @param reserve Reserve storage to manipulate /// @param delRisky Amount of risky tokens to remove to the reserve /// @param delStable Amount of stable tokens to remove to the reserve /// @param delLiquidity Amount of liquidity removed from total supply /// @param blockTimestamp Timestamp used to update cumulative reserves function remove( Data storage reserve, uint256 delRisky, uint256 delStable, uint256 delLiquidity, uint32 blockTimestamp ) internal { update(reserve, blockTimestamp); reserve.reserveRisky -= delRisky.toUint128(); reserve.reserveStable -= delStable.toUint128(); reserve.liquidity -= delLiquidity.toUint128(); } /// @notice Calculates risky and stable token amounts of `delLiquidity` /// @param reserve Reserve in memory to use reserves and liquidity of /// @param delLiquidity Amount of liquidity to fetch underlying tokens of /// @return delRisky Amount of risky tokens controlled by `delLiquidity` /// @return delStable Amount of stable tokens controlled by `delLiquidity` function getAmounts(Data memory reserve, uint256 delLiquidity) internal pure returns (uint256 delRisky, uint256 delStable) { uint256 liq = uint256(reserve.liquidity); delRisky = (delLiquidity * uint256(reserve.reserveRisky)) / liq; delStable = (delLiquidity * uint256(reserve.reserveStable)) / liq; } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; /// @title SafeCast /// @notice Safely cast between uint256 and uint128 library SafeCast { /// @notice reverts if x > type(uint128).max function toUint128(uint256 x) internal pure returns (uint128 z) { require(x <= type(uint128).max); z = uint128(x); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.6.0; import "../interfaces/IERC20.sol"; /// @title Transfers library Transfers { /// @notice Performs an ERC20 `transfer` call and checks return data /// @param token ERC20 token to transfer /// @param to Recipient of the ERC20 token /// @param value Amount of ERC20 to transfer function safeTransfer( IERC20 token, address to, uint256 value ) internal { (bool success, bytes memory returnData) = address(token).call( abi.encodeWithSelector(token.transfer.selector, to, value) ); require(success && (returnData.length == 0 || abi.decode(returnData, (bool))), "Transfer fail"); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "./ABDKMath64x64.sol"; /// @title Units library /// @author Primitive /// @notice Utility functions for unit conversions library Units { using ABDKMath64x64 for int128; using ABDKMath64x64 for uint256; uint256 internal constant YEAR = 31556952; // 365.24219 ephemeris day = 1 year, in seconds uint256 internal constant PRECISION = 1e18; // precision to scale to uint256 internal constant PERCENTAGE = 1e4; // precision of percentages // ===== Unit Conversion ===== /// @notice Scales a wei value to a precision of 1e18 using the scaling factor /// @param value Unsigned 256-bit wei amount to convert with native decimals /// @param factor Scaling factor to multiply by, i.e. 10^(18 - value.decimals()) /// @return y Unsigned 256-bit wei amount scaled to a precision of 1e18 function scaleUp(uint256 value, uint256 factor) internal pure returns (uint256 y) { y = value * factor; } /// @notice Scales a wei value from a precision of 1e18 to 10^(18 - precision) /// @param value Unsigned 256-bit wei amount with 18 decimals /// @param factor Scaling factor to divide by, i.e. 10^(18 - value.decimals()) /// @return y Unsigned 256-bit wei amount scaled to 10^(18 - factor) function scaleDown(uint256 value, uint256 factor) internal pure returns (uint256 y) { y = value / factor; } /// @notice Converts unsigned 256-bit wei value into a fixed point 64.64 number /// @param value Unsigned 256-bit wei amount, in native precision /// @param factor Scaling factor for `value`, used to calculate decimals of `value` /// @return y Signed 64.64 fixed point number scaled from native precision function scaleToX64(uint256 value, uint256 factor) internal pure returns (int128 y) { uint256 scaleFactor = PRECISION / factor; y = value.divu(scaleFactor); } /// @notice Converts signed fixed point 64.64 number into unsigned 256-bit wei value /// @param value Signed fixed point 64.64 number to convert from precision of 10^18 /// @param factor Scaling factor for `value`, used to calculate decimals of `value` /// @return y Unsigned 256-bit wei amount scaled to native precision of 10^(18 - factor) function scaleFromX64(int128 value, uint256 factor) internal pure returns (uint256 y) { uint256 scaleFactor = PRECISION / factor; y = value.mulu(scaleFactor); } /// @notice Converts denormalized percentage integer to a fixed point 64.64 number /// @dev Convert unsigned 256-bit integer number into signed 64.64 fixed point number /// @param denorm Unsigned percentage integer with precision of 1e4 /// @return Signed 64.64 fixed point percentage with precision of 1e4 function percentageToX64(uint256 denorm) internal pure returns (int128) { return denorm.divu(PERCENTAGE); } /// @notice Converts unsigned seconds integer into years as a signed 64.64 fixed point number /// @dev Convert unsigned 256-bit integer number into signed 64.64 fixed point number /// @param s Unsigned 256-bit integer amount of seconds to convert into year units /// @return Fixed point 64.64 number of years equal to `seconds` function toYears(uint256 s) internal pure returns (int128) { return s.divu(YEAR); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; /// @title Primitive Create Callback /// @author Primitive interface IPrimitiveCreateCallback { /// @notice Triggered when creating a new pool for an Engine /// @param delRisky Amount of risky tokens required to initialize risky reserve /// @param delStable Amount of stable tokens required to initialize stable reserve /// @param data Calldata passed on create function call function createCallback( uint256 delRisky, uint256 delStable, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; /// @title Primitive Deposit Callback /// @author Primitive interface IPrimitiveDepositCallback { /// @notice Triggered when depositing tokens to an Engine /// @param delRisky Amount of risky tokens required to deposit to risky margin balance /// @param delStable Amount of stable tokens required to deposit to stable margin balance /// @param data Calldata passed on deposit function call function depositCallback( uint256 delRisky, uint256 delStable, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; /// @title Primitive Liquidity Callback /// @author Primitive interface IPrimitiveLiquidityCallback { /// @notice Triggered when providing liquidity to an Engine /// @param delRisky Amount of risky tokens required to provide to risky reserve /// @param delStable Amount of stable tokens required to provide to stable reserve /// @param data Calldata passed on allocate function call function allocateCallback( uint256 delRisky, uint256 delStable, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; /// @title Primitive Swap Callback /// @author Primitive interface IPrimitiveSwapCallback { /// @notice Triggered when swapping tokens in an Engine /// @param delRisky Amount of risky tokens required to pay the swap with /// @param delStable Amount of stable tokens required to pay the swap with /// @param data Calldata passed on swap function call function swapCallback( uint256 delRisky, uint256 delStable, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); function decimals() external view returns (uint8); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.4; import "./engine/IPrimitiveEngineActions.sol"; import "./engine/IPrimitiveEngineEvents.sol"; import "./engine/IPrimitiveEngineView.sol"; import "./engine/IPrimitiveEngineErrors.sol"; /// @title Primitive Engine Interface interface IPrimitiveEngine is IPrimitiveEngineActions, IPrimitiveEngineEvents, IPrimitiveEngineView, IPrimitiveEngineErrors { }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.4; /// @title Primitive Factory Interface /// @author Primitive interface IPrimitiveFactory { /// @notice Created a new engine contract! /// @param from Calling `msg.sender` of deploy /// @param risky Risky token of Engine to deploy /// @param stable Stable token of Engine to deploy /// @param engine Deployed engine address event DeployEngine(address indexed from, address indexed risky, address indexed stable, address engine); /// @notice Deploys a new Engine contract and sets the `getEngine` mapping for the tokens /// @param risky Risky token, the underlying token /// @param stable Stable token, the quote token function deploy(address risky, address stable) external returns (address engine); // ===== View ===== /// @notice Used to scale the minimum amount of liquidity to lowest precision /// @dev E.g. if the lowest decimal token is 6, min liquidity w/ 18 decimals /// cannot be 1000 wei, therefore the token decimals /// divided by the min liquidity factor is the amount of minimum liquidity /// MIN_LIQUIDITY = 10 ^ (Decimals / MIN_LIQUIDITY_FACTOR) function MIN_LIQUIDITY_FACTOR() external pure returns (uint256); /// @notice Called within Engine constructor so Engine can set immutable /// variables without constructor args /// @return factory Smart contract deploying the Engine contract /// @return risky Risky token /// @return stable Stable token /// @return scaleFactorRisky Scale factor of the risky token, 10^(18 - riskyTokenDecimals) /// @return scaleFactorStable Scale factor of the stable token, 10^(18 - stableTokenDecimals) /// @return minLiquidity Minimum amount of liquidity on pool creation function args() external view returns ( address factory, address risky, address stable, uint256 scaleFactorRisky, uint256 scaleFactorStable, uint256 minLiquidity ); /// @notice Fetches engine address of a token pair which has been deployed from this factory /// @param risky Risky token, the underlying token /// @param stable Stable token, the quote token /// @return engine Engine address for a risky and stable token function getEngine(address risky, address stable) external view returns (address engine); /// @notice Deployer does not have any access controls to wield /// @return Deployer of this factory contract function deployer() external view returns (address); }
// SPDX-License-Identifier: BSD-4-Clause /* * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. * Author: Mikhail Vladimirov <[email protected]> */ pragma solidity ^0.8.0; /** * Smart contract library of mathematical functions operating with signed * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is * basically a simple fraction whose numerator is signed 128-bit integer and * denominator is 2^64. As long as denominator is always the same, there is no * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are * represented by int128 type holding only the numerator. */ library ABDKMath64x64 { /* * Minimum value signed 64.64-bit fixed point number may have. */ int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; /* * Maximum value signed 64.64-bit fixed point number may have. */ int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /** * Convert signed 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromInt(int256 x) internal pure returns (int128) { unchecked { require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); return int128(x << 64); } } /** * Convert signed 64.64 fixed point number into signed 64-bit integer number * rounding down. * * @param x signed 64.64-bit fixed point number * @return signed 64-bit integer number */ function toInt(int128 x) internal pure returns (int64) { unchecked { return int64(x >> 64); } } /** * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromUInt(uint256 x) internal pure returns (int128) { unchecked { require(x <= 0x7FFFFFFFFFFFFFFF); return int128(int256(x << 64)); } } /** * Convert signed 64.64 fixed point number into unsigned 64-bit integer * number rounding down. Revert on underflow. * * @param x signed 64.64-bit fixed point number * @return unsigned 64-bit integer number */ function toUInt(int128 x) internal pure returns (uint64) { unchecked { require(x >= 0); return uint64(uint128(x >> 64)); } } /** * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point * number rounding down. Revert on overflow. * * @param x signed 128.128-bin fixed point number * @return signed 64.64-bit fixed point number */ function from128x128(int256 x) internal pure returns (int128) { unchecked { int256 result = x >> 64; require(result >= MIN_64x64 && result <= MAX_64x64); return int128(result); } } /** * Convert signed 64.64 fixed point number into signed 128.128 fixed point * number. * * @param x signed 64.64-bit fixed point number * @return signed 128.128 fixed point number */ function to128x128(int128 x) internal pure returns (int256) { unchecked { return int256(x) << 64; } } /** * Calculate x + y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function add(int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) + y; require(result >= MIN_64x64 && result <= MAX_64x64); return int128(result); } } /** * Calculate x - y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sub(int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) - y; require(result >= MIN_64x64 && result <= MAX_64x64); return int128(result); } } /** * Calculate x * y rounding down. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function mul(int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = (int256(x) * y) >> 64; require(result >= MIN_64x64 && result <= MAX_64x64); return int128(result); } } /** * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point * number and y is signed 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y signed 256-bit integer number * @return signed 256-bit integer number */ function muli(int128 x, int256 y) internal pure returns (int256) { unchecked { if (x == MIN_64x64) { require( y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && y <= 0x1000000000000000000000000000000000000000000000000 ); return -y << 63; } else { bool negativeResult = false; if (x < 0) { x = -x; negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint256 absoluteResult = mulu(x, uint256(y)); if (negativeResult) { require(absoluteResult <= 0x8000000000000000000000000000000000000000000000000000000000000000); return -int256(absoluteResult); // We rely on overflow behavior here } else { require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int256(absoluteResult); } } } } /** * Calculate x * y rounding down, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y unsigned 256-bit integer number * @return unsigned 256-bit integer number */ function mulu(int128 x, uint256 y) internal pure returns (uint256) { unchecked { if (y == 0) return 0; require(x >= 0); uint256 lo = (uint256(int256(x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; uint256 hi = uint256(int256(x)) * (y >> 128); require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); hi <<= 64; require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo); return hi + lo; } } /** * Calculate x / y rounding towards zero. Revert on overflow or when y is * zero. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function div(int128 x, int128 y) internal pure returns (int128) { unchecked { require(y != 0); int256 result = (int256(x) << 64) / y; require(result >= MIN_64x64 && result <= MAX_64x64); return int128(result); } } /** * Calculate x / y rounding towards zero, where x and y are signed 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x signed 256-bit integer number * @param y signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function divi(int256 x, int256 y) internal pure returns (int128) { unchecked { require(y != 0); bool negativeResult = false; if (x < 0) { x = -x; // We rely on overflow behavior here negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint128 absoluteResult = divuu(uint256(x), uint256(y)); if (negativeResult) { require(absoluteResult <= 0x80000000000000000000000000000000); return -int128(absoluteResult); // We rely on overflow behavior here } else { require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int128(absoluteResult); // We rely on overflow behavior here } } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function divu(uint256 x, uint256 y) internal pure returns (int128) { unchecked { require(y != 0); uint128 result = divuu(x, y); require(result <= uint128(MAX_64x64)); return int128(result); } } /** * Calculate -x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function neg(int128 x) internal pure returns (int128) { unchecked { require(x != MIN_64x64); return -x; } } /** * Calculate |x|. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function abs(int128 x) internal pure returns (int128) { unchecked { require(x != MIN_64x64); return x < 0 ? -x : x; } } /** * Calculate 1 / x rounding towards zero. Revert on overflow or when x is * zero. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function inv(int128 x) internal pure returns (int128) { unchecked { require(x != 0); int256 result = int256(0x100000000000000000000000000000000) / x; require(result >= MIN_64x64 && result <= MAX_64x64); return int128(result); } } /** * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function avg(int128 x, int128 y) internal pure returns (int128) { unchecked { return int128((int256(x) + int256(y)) >> 1); } } /** * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. * Revert on overflow or in case x * y is negative. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function gavg(int128 x, int128 y) internal pure returns (int128) { unchecked { int256 m = int256(x) * int256(y); require(m >= 0); require(m < 0x4000000000000000000000000000000000000000000000000000000000000000); return int128(sqrtu(uint256(m))); } } /** * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y uint256 value * @return signed 64.64-bit fixed point number */ function pow(int128 x, uint256 y) internal pure returns (int128) { unchecked { bool negative = x < 0 && y & 1 == 1; uint256 absX = uint128(x < 0 ? -x : x); uint256 absResult; absResult = 0x100000000000000000000000000000000; if (absX <= 0x10000000000000000) { absX <<= 63; while (y != 0) { if (y & 0x1 != 0) { absResult = (absResult * absX) >> 127; } absX = (absX * absX) >> 127; if (y & 0x2 != 0) { absResult = (absResult * absX) >> 127; } absX = (absX * absX) >> 127; if (y & 0x4 != 0) { absResult = (absResult * absX) >> 127; } absX = (absX * absX) >> 127; if (y & 0x8 != 0) { absResult = (absResult * absX) >> 127; } absX = (absX * absX) >> 127; y >>= 4; } absResult >>= 64; } else { uint256 absXShift = 63; if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; } if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; } if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; } if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; } if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; } if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; } uint256 resultShift = 0; while (y != 0) { require(absXShift < 64); if (y & 0x1 != 0) { absResult = (absResult * absX) >> 127; resultShift += absXShift; if (absResult > 0x100000000000000000000000000000000) { absResult >>= 1; resultShift += 1; } } absX = (absX * absX) >> 127; absXShift <<= 1; if (absX >= 0x100000000000000000000000000000000) { absX >>= 1; absXShift += 1; } y >>= 1; } require(resultShift < 64); absResult >>= 64 - resultShift; } int256 result = negative ? -int256(absResult) : int256(absResult); require(result >= MIN_64x64 && result <= MAX_64x64); return int128(result); } } /** * Calculate sqrt (x) rounding down. Revert if x < 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sqrt(int128 x) internal pure returns (int128) { unchecked { require(x >= 0); return int128(sqrtu(uint256(int256(x)) << 64)); } } /** * Calculate binary logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function log_2(int128 x) internal pure returns (int128) { unchecked { require(x > 0); int256 msb = 0; int256 xc = x; if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore int256 result = (msb - 64) << 64; uint256 ux = uint256(int256(x)) << uint256(127 - msb); for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { ux *= ux; uint256 b = ux >> 255; ux >>= 127 + b; result += bit * int256(b); } return int128(result); } } /** * Calculate natural logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function ln(int128 x) internal pure returns (int128) { unchecked { require(x > 0); return int128(int256((uint256(int256(log_2(x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128)); } } /** * Calculate binary exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp_2(int128 x) internal pure returns (int128) { unchecked { require(x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow uint256 result = 0x80000000000000000000000000000000; if (x & 0x8000000000000000 > 0) result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128; if (x & 0x4000000000000000 > 0) result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128; if (x & 0x2000000000000000 > 0) result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128; if (x & 0x1000000000000000 > 0) result = (result * 0x10B5586CF9890F6298B92B71842A98363) >> 128; if (x & 0x800000000000000 > 0) result = (result * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128; if (x & 0x400000000000000 > 0) result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128; if (x & 0x200000000000000 > 0) result = (result * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128; if (x & 0x100000000000000 > 0) result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128; if (x & 0x80000000000000 > 0) result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128; if (x & 0x40000000000000 > 0) result = (result * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128; if (x & 0x20000000000000 > 0) result = (result * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128; if (x & 0x10000000000000 > 0) result = (result * 0x1000B175EFFDC76BA38E31671CA939725) >> 128; if (x & 0x8000000000000 > 0) result = (result * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128; if (x & 0x4000000000000 > 0) result = (result * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128; if (x & 0x2000000000000 > 0) result = (result * 0x1000162E525EE054754457D5995292026) >> 128; if (x & 0x1000000000000 > 0) result = (result * 0x10000B17255775C040618BF4A4ADE83FC) >> 128; if (x & 0x800000000000 > 0) result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128; if (x & 0x400000000000 > 0) result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128; if (x & 0x200000000000 > 0) result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128; if (x & 0x100000000000 > 0) result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128; if (x & 0x80000000000 > 0) result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128; if (x & 0x40000000000 > 0) result = (result * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128; if (x & 0x20000000000 > 0) result = (result * 0x100000162E430E5A18F6119E3C02282A5) >> 128; if (x & 0x10000000000 > 0) result = (result * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128; if (x & 0x8000000000 > 0) result = (result * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128; if (x & 0x4000000000 > 0) result = (result * 0x10000002C5C8601CC6B9E94213C72737A) >> 128; if (x & 0x2000000000 > 0) result = (result * 0x1000000162E42FFF037DF38AA2B219F06) >> 128; if (x & 0x1000000000 > 0) result = (result * 0x10000000B17217FBA9C739AA5819F44F9) >> 128; if (x & 0x800000000 > 0) result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128; if (x & 0x400000000 > 0) result = (result * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128; if (x & 0x200000000 > 0) result = (result * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128; if (x & 0x100000000 > 0) result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128; if (x & 0x80000000 > 0) result = (result * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128; if (x & 0x40000000 > 0) result = (result * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128; if (x & 0x20000000 > 0) result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128; if (x & 0x10000000 > 0) result = (result * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128; if (x & 0x8000000 > 0) result = (result * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128; if (x & 0x4000000 > 0) result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128; if (x & 0x2000000 > 0) result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128; if (x & 0x1000000 > 0) result = (result * 0x10000000000B17217F7D20CF927C8E94C) >> 128; if (x & 0x800000 > 0) result = (result * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128; if (x & 0x400000 > 0) result = (result * 0x100000000002C5C85FDF477B662B26945) >> 128; if (x & 0x200000 > 0) result = (result * 0x10000000000162E42FEFA3AE53369388C) >> 128; if (x & 0x100000 > 0) result = (result * 0x100000000000B17217F7D1D351A389D40) >> 128; if (x & 0x80000 > 0) result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128; if (x & 0x40000 > 0) result = (result * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128; if (x & 0x20000 > 0) result = (result * 0x100000000000162E42FEFA39FE95583C2) >> 128; if (x & 0x10000 > 0) result = (result * 0x1000000000000B17217F7D1CFB72B45E1) >> 128; if (x & 0x8000 > 0) result = (result * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128; if (x & 0x4000 > 0) result = (result * 0x10000000000002C5C85FDF473E242EA38) >> 128; if (x & 0x2000 > 0) result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128; if (x & 0x1000 > 0) result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128; if (x & 0x800 > 0) result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128; if (x & 0x400 > 0) result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128; if (x & 0x200 > 0) result = (result * 0x10000000000000162E42FEFA39EF44D91) >> 128; if (x & 0x100 > 0) result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128; if (x & 0x80 > 0) result = (result * 0x10000000000000058B90BFBE8E7BCE544) >> 128; if (x & 0x40 > 0) result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128; if (x & 0x20 > 0) result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128; if (x & 0x10 > 0) result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128; if (x & 0x8 > 0) result = (result * 0x100000000000000058B90BFBE8E7BCD6D) >> 128; if (x & 0x4 > 0) result = (result * 0x10000000000000002C5C85FDF473DE6B2) >> 128; if (x & 0x2 > 0) result = (result * 0x1000000000000000162E42FEFA39EF358) >> 128; if (x & 0x1 > 0) result = (result * 0x10000000000000000B17217F7D1CF79AB) >> 128; result >>= uint256(int256(63 - (x >> 64))); require(result <= uint256(int256(MAX_64x64))); return int128(int256(result)); } } /** * Calculate natural exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp(int128 x) internal pure returns (int128) { unchecked { require(x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow return exp_2(int128((int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12) >> 128)); } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return unsigned 64.64-bit fixed point number */ function divuu(uint256 x, uint256 y) private pure returns (uint128) { unchecked { require(y != 0); uint256 result; if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y; else { uint256 msb = 192; uint256 xc = x >> 192; if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore result = (x << (255 - msb)) / (((y - 1) >> (msb - 191)) + 1); require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 hi = result * (y >> 128); uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 xh = x >> 192; uint256 xl = x << 64; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here lo = hi << 128; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here assert(xh == hi >> 128); result += xl / y; } require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return uint128(result); } } /** * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer * number. * * @param x unsigned 256-bit integer number * @return unsigned 128-bit integer number */ function sqrtu(uint256 x) private pure returns (uint128) { unchecked { if (x == 0) return 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x8) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return uint128(r < r1 ? r : r1); } } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.4; import "./ABDKMath64x64.sol"; /// @title Cumulative Normal Distribution Math Library /// @author Primitive library CumulativeNormalDistribution { using ABDKMath64x64 for int128; using ABDKMath64x64 for uint256; /// @notice Thrown on passing an arg that is out of the input range for these math functions error InverseOutOfBounds(int128 value); int128 public constant ONE_INT = 0x10000000000000000; int128 public constant TWO_INT = 0x20000000000000000; int128 public constant CDF0 = 0x53dd02a4f5ee2e46; int128 public constant CDF1 = 0x413c831bb169f874; int128 public constant CDF2 = -0x48d4c730f051a5fe; int128 public constant CDF3 = 0x16a09e667f3bcc908; int128 public constant CDF4 = -0x17401c57014c38f14; int128 public constant CDF5 = 0x10fb844255a12d72e; /// @notice Uses Abramowitz and Stegun approximation: /// https://en.wikipedia.org/wiki/Abramowitz_and_Stegun /// @dev Maximum error: 3.15x10-3 /// @return Standard Normal Cumulative Distribution Function of `x` function getCDF(int128 x) internal pure returns (int128) { int128 z = x.div(CDF3); int128 t = ONE_INT.div(ONE_INT.add(CDF0.mul(z.abs()))); int128 erf = getErrorFunction(z, t); if (z < 0) { erf = erf.neg(); } int128 result = (HALF_INT).mul(ONE_INT.add(erf)); return result; } /// @notice Uses Abramowitz and Stegun approximation: /// https://en.wikipedia.org/wiki/Error_function /// @dev Maximum error: 1.5×10−7 /// @return Error Function for approximating the Standard Normal CDF function getErrorFunction(int128 z, int128 t) internal pure returns (int128) { int128 step1 = t.mul(CDF3.add(t.mul(CDF4.add(t.mul(CDF5))))); int128 step2 = CDF1.add(t.mul(CDF2.add(step1))); int128 result = ONE_INT.sub(t.mul(step2.mul((z.mul(z).neg()).exp()))); return result; } int128 public constant HALF_INT = 0x8000000000000000; int128 public constant INVERSE0 = 0x26A8F3C1F21B336E; int128 public constant INVERSE1 = -0x87C57E5DA70D3C90; int128 public constant INVERSE2 = 0x15D71F5721242C787; int128 public constant INVERSE3 = 0x21D0A04B0E9B94F1; int128 public constant INVERSE4 = -0xC2BF5D74C724E53F; int128 public constant LOW_TAIL = 0x666666666666666; // 0.025 int128 public constant HIGH_TAIL = 0xF999999999999999; // 0.975 /// @notice Returns the inverse CDF, or quantile function of `p`. /// @dev Source: https://arxiv.org/pdf/1002.0567.pdf /// Maximum error of central region is 1.16x10−4 /// @return fcentral(p) = q * (a2 + (a1r + a0) / (r^2 + b1r +b0)) function getInverseCDF(int128 p) internal pure returns (int128) { if (p >= ONE_INT || p <= 0) revert InverseOutOfBounds(p); // Short circuit for the central region, central region inclusive of tails if (p <= HIGH_TAIL && p >= LOW_TAIL) { return central(p); } else if (p < LOW_TAIL) { return tail(p); } else { int128 negativeTail = -tail(ONE_INT.sub(p)); return negativeTail; } } /// @dev Maximum error: 1.16x10−4 /// @return Inverse CDF around the central area of 0.025 <= p <= 0.975 function central(int128 p) internal pure returns (int128) { int128 q = p.sub(HALF_INT); int128 r = q.mul(q); int128 result = q.mul( INVERSE2.add((INVERSE1.mul(r).add(INVERSE0)).div((r.mul(r).add(INVERSE4.mul(r)).add(INVERSE3)))) ); return result; } int128 public constant C0 = 0x10E56D75CE8BCE9FAE; int128 public constant C1 = -0x2CB2447D36D513DAE; int128 public constant C2 = -0x8BB4226952BD69EDF; int128 public constant C3 = -0x1000BF627FA188411; int128 public constant C0_D = 0x10AEAC93F55267A9A5; int128 public constant C1_D = 0x41ED34A2561490236; int128 public constant C2_D = 0x7A1E70F720ECA43; int128 public constant D0 = 0x72C7D592D021FB1DB; int128 public constant D1 = 0x8C27B4617F5F800EA; /// @dev Maximum error: 2.458x10-5 /// @return Inverse CDF of the tail, defined for p < 0.0465, used with p < 0.025 function tail(int128 p) internal pure returns (int128) { int128 r = ONE_INT.div(p.mul(p)).ln().sqrt(); int128 step0 = C3.mul(r).add(C2_D); int128 numerator = C1_D.mul(r).add(C0_D); int128 denominator = r.mul(r).add(D1.mul(r)).add(D0); int128 result = step0.add(numerator.div(denominator)); return result; } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; /// @title Action functions for the Primitive Engine contract /// @author Primitive interface IPrimitiveEngineActions { // ===== Pool Updates ===== /// @notice Updates the time until expiry of the pool by setting its last timestamp value /// @param poolId Keccak256 hash of engine address, strike, sigma, maturity, and gamma /// @return lastTimestamp Timestamp loaded into the state of the pool's Calibration.lastTimestamp function updateLastTimestamp(bytes32 poolId) external returns (uint32 lastTimestamp); /// @notice Initializes a curve with parameters in the `calibrations` storage mapping in the Engine /// @param strike Marginal price of the pool's risky token at maturity, with the same decimals as the stable token, valid [0, 2^128-1] /// @param sigma AKA Implied Volatility in basis points, determines the price impact of swaps, valid for (1, 10_000_000) /// @param maturity Timestamp which starts the BUFFER countdown until swaps will cease, in seconds, valid for (block.timestamp, 2^32-1] /// @param gamma Multiplied against swap in amounts to apply fee, equal to 1 - fee % but units are in basis points, valid for (9_000, 10_000) /// @param riskyPerLp Risky reserve per liq. with risky decimals, = 1 - N(d1), d1 = (ln(S/K)+(r*σ^2/2))/σ√τ, valid for [0, 1e^(risky token decimals)) /// @param delLiquidity Amount of liquidity units to allocate to the curve, wei value with 18 decimals of precision /// @param data Arbitrary data that is passed to the createCallback function /// @return poolId Keccak256 hash of engine address, strike, sigma, maturity, and gamma /// @return delRisky Total amount of risky tokens provided to reserves /// @return delStable Total amount of stable tokens provided to reserves function create( uint128 strike, uint32 sigma, uint32 maturity, uint32 gamma, uint256 riskyPerLp, uint256 delLiquidity, bytes calldata data ) external returns ( bytes32 poolId, uint256 delRisky, uint256 delStable ); // ===== Margin ==== /// @notice Adds risky and/or stable tokens to a `recipient`'s internal balance account /// @param recipient Recipient margin account of the deposited tokens /// @param delRisky Amount of risky tokens to deposit /// @param delStable Amount of stable tokens to deposit /// @param data Arbitrary data that is passed to the depositCallback function function deposit( address recipient, uint256 delRisky, uint256 delStable, bytes calldata data ) external; /// @notice Removes risky and/or stable tokens from a `msg.sender`'s internal balance account /// @param recipient Address that tokens are transferred to /// @param delRisky Amount of risky tokens to withdraw /// @param delStable Amount of stable tokens to withdraw function withdraw( address recipient, uint256 delRisky, uint256 delStable ) external; // ===== Liquidity ===== /// @notice Allocates risky and stable tokens to a specific curve with `poolId` /// @param poolId Keccak256 hash of engine address, strike, sigma, maturity, and gamma /// @param recipient Address to give the allocated liquidity to /// @param delRisky Amount of risky tokens to add /// @param delStable Amount of stable tokens to add /// @param fromMargin Whether the `msg.sender` pays with their margin balance, or must send tokens /// @param data Arbitrary data that is passed to the allocateCallback function /// @return delLiquidity Amount of liquidity given to `recipient` function allocate( bytes32 poolId, address recipient, uint256 delRisky, uint256 delStable, bool fromMargin, bytes calldata data ) external returns (uint256 delLiquidity); /// @notice Unallocates risky and stable tokens from a specific curve with `poolId` /// @param poolId Keccak256 hash of engine address, strike, sigma, maturity, and gamma /// @param delLiquidity Amount of liquidity to remove /// @return delRisky Amount of risky tokens received from removed liquidity /// @return delStable Amount of stable tokens received from removed liquidity function remove(bytes32 poolId, uint256 delLiquidity) external returns (uint256 delRisky, uint256 delStable); // ===== Swaps ===== /// @notice Swaps between `risky` and `stable` tokens /// @param recipient Address that receives output token `deltaOut` amount /// @param poolId Keccak256 hash of engine address, strike, sigma, maturity, and gamma /// @param riskyForStable If true, swap risky to stable, else swap stable to risky /// @param deltaIn Amount of tokens to swap in /// @param deltaOut Amount of tokens to swap out /// @param fromMargin Whether the `msg.sender` uses their margin balance, or must send tokens /// @param toMargin Whether the `deltaOut` amount is transferred or deposited into margin /// @param data Arbitrary data that is passed to the swapCallback function function swap( address recipient, bytes32 poolId, bool riskyForStable, uint256 deltaIn, uint256 deltaOut, bool fromMargin, bool toMargin, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; /// @title Events of the Primitive Engine contract /// @author Primitive interface IPrimitiveEngineEvents { /// @notice Creates a pool with liquidity /// @dev Keccak256 hash of the engine address, strike, sigma, maturity, and gamma /// @param from Calling `msg.sender` of the create function /// @param strike Marginal price of the pool's risky token at maturity, with the same decimals as the stable token, valid [0, 2^128-1] /// @param sigma AKA Implied Volatility in basis points, determines the price impact of swaps, valid for (1, 10_000_000) /// @param maturity Timestamp which starts the BUFFER countdown until swaps will cease, in seconds, valid for (block.timestamp, 2^32-1] /// @param gamma Multiplied against swap in amounts to apply fee, equal to 1 - fee % but units are in basis points, valid for (9000, 10_000) /// @param delRisky Amount of risky tokens deposited /// @param delStable Amount of stable tokens deposited /// @param delLiquidity Amount of liquidity granted to `recipient` event Create( address indexed from, uint128 strike, uint32 sigma, uint32 indexed maturity, uint32 indexed gamma, uint256 delRisky, uint256 delStable, uint256 delLiquidity ); /// @notice Updates the time until expiry of the pool with `poolId` /// @param poolId Keccak256 hash of the engine address, strike, sigma, maturity, and gamma event UpdateLastTimestamp(bytes32 indexed poolId); // ===== Margin ==== /// @notice Added stable and/or risky tokens to a margin account /// @param from Method caller `msg.sender` /// @param recipient Margin account recieving deposits /// @param delRisky Amount of risky tokens deposited /// @param delStable Amount of stable tokens deposited event Deposit(address indexed from, address indexed recipient, uint256 delRisky, uint256 delStable); /// @notice Removes stable and/or risky from a margin account /// @param from Method caller `msg.sender` /// @param recipient Address that tokens are sent to /// @param delRisky Amount of risky tokens withdrawn /// @param delStable Amount of stable tokens withdrawn event Withdraw(address indexed from, address indexed recipient, uint256 delRisky, uint256 delStable); // ===== Liquidity ===== /// @notice Adds liquidity of risky and stable tokens to a specified `poolId` /// @param from Method caller `msg.sender` /// @param recipient Address that receives liquidity /// @param poolId Keccak256 hash of the engine address, strike, sigma, maturity, and gamma /// @param delRisky Amount of risky tokens deposited /// @param delStable Amount of stable tokens deposited /// @param delLiquidity Amount of liquidity granted to `recipient` event Allocate( address indexed from, address indexed recipient, bytes32 indexed poolId, uint256 delRisky, uint256 delStable, uint256 delLiquidity ); /// @notice Adds liquidity of risky and stable tokens to a specified `poolId` /// @param from Method caller `msg.sender` /// @param poolId Keccak256 hash of the engine address, strike, sigma, maturity, and gamma /// @param delRisky Amount of risky tokens deposited /// @param delStable Amount of stable tokens deposited /// @param delLiquidity Amount of liquidity decreased from `from` event Remove( address indexed from, bytes32 indexed poolId, uint256 delRisky, uint256 delStable, uint256 delLiquidity ); // ===== Swaps ===== /// @notice Swaps between `risky` and `stable` assets /// @param from Method caller `msg.sender` /// @param recipient Address that receives `deltaOut` amount of tokens /// @param poolId Keccak256 hash of the engine address, strike, sigma, maturity, and gamma /// @param riskyForStable If true, swaps risky to stable, else swaps stable to risky /// @param deltaIn Amount of tokens added to reserves /// @param deltaOut Amount of tokens removed from reserves event Swap( address indexed from, address indexed recipient, bytes32 indexed poolId, bool riskyForStable, uint256 deltaIn, uint256 deltaOut ); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; /// @title View functions of the Primitive Engine contract /// @author Primitive interface IPrimitiveEngineView { // ===== View ===== /// @notice Fetches the current invariant, notation is usually `k`, based on risky and stable token reserves of pool with `poolId` /// @param poolId Keccak256 hash of the engine address, strike, sigma, maturity, and gamma /// @return invariant Signed fixed point 64.64 number, invariant of `poolId` function invariantOf(bytes32 poolId) external view returns (int128 invariant); // ===== Constants ===== /// @return Precision units to scale to when doing token related calculations function PRECISION() external view returns (uint256); /// @return Amount of seconds after pool expiry which allows swaps, no swaps after buffer function BUFFER() external view returns (uint256); // ===== Immutables ===== /// @return Amount of liquidity burned on `create()` calls function MIN_LIQUIDITY() external view returns (uint256); //// @return Factory address which deployed this engine contract function factory() external view returns (address); //// @return Risky token address, a more accurate name is the underlying token function risky() external view returns (address); /// @return Stable token address, a more accurate name is the quote token function stable() external view returns (address); /// @return Multiplier to scale amounts to/from, equal to 10^(18 - riskyDecimals) function scaleFactorRisky() external view returns (uint256); /// @return Multiplier to scale amounts to/from, equal to 10^(18 - stableDecimals) function scaleFactorStable() external view returns (uint256); // ===== Pool State ===== /// @notice Fetches the global reserve state for a pool with `poolId` /// @param poolId Keccak256 hash of the engine address, strike, sigma, maturity, and gamma /// @return reserveRisky Risky token balance in the reserve /// @return reserveStable Stable token balance in the reserve /// @return liquidity Total supply of liquidity for the curve /// @return blockTimestamp Timestamp when the cumulative reserve values were last updated /// @return cumulativeRisky Cumulative sum of risky token reserves of the previous update /// @return cumulativeStable Cumulative sum of stable token reserves of the previous update /// @return cumulativeLiquidity Cumulative sum of total supply of liquidity of the previous update function reserves(bytes32 poolId) external view returns ( uint128 reserveRisky, uint128 reserveStable, uint128 liquidity, uint32 blockTimestamp, uint256 cumulativeRisky, uint256 cumulativeStable, uint256 cumulativeLiquidity ); /// @notice Fetches `Calibration` pool parameters /// @param poolId Keccak256 hash of the engine address, strike, sigma, maturity, and gamma /// @return strike Marginal price of the pool's risky token at maturity, with the same decimals as the stable token, valid [0, 2^128-1] /// @return sigma AKA Implied Volatility in basis points, determines the price impact of swaps, valid for (1, 10_000_000) /// @return maturity Timestamp which starts the BUFFER countdown until swaps will cease, in seconds, valid for (block.timestamp, 2^32-1] /// @return lastTimestamp Last timestamp used to calculate time until expiry, aka "tau" /// @return gamma Multiplied against swap in amounts to apply fee, equal to 1 - fee % but units are in basis points, valid for (9_000, 10_000) function calibrations(bytes32 poolId) external view returns ( uint128 strike, uint32 sigma, uint32 maturity, uint32 lastTimestamp, uint32 gamma ); /// @notice Fetches position liquidity an account address and poolId /// @param poolId Keccak256 hash of the engine address, strike, sigma, maturity, and gamma /// @return liquidity Liquidity owned by `account` in `poolId` function liquidity(address account, bytes32 poolId) external view returns (uint256 liquidity); /// @notice Fetches the margin balances of `account` /// @param account Margin account to fetch /// @return balanceRisky Balance of the risky token /// @return balanceStable Balance of the stable token function margins(address account) external view returns (uint128 balanceRisky, uint128 balanceStable); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.4; /// @title Errors for the Primitive Engine contract /// @author Primitive /// @notice Custom errors are encoded with their selector and arguments /// @dev Peripheral smart contracts should try catch and check if data matches another custom error interface IPrimitiveEngineErrors { /// @notice Thrown on attempted re-entrancy on a function with a re-entrancy guard error LockedError(); /// @notice Thrown when the balanceOf function is not successful and does not return data error BalanceError(); /// @notice Thrown in create when a pool with computed poolId already exists error PoolDuplicateError(); /// @notice Thrown when calling an expired pool, where block.timestamp > maturity, + BUFFER if swap error PoolExpiredError(); /// @notice Thrown when liquidity is lower than or equal to the minimum amount of liquidity error MinLiquidityError(uint256 value); /// @notice Thrown when riskyPerLp is outside the range of acceptable values, 0 < riskyPerLp <= 1eRiskyDecimals error RiskyPerLpError(uint256 value); /// @notice Thrown when sigma is outside the range of acceptable values, 1 <= sigma <= 1e7 with 4 precision error SigmaError(uint256 value); /// @notice Thrown when strike is not valid, i.e. equal to 0 or greater than 2^128 error StrikeError(uint256 value); /// @notice Thrown when gamma, equal to 1 - fee %, is outside its bounds: 9_000 <= gamma <= 10_000; 1_000 = 10% fee error GammaError(uint256 value); /// @notice Thrown when the parameters of a new pool are invalid, causing initial reserves to be 0 error CalibrationError(uint256 delRisky, uint256 delStable); /// @notice Thrown when the expected risky balance is less than the actual balance /// @param expected Expected risky balance /// @param actual Actual risky balance error RiskyBalanceError(uint256 expected, uint256 actual); /// @notice Thrown when the expected stable balance is less than the actual balance /// @param expected Expected stable balance /// @param actual Actual stable balance error StableBalanceError(uint256 expected, uint256 actual); /// @notice Thrown when the pool with poolId has not been created error UninitializedError(); /// @notice Thrown when the risky or stable amount is 0 error ZeroDeltasError(); /// @notice Thrown when the liquidity parameter is 0 error ZeroLiquidityError(); /// @notice Thrown when the deltaIn parameter is 0 error DeltaInError(); /// @notice Thrown when the deltaOut parameter is 0 error DeltaOutError(); /// @notice Thrown when the invariant check fails /// @dev Most important check as it verifies the validity of a desired swap /// @param invariant Pre-swap invariant updated with new tau /// @param nextInvariant Post-swap invariant after the swap amounts are applied to reserves error InvariantError(int128 invariant, int128 nextInvariant); }
{ "optimizer": { "enabled": true, "runs": 1000 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceError","type":"error"},{"inputs":[{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"}],"name":"CalibrationError","type":"error"},{"inputs":[],"name":"DeltaInError","type":"error"},{"inputs":[],"name":"DeltaOutError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"GammaError","type":"error"},{"inputs":[{"internalType":"int128","name":"invariant","type":"int128"},{"internalType":"int128","name":"nextInvariant","type":"int128"}],"name":"InvariantError","type":"error"},{"inputs":[{"internalType":"int128","name":"value","type":"int128"}],"name":"InverseOutOfBounds","type":"error"},{"inputs":[],"name":"LockedError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"MinLiquidityError","type":"error"},{"inputs":[],"name":"PoolDuplicateError","type":"error"},{"inputs":[],"name":"PoolExpiredError","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"RiskyBalanceError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"RiskyPerLpError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SigmaError","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"StableBalanceError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"StrikeError","type":"error"},{"inputs":[],"name":"UninitializedError","type":"error"},{"inputs":[],"name":"ZeroDeltasError","type":"error"},{"inputs":[],"name":"ZeroLiquidityError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"name":"Allocate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint128","name":"strike","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"sigma","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"maturity","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"gamma","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"name":"Create","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"name":"Remove","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"riskyForStable","type":"bool"},{"indexed":false,"internalType":"uint256","name":"deltaIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deltaOut","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"UpdateLastTimestamp","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"BUFFER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"},{"internalType":"bool","name":"fromMargin","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"allocate","outputs":[{"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"calibrations","outputs":[{"internalType":"uint128","name":"strike","type":"uint128"},{"internalType":"uint32","name":"sigma","type":"uint32"},{"internalType":"uint32","name":"maturity","type":"uint32"},{"internalType":"uint32","name":"lastTimestamp","type":"uint32"},{"internalType":"uint32","name":"gamma","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"strike","type":"uint128"},{"internalType":"uint32","name":"sigma","type":"uint32"},{"internalType":"uint32","name":"maturity","type":"uint32"},{"internalType":"uint32","name":"gamma","type":"uint32"},{"internalType":"uint256","name":"riskyPerLp","type":"uint256"},{"internalType":"uint256","name":"delLiquidity","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"create","outputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"invariantOf","outputs":[{"internalType":"int128","name":"invariant","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"liquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"margins","outputs":[{"internalType":"uint128","name":"balanceRisky","type":"uint128"},{"internalType":"uint128","name":"balanceStable","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"name":"remove","outputs":[{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"reserves","outputs":[{"internalType":"uint128","name":"reserveRisky","type":"uint128"},{"internalType":"uint128","name":"reserveStable","type":"uint128"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint32","name":"blockTimestamp","type":"uint32"},{"internalType":"uint256","name":"cumulativeRisky","type":"uint256"},{"internalType":"uint256","name":"cumulativeStable","type":"uint256"},{"internalType":"uint256","name":"cumulativeLiquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"risky","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scaleFactorRisky","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scaleFactorStable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stable","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"bool","name":"riskyForStable","type":"bool"},{"internalType":"uint256","name":"deltaIn","type":"uint256"},{"internalType":"uint256","name":"deltaOut","type":"uint256"},{"internalType":"bool","name":"fromMargin","type":"bool"},{"internalType":"bool","name":"toMargin","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"updateLastTimestamp","outputs":[{"internalType":"uint32","name":"lastTimestamp","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
61014060405260016000553480156200001757600080fd5b50336001600160a01b0316634e9b75b66040518163ffffffff1660e01b815260040160c06040518083038186803b1580156200005257600080fd5b505afa15801562000067573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200008d9190620000de565b60805260c05260a0526001600160601b0319606091821b81166101205291811b8216610100529190911b1660e05262000144565b80516001600160a01b0381168114620000d957600080fd5b919050565b60008060008060008060c08789031215620000f857600080fd5b6200010387620000c1565b95506200011360208801620000c1565b94506200012360408801620000c1565b9350606087015192506080870151915060a087015190509295509295509295565b60805160a05160c05160e05160601c6101005160601c6101205160601c614188620001ff6000396000818161028201528181610af3015281816116850152611ed90152600081816104dc01528181610ab90152818161179a0152611d95015260006105030152600081816103a8015281816106bd01528181610c4101526115670152600081816103f50152818161069c01528181610c1f015261154601526000818161024d01528181610d7e01526110b601526141886000f3fe608060405234801561001057600080fd5b506004361061016b5760003560e01c80638fbc3ecd116100cd578063be00763a11610081578063c45a015511610066578063c45a0155146104fe578063ca28fcd614610525578063d2957b8f1461053857600080fd5b8063be00763a146104a9578063c08165d4146104d757600080fd5b80639f8cfade116100b25780639f8cfade1461045f578063aaf5eb6814610487578063b5c5f6721461049657600080fd5b80638fbc3ecd146104425780639e48ff5a1461044a57600080fd5b80633882046511610124578063546fecae11610109578063546fecae146103ca57806355ebb825146103f05780636b35bb6b1461041757600080fd5b8063388204651461034f5780634dd0d056146103a357600080fd5b806321b77d631161015557806321b77d631461024857806322be3de11461027d57806323c5b952146102bc57600080fd5b8062bbf1e41461017057806311f9908614610220575b600080fd5b6101cf61017e366004613e1e565b6003602081905260009182526040909120805460018201546002830154938301546004909301546001600160801b0380841695600160801b948590048216959184169490930463ffffffff16929187565b604080516001600160801b0398891681529688166020880152949096169385019390935263ffffffff919091166060840152608083015260a082015260c081019190915260e0015b60405180910390f35b61023361022e366004613e1e565b61054b565b60405163ffffffff9091168152602001610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610217565b6102a47f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610217565b6103126102ca366004613e1e565b6001602052600090815260409020546001600160801b0381169063ffffffff600160801b8204811691600160a01b8104821691600160c01b8204811691600160e01b90041685565b604080516001600160801b03909616865263ffffffff9485166020870152928416928501929092528216606084015216608082015260a001610217565b61038361035d366004613c75565b6002602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000081565b6103dd6103d8366004613e1e565b610588565b604051600f9190910b8152602001610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000081565b61026f610425366004613c90565b600460209081526000928352604080842090915290825290205481565b61026f607881565b61045d610458366004613d99565b61070d565b005b61047261046d366004613ebb565b6108a1565b60408051928352602083019190915201610217565b61026f670de0b6b3a764000081565b61045d6104a4366004613d66565b610a47565b6104bc6104b7366004613edd565b610b6a565b60408051938452602084019290925290820152606001610217565b6102a47f000000000000000000000000000000000000000000000000000000000000000081565b6102a47f000000000000000000000000000000000000000000000000000000000000000081565b61045d610533366004613cba565b611260565b61026f610546366004613e37565b6118ea565b6000805460011461056f57604051635cd4e48360e01b815260040160405180910390fd5b600260005561057d82611b7a565b600160005592915050565b6000818152600160209081526040808320815160a08101835290546001600160801b038116825263ffffffff600160801b8204811694830194909452600160a01b81048416928201839052600160c01b8104841660608301819052600160e01b909104909316608082015291839161060091906140e4565b6000858152600360208181526040808420815160e08101835281546001600160801b038082168352600160801b9182900481169583019590955260018301549485169382019390935263ffffffff9290930482166060840152600281015460808401529283015460a083015260049092015460c0820152929350909182916106939190670de0b6b3a764000090611c2416565b915091506107037f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000848488600001516001600160801b0316896020015163ffffffff168963ffffffff16611c83565b9695505050505050565b60005460011461073057604051635cd4e48360e01b815260040160405180910390fd5b600260005583158015610741575082155b1561075f576040516381552f0360e01b815260040160405180910390fd5b6001600160a01b0385166000908152600260205260409020610782908585611cd1565b600080851561079657610793611d7b565b91505b84156107a7576107a4611ebf565b90505b6040517fc536e605000000000000000000000000000000000000000000000000000000008152339063c536e605906107e9908990899089908990600401614001565b600060405180830381600087803b15801561080357600080fd5b505af1158015610817573d6000803e3d6000fd5b505050508560001461083557610835610830878461404c565b611f0b565b841561084d5761084d610848868361404c565b611f5f565b60408051878152602081018790526001600160a01b0389169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3505060016000555050505050565b6000806000546001146108c757604051635cd4e48360e01b815260040160405180910390fd5b6002600055826108ea5760405163e5664db760e01b815260040160405180910390fd5b60008481526003602052604090206001810154600160801b900463ffffffff1661092757604051631d2ad63f60e01b815260040160405180910390fd5b6040805160e08101825282546001600160801b038082168352600160801b918290048116602084015260018501549081169383019390935263ffffffff92048216606082015260028301546080820152600383015460a0820152600483015460c0820152610997918690611c2416565b3360009081526004602090815260408083208a84529091528120805493965091945086926109c69084906140cd565b909155506109d990508184848742611faf565b3360009081526002602052604090206109f3908484611cd1565b6040805184815260208101849052908101859052859033907f59c6598fc34aefe62579a03667748ef83b4682874f311affb26b318095359b0e9060600160405180910390a350600160005590939092509050565b600054600114610a6a57604051635cd4e48360e01b815260040160405180910390fd5b600260005581158015610a7b575080155b15610a99576040516381552f0360e01b815260040160405180910390fd5b610aa5600283836120a1565b508115610ae057610ae06001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016848461215c565b8015610b1a57610b1a6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016848361215c565b60408051838152602081018390526001600160a01b0385169133917ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567910160405180910390a35050600160005550565b60008060008054600114610b9157604051635cd4e48360e01b815260040160405180910390fd5b60026000556040516bffffffffffffffffffffffff193060601b1660208201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000060808d901b1660348201527fffffffff0000000000000000000000000000000000000000000000000000000060e08c811b821660448401528b811b821660488401528a901b16604c8201527f0000000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009060500160408051808303601f19018152918152815160209283012060008181526001909352912054909550600160c01b900463ffffffff1615610cd0576040517fd946062c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b629896808c63ffffffff161180610ced575060018c63ffffffff16105b15610d31576040517f87a2c00100000000000000000000000000000000000000000000000000000000815263ffffffff8d1660048201526024015b60405180910390fd5b6001600160801b038d16610d7c576040517fb3f4bc550000000000000000000000000000000000000000000000000000000081526001600160801b038e166004820152602401610d28565b7f00000000000000000000000000000000000000000000000000000000000000008811610dd8576040517f325951a500000000000000000000000000000000000000000000000000000000815260048101899052602401610d28565b610dea82670de0b6b3a7640000614064565b891180610df5575088155b15610e2f576040517f47ac541e000000000000000000000000000000000000000000000000000000008152600481018a9052602401610d28565b6127108a63ffffffff161180610e4c57506123288a63ffffffff16105b15610e8b576040517ffdb23f4a00000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152602401610d28565b60006040518060a001604052808f6001600160801b031681526020018e63ffffffff1681526020018d63ffffffff168152602001610ec64290565b63ffffffff1681526020018c63ffffffff168152509050806040015163ffffffff16816060015163ffffffff161115610f125760405163536e15e160e01b815260040160405180910390fd5b600081606001518260400151610f2891906140e4565b9050610f57600085858e86600001516001600160801b0316876020015163ffffffff168763ffffffff166122aa565b9450670de0b6b3a7640000610f6c8b8d614086565b610f769190614064565b9550670de0b6b3a7640000610f8b8b87614086565b610f959190614064565b9450851580610fa2575084155b15610fe3576040517f492d6d170000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604401610d28565b6000878152600160209081526040808320855181549387015192870151606088015160808901516001600160801b039093167fffffffffffffffffffffffff000000000000000000000000000000000000000090961695909517600160801b63ffffffff95861602177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff16600160a01b9185169190910263ffffffff60c01b191617600160c01b94841694909402939093176001600160e01b0316600160e01b92909316919091029190911790556110db7f00000000000000000000000000000000000000000000000000000000000000008c6140cd565b3360009081526004602090815260408083208c845290915281208054929350839290919061110a90849061404c565b90915550506060830151600089815260036020526040902061113191899089908f90612380565b60008061113c611d7b565b611144611ebf565b91509150336001600160a01b031663c171d27e8a8a8f8f6040518563ffffffff1660e01b815260040161117a9493929190614001565b600060405180830381600087803b15801561119457600080fd5b505af11580156111a8573d6000803e3d6000fd5b505050506111bb8983610830919061404c565b6111c8610848898361404c565b60808086015160408088015188516020808b015184516001600160801b03909316835263ffffffff908116918301919091529281018e9052606081018d90529384018790529181169291169033907ff38f2bc2b712352b1d4a9ea95291a1c688835ac9f70a7a9d0aec57712c8b19129060a00160405180910390a4505050505050506001600081905550985098509895505050505050565b60005460011461128357604051635cd4e48360e01b815260040160405180910390fd5b6002600055856112bf576040517f28226f1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846112f6576040517f564b486500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006040518061010001604052808b6001600160a01b031681526020018915158152602001861515815260200185151581526020016113324290565b63ffffffff1681526020018a8152602001888152602001878152509050600061135e8260a00151611b7a565b9050611371607863ffffffff831661404c565b826080015163ffffffff16111561139b5760405163536e15e160e01b815260040160405180910390fd5b60006113aa8360a00151610588565b60a08085018051600090815260016020908152604080832081519586018252546001600160801b038116865263ffffffff600160801b8204811687850152600160a01b82048116878401908152600160c01b8304821660608901908152600160e01b909304909116608088015294518452600390925282209051925194955092939091611436916140e4565b90506000612710846080015163ffffffff168860c001516114579190614086565b6114619190614064565b90506000808860200151156114ad5784546114869084906001600160801b031661404c565b85549092506114a6908f90600160801b90046001600160801b03166140cd565b90506114e6565b84546114c3908f906001600160801b03166140cd565b85549092506114e3908490600160801b90046001600160801b031661404c565b90505b60018501546001600160801b0316611506670de0b6b3a764000084614086565b6115109190614064565b60018601549092506001600160801b0316611533670de0b6b3a764000083614086565b61153d9190614064565b905060006115ad7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000085858b600001516001600160801b03168c6020015163ffffffff168b63ffffffff16611c83565b905080600f0b88600f0b13156115fd576040517fcd4424b0000000000000000000000000000000000000000000000000000000008152600f89810b600483015282900b6024820152604401610d28565b6116268a602001518b60c001518c60e001518d608001518a61244790949392919063ffffffff16565b5050505050505082602001511561174d578260600151156116705760e083015183516001600160a01b0316600090815260026020526040812061166b92909190611cd1565b6116ab565b825160e08401516116ab916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169161215c565b8260400151156116ce5760c08301516116c89060029060006120a1565b50611860565b60006116d8611d7b565b60c085015160405163491dc51560e11b8152919250339163923b8a2a91611708916000908b908b90600401614001565b600060405180830381600087803b15801561172257600080fd5b505af1158015611736573d6000803e3d6000fd5b505050506116c88460c0015182610830919061404c565b8260600151156117855760e083015183516001600160a01b03166000908152600260205260408120611780929091611cd1565b6117c0565b825160e08401516117c0916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169161215c565b8260400151156117de5760c08301516116c8906002906000906120a1565b60006117e8611ebf565b60c085015160405163491dc51560e11b8152919250339163923b8a2a9161181991600091908b908b90600401614001565b600060405180830381600087803b15801561183357600080fd5b505af1158015611847573d6000803e3d6000fd5b5050505061185e8460c0015182610848919061404c565b505b8260a0015183600001516001600160a01b0316336001600160a01b03167fe42b42fe878e256d1efbfb2ba292aadeac0228bbaa31f4ef8537522bdfa3ea1986602001518760c001518860e001516040516118cf9392919092151583526020830191909152604082015260600190565b60405180910390a45050600160005550505050505050505050565b6000805460011461190e57604051635cd4e48360e01b815260040160405180910390fd5b600260005585158061191e575084155b1561193c576040516381552f0360e01b815260040160405180910390fd5b60008881526003602052604090206001810154600160801b900463ffffffff1661197957604051631d2ad63f60e01b815260040160405180910390fd5b8054600182015442916000916001600160801b039182169161199c91168b614086565b6119a69190614064565b835460018501549192506000916001600160801b03600160801b9092048216916119d191168b614086565b6119db9190614064565b90508082106119ea57806119ec565b815b945084611a0c5760405163e5664db760e01b815260040160405180910390fd5b6001600160a01b038b1660009081526004602090815260408083208f845290915281208054879290611a3f90849061404c565b90915550611a529050848b8b8887612380565b8715611a6a57611a6460028b8b6120a1565b50611b15565b600080611a75611d7b565b611a7d611ebf565b6040517f151a8bf80000000000000000000000000000000000000000000000000000000081529193509150339063151a8bf890611ac4908f908f908e908e90600401614001565b600060405180830381600087803b158015611ade57600080fd5b505af1158015611af2573d6000803e3d6000fd5b50505050611b058c83610830919061404c565b611b126108488c8361404c565b50505b604080518b8152602081018b90529081018690528c906001600160a01b038d169033907f86e4803447f73017bf832b0ac69cb7afa3333a5fa823ec42a9f6cf8d17b50ecc9060600160405180910390a450506001600055509098975050505050505050565b60008181526001602052604081208054600160c01b900463ffffffff16611bb457604051631d2ad63f60e01b815260040160405180910390fd5b8054429250600160a01b900463ffffffff908116908316811015611bd6578092505b815463ffffffff60c01b1916600160c01b63ffffffff85160217825560405184907fd69bdb2831892ad6046f30af0f12362aef46cce34d58ed7ad9b3ef8c44b1b46690600090a25050919050565b6040820151825160009182916001600160801b03918216918291611c49911686614086565b611c539190614064565b92508085602001516001600160801b031685611c6f9190614086565b611c799190614064565b9150509250929050565b600080611c9660008a8a8a8989896122aa565b90506000611ca4828a612566565b90506000611cb2888b612566565b9050611cc2600f82900b8361258f565b9b9a5050505050505050505050565b8115611d2057611ce0826125d5565b83548490600090611cfb9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b8015611d7657611d2f816125d5565b83548490601090611d51908490600160801b90046001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b505050565b604051306024820152600090819081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823160e01b906044015b60408051601f198184030181529181526020820180516001600160e01b03167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611e199190613fc6565b600060405180830381855afa9150503d8060008114611e54576040519150601f19603f3d011682016040523d82523d6000602084013e611e59565b606091505b5091509150811580611e6d57508051602014155b15611ea4576040517fad4ea8a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80806020019051810190611eb89190613f84565b9250505090565b604051306024820152600090819081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823160e01b90604401611dc3565b6000611f15611d7b565b905081811015611f5b576040517f9ce3223a0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610d28565b5050565b6000611f69611ebf565b905081811015611f5b576040517fc66162eb0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610d28565b611fb985826125ef565b611fc2846125d5565b85548690600090611fdd9084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061200a836125d5565b8554869060109061202c908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612059826125d5565b6001860180546000906120769084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505050505050565b33600090815260208490526040902082156120ff576120bf836125d5565b815482906000906120da9084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b81156121555761210e826125d5565b81548290601090612130908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b9392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000928392908716916121d19190613fc6565b6000604051808303816000865af19150503d806000811461220e576040519150601f19603f3d011682016040523d82523d6000602084013e612213565b606091505b509150915081801561223d57508051158061223d57508080602001905181019061223d9190613e01565b6122a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f5472616e73666572206661696c000000000000000000000000000000000000006044820152606401610d28565b5050505050565b6000806122b78588612566565b905060006122c5878a612566565b905060006122d7600160401b8361258f565b905084156123535760006122eb878761269c565b905060006122fb83600f0b6126d9565b9050600061230d600f83900b8461258f565b905060006123388f61232f61232485600f0b6127ac565b600f8b900b9061284d565b600f0b9061288c565b9050612348600f82900b8e6128c8565b975050505050612372565b611cc2896123698d61232f600f88900b8661284d565b600f0b906128c8565b505050979650505050505050565b61238a85826125ef565b612393846125d5565b855486906000906123ae9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506123db836125d5565b855486906010906123fd908490600160801b90046001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061242a826125d5565b6001860180546000906120769084906001600160801b0316614021565b61245185826125ef565b83156124f357612460836125d5565b8554869060009061247b9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506124a8826125d5565b855486906010906124ca908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506122a3565b6124fc826125d5565b855486906000906125179084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612544836125d5565b85548690601090612076908490600160801b90046001600160801b0316614021565b60008061257b83670de0b6b3a7640000614064565b905061258784826128ed565b949350505050565b6000600f82810b9084900b0360016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113155b6125cc57600080fd5b90505b92915050565b60006001600160801b038211156125eb57600080fd5b5090565b600182015460009061260e90600160801b900463ffffffff16836140e4565b905063ffffffff811615611d7657825460028401805463ffffffff9384166001600160801b038085168202909201909255600386018054600160801b9485900483168402019055600186018054600490970180549288169093029091019091557fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff9094169290911602179055565b6000806126b36126ab8461292d565b600f0b61293d565b905060006126c08561295f565b90506126d0600f82900b8361284d565b95945050505050565b6000600160401b600f83900b1215806126f65750600082600f0b13155b15612733576040517f72cc98e8000000000000000000000000000000000000000000000000000000008152600f83900b6004820152602401610d28565b67f999999999999999600f83900b1380159061275b5750670666666666666666600f83900b12155b15612769576125cf8261296d565b670666666666666666600f83900b1215612786576125cf82612a18565b600061279e612799600160401b8561258f565b612a18565b61215590614101565b919050565b6000806127c6600f84900b68016a09e667f3bcc908612ae2565b905060006128046127f96127ee6127df85600f0b612b43565b6753dd02a4f5ee2e469061284d565b600160401b9061288c565b600160401b90612ae2565b905060006128128383612b76565b9050600083600f0b121561282f5761282c81600f0b612c46565b90505b6000610703612842600160401b8461288c565b678000000000000000905b6000600f83810b9083900b0260401d60016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000600f83810b9083900b0160016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000806128dd83670de0b6b3a7640000614064565b9050612587600f85900b82612c61565b6000816128f957600080fd5b60006129058484612cda565b90506f7fffffffffffffffffffffffffffffff6001600160801b03821611156125cc57600080fd5b60006125cf826301e185586128ed565b60008082600f0b121561294f57600080fd5b6125cf604083600f0b901b612e4d565b60006125cf826127106128ed565b600080612986600f84900b67800000000000000061258f565b90506000612998600f83900b8361284d565b905060006126d0612a0d6129fd6129d56721d0a04b0e9b94f161232f6129c767c2bf5d74c724e53e198961284d565b61232f600f8a900b8a61284d565b6129f46726a8f3c1f21b336e61232f6787c57e5da70d3c8f198961284d565b600f0b90612ae2565b68015d71f5721242c7879061288c565b600f85900b9061284d565b600080612a3a6126ab612a326127f9600f87900b8761284d565b600f0b612fbb565b90506000612a5e6707a1e70f720eca4361232f6801000bf627fa188410198561284d565b90506000612a826810aeac93f55267a9a561232f68041ed34a25614902368661284d565b90506000612ab768072c7d592d021fb1db61232f612aa96808c27b4617f5f800ea8861284d565b61232f600f89900b8961284d565b90506000612ad7612acc600f85900b84612ae2565b600f86900b9061288c565b979650505050505050565b600081600f0b60001415612af557600080fd5b600082600f0b604085600f0b901b81612b1057612b10614154565b05905060016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000600f82900b60016001607f1b03191415612b5e57600080fd5b600082600f0b12612b6f57816125cf565b5060000390565b600080612bc8612a0d612bb8612bad612b9c600f88900b68010fb844255a12d72e61284d565b68017401c57014c38f13199061288c565b600f87900b9061284d565b68016a09e667f3bcc9089061288c565b90506000612bf4612be5612bad6748d4c730f051a5fd198561288c565b67413c831bb169f8749061288c565b90506000610703612c3b612c30612c25612c1d612c15600f8c900b8c61284d565b600f0b612c46565b600f0b612ff5565b600f86900b9061284d565b600f88900b9061284d565b600160401b9061258f565b6000600f82900b60016001607f1b03191415612b6f57600080fd5b600081612c70575060006125cf565b600083600f0b1215612c8157600080fd5b600f83900b6001600160801b038316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff811115612cc157600080fd5b60401b8119811115612cd257600080fd5b019392505050565b600081612ce657600080fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff8411612d225782604085901b81612d1a57612d1a614154565b049050612e39565b60c084811c6401000000008110612d3b576020918201911c5b620100008110612d4d576010918201911c5b6101008110612d5e576008918201911c5b60108110612d6e576004918201911c5b60048110612d7e576002918201911c5b60028110612d8d576001820191505b60bf820360018603901c6001018260ff0387901b81612dae57612dae614154565b0492506001600160801b03831115612dc557600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015612df1576001820391505b608084901b92900382811015612e08576001820391505b829003608084901c8214612e1e57612e1e614128565b888181612e2d57612e2d614154565b04870196505050505050505b6001600160801b038111156125cc57600080fd5b600081612e5c57506000919050565b816001600160801b8210612e755760809190911c9060401b5b600160401b8210612e8b5760409190911c9060201b5b6401000000008210612ea25760209190911c9060101b5b620100008210612eb75760109190911c9060081b5b6101008210612ecb5760089190911c9060041b5b60108210612ede5760049190911c9060021b5b60088210612eea5760011b5b6001818581612efb57612efb614154565b048201901c90506001818581612f1357612f13614154565b048201901c90506001818581612f2b57612f2b614154565b048201901c90506001818581612f4357612f43614154565b048201901c90506001818581612f5b57612f5b614154565b048201901c90506001818581612f7357612f73614154565b048201901c90506001818581612f8b57612f8b614154565b048201901c90506000818581612fa357612fa3614154565b049050808210612fb357806126d0565b509392505050565b60008082600f0b13612fcc57600080fd5b6080612fd78361304f565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b60006840000000000000000082600f0b1261300f57600080fd5b683fffffffffffffffff1982600f0b121561302c57506000919050565b6125cf608083600f0b700171547652b82fe1777d0ffda0d23a7d1202901d61312e565b60008082600f0b1361306057600080fd5b6000600f83900b600160401b811261307a576040918201911d5b640100000000811261308e576020918201911d5b6201000081126130a0576010918201911d5b61010081126130b1576008918201911d5b601081126130c1576004918201911d5b600481126130d1576002918201911d5b600281126130e0576001820191505b603f19820160401b600f85900b607f8490031b6780000000000000005b60008113156131235790800260ff81901c8281029390930192607f011c9060011d6130fd565b509095945050505050565b60006840000000000000000082600f0b1261314857600080fd5b683fffffffffffffffff1982600f0b121561316557506000919050565b6f8000000000000000000000000000000060006780000000000000008416600f0b13156131a35770016a09e667f3bcc908b2fb1366ea957d3e0260801c5b60008367400000000000000016600f0b13156131d0577001306fe0a31b7152de8d5a46305c85edec0260801c5b60008367200000000000000016600f0b13156131fd577001172b83c7d517adcdf7c8c50eb14a791f0260801c5b60008367100000000000000016600f0b131561322a5770010b5586cf9890f6298b92b71842a983630260801c5b60008367080000000000000016600f0b1315613257577001059b0d31585743ae7c548eb68ca417fd0260801c5b60008367040000000000000016600f0b131561328457700102c9a3e778060ee6f7caca4f7a29bde80260801c5b60008367020000000000000016600f0b13156132b15770010163da9fb33356d84a66ae336dcdfa3f0260801c5b60008367010000000000000016600f0b13156132de57700100b1afa5abcbed6129ab13ec11dc95430260801c5b600083668000000000000016600f0b131561330a5770010058c86da1c09ea1ff19d294cf2f679b0260801c5b600083664000000000000016600f0b1315613336577001002c605e2e8cec506d21bfc89a23a00f0260801c5b600083662000000000000016600f0b131561336257700100162f3904051fa128bca9c55c31e5df0260801c5b600083661000000000000016600f0b131561338e577001000b175effdc76ba38e31671ca9397250260801c5b600083660800000000000016600f0b13156133ba57700100058ba01fb9f96d6cacd4b180917c3d0260801c5b600083660400000000000016600f0b13156133e65770010002c5cc37da9491d0985c348c68e7b30260801c5b600083660200000000000016600f0b1315613412577001000162e525ee054754457d59952920260260801c5b600083660100000000000016600f0b131561343e5770010000b17255775c040618bf4a4ade83fc0260801c5b6000836580000000000016600f0b1315613469577001000058b91b5bc9ae2eed81e9b7d4cfab0260801c5b6000836540000000000016600f0b131561349457700100002c5c89d5ec6ca4d7c8acc017b7c90260801c5b6000836520000000000016600f0b13156134bf5770010000162e43f4f831060e02d839a9d16d0260801c5b6000836510000000000016600f0b13156134ea57700100000b1721bcfc99d9f890ea069117630260801c5b6000836508000000000016600f0b13156135155770010000058b90cf1e6d97f9ca14dbcc16280260801c5b6000836504000000000016600f0b1315613540577001000002c5c863b73f016468f6bac5ca2b0260801c5b6000836502000000000016600f0b131561356b57700100000162e430e5a18f6119e3c02282a50260801c5b6000836501000000000016600f0b1315613596577001000000b1721835514b86e6d96efd1bfe0260801c5b60008364800000000016600f0b13156135c057700100000058b90c0b48c6be5df846c5b2ef0260801c5b60008364400000000016600f0b13156135ea5770010000002c5c8601cc6b9e94213c72737a0260801c5b60008364200000000016600f0b1315613614577001000000162e42fff037df38aa2b219f060260801c5b60008364100000000016600f0b131561363e5770010000000b17217fba9c739aa5819f44f90260801c5b60008364080000000016600f0b1315613668577001000000058b90bfcdee5acd3c1cedc8230260801c5b60008364040000000016600f0b131561369257700100000002c5c85fe31f35a6a30da1be500260801c5b60008364020000000016600f0b13156136bc5770010000000162e42ff0999ce3541b9fffcf0260801c5b60008364010000000016600f0b13156136e657700100000000b17217f80f4ef5aadda455540260801c5b600083638000000016600f0b131561370f5770010000000058b90bfbf8479bd5a81b51ad0260801c5b600083634000000016600f0b1315613738577001000000002c5c85fdf84bd62ae30a74cc0260801c5b600083632000000016600f0b131561376157700100000000162e42fefb2fed257559bdaa0260801c5b600083631000000016600f0b131561378a577001000000000b17217f7d5a7716bba4a9ae0260801c5b600083630800000016600f0b13156137b357700100000000058b90bfbe9ddbac5e109cce0260801c5b600083630400000016600f0b13156137dc5770010000000002c5c85fdf4b15de6f17eb0d0260801c5b600083630200000016600f0b1315613805577001000000000162e42fefa494f1478fde050260801c5b600083630100000016600f0b131561382e5770010000000000b17217f7d20cf927c8e94c0260801c5b6000836280000016600f0b1315613856577001000000000058b90bfbe8f71cb4e4b33d0260801c5b6000836240000016600f0b131561387e57700100000000002c5c85fdf477b662b269450260801c5b6000836220000016600f0b13156138a65770010000000000162e42fefa3ae53369388c0260801c5b6000836210000016600f0b13156138ce57700100000000000b17217f7d1d351a389d400260801c5b6000836208000016600f0b13156138f65770010000000000058b90bfbe8e8b2d3d4ede0260801c5b6000836204000016600f0b131561391e577001000000000002c5c85fdf4741bea6e77e0260801c5b6000836202000016600f0b131561394657700100000000000162e42fefa39fe95583c20260801c5b6000836201000016600f0b131561396e577001000000000000b17217f7d1cfb72b45e10260801c5b60008361800016600f0b131561399557700100000000000058b90bfbe8e7cc35c3f00260801c5b60008361400016600f0b13156139bc5770010000000000002c5c85fdf473e242ea380260801c5b60008361200016600f0b13156139e3577001000000000000162e42fefa39f02b772c0260801c5b60008361100016600f0b1315613a0a5770010000000000000b17217f7d1cf7d83c1a0260801c5b60008361080016600f0b1315613a31577001000000000000058b90bfbe8e7bdcbe2e0260801c5b60008361040016600f0b1315613a5857700100000000000002c5c85fdf473dea871f0260801c5b60008361020016600f0b1315613a7f5770010000000000000162e42fefa39ef44d910260801c5b60008361010016600f0b1315613aa657700100000000000000b17217f7d1cf79e9490260801c5b600083608016600f0b1315613acc5770010000000000000058b90bfbe8e7bce5440260801c5b600083604016600f0b1315613af2577001000000000000002c5c85fdf473de6eca0260801c5b600083602016600f0b1315613b1857700100000000000000162e42fefa39ef366f0260801c5b600083601016600f0b1315613b3e577001000000000000000b17217f7d1cf79afa0260801c5b600083600816600f0b1315613b6457700100000000000000058b90bfbe8e7bcd6d0260801c5b600083600416600f0b1315613b8a5770010000000000000002c5c85fdf473de6b20260801c5b600083600216600f0b1315613bb0577001000000000000000162e42fefa39ef3580260801c5b600083600116600f0b1315613bd65770010000000000000000b17217f7d1cf79ab0260801c5b600f83810b60401d603f03900b1c6f7fffffffffffffffffffffffffffffff8111156125cf57600080fd5b80356001600160a01b03811681146127a757600080fd5b60008083601f840112613c2a57600080fd5b50813567ffffffffffffffff811115613c4257600080fd5b602083019150836020828501011115613c5a57600080fd5b9250929050565b803563ffffffff811681146127a757600080fd5b600060208284031215613c8757600080fd5b61215582613c01565b60008060408385031215613ca357600080fd5b613cac83613c01565b946020939093013593505050565b60008060008060008060008060006101008a8c031215613cd957600080fd5b613ce28a613c01565b985060208a0135975060408a0135613cf98161416a565b965060608a0135955060808a0135945060a08a0135613d178161416a565b935060c08a0135613d278161416a565b925060e08a013567ffffffffffffffff811115613d4357600080fd5b613d4f8c828d01613c18565b915080935050809150509295985092959850929598565b600080600060608486031215613d7b57600080fd5b613d8484613c01565b95602085013595506040909401359392505050565b600080600080600060808688031215613db157600080fd5b613dba86613c01565b94506020860135935060408601359250606086013567ffffffffffffffff811115613de457600080fd5b613df088828901613c18565b969995985093965092949392505050565b600060208284031215613e1357600080fd5b81516125cc8161416a565b600060208284031215613e3057600080fd5b5035919050565b600080600080600080600060c0888a031215613e5257600080fd5b87359650613e6260208901613c01565b955060408801359450606088013593506080880135613e808161416a565b925060a088013567ffffffffffffffff811115613e9c57600080fd5b613ea88a828b01613c18565b989b979a50959850939692959293505050565b60008060408385031215613ece57600080fd5b50508035926020909101359150565b60008060008060008060008060e0898b031215613ef957600080fd5b88356001600160801b0381168114613f1057600080fd5b9750613f1e60208a01613c61565b9650613f2c60408a01613c61565b9550613f3a60608a01613c61565b94506080890135935060a0890135925060c089013567ffffffffffffffff811115613f6457600080fd5b613f708b828c01613c18565b999c989b5096995094979396929594505050565b600060208284031215613f9657600080fd5b5051919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000825160005b81811015613fe75760208186018101518583015201613fcd565b81811115613ff6576000828501525b509190910192915050565b848152836020820152606060408201526000610703606083018486613f9d565b60006001600160801b038083168185168083038211156140435761404361413e565b01949350505050565b6000821982111561405f5761405f61413e565b500190565b60008261408157634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156140a0576140a061413e565b500290565b60006001600160801b03838116908316818110156140c5576140c561413e565b039392505050565b6000828210156140df576140df61413e565b500390565b600063ffffffff838116908316818110156140c5576140c561413e565b600081600f0b60016001607f1b031981141561411f5761411f61413e565b60000392915050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b801515811461417857600080fd5b5056fea164736f6c6343000806000a
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061016b5760003560e01c80638fbc3ecd116100cd578063be00763a11610081578063c45a015511610066578063c45a0155146104fe578063ca28fcd614610525578063d2957b8f1461053857600080fd5b8063be00763a146104a9578063c08165d4146104d757600080fd5b80639f8cfade116100b25780639f8cfade1461045f578063aaf5eb6814610487578063b5c5f6721461049657600080fd5b80638fbc3ecd146104425780639e48ff5a1461044a57600080fd5b80633882046511610124578063546fecae11610109578063546fecae146103ca57806355ebb825146103f05780636b35bb6b1461041757600080fd5b8063388204651461034f5780634dd0d056146103a357600080fd5b806321b77d631161015557806321b77d631461024857806322be3de11461027d57806323c5b952146102bc57600080fd5b8062bbf1e41461017057806311f9908614610220575b600080fd5b6101cf61017e366004613e1e565b6003602081905260009182526040909120805460018201546002830154938301546004909301546001600160801b0380841695600160801b948590048216959184169490930463ffffffff16929187565b604080516001600160801b0398891681529688166020880152949096169385019390935263ffffffff919091166060840152608083015260a082015260c081019190915260e0015b60405180910390f35b61023361022e366004613e1e565b61054b565b60405163ffffffff9091168152602001610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000a81565b604051908152602001610217565b6102a47f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b6040516001600160a01b039091168152602001610217565b6103126102ca366004613e1e565b6001602052600090815260409020546001600160801b0381169063ffffffff600160801b8204811691600160a01b8104821691600160c01b8204811691600160e01b90041685565b604080516001600160801b03909616865263ffffffff9485166020870152928416928501929092528216606084015216608082015260a001610217565b61038361035d366004613c75565b6002602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610217565b61026f7f000000000000000000000000000000000000000000000000000000e8d4a5100081565b6103dd6103d8366004613e1e565b610588565b604051600f9190910b8152602001610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000181565b61026f610425366004613c90565b600460209081526000928352604080842090915290825290205481565b61026f607881565b61045d610458366004613d99565b61070d565b005b61047261046d366004613ebb565b6108a1565b60408051928352602083019190915201610217565b61026f670de0b6b3a764000081565b61045d6104a4366004613d66565b610a47565b6104bc6104b7366004613edd565b610b6a565b60408051938452602084019290925290820152606001610217565b6102a47f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6102a47f0000000000000000000000005ca2d631a37b21e5de2bcb0cbb892d723a96b06881565b61045d610533366004613cba565b611260565b61026f610546366004613e37565b6118ea565b6000805460011461056f57604051635cd4e48360e01b815260040160405180910390fd5b600260005561057d82611b7a565b600160005592915050565b6000818152600160209081526040808320815160a08101835290546001600160801b038116825263ffffffff600160801b8204811694830194909452600160a01b81048416928201839052600160c01b8104841660608301819052600160e01b909104909316608082015291839161060091906140e4565b6000858152600360208181526040808420815160e08101835281546001600160801b038082168352600160801b9182900481169583019590955260018301549485169382019390935263ffffffff9290930482166060840152600281015460808401529283015460a083015260049092015460c0820152929350909182916106939190670de0b6b3a764000090611c2416565b915091506107037f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000000000000000000000000000000000e8d4a51000848488600001516001600160801b0316896020015163ffffffff168963ffffffff16611c83565b9695505050505050565b60005460011461073057604051635cd4e48360e01b815260040160405180910390fd5b600260005583158015610741575082155b1561075f576040516381552f0360e01b815260040160405180910390fd5b6001600160a01b0385166000908152600260205260409020610782908585611cd1565b600080851561079657610793611d7b565b91505b84156107a7576107a4611ebf565b90505b6040517fc536e605000000000000000000000000000000000000000000000000000000008152339063c536e605906107e9908990899089908990600401614001565b600060405180830381600087803b15801561080357600080fd5b505af1158015610817573d6000803e3d6000fd5b505050508560001461083557610835610830878461404c565b611f0b565b841561084d5761084d610848868361404c565b611f5f565b60408051878152602081018790526001600160a01b0389169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3505060016000555050505050565b6000806000546001146108c757604051635cd4e48360e01b815260040160405180910390fd5b6002600055826108ea5760405163e5664db760e01b815260040160405180910390fd5b60008481526003602052604090206001810154600160801b900463ffffffff1661092757604051631d2ad63f60e01b815260040160405180910390fd5b6040805160e08101825282546001600160801b038082168352600160801b918290048116602084015260018501549081169383019390935263ffffffff92048216606082015260028301546080820152600383015460a0820152600483015460c0820152610997918690611c2416565b3360009081526004602090815260408083208a84529091528120805493965091945086926109c69084906140cd565b909155506109d990508184848742611faf565b3360009081526002602052604090206109f3908484611cd1565b6040805184815260208101849052908101859052859033907f59c6598fc34aefe62579a03667748ef83b4682874f311affb26b318095359b0e9060600160405180910390a350600160005590939092509050565b600054600114610a6a57604051635cd4e48360e01b815260040160405180910390fd5b600260005581158015610a7b575080155b15610a99576040516381552f0360e01b815260040160405180910390fd5b610aa5600283836120a1565b508115610ae057610ae06001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216848461215c565b8015610b1a57610b1a6001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816848361215c565b60408051838152602081018390526001600160a01b0385169133917ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567910160405180910390a35050600160005550565b60008060008054600114610b9157604051635cd4e48360e01b815260040160405180910390fd5b60026000556040516bffffffffffffffffffffffff193060601b1660208201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000060808d901b1660348201527fffffffff0000000000000000000000000000000000000000000000000000000060e08c811b821660448401528b811b821660488401528a901b16604c8201527f0000000000000000000000000000000000000000000000000000000000000001907f000000000000000000000000000000000000000000000000000000e8d4a510009060500160408051808303601f19018152918152815160209283012060008181526001909352912054909550600160c01b900463ffffffff1615610cd0576040517fd946062c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b629896808c63ffffffff161180610ced575060018c63ffffffff16105b15610d31576040517f87a2c00100000000000000000000000000000000000000000000000000000000815263ffffffff8d1660048201526024015b60405180910390fd5b6001600160801b038d16610d7c576040517fb3f4bc550000000000000000000000000000000000000000000000000000000081526001600160801b038e166004820152602401610d28565b7f000000000000000000000000000000000000000000000000000000000000000a8811610dd8576040517f325951a500000000000000000000000000000000000000000000000000000000815260048101899052602401610d28565b610dea82670de0b6b3a7640000614064565b891180610df5575088155b15610e2f576040517f47ac541e000000000000000000000000000000000000000000000000000000008152600481018a9052602401610d28565b6127108a63ffffffff161180610e4c57506123288a63ffffffff16105b15610e8b576040517ffdb23f4a00000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152602401610d28565b60006040518060a001604052808f6001600160801b031681526020018e63ffffffff1681526020018d63ffffffff168152602001610ec64290565b63ffffffff1681526020018c63ffffffff168152509050806040015163ffffffff16816060015163ffffffff161115610f125760405163536e15e160e01b815260040160405180910390fd5b600081606001518260400151610f2891906140e4565b9050610f57600085858e86600001516001600160801b0316876020015163ffffffff168763ffffffff166122aa565b9450670de0b6b3a7640000610f6c8b8d614086565b610f769190614064565b9550670de0b6b3a7640000610f8b8b87614086565b610f959190614064565b9450851580610fa2575084155b15610fe3576040517f492d6d170000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604401610d28565b6000878152600160209081526040808320855181549387015192870151606088015160808901516001600160801b039093167fffffffffffffffffffffffff000000000000000000000000000000000000000090961695909517600160801b63ffffffff95861602177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff16600160a01b9185169190910263ffffffff60c01b191617600160c01b94841694909402939093176001600160e01b0316600160e01b92909316919091029190911790556110db7f000000000000000000000000000000000000000000000000000000000000000a8c6140cd565b3360009081526004602090815260408083208c845290915281208054929350839290919061110a90849061404c565b90915550506060830151600089815260036020526040902061113191899089908f90612380565b60008061113c611d7b565b611144611ebf565b91509150336001600160a01b031663c171d27e8a8a8f8f6040518563ffffffff1660e01b815260040161117a9493929190614001565b600060405180830381600087803b15801561119457600080fd5b505af11580156111a8573d6000803e3d6000fd5b505050506111bb8983610830919061404c565b6111c8610848898361404c565b60808086015160408088015188516020808b015184516001600160801b03909316835263ffffffff908116918301919091529281018e9052606081018d90529384018790529181169291169033907ff38f2bc2b712352b1d4a9ea95291a1c688835ac9f70a7a9d0aec57712c8b19129060a00160405180910390a4505050505050506001600081905550985098509895505050505050565b60005460011461128357604051635cd4e48360e01b815260040160405180910390fd5b6002600055856112bf576040517f28226f1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846112f6576040517f564b486500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006040518061010001604052808b6001600160a01b031681526020018915158152602001861515815260200185151581526020016113324290565b63ffffffff1681526020018a8152602001888152602001878152509050600061135e8260a00151611b7a565b9050611371607863ffffffff831661404c565b826080015163ffffffff16111561139b5760405163536e15e160e01b815260040160405180910390fd5b60006113aa8360a00151610588565b60a08085018051600090815260016020908152604080832081519586018252546001600160801b038116865263ffffffff600160801b8204811687850152600160a01b82048116878401908152600160c01b8304821660608901908152600160e01b909304909116608088015294518452600390925282209051925194955092939091611436916140e4565b90506000612710846080015163ffffffff168860c001516114579190614086565b6114619190614064565b90506000808860200151156114ad5784546114869084906001600160801b031661404c565b85549092506114a6908f90600160801b90046001600160801b03166140cd565b90506114e6565b84546114c3908f906001600160801b03166140cd565b85549092506114e3908490600160801b90046001600160801b031661404c565b90505b60018501546001600160801b0316611506670de0b6b3a764000084614086565b6115109190614064565b60018601549092506001600160801b0316611533670de0b6b3a764000083614086565b61153d9190614064565b905060006115ad7f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000000000000000000000000000000000e8d4a5100085858b600001516001600160801b03168c6020015163ffffffff168b63ffffffff16611c83565b905080600f0b88600f0b13156115fd576040517fcd4424b0000000000000000000000000000000000000000000000000000000008152600f89810b600483015282900b6024820152604401610d28565b6116268a602001518b60c001518c60e001518d608001518a61244790949392919063ffffffff16565b5050505050505082602001511561174d578260600151156116705760e083015183516001600160a01b0316600090815260026020526040812061166b92909190611cd1565b6116ab565b825160e08401516116ab916001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169161215c565b8260400151156116ce5760c08301516116c89060029060006120a1565b50611860565b60006116d8611d7b565b60c085015160405163491dc51560e11b8152919250339163923b8a2a91611708916000908b908b90600401614001565b600060405180830381600087803b15801561172257600080fd5b505af1158015611736573d6000803e3d6000fd5b505050506116c88460c0015182610830919061404c565b8260600151156117855760e083015183516001600160a01b03166000908152600260205260408120611780929091611cd1565b6117c0565b825160e08401516117c0916001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169161215c565b8260400151156117de5760c08301516116c8906002906000906120a1565b60006117e8611ebf565b60c085015160405163491dc51560e11b8152919250339163923b8a2a9161181991600091908b908b90600401614001565b600060405180830381600087803b15801561183357600080fd5b505af1158015611847573d6000803e3d6000fd5b5050505061185e8460c0015182610848919061404c565b505b8260a0015183600001516001600160a01b0316336001600160a01b03167fe42b42fe878e256d1efbfb2ba292aadeac0228bbaa31f4ef8537522bdfa3ea1986602001518760c001518860e001516040516118cf9392919092151583526020830191909152604082015260600190565b60405180910390a45050600160005550505050505050505050565b6000805460011461190e57604051635cd4e48360e01b815260040160405180910390fd5b600260005585158061191e575084155b1561193c576040516381552f0360e01b815260040160405180910390fd5b60008881526003602052604090206001810154600160801b900463ffffffff1661197957604051631d2ad63f60e01b815260040160405180910390fd5b8054600182015442916000916001600160801b039182169161199c91168b614086565b6119a69190614064565b835460018501549192506000916001600160801b03600160801b9092048216916119d191168b614086565b6119db9190614064565b90508082106119ea57806119ec565b815b945084611a0c5760405163e5664db760e01b815260040160405180910390fd5b6001600160a01b038b1660009081526004602090815260408083208f845290915281208054879290611a3f90849061404c565b90915550611a529050848b8b8887612380565b8715611a6a57611a6460028b8b6120a1565b50611b15565b600080611a75611d7b565b611a7d611ebf565b6040517f151a8bf80000000000000000000000000000000000000000000000000000000081529193509150339063151a8bf890611ac4908f908f908e908e90600401614001565b600060405180830381600087803b158015611ade57600080fd5b505af1158015611af2573d6000803e3d6000fd5b50505050611b058c83610830919061404c565b611b126108488c8361404c565b50505b604080518b8152602081018b90529081018690528c906001600160a01b038d169033907f86e4803447f73017bf832b0ac69cb7afa3333a5fa823ec42a9f6cf8d17b50ecc9060600160405180910390a450506001600055509098975050505050505050565b60008181526001602052604081208054600160c01b900463ffffffff16611bb457604051631d2ad63f60e01b815260040160405180910390fd5b8054429250600160a01b900463ffffffff908116908316811015611bd6578092505b815463ffffffff60c01b1916600160c01b63ffffffff85160217825560405184907fd69bdb2831892ad6046f30af0f12362aef46cce34d58ed7ad9b3ef8c44b1b46690600090a25050919050565b6040820151825160009182916001600160801b03918216918291611c49911686614086565b611c539190614064565b92508085602001516001600160801b031685611c6f9190614086565b611c799190614064565b9150509250929050565b600080611c9660008a8a8a8989896122aa565b90506000611ca4828a612566565b90506000611cb2888b612566565b9050611cc2600f82900b8361258f565b9b9a5050505050505050505050565b8115611d2057611ce0826125d5565b83548490600090611cfb9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b8015611d7657611d2f816125d5565b83548490601090611d51908490600160801b90046001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b505050565b604051306024820152600090819081906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823160e01b906044015b60408051601f198184030181529181526020820180516001600160e01b03167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611e199190613fc6565b600060405180830381855afa9150503d8060008114611e54576040519150601f19603f3d011682016040523d82523d6000602084013e611e59565b606091505b5091509150811580611e6d57508051602014155b15611ea4576040517fad4ea8a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80806020019051810190611eb89190613f84565b9250505090565b604051306024820152600090819081906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816906370a0823160e01b90604401611dc3565b6000611f15611d7b565b905081811015611f5b576040517f9ce3223a0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610d28565b5050565b6000611f69611ebf565b905081811015611f5b576040517fc66162eb0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610d28565b611fb985826125ef565b611fc2846125d5565b85548690600090611fdd9084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061200a836125d5565b8554869060109061202c908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612059826125d5565b6001860180546000906120769084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505050505050565b33600090815260208490526040902082156120ff576120bf836125d5565b815482906000906120da9084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b81156121555761210e826125d5565b81548290601090612130908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b9392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000928392908716916121d19190613fc6565b6000604051808303816000865af19150503d806000811461220e576040519150601f19603f3d011682016040523d82523d6000602084013e612213565b606091505b509150915081801561223d57508051158061223d57508080602001905181019061223d9190613e01565b6122a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f5472616e73666572206661696c000000000000000000000000000000000000006044820152606401610d28565b5050505050565b6000806122b78588612566565b905060006122c5878a612566565b905060006122d7600160401b8361258f565b905084156123535760006122eb878761269c565b905060006122fb83600f0b6126d9565b9050600061230d600f83900b8461258f565b905060006123388f61232f61232485600f0b6127ac565b600f8b900b9061284d565b600f0b9061288c565b9050612348600f82900b8e6128c8565b975050505050612372565b611cc2896123698d61232f600f88900b8661284d565b600f0b906128c8565b505050979650505050505050565b61238a85826125ef565b612393846125d5565b855486906000906123ae9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506123db836125d5565b855486906010906123fd908490600160801b90046001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061242a826125d5565b6001860180546000906120769084906001600160801b0316614021565b61245185826125ef565b83156124f357612460836125d5565b8554869060009061247b9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506124a8826125d5565b855486906010906124ca908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506122a3565b6124fc826125d5565b855486906000906125179084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612544836125d5565b85548690601090612076908490600160801b90046001600160801b0316614021565b60008061257b83670de0b6b3a7640000614064565b905061258784826128ed565b949350505050565b6000600f82810b9084900b0360016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113155b6125cc57600080fd5b90505b92915050565b60006001600160801b038211156125eb57600080fd5b5090565b600182015460009061260e90600160801b900463ffffffff16836140e4565b905063ffffffff811615611d7657825460028401805463ffffffff9384166001600160801b038085168202909201909255600386018054600160801b9485900483168402019055600186018054600490970180549288169093029091019091557fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff9094169290911602179055565b6000806126b36126ab8461292d565b600f0b61293d565b905060006126c08561295f565b90506126d0600f82900b8361284d565b95945050505050565b6000600160401b600f83900b1215806126f65750600082600f0b13155b15612733576040517f72cc98e8000000000000000000000000000000000000000000000000000000008152600f83900b6004820152602401610d28565b67f999999999999999600f83900b1380159061275b5750670666666666666666600f83900b12155b15612769576125cf8261296d565b670666666666666666600f83900b1215612786576125cf82612a18565b600061279e612799600160401b8561258f565b612a18565b61215590614101565b919050565b6000806127c6600f84900b68016a09e667f3bcc908612ae2565b905060006128046127f96127ee6127df85600f0b612b43565b6753dd02a4f5ee2e469061284d565b600160401b9061288c565b600160401b90612ae2565b905060006128128383612b76565b9050600083600f0b121561282f5761282c81600f0b612c46565b90505b6000610703612842600160401b8461288c565b678000000000000000905b6000600f83810b9083900b0260401d60016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000600f83810b9083900b0160016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000806128dd83670de0b6b3a7640000614064565b9050612587600f85900b82612c61565b6000816128f957600080fd5b60006129058484612cda565b90506f7fffffffffffffffffffffffffffffff6001600160801b03821611156125cc57600080fd5b60006125cf826301e185586128ed565b60008082600f0b121561294f57600080fd5b6125cf604083600f0b901b612e4d565b60006125cf826127106128ed565b600080612986600f84900b67800000000000000061258f565b90506000612998600f83900b8361284d565b905060006126d0612a0d6129fd6129d56721d0a04b0e9b94f161232f6129c767c2bf5d74c724e53e198961284d565b61232f600f8a900b8a61284d565b6129f46726a8f3c1f21b336e61232f6787c57e5da70d3c8f198961284d565b600f0b90612ae2565b68015d71f5721242c7879061288c565b600f85900b9061284d565b600080612a3a6126ab612a326127f9600f87900b8761284d565b600f0b612fbb565b90506000612a5e6707a1e70f720eca4361232f6801000bf627fa188410198561284d565b90506000612a826810aeac93f55267a9a561232f68041ed34a25614902368661284d565b90506000612ab768072c7d592d021fb1db61232f612aa96808c27b4617f5f800ea8861284d565b61232f600f89900b8961284d565b90506000612ad7612acc600f85900b84612ae2565b600f86900b9061288c565b979650505050505050565b600081600f0b60001415612af557600080fd5b600082600f0b604085600f0b901b81612b1057612b10614154565b05905060016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000600f82900b60016001607f1b03191415612b5e57600080fd5b600082600f0b12612b6f57816125cf565b5060000390565b600080612bc8612a0d612bb8612bad612b9c600f88900b68010fb844255a12d72e61284d565b68017401c57014c38f13199061288c565b600f87900b9061284d565b68016a09e667f3bcc9089061288c565b90506000612bf4612be5612bad6748d4c730f051a5fd198561288c565b67413c831bb169f8749061288c565b90506000610703612c3b612c30612c25612c1d612c15600f8c900b8c61284d565b600f0b612c46565b600f0b612ff5565b600f86900b9061284d565b600f88900b9061284d565b600160401b9061258f565b6000600f82900b60016001607f1b03191415612b6f57600080fd5b600081612c70575060006125cf565b600083600f0b1215612c8157600080fd5b600f83900b6001600160801b038316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff811115612cc157600080fd5b60401b8119811115612cd257600080fd5b019392505050565b600081612ce657600080fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff8411612d225782604085901b81612d1a57612d1a614154565b049050612e39565b60c084811c6401000000008110612d3b576020918201911c5b620100008110612d4d576010918201911c5b6101008110612d5e576008918201911c5b60108110612d6e576004918201911c5b60048110612d7e576002918201911c5b60028110612d8d576001820191505b60bf820360018603901c6001018260ff0387901b81612dae57612dae614154565b0492506001600160801b03831115612dc557600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015612df1576001820391505b608084901b92900382811015612e08576001820391505b829003608084901c8214612e1e57612e1e614128565b888181612e2d57612e2d614154565b04870196505050505050505b6001600160801b038111156125cc57600080fd5b600081612e5c57506000919050565b816001600160801b8210612e755760809190911c9060401b5b600160401b8210612e8b5760409190911c9060201b5b6401000000008210612ea25760209190911c9060101b5b620100008210612eb75760109190911c9060081b5b6101008210612ecb5760089190911c9060041b5b60108210612ede5760049190911c9060021b5b60088210612eea5760011b5b6001818581612efb57612efb614154565b048201901c90506001818581612f1357612f13614154565b048201901c90506001818581612f2b57612f2b614154565b048201901c90506001818581612f4357612f43614154565b048201901c90506001818581612f5b57612f5b614154565b048201901c90506001818581612f7357612f73614154565b048201901c90506001818581612f8b57612f8b614154565b048201901c90506000818581612fa357612fa3614154565b049050808210612fb357806126d0565b509392505050565b60008082600f0b13612fcc57600080fd5b6080612fd78361304f565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b60006840000000000000000082600f0b1261300f57600080fd5b683fffffffffffffffff1982600f0b121561302c57506000919050565b6125cf608083600f0b700171547652b82fe1777d0ffda0d23a7d1202901d61312e565b60008082600f0b1361306057600080fd5b6000600f83900b600160401b811261307a576040918201911d5b640100000000811261308e576020918201911d5b6201000081126130a0576010918201911d5b61010081126130b1576008918201911d5b601081126130c1576004918201911d5b600481126130d1576002918201911d5b600281126130e0576001820191505b603f19820160401b600f85900b607f8490031b6780000000000000005b60008113156131235790800260ff81901c8281029390930192607f011c9060011d6130fd565b509095945050505050565b60006840000000000000000082600f0b1261314857600080fd5b683fffffffffffffffff1982600f0b121561316557506000919050565b6f8000000000000000000000000000000060006780000000000000008416600f0b13156131a35770016a09e667f3bcc908b2fb1366ea957d3e0260801c5b60008367400000000000000016600f0b13156131d0577001306fe0a31b7152de8d5a46305c85edec0260801c5b60008367200000000000000016600f0b13156131fd577001172b83c7d517adcdf7c8c50eb14a791f0260801c5b60008367100000000000000016600f0b131561322a5770010b5586cf9890f6298b92b71842a983630260801c5b60008367080000000000000016600f0b1315613257577001059b0d31585743ae7c548eb68ca417fd0260801c5b60008367040000000000000016600f0b131561328457700102c9a3e778060ee6f7caca4f7a29bde80260801c5b60008367020000000000000016600f0b13156132b15770010163da9fb33356d84a66ae336dcdfa3f0260801c5b60008367010000000000000016600f0b13156132de57700100b1afa5abcbed6129ab13ec11dc95430260801c5b600083668000000000000016600f0b131561330a5770010058c86da1c09ea1ff19d294cf2f679b0260801c5b600083664000000000000016600f0b1315613336577001002c605e2e8cec506d21bfc89a23a00f0260801c5b600083662000000000000016600f0b131561336257700100162f3904051fa128bca9c55c31e5df0260801c5b600083661000000000000016600f0b131561338e577001000b175effdc76ba38e31671ca9397250260801c5b600083660800000000000016600f0b13156133ba57700100058ba01fb9f96d6cacd4b180917c3d0260801c5b600083660400000000000016600f0b13156133e65770010002c5cc37da9491d0985c348c68e7b30260801c5b600083660200000000000016600f0b1315613412577001000162e525ee054754457d59952920260260801c5b600083660100000000000016600f0b131561343e5770010000b17255775c040618bf4a4ade83fc0260801c5b6000836580000000000016600f0b1315613469577001000058b91b5bc9ae2eed81e9b7d4cfab0260801c5b6000836540000000000016600f0b131561349457700100002c5c89d5ec6ca4d7c8acc017b7c90260801c5b6000836520000000000016600f0b13156134bf5770010000162e43f4f831060e02d839a9d16d0260801c5b6000836510000000000016600f0b13156134ea57700100000b1721bcfc99d9f890ea069117630260801c5b6000836508000000000016600f0b13156135155770010000058b90cf1e6d97f9ca14dbcc16280260801c5b6000836504000000000016600f0b1315613540577001000002c5c863b73f016468f6bac5ca2b0260801c5b6000836502000000000016600f0b131561356b57700100000162e430e5a18f6119e3c02282a50260801c5b6000836501000000000016600f0b1315613596577001000000b1721835514b86e6d96efd1bfe0260801c5b60008364800000000016600f0b13156135c057700100000058b90c0b48c6be5df846c5b2ef0260801c5b60008364400000000016600f0b13156135ea5770010000002c5c8601cc6b9e94213c72737a0260801c5b60008364200000000016600f0b1315613614577001000000162e42fff037df38aa2b219f060260801c5b60008364100000000016600f0b131561363e5770010000000b17217fba9c739aa5819f44f90260801c5b60008364080000000016600f0b1315613668577001000000058b90bfcdee5acd3c1cedc8230260801c5b60008364040000000016600f0b131561369257700100000002c5c85fe31f35a6a30da1be500260801c5b60008364020000000016600f0b13156136bc5770010000000162e42ff0999ce3541b9fffcf0260801c5b60008364010000000016600f0b13156136e657700100000000b17217f80f4ef5aadda455540260801c5b600083638000000016600f0b131561370f5770010000000058b90bfbf8479bd5a81b51ad0260801c5b600083634000000016600f0b1315613738577001000000002c5c85fdf84bd62ae30a74cc0260801c5b600083632000000016600f0b131561376157700100000000162e42fefb2fed257559bdaa0260801c5b600083631000000016600f0b131561378a577001000000000b17217f7d5a7716bba4a9ae0260801c5b600083630800000016600f0b13156137b357700100000000058b90bfbe9ddbac5e109cce0260801c5b600083630400000016600f0b13156137dc5770010000000002c5c85fdf4b15de6f17eb0d0260801c5b600083630200000016600f0b1315613805577001000000000162e42fefa494f1478fde050260801c5b600083630100000016600f0b131561382e5770010000000000b17217f7d20cf927c8e94c0260801c5b6000836280000016600f0b1315613856577001000000000058b90bfbe8f71cb4e4b33d0260801c5b6000836240000016600f0b131561387e57700100000000002c5c85fdf477b662b269450260801c5b6000836220000016600f0b13156138a65770010000000000162e42fefa3ae53369388c0260801c5b6000836210000016600f0b13156138ce57700100000000000b17217f7d1d351a389d400260801c5b6000836208000016600f0b13156138f65770010000000000058b90bfbe8e8b2d3d4ede0260801c5b6000836204000016600f0b131561391e577001000000000002c5c85fdf4741bea6e77e0260801c5b6000836202000016600f0b131561394657700100000000000162e42fefa39fe95583c20260801c5b6000836201000016600f0b131561396e577001000000000000b17217f7d1cfb72b45e10260801c5b60008361800016600f0b131561399557700100000000000058b90bfbe8e7cc35c3f00260801c5b60008361400016600f0b13156139bc5770010000000000002c5c85fdf473e242ea380260801c5b60008361200016600f0b13156139e3577001000000000000162e42fefa39f02b772c0260801c5b60008361100016600f0b1315613a0a5770010000000000000b17217f7d1cf7d83c1a0260801c5b60008361080016600f0b1315613a31577001000000000000058b90bfbe8e7bdcbe2e0260801c5b60008361040016600f0b1315613a5857700100000000000002c5c85fdf473dea871f0260801c5b60008361020016600f0b1315613a7f5770010000000000000162e42fefa39ef44d910260801c5b60008361010016600f0b1315613aa657700100000000000000b17217f7d1cf79e9490260801c5b600083608016600f0b1315613acc5770010000000000000058b90bfbe8e7bce5440260801c5b600083604016600f0b1315613af2577001000000000000002c5c85fdf473de6eca0260801c5b600083602016600f0b1315613b1857700100000000000000162e42fefa39ef366f0260801c5b600083601016600f0b1315613b3e577001000000000000000b17217f7d1cf79afa0260801c5b600083600816600f0b1315613b6457700100000000000000058b90bfbe8e7bcd6d0260801c5b600083600416600f0b1315613b8a5770010000000000000002c5c85fdf473de6b20260801c5b600083600216600f0b1315613bb0577001000000000000000162e42fefa39ef3580260801c5b600083600116600f0b1315613bd65770010000000000000000b17217f7d1cf79ab0260801c5b600f83810b60401d603f03900b1c6f7fffffffffffffffffffffffffffffff8111156125cf57600080fd5b80356001600160a01b03811681146127a757600080fd5b60008083601f840112613c2a57600080fd5b50813567ffffffffffffffff811115613c4257600080fd5b602083019150836020828501011115613c5a57600080fd5b9250929050565b803563ffffffff811681146127a757600080fd5b600060208284031215613c8757600080fd5b61215582613c01565b60008060408385031215613ca357600080fd5b613cac83613c01565b946020939093013593505050565b60008060008060008060008060006101008a8c031215613cd957600080fd5b613ce28a613c01565b985060208a0135975060408a0135613cf98161416a565b965060608a0135955060808a0135945060a08a0135613d178161416a565b935060c08a0135613d278161416a565b925060e08a013567ffffffffffffffff811115613d4357600080fd5b613d4f8c828d01613c18565b915080935050809150509295985092959850929598565b600080600060608486031215613d7b57600080fd5b613d8484613c01565b95602085013595506040909401359392505050565b600080600080600060808688031215613db157600080fd5b613dba86613c01565b94506020860135935060408601359250606086013567ffffffffffffffff811115613de457600080fd5b613df088828901613c18565b969995985093965092949392505050565b600060208284031215613e1357600080fd5b81516125cc8161416a565b600060208284031215613e3057600080fd5b5035919050565b600080600080600080600060c0888a031215613e5257600080fd5b87359650613e6260208901613c01565b955060408801359450606088013593506080880135613e808161416a565b925060a088013567ffffffffffffffff811115613e9c57600080fd5b613ea88a828b01613c18565b989b979a50959850939692959293505050565b60008060408385031215613ece57600080fd5b50508035926020909101359150565b60008060008060008060008060e0898b031215613ef957600080fd5b88356001600160801b0381168114613f1057600080fd5b9750613f1e60208a01613c61565b9650613f2c60408a01613c61565b9550613f3a60608a01613c61565b94506080890135935060a0890135925060c089013567ffffffffffffffff811115613f6457600080fd5b613f708b828c01613c18565b999c989b5096995094979396929594505050565b600060208284031215613f9657600080fd5b5051919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000825160005b81811015613fe75760208186018101518583015201613fcd565b81811115613ff6576000828501525b509190910192915050565b848152836020820152606060408201526000610703606083018486613f9d565b60006001600160801b038083168185168083038211156140435761404361413e565b01949350505050565b6000821982111561405f5761405f61413e565b500190565b60008261408157634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156140a0576140a061413e565b500290565b60006001600160801b03838116908316818110156140c5576140c561413e565b039392505050565b6000828210156140df576140df61413e565b500390565b600063ffffffff838116908316818110156140c5576140c561413e565b600081600f0b60016001607f1b031981141561411f5761411f61413e565b60000392915050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b801515811461417857600080fd5b5056fea164736f6c6343000806000a
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.