More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
19392811 | 238 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Minimal Proxy Contract for 0xfa6ae0942177e41a40f8f01eed98d1200b66544d
Contract Name:
DSP
Compiler Version
v0.6.9+commit.3e3065ac
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2021-04-23 */ // File: contracts/lib/InitializableOwnable.sol /* Copyright 2020 DODO ZOO. SPDX-License-Identifier: Apache-2.0 */ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; /** * @title Ownable * @author DODO Breeder * * @notice Ownership related functions */ contract InitializableOwnable { address public _OWNER_; address public _NEW_OWNER_; bool internal _INITIALIZED_; // ============ Events ============ event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // ============ Modifiers ============ modifier notInitialized() { require(!_INITIALIZED_, "DODO_INITIALIZED"); _; } modifier onlyOwner() { require(msg.sender == _OWNER_, "NOT_OWNER"); _; } // ============ Functions ============ function initOwner(address newOwner) public notInitialized { _INITIALIZED_ = true; _OWNER_ = newOwner; } function transferOwnership(address newOwner) public onlyOwner { emit OwnershipTransferPrepared(_OWNER_, newOwner); _NEW_OWNER_ = newOwner; } function claimOwnership() public { require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); _OWNER_ = _NEW_OWNER_; _NEW_OWNER_ = address(0); } } // File: contracts/lib/FeeRateModel.sol interface IFeeRateImpl { function getFeeRate(address pool, address trader) external view returns (uint256); } interface IFeeRateModel { function getFeeRate(address trader) external view returns (uint256); } contract FeeRateModel is InitializableOwnable { address public feeRateImpl; function setFeeProxy(address _feeRateImpl) public onlyOwner { feeRateImpl = _feeRateImpl; } function getFeeRate(address trader) external view returns (uint256) { if(feeRateImpl == address(0)) return 0; return IFeeRateImpl(feeRateImpl).getFeeRate(msg.sender,trader); } } // File: contracts/intf/IERC20.sol /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); function decimals() external view returns (uint8); function name() external view returns (string memory); function symbol() external view returns (string memory); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); } // File: contracts/lib/SafeMath.sol /** * @title SafeMath * @author DODO Breeder * * @notice Math operations with safety checks that revert on error */ library SafeMath { function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "MUL_ERROR"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "DIVIDING_ERROR"); return a / b; } function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { uint256 quotient = div(a, b); uint256 remainder = a - quotient * b; if (remainder > 0) { return quotient + 1; } else { return quotient; } } function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SUB_ERROR"); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "ADD_ERROR"); return c; } function sqrt(uint256 x) internal pure returns (uint256 y) { uint256 z = x / 2 + 1; y = x; while (z < y) { y = z; z = (x / z + z) / 2; } } } // File: contracts/lib/DecimalMath.sol /** * @title DecimalMath * @author DODO Breeder * * @notice Functions for fixed point number with 18 decimals */ library DecimalMath { using SafeMath for uint256; uint256 internal constant ONE = 10**18; uint256 internal constant ONE2 = 10**36; function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { return target.mul(d) / (10**18); } function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { return target.mul(d).divCeil(10**18); } function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { return target.mul(10**18).div(d); } function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { return target.mul(10**18).divCeil(d); } function reciprocalFloor(uint256 target) internal pure returns (uint256) { return uint256(10**36).div(target); } function reciprocalCeil(uint256 target) internal pure returns (uint256) { return uint256(10**36).divCeil(target); } } // File: contracts/lib/DODOMath.sol /** * @title DODOMath * @author DODO Breeder * * @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions */ library DODOMath { using SafeMath for uint256; /* Integrate dodo curve from V1 to V2 require V0>=V1>=V2>0 res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1) let V1-V2=delta res = i*delta*(1-k+k(V0^2/V1/V2)) i is the price of V-res trading pair support k=1 & k=0 case [round down] */ function _GeneralIntegrate( uint256 V0, uint256 V1, uint256 V2, uint256 i, uint256 k ) internal pure returns (uint256) { require(V0 > 0, "TARGET_IS_ZERO"); uint256 fairAmount = i.mul(V1.sub(V2)); // i*delta if (k == 0) { return fairAmount.div(DecimalMath.ONE); } uint256 V0V0V1V2 = DecimalMath.divFloor(V0.mul(V0).div(V1), V2); uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2) return DecimalMath.ONE.sub(k).add(penalty).mul(fairAmount).div(DecimalMath.ONE2); } /* Follow the integration function above i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) Assume Q2=Q0, Given Q1 and deltaB, solve Q0 i is the price of delta-V trading pair give out target of V support k=1 & k=0 case [round down] */ function _SolveQuadraticFunctionForTarget( uint256 V1, uint256 delta, uint256 i, uint256 k ) internal pure returns (uint256) { if (V1 == 0) { return 0; } if (k == 0) { return V1.add(DecimalMath.mulFloor(i, delta)); } // V0 = V1*(1+(sqrt-1)/2k) // sqrt = √(1+4kidelta/V1) // premium = 1+(sqrt-1)/2k // uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt(); uint256 sqrt; uint256 ki = (4 * k).mul(i); if (ki == 0) { sqrt = DecimalMath.ONE; } else if ((ki * delta) / ki == delta) { sqrt = (ki * delta).div(V1).add(DecimalMath.ONE2).sqrt(); } else { sqrt = ki.div(V1).mul(delta).add(DecimalMath.ONE2).sqrt(); } uint256 premium = DecimalMath.divFloor(sqrt.sub(DecimalMath.ONE), k * 2).add(DecimalMath.ONE); // V0 is greater than or equal to V1 according to the solution return DecimalMath.mulFloor(V1, premium); } /* Follow the integration expression above, we have: i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) Given Q1 and deltaB, solve Q2 This is a quadratic function and the standard version is aQ2^2 + bQ2 + c = 0, where a=1-k -b=(1-k)Q1-kQ0^2/Q1+i*deltaB c=-kQ0^2 and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k) note: another root is negative, abondan if deltaBSig=true, then Q2>Q1, user sell Q and receive B if deltaBSig=false, then Q2<Q1, user sell B and receive Q return |Q1-Q2| as we only support sell amount as delta, the deltaB is always negative the input ideltaB is actually -ideltaB in the equation i is the price of delta-V trading pair support k=1 & k=0 case [round down] */ function _SolveQuadraticFunctionForTrade( uint256 V0, uint256 V1, uint256 delta, uint256 i, uint256 k ) internal pure returns (uint256) { require(V0 > 0, "TARGET_IS_ZERO"); if (delta == 0) { return 0; } if (k == 0) { return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta); } if (k == DecimalMath.ONE) { // if k==1 // Q2=Q1/(1+ideltaBQ1/Q0/Q0) // temp = ideltaBQ1/Q0/Q0 // Q2 = Q1/(1+temp) // Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp)) // uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0)); uint256 temp; uint256 idelta = i.mul(delta); if (idelta == 0) { temp = 0; } else if ((idelta * V1) / idelta == V1) { temp = (idelta * V1).div(V0.mul(V0)); } else { temp = delta.mul(V1).div(V0).mul(i).div(V0); } return V1.mul(temp).div(temp.add(DecimalMath.ONE)); } // calculate -b value and sig // b = kQ0^2/Q1-i*deltaB-(1-k)Q1 // part1 = (1-k)Q1 >=0 // part2 = kQ0^2/Q1-i*deltaB >=0 // bAbs = abs(part1-part2) // if part1>part2 => b is negative => bSig is false // if part2>part1 => b is positive => bSig is true uint256 part2 = k.mul(V0).div(V1).mul(V0).add(i.mul(delta)); // kQ0^2/Q1-i*deltaB uint256 bAbs = DecimalMath.ONE.sub(k).mul(V1); // (1-k)Q1 bool bSig; if (bAbs >= part2) { bAbs = bAbs - part2; bSig = false; } else { bAbs = part2 - bAbs; bSig = true; } bAbs = bAbs.div(DecimalMath.ONE); // calculate sqrt uint256 squareRoot = DecimalMath.mulFloor( DecimalMath.ONE.sub(k).mul(4), DecimalMath.mulFloor(k, V0).mul(V0) ); // 4(1-k)kQ0^2 squareRoot = bAbs.mul(bAbs).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0) // final res uint256 denominator = DecimalMath.ONE.sub(k).mul(2); // 2(1-k) uint256 numerator; if (bSig) { numerator = squareRoot.sub(bAbs); } else { numerator = bAbs.add(squareRoot); } uint256 V2 = DecimalMath.divCeil(numerator, denominator); if (V2 > V1) { return 0; } else { return V1 - V2; } } } // File: contracts/lib/PMMPricing.sol /** * @title Pricing * @author DODO Breeder * * @notice DODO Pricing model */ library PMMPricing { using SafeMath for uint256; enum RState {ONE, ABOVE_ONE, BELOW_ONE} struct PMMState { uint256 i; uint256 K; uint256 B; uint256 Q; uint256 B0; uint256 Q0; RState R; } // ============ buy & sell ============ function sellBaseToken(PMMState memory state, uint256 payBaseAmount) internal pure returns (uint256 receiveQuoteAmount, RState newR) { if (state.R == RState.ONE) { // case 1: R=1 // R falls below one receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount); newR = RState.BELOW_ONE; } else if (state.R == RState.ABOVE_ONE) { uint256 backToOnePayBase = state.B0.sub(state.B); uint256 backToOneReceiveQuote = state.Q.sub(state.Q0); // case 2: R>1 // complex case, R status depends on trading amount if (payBaseAmount < backToOnePayBase) { // case 2.1: R status do not change receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount); newR = RState.ABOVE_ONE; if (receiveQuoteAmount > backToOneReceiveQuote) { // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount // to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote receiveQuoteAmount = backToOneReceiveQuote; } } else if (payBaseAmount == backToOnePayBase) { // case 2.2: R status changes to ONE receiveQuoteAmount = backToOneReceiveQuote; newR = RState.ONE; } else { // case 2.3: R status changes to BELOW_ONE receiveQuoteAmount = backToOneReceiveQuote.add( _ROneSellBaseToken(state, payBaseAmount.sub(backToOnePayBase)) ); newR = RState.BELOW_ONE; } } else { // state.R == RState.BELOW_ONE // case 3: R<1 receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount); newR = RState.BELOW_ONE; } } function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount) internal pure returns (uint256 receiveBaseAmount, RState newR) { if (state.R == RState.ONE) { receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount); newR = RState.ABOVE_ONE; } else if (state.R == RState.ABOVE_ONE) { receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount); newR = RState.ABOVE_ONE; } else { uint256 backToOnePayQuote = state.Q0.sub(state.Q); uint256 backToOneReceiveBase = state.B.sub(state.B0); if (payQuoteAmount < backToOnePayQuote) { receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount); newR = RState.BELOW_ONE; if (receiveBaseAmount > backToOneReceiveBase) { receiveBaseAmount = backToOneReceiveBase; } } else if (payQuoteAmount == backToOnePayQuote) { receiveBaseAmount = backToOneReceiveBase; newR = RState.ONE; } else { receiveBaseAmount = backToOneReceiveBase.add( _ROneSellQuoteToken(state, payQuoteAmount.sub(backToOnePayQuote)) ); newR = RState.ABOVE_ONE; } } } // ============ R = 1 cases ============ function _ROneSellBaseToken(PMMState memory state, uint256 payBaseAmount) internal pure returns ( uint256 // receiveQuoteToken ) { // in theory Q2 <= targetQuoteTokenAmount // however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount return DODOMath._SolveQuadraticFunctionForTrade( state.Q0, state.Q0, payBaseAmount, state.i, state.K ); } function _ROneSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) internal pure returns ( uint256 // receiveBaseToken ) { return DODOMath._SolveQuadraticFunctionForTrade( state.B0, state.B0, payQuoteAmount, DecimalMath.reciprocalFloor(state.i), state.K ); } // ============ R < 1 cases ============ function _RBelowSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) internal pure returns ( uint256 // receiveBaseToken ) { return DODOMath._GeneralIntegrate( state.Q0, state.Q.add(payQuoteAmount), state.Q, DecimalMath.reciprocalFloor(state.i), state.K ); } function _RBelowSellBaseToken(PMMState memory state, uint256 payBaseAmount) internal pure returns ( uint256 // receiveQuoteToken ) { return DODOMath._SolveQuadraticFunctionForTrade( state.Q0, state.Q, payBaseAmount, state.i, state.K ); } // ============ R > 1 cases ============ function _RAboveSellBaseToken(PMMState memory state, uint256 payBaseAmount) internal pure returns ( uint256 // receiveQuoteToken ) { return DODOMath._GeneralIntegrate( state.B0, state.B.add(payBaseAmount), state.B, state.i, state.K ); } function _RAboveSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) internal pure returns ( uint256 // receiveBaseToken ) { return DODOMath._SolveQuadraticFunctionForTrade( state.B0, state.B, payQuoteAmount, DecimalMath.reciprocalFloor(state.i), state.K ); } // ============ Helper functions ============ function adjustedTarget(PMMState memory state) internal pure { if (state.R == RState.BELOW_ONE) { state.Q0 = DODOMath._SolveQuadraticFunctionForTarget( state.Q, state.B.sub(state.B0), state.i, state.K ); } else if (state.R == RState.ABOVE_ONE) { state.B0 = DODOMath._SolveQuadraticFunctionForTarget( state.B, state.Q.sub(state.Q0), DecimalMath.reciprocalFloor(state.i), state.K ); } } function getMidPrice(PMMState memory state) internal pure returns (uint256) { if (state.R == RState.BELOW_ONE) { uint256 R = DecimalMath.divFloor(state.Q0.mul(state.Q0).div(state.Q), state.Q); R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R)); return DecimalMath.divFloor(state.i, R); } else { uint256 R = DecimalMath.divFloor(state.B0.mul(state.B0).div(state.B), state.B); R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R)); return DecimalMath.mulFloor(state.i, R); } } } // File: contracts/lib/SafeERC20.sol /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File: contracts/lib/ReentrancyGuard.sol /** * @title ReentrancyGuard * @author DODO Breeder * * @notice Protect functions from Reentrancy Attack */ contract ReentrancyGuard { // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations // zero-state of _ENTERED_ is false bool private _ENTERED_; modifier preventReentrant() { require(!_ENTERED_, "REENTRANT"); _ENTERED_ = true; _; _ENTERED_ = false; } } // File: contracts/DODOStablePool/impl/DSPStorage.sol contract DSPStorage is ReentrancyGuard { using SafeMath for uint256; bool internal _DSP_INITIALIZED_; bool public _IS_OPEN_TWAP_ = false; // ============ Core Address ============ address public _MAINTAINER_; IERC20 public _BASE_TOKEN_; IERC20 public _QUOTE_TOKEN_; uint112 public _BASE_RESERVE_; uint112 public _QUOTE_RESERVE_; uint32 public _BLOCK_TIMESTAMP_LAST_; uint256 public _BASE_PRICE_CUMULATIVE_LAST_; uint112 public _BASE_TARGET_; uint112 public _QUOTE_TARGET_; uint32 public _RState_; // ============ Shares (ERC20) ============ string public symbol; uint8 public decimals; string public name; uint256 public totalSupply; mapping(address => uint256) internal _SHARES_; mapping(address => mapping(address => uint256)) internal _ALLOWED_; // ================= Permit ====================== bytes32 public DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint256) public nonces; // ============ Variables for Pricing ============ IFeeRateModel public _MT_FEE_RATE_MODEL_; uint256 public _LP_FEE_RATE_; uint256 public _K_; uint256 public _I_; // ============ Helper Functions ============ function getPMMState() public view returns (PMMPricing.PMMState memory state) { state.i = _I_; state.K = _K_; state.B = _BASE_RESERVE_; state.Q = _QUOTE_RESERVE_; state.B0 = _BASE_TARGET_; // will be calculated in adjustedTarget state.Q0 = _QUOTE_TARGET_; state.R = PMMPricing.RState(_RState_); PMMPricing.adjustedTarget(state); } function getPMMStateForCall() external view returns ( uint256 i, uint256 K, uint256 B, uint256 Q, uint256 B0, uint256 Q0, uint256 R ) { PMMPricing.PMMState memory state = getPMMState(); i = state.i; K = state.K; B = state.B; Q = state.Q; B0 = state.B0; Q0 = state.Q0; R = uint256(state.R); } function getMidPrice() public view returns (uint256 midPrice) { return PMMPricing.getMidPrice(getPMMState()); } } // File: contracts/DODOStablePool/impl/DSPVault.sol contract DSPVault is DSPStorage { using SafeMath for uint256; using SafeERC20 for IERC20; // ============ Events ============ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); event Mint(address indexed user, uint256 value); event Burn(address indexed user, uint256 value); // ============ View Functions ============ function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve) { baseReserve = _BASE_RESERVE_; quoteReserve = _QUOTE_RESERVE_; } function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate) { lpFeeRate = _LP_FEE_RATE_; mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(user); } // ============ Asset In ============ function getBaseInput() public view returns (uint256 input) { return _BASE_TOKEN_.balanceOf(address(this)).sub(uint256(_BASE_RESERVE_)); } function getQuoteInput() public view returns (uint256 input) { return _QUOTE_TOKEN_.balanceOf(address(this)).sub(uint256(_QUOTE_RESERVE_)); } // ============ TWAP UPDATE =========== function _twapUpdate() internal { uint32 blockTimestamp = uint32(block.timestamp % 2**32); uint32 timeElapsed = blockTimestamp - _BLOCK_TIMESTAMP_LAST_; if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) { _BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed; } _BLOCK_TIMESTAMP_LAST_ = blockTimestamp; } // ============ Set States ============ function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal { require(baseReserve <= uint112(-1) && quoteReserve <= uint112(-1), "OVERFLOW"); _BASE_RESERVE_ = uint112(baseReserve); _QUOTE_RESERVE_ = uint112(quoteReserve); if (_IS_OPEN_TWAP_) _twapUpdate(); } function _sync() internal { uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)); uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); require(baseBalance <= uint112(-1) && quoteBalance <= uint112(-1), "OVERFLOW"); if (baseBalance != _BASE_RESERVE_) { _BASE_RESERVE_ = uint112(baseBalance); } if (quoteBalance != _QUOTE_RESERVE_) { _QUOTE_RESERVE_ = uint112(quoteBalance); } if (_IS_OPEN_TWAP_) _twapUpdate(); } function sync() external preventReentrant { _sync(); } function correctRState() public { if (_RState_ == uint32(PMMPricing.RState.BELOW_ONE) && _BASE_RESERVE_<_BASE_TARGET_) { _RState_ = uint32(PMMPricing.RState.ONE); _BASE_TARGET_ = _BASE_RESERVE_; _QUOTE_TARGET_ = _QUOTE_RESERVE_; } if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_<_QUOTE_TARGET_) { _RState_ = uint32(PMMPricing.RState.ONE); _BASE_TARGET_ = _BASE_RESERVE_; _QUOTE_TARGET_ = _QUOTE_RESERVE_; } } // ============ Asset Out ============ function _transferBaseOut(address to, uint256 amount) internal { if (amount > 0) { _BASE_TOKEN_.safeTransfer(to, amount); } } function _transferQuoteOut(address to, uint256 amount) internal { if (amount > 0) { _QUOTE_TOKEN_.safeTransfer(to, amount); } } // ============ Shares (ERC20) ============ /** * @dev transfer token for a specified address * @param to The address to transfer to. * @param amount The amount to be transferred. */ function transfer(address to, uint256 amount) public returns (bool) { require(amount <= _SHARES_[msg.sender], "BALANCE_NOT_ENOUGH"); _SHARES_[msg.sender] = _SHARES_[msg.sender].sub(amount); _SHARES_[to] = _SHARES_[to].add(amount); emit Transfer(msg.sender, to, amount); return true; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the the balance of. * @return balance An uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) external view returns (uint256 balance) { return _SHARES_[owner]; } /** * @dev Transfer tokens from one address to another * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param amount uint256 the amount of tokens to be transferred */ function transferFrom( address from, address to, uint256 amount ) public returns (bool) { require(amount <= _SHARES_[from], "BALANCE_NOT_ENOUGH"); require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); _SHARES_[from] = _SHARES_[from].sub(amount); _SHARES_[to] = _SHARES_[to].add(amount); _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(amount); emit Transfer(from, to, amount); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * @param spender The address which will spend the funds. * @param amount The amount of tokens to be spent. */ function approve(address spender, uint256 amount) public returns (bool) { _approve(msg.sender, spender, amount); return true; } function _approve( address owner, address spender, uint256 amount ) private { _ALLOWED_[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Function to check the amount of tokens that an owner _ALLOWED_ to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address owner, address spender) public view returns (uint256) { return _ALLOWED_[owner][spender]; } function _mint(address user, uint256 value) internal { require(value > 1000, "MINT_AMOUNT_NOT_ENOUGH"); _SHARES_[user] = _SHARES_[user].add(value); totalSupply = totalSupply.add(value); emit Mint(user, value); emit Transfer(address(0), user, value); } function _burn(address user, uint256 value) internal { _SHARES_[user] = _SHARES_[user].sub(value); totalSupply = totalSupply.sub(value); emit Burn(user, value); emit Transfer(user, address(0), value); } // ============================ Permit ====================================== function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require(deadline >= block.timestamp, "DODO_DSP_LP: EXPIRED"); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline ) ) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require( recoveredAddress != address(0) && recoveredAddress == owner, "DODO_DSP_LP: INVALID_SIGNATURE" ); _approve(owner, spender, value); } } // File: contracts/intf/IDODOCallee.sol interface IDODOCallee { function DVMSellShareCall( address sender, uint256 burnShareAmount, uint256 baseAmount, uint256 quoteAmount, bytes calldata data ) external; function DVMFlashLoanCall( address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data ) external; function DPPFlashLoanCall( address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data ) external; function DSPFlashLoanCall( address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data ) external; function CPCancelCall( address sender, uint256 amount, bytes calldata data ) external; function CPClaimBidCall( address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data ) external; } // File: contracts/DODOStablePool/impl/DSPTrader.sol contract DSPTrader is DSPVault { using SafeMath for uint256; // ============ Events ============ event DODOSwap( address fromToken, address toToken, uint256 fromAmount, uint256 toAmount, address trader, address receiver ); event DODOFlashLoan(address borrower, address assetTo, uint256 baseAmount, uint256 quoteAmount); event RChange(PMMPricing.RState newRState); // ============ Trade Functions ============ function sellBase(address to) external preventReentrant returns (uint256 receiveQuoteAmount) { uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)); uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_)); uint256 mtFee; uint256 newBaseTarget; PMMPricing.RState newRState; (receiveQuoteAmount, mtFee, newRState, newBaseTarget) = querySellBase(tx.origin, baseInput); _transferQuoteOut(to, receiveQuoteAmount); _transferQuoteOut(_MAINTAINER_, mtFee); // update TARGET if (_RState_ != uint32(newRState)) { require(newBaseTarget <= uint112(-1), "OVERFLOW"); _BASE_TARGET_ = uint112(newBaseTarget); _RState_ = uint32(newRState); emit RChange(newRState); } _setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this))); emit DODOSwap( address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, to ); } function sellQuote(address to) external preventReentrant returns (uint256 receiveBaseAmount) { uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_)); uint256 mtFee; uint256 newQuoteTarget; PMMPricing.RState newRState; (receiveBaseAmount, mtFee, newRState, newQuoteTarget) = querySellQuote( tx.origin, quoteInput ); _transferBaseOut(to, receiveBaseAmount); _transferBaseOut(_MAINTAINER_, mtFee); // update TARGET if (_RState_ != uint32(newRState)) { require(newQuoteTarget <= uint112(-1), "OVERFLOW"); _QUOTE_TARGET_ = uint112(newQuoteTarget); _RState_ = uint32(newRState); emit RChange(newRState); } _setReserve(_BASE_TOKEN_.balanceOf(address(this)), quoteBalance); emit DODOSwap( address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, to ); } function flashLoan( uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data ) external preventReentrant { _transferBaseOut(assetTo, baseAmount); _transferQuoteOut(assetTo, quoteAmount); if (data.length > 0) IDODOCallee(assetTo).DSPFlashLoanCall(msg.sender, baseAmount, quoteAmount, data); uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)); uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); // no input -> pure loss require( baseBalance >= _BASE_RESERVE_ || quoteBalance >= _QUOTE_RESERVE_, "FLASH_LOAN_FAILED" ); // sell quote case // quote input + base output if (baseBalance < _BASE_RESERVE_) { uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_)); ( uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget ) = querySellQuote(tx.origin, quoteInput); // revert if quoteBalance<quoteReserve require( uint256(_BASE_RESERVE_).sub(baseBalance) <= receiveBaseAmount, "FLASH_LOAN_FAILED" ); _transferBaseOut(_MAINTAINER_, mtFee); if (_RState_ != uint32(newRState)) { require(newQuoteTarget <= uint112(-1), "OVERFLOW"); _QUOTE_TARGET_ = uint112(newQuoteTarget); _RState_ = uint32(newRState); emit RChange(newRState); } emit DODOSwap( address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo ); } // sell base case // base input + quote output if (quoteBalance < _QUOTE_RESERVE_) { uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_)); ( uint256 receiveQuoteAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newBaseTarget ) = querySellBase(tx.origin, baseInput); // revert if baseBalance<baseReserve require( uint256(_QUOTE_RESERVE_).sub(quoteBalance) <= receiveQuoteAmount, "FLASH_LOAN_FAILED" ); _transferQuoteOut(_MAINTAINER_, mtFee); if (_RState_ != uint32(newRState)) { require(newBaseTarget <= uint112(-1), "OVERFLOW"); _BASE_TARGET_ = uint112(newBaseTarget); _RState_ = uint32(newRState); emit RChange(newRState); } emit DODOSwap( address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, assetTo ); } _sync(); emit DODOFlashLoan(msg.sender, assetTo, baseAmount, quoteAmount); } // ============ Query Functions ============ function querySellBase(address trader, uint256 payBaseAmount) public view returns ( uint256 receiveQuoteAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newBaseTarget ) { PMMPricing.PMMState memory state = getPMMState(); (receiveQuoteAmount, newRState) = PMMPricing.sellBaseToken(state, payBaseAmount); uint256 lpFeeRate = _LP_FEE_RATE_; uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader); mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate); receiveQuoteAmount = receiveQuoteAmount .sub(DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate)) .sub(mtFee); newBaseTarget = state.B0; } function querySellQuote(address trader, uint256 payQuoteAmount) public view returns ( uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget ) { PMMPricing.PMMState memory state = getPMMState(); (receiveBaseAmount, newRState) = PMMPricing.sellQuoteToken(state, payQuoteAmount); uint256 lpFeeRate = _LP_FEE_RATE_; uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader); mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate); receiveBaseAmount = receiveBaseAmount .sub(DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate)) .sub(mtFee); newQuoteTarget = state.Q0; } } // File: contracts/DODOStablePool/impl/DSPFunding.sol contract DSPFunding is DSPVault { // ============ Events ============ event BuyShares(address to, uint256 increaseShares, uint256 totalShares); event SellShares(address payer, address to, uint256 decreaseShares, uint256 totalShares); // ============ Buy & Sell Shares ============ // buy shares [round down] function buyShares(address to) external preventReentrant returns ( uint256 shares, uint256 baseInput, uint256 quoteInput ) { uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)); uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); uint256 baseReserve = _BASE_RESERVE_; uint256 quoteReserve = _QUOTE_RESERVE_; baseInput = baseBalance.sub(baseReserve); quoteInput = quoteBalance.sub(quoteReserve); require(baseInput > 0, "NO_BASE_INPUT"); // Round down when withdrawing. Therefore, never be a situation occuring balance is 0 but totalsupply is not 0 // But May Happen,reserve >0 But totalSupply = 0 if (totalSupply == 0) { // case 1. initial supply shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_) ? DecimalMath.divFloor(quoteBalance, _I_) : baseBalance; _BASE_TARGET_ = uint112(shares); _QUOTE_TARGET_ = uint112(DecimalMath.mulFloor(shares, _I_)); } else if (baseReserve > 0 && quoteReserve > 0) { // case 2. normal case uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve); uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve); uint256 mintRatio = quoteInputRatio < baseInputRatio ? quoteInputRatio : baseInputRatio; shares = DecimalMath.mulFloor(totalSupply, mintRatio); _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_).add(DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio))); _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_).add(DecimalMath.mulFloor(uint256(_QUOTE_TARGET_), mintRatio))); } _mint(to, shares); _setReserve(baseBalance, quoteBalance); emit BuyShares(to, shares, _SHARES_[to]); } // sell shares [round down] function sellShares( uint256 shareAmount, address to, uint256 baseMinAmount, uint256 quoteMinAmount, bytes calldata data, uint256 deadline ) external preventReentrant returns (uint256 baseAmount, uint256 quoteAmount) { require(deadline >= block.timestamp, "TIME_EXPIRED"); require(shareAmount <= _SHARES_[msg.sender], "DLP_NOT_ENOUGH"); uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)); uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); uint256 totalShares = totalSupply; baseAmount = baseBalance.mul(shareAmount).div(totalShares); quoteAmount = quoteBalance.mul(shareAmount).div(totalShares); _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_).sub(uint256(_BASE_TARGET_).mul(shareAmount).divCeil(totalShares))); _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_).sub(uint256(_QUOTE_TARGET_).mul(shareAmount).divCeil(totalShares))); require( baseAmount >= baseMinAmount && quoteAmount >= quoteMinAmount, "WITHDRAW_NOT_ENOUGH" ); _burn(msg.sender, shareAmount); _transferBaseOut(to, baseAmount); _transferQuoteOut(to, quoteAmount); _sync(); if (data.length > 0) { //Same as DVM IDODOCallee(to).DVMSellShareCall( msg.sender, shareAmount, baseAmount, quoteAmount, data ); } emit SellShares(msg.sender, to, shareAmount, _SHARES_[msg.sender]); } } // File: contracts/DODOStablePool/impl/DSP.sol /** * @title DODO StablePool * @author DODO Breeder * * @notice DODOStablePool initialization */ contract DSP is DSPTrader, DSPFunding { function init( address maintainer, address baseTokenAddress, address quoteTokenAddress, uint256 lpFeeRate, address mtFeeRateModel, uint256 i, uint256 k, bool isOpenTWAP ) external { require(!_DSP_INITIALIZED_, "DSP_INITIALIZED"); _DSP_INITIALIZED_ = true; require(baseTokenAddress != quoteTokenAddress, "BASE_QUOTE_CAN_NOT_BE_SAME"); _BASE_TOKEN_ = IERC20(baseTokenAddress); _QUOTE_TOKEN_ = IERC20(quoteTokenAddress); require(i > 0 && i <= 10**36); _I_ = i; require(k <= 10**18); _K_ = k; _LP_FEE_RATE_ = lpFeeRate; _MT_FEE_RATE_MODEL_ = IFeeRateModel(mtFeeRateModel); _MAINTAINER_ = maintainer; _IS_OPEN_TWAP_ = isOpenTWAP; if (isOpenTWAP) _BLOCK_TIMESTAMP_LAST_ = uint32(block.timestamp % 2**32); string memory connect = "_"; string memory suffix = "DLP"; name = string(abi.encodePacked(suffix, connect, addressToShortString(address(this)))); symbol = "DLP"; decimals = _BASE_TOKEN_.decimals(); // ============================== Permit ==================================== uint256 chainId; assembly { chainId := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, keccak256(bytes(name)), keccak256(bytes("1")), chainId, address(this) ) ); // ========================================================================== } function addressToShortString(address _addr) public pure returns (string memory) { bytes32 value = bytes32(uint256(_addr)); bytes memory alphabet = "0123456789abcdef"; bytes memory str = new bytes(8); for (uint256 i = 0; i < 4; i++) { str[i * 2] = alphabet[uint8(value[i + 12] >> 4)]; str[1 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)]; } return string(str); } // ============ Version Control ============ function version() external pure returns (string memory) { return "DSP 1.0.1"; } }
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"increaseShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"BuyShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"address","name":"assetTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"name":"DODOFlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"fromToken","type":"address"},{"indexed":false,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"DODOSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"}],"name":"RChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"decreaseShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"SellShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_PRICE_CUMULATIVE_LAST_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_RESERVE_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_TARGET_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_TOKEN_","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BLOCK_TIMESTAMP_LAST_","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_IS_OPEN_TWAP_","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_I_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_K_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_LP_FEE_RATE_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_MAINTAINER_","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_MT_FEE_RATE_MODEL_","outputs":[{"internalType":"contract IFeeRateModel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_RESERVE_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_TARGET_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_TOKEN_","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_RState_","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"addressToShortString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"buyShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"baseInput","type":"uint256"},{"internalType":"uint256","name":"quoteInput","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"correctRState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"},{"internalType":"address","name":"assetTo","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBaseInput","outputs":[{"internalType":"uint256","name":"input","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMidPrice","outputs":[{"internalType":"uint256","name":"midPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPMMState","outputs":[{"components":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"B","type":"uint256"},{"internalType":"uint256","name":"Q","type":"uint256"},{"internalType":"uint256","name":"B0","type":"uint256"},{"internalType":"uint256","name":"Q0","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"R","type":"uint8"}],"internalType":"struct PMMPricing.PMMState","name":"state","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPMMStateForCall","outputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"B","type":"uint256"},{"internalType":"uint256","name":"Q","type":"uint256"},{"internalType":"uint256","name":"B0","type":"uint256"},{"internalType":"uint256","name":"Q0","type":"uint256"},{"internalType":"uint256","name":"R","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getQuoteInput","outputs":[{"internalType":"uint256","name":"input","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserFeeRate","outputs":[{"internalType":"uint256","name":"lpFeeRate","type":"uint256"},{"internalType":"uint256","name":"mtFeeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultReserve","outputs":[{"internalType":"uint256","name":"baseReserve","type":"uint256"},{"internalType":"uint256","name":"quoteReserve","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"maintainer","type":"address"},{"internalType":"address","name":"baseTokenAddress","type":"address"},{"internalType":"address","name":"quoteTokenAddress","type":"address"},{"internalType":"uint256","name":"lpFeeRate","type":"uint256"},{"internalType":"address","name":"mtFeeRateModel","type":"address"},{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"k","type":"uint256"},{"internalType":"bool","name":"isOpenTWAP","type":"bool"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"payBaseAmount","type":"uint256"}],"name":"querySellBase","outputs":[{"internalType":"uint256","name":"receiveQuoteAmount","type":"uint256"},{"internalType":"uint256","name":"mtFee","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"},{"internalType":"uint256","name":"newBaseTarget","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"payQuoteAmount","type":"uint256"}],"name":"querySellQuote","outputs":[{"internalType":"uint256","name":"receiveBaseAmount","type":"uint256"},{"internalType":"uint256","name":"mtFee","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"},{"internalType":"uint256","name":"newQuoteTarget","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sellBase","outputs":[{"internalType":"uint256","name":"receiveQuoteAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sellQuote","outputs":[{"internalType":"uint256","name":"receiveBaseAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shareAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"baseMinAmount","type":"uint256"},{"internalType":"uint256","name":"quoteMinAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"sellShares","outputs":[{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $2,512.43 | 7.8039 | $19,606.76 |
Loading...
Loading
[ Download: CSV Export ]
[ 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.