Compare commits
12 Commits
e92394edf3
...
d946606870
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d946606870 | ||
|
|
2f1bcdac7e | ||
|
|
82733a61f3 | ||
|
|
c58c067c40 | ||
|
|
d055deeace | ||
|
|
4e487c869b | ||
|
|
90a6c338c8 | ||
|
|
18ff2242be | ||
|
|
f701133a50 | ||
|
|
ef585cd4b8 | ||
|
|
3adad6b603 | ||
|
|
2aa9336584 |
61
config/dodo-testnet-config.js
Normal file
61
config/dodo-testnet-config.js
Normal file
@@ -0,0 +1,61 @@
|
||||
module.exports = {
|
||||
DODO_TESTNET_CONFIG: {
|
||||
//TOKEN
|
||||
WETH: "0xDA89314035264Ade23313f971AaE5393068Ea6F7",
|
||||
|
||||
//Helper
|
||||
DODOSellHelper: "0x3A7Bc5F9E41356728f037f17D88c642EE46d1Aaa",
|
||||
DODOCalleeHelper: "0x2Ba2696c63fA8b67b395de6D12F849b17C3515F9",
|
||||
DODOV1PmmHelper: "0x2e50e3e18c19C7d80B81888a961A13aEE49b962E",
|
||||
DODOV2RouteHelper: "0xA3148a1765897EC0A9bCA57f855C0B4718060b78",
|
||||
ERC20Helper: "0xE6cecb7460c9E52aA483cb1f0E87d78D7085686F",
|
||||
DODOSwapCalcHelper: "0x8b87D4B384517d2E1AFC61b3292d6e6C411ea303",
|
||||
MultiCall: "0xD0CF7dfbF09CAfaB8AEf00e0Ce19a4638004a364",
|
||||
MultiCallWithValid: "0x2Da0855f04919D402Af88D0D04AbB38177FE47fa",
|
||||
|
||||
//Template
|
||||
CloneFactory: "0x5eC9BEaCe4a0f46F77945D54511e2b454cb8F38E",
|
||||
FeeRateModel: "0xa97c5a70Be5B81f573a688F656E7bE569B492A56",
|
||||
FeeRateDIP3Impl: "0x710409D2121B7C8EA4aCAdd6803FDE2D85DF6473",
|
||||
PermissionManager: "0x5245f5E66792cfbaCB64eFAef3121e7c5ccD879b",
|
||||
DVM: "0xc88492049C990c0eF2eB0F77D1Aef8D66Bf16ba8",
|
||||
DSP: "0x46AFE01D758a46d64c7d8E0791314D5db3E2e683",
|
||||
DPPAdvanced: "0x69716E51E3F8Bec9c3D4E1bB46396384AE11C594",
|
||||
DPPAdvancedAdmin: "0xFe837A3530dD566401d35beFCd55582AF7c4dfFC",
|
||||
CP: "0x056FcE6B76AF3050F54B71Fc9B5fcb7C387BfC1A",
|
||||
ERC20MineV2: "0x701Ac6fAD7850956f966a85655348ac1B7c93368",
|
||||
ERC20MineV3: "0xB88f7eb2dA59E1E8E00E0158b195D5ca48403921",
|
||||
ERC20: "0x5977F12664b4E634dFbAAD0ad4a6a81057254dA8",
|
||||
CustomERC20: "0x0343C5757Fb98aD9eF39824e08B852aF61C71c64",
|
||||
CustomMintableERC20: "0x306ae919b99c187Fe5eCBdE980E24228ae888182",
|
||||
|
||||
//Factory
|
||||
DVMFactory: "0x7D381e6a9c23A0E6969658f6B8Eba57A4Dbf93a0",
|
||||
DPPFactory: "0x72A2c79E6D882AdabE02cEbC606b834b06831b3A",
|
||||
DSPFactory: "0x22AdDF954Ea92a3A80403Fe353093f7bF58665EC",
|
||||
CrowdPoolingFactory: "0xF9a6c9BE0a1149C69EF537359c78DeB5e6264ec6",
|
||||
ERC20V3Factory: "0xDC249Ea92D2e532ed63B45dc7C05B21926B97c6F",
|
||||
DODOMineV2Factory: "0xDA89314035264Ade23313f971AaE5393068Ea6F7",
|
||||
DODOMineV3Registry: "0x85C23863977dA7c3CEaCe129e292ba0F4d8aAA00",
|
||||
|
||||
//Approve
|
||||
DODOApprove: "0x82B26eb18382f7532015248078AB1f6030413396",
|
||||
DODOApproveProxy: "0x2E87af020F91b10C99939AF66333366D0cb8C098",
|
||||
|
||||
//Adpater
|
||||
DODOV2Adapter: "0xec1Ac56f266d1937ab69f7EB08D8889A41E39278",
|
||||
|
||||
//Proxy
|
||||
DODOV2Proxy: "0x464518e33F7Bd5E61a92b98b4332782915442580",
|
||||
DSPProxy: "0x886F677605512351e554AA53BA77eA493058b460",
|
||||
CpProxy: "0x63eEc8527884582358Ce6e93d530Df725D5Cf7d1",
|
||||
DPPProxy: "0xB4f2E70DE64F94116A6e93ff4dF2D020E91e7a63",
|
||||
FeeRouteProxy1: "0x669c8c9eee43A7e782A2a7D5497eBe6a28f19AcE", // for front-end
|
||||
FeeRouteProxy2: "0x470525b96b84dE0A5D5fBA2053ba903FD031d3fF", // for widget
|
||||
DODOMineV3Proxy: "0x391f8D754E4BaC453d059BA7D2137C4905bF48d4",
|
||||
|
||||
//Account
|
||||
multiSigAddress: "0xfa0d8ebca31a1501144a785a2929e9f91b0571d0",
|
||||
defaultMaintainer: "0xfa0d8ebca31a1501144a785a2929e9f91b0571d0",
|
||||
}
|
||||
}
|
||||
61
config/sepolia-config.js
Normal file
61
config/sepolia-config.js
Normal file
@@ -0,0 +1,61 @@
|
||||
module.exports = {
|
||||
SEPOLIA_CONFIG: {
|
||||
//TOKEN
|
||||
WETH: "0x7B07164ecFaF0F0D85DFC062Bc205a4674c75Aa0",
|
||||
|
||||
//Helper
|
||||
DODOSellHelper: "0xa1609A1fa7DC16c025feA194c02b2822441b8c10",
|
||||
DODOCalleeHelper: "0xCD536b4DECFD2fa0443666B6becD145F8aDe2E48",
|
||||
DODOV1PmmHelper: "0x3dD629473A2eD7f3C6299FFD9F3e0C283d073f11",
|
||||
DODOV2RouteHelper: "0x03e89fC55A5ad0531576E5a502c4CA52c8bf391B",
|
||||
ERC20Helper: "0x297da061D1dE0132D241Fafed224288B34d81005",
|
||||
DODOSwapCalcHelper: "0x6a9De0C6235bDD14B52eeA53F5a08Ff7D4183b3e",
|
||||
MultiCall: "0x0fcB5237A1997C4700Ffa2BB4522EA38d4F851Fc",
|
||||
MultiCallWithValid: "0x1fC8EC204549C865a17b4059A57decA66A4Bd4cC",
|
||||
|
||||
//Template
|
||||
CloneFactory: "0x8414560d69650bC0c915d5d4385e1714a23cbe81",
|
||||
FeeRateModel: "0xEAC4BFef7D1c872Ed705B01856af7f9802adC596",
|
||||
FeeRateDIP3Impl: "0x89872650fA1A391f58B4E144222bB02e44db7e3B",
|
||||
PermissionManager: "0xCb3dC90E800C961d4a206BeAAFd92A6d2E06495e",
|
||||
DVM: "0xa23137871C4A4ce8514f581EE82262CD1A49EEAE",
|
||||
DSP: "0xF3e3c6065C83b1E2F8B6701f07dF3a55aAA249BB",
|
||||
DPPAdvanced: "0x14DD1cb49e08b53DE3bD67Ee2815Ba5b2e9f269d",
|
||||
DPPAdvancedAdmin: "0xFaFC924BF0F70BdF4a722231622f559b94e53902",
|
||||
CP: "0xa71415675F68f29259ddD63215E5518d2735bf0a",
|
||||
ERC20MineV2: "0x1506b54A1c0eA1B2F4a84866Ec5776F7F6e7f0B1",
|
||||
ERC20MineV3: "0x46AF6b152F2cb02a3cFcc74014C2617BC4F6cD5C",
|
||||
ERC20: "0xFD2b7994f91c08aAa5e013E899334A2DBb500DF1",
|
||||
CustomERC20: "0x8dD0Fea5FA2f7df535F87f312641Cc15d8B151BA",
|
||||
CustomMintableERC20: "0x29C7718e8B606cEF1c44Fe6e43e07aF9D0875DE1",
|
||||
|
||||
//Factory
|
||||
DVMFactory: "0x2F86652dAEF5f1728c54191C955F065Ec3C188c7",
|
||||
DPPFactory: "0x0B1467f71c082D8d410aF4376C685D9A6893cF36",
|
||||
DSPFactory: "0xe7979E2F3e77196Bb2AB206eaa67Ea278A3E33A2",
|
||||
CrowdPoolingFactory: "0xCDA4a6cc5997002B87f28D46852F9F0aA0f3c897",
|
||||
ERC20V3Factory: "0x4CAD0052524648A7Fa2cfE279997b00239295F33",
|
||||
DODOMineV2Factory: "0x49186E32fEd50fd6B5604A2618c7B0b03Cd41414",
|
||||
DODOMineV3Registry: "0xa5fc92Ca57a21C87AA0477b1c8fE8B9Bbf69d6C2",
|
||||
|
||||
//Approve
|
||||
DODOApprove: "0x66c45FF040e86DC613F239123A5E21FFdC3A3fEC",
|
||||
DODOApproveProxy: "0xE2004eE21f88a7D8e1A5EDc3c9617a0460CC7b99",
|
||||
|
||||
//Adpater
|
||||
DODOV2Adapter: "0x70B9C57E1fF24761C1C3ced57Ddae9A3F3570698",
|
||||
|
||||
//Proxy
|
||||
DODOV2Proxy: "0x6292e8f7647b3b9dDf5795b1Fb77D0187e30E0F9",
|
||||
DSPProxy: "0x987bFBE33c9cF18cAA665B792Db66339a9c16D32",
|
||||
CpProxy: "0xA376762070F7fCE8f3646AAe90e6e375e6daF128",
|
||||
DPPProxy: "0x5e1251f4873248a030e1c92FFEB133AF1513277f",
|
||||
FeeRouteProxy1: "0x5fa9e06111814840398ceF6E9563d400F6ed3a8d", // for front-end
|
||||
FeeRouteProxy2: "0xb38D394D52A15910b8acc173b816624dc90066cd", // for widget
|
||||
DODOMineV3Proxy: "0xBbD59b9316eE65526DbBdEc2A748Cc05A285d54C",
|
||||
|
||||
//Account
|
||||
multiSigAddress: "0xb37136B338C6cC0E459A35fe9Aa036f6b5A147c0",
|
||||
defaultMaintainer: "0xb37136B338C6cC0E459A35fe9Aa036f6b5A147c0",
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,17 @@ const { MOONRIVER_CONFIG } = require("./config/moonriver-config");
|
||||
const { BOBA_CONFIG } = require("./config/boba-config");
|
||||
const { AVAX_CONFIG } = require("./config/avax-config");
|
||||
const { DASHBOARD_CONFIG } = require("./config/dashboard-config");
|
||||
const {CFX_CONFIG} = require("./config/cfx-config.js");
|
||||
const { CFX_CONFIG } = require("./config/cfx-config.js");
|
||||
const { LINEA_CONFIG } = require("./config/linea-config");
|
||||
const { BASE_CONFIG } = require("./config/base-config");
|
||||
const { SCROLL_SEPOLIA_CONFIG } = require("./config/scroll-sepolia-config");
|
||||
const { SEPOLIA_CONFIG } = require("./config/sepolia-config");
|
||||
const { MANTA_TESTNET_CONFIG } = require("./config/manta-testnet-config");
|
||||
const { MANTA_CONFIG } = require("./config/manta-config");
|
||||
const { MANTLE_CONFIG } = require("./config/mantle-config");
|
||||
const { OPTIMISM_CONFIG } = require("./config/optimism-config");
|
||||
const { SCROLL_CONFIG } = require("./config/scroll-config");
|
||||
const { DODO_TESTNET_CONFIG } = require("./config/dodo-testnet-config");
|
||||
|
||||
exports.GetConfig = function (network, accounts) {
|
||||
var CONFIG = {}
|
||||
@@ -64,7 +66,7 @@ exports.GetConfig = function (network, accounts) {
|
||||
CONFIG = BASE_CONFIG
|
||||
break;
|
||||
case "dashboard":
|
||||
CONFIG = MANTLE_CONFIG
|
||||
CONFIG = SEPOLIA_CONFIG
|
||||
break;
|
||||
//testnet
|
||||
case "kovan":
|
||||
@@ -83,6 +85,8 @@ exports.GetConfig = function (network, accounts) {
|
||||
case "manta-testnet":
|
||||
CONFIG = MANTA_TESTNET_CONFIG
|
||||
break;
|
||||
case "dodotest":
|
||||
CONFIG = DODO_TESTNET_CONFIG
|
||||
}
|
||||
return CONFIG
|
||||
}
|
||||
|
||||
139
contracts/DODOGasSavingPool/GasSavingPool/impl/GSP.sol
Normal file
139
contracts/DODOGasSavingPool/GasSavingPool/impl/GSP.sol
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
||||
import {GSPTrader} from "./GSPTrader.sol";
|
||||
import {GSPFunding} from "./GSPFunding.sol";
|
||||
import {GSPVault} from "./GSPVault.sol";
|
||||
|
||||
/**
|
||||
* @title DODO GasSavingPool
|
||||
* @author DODO Breeder
|
||||
*
|
||||
* @notice DODO GasSavingPool initialization
|
||||
*/
|
||||
contract GSP is GSPTrader, GSPFunding {
|
||||
/**
|
||||
* @notice Function will be called in factory, init risk should not be included.
|
||||
* @param maintainer The dodo's address, who can claim mtFee and own this pool
|
||||
* @param admin oracle owner address, who can set price.
|
||||
* @param baseTokenAddress The base token address
|
||||
* @param quoteTokenAddress The quote token address
|
||||
* @param lpFeeRate The rate of lp fee, with 18 decimal
|
||||
* @param mtFeeRate The rate of mt fee, with 18 decimal
|
||||
* @param i The oracle price, possible to be changed only by maintainer
|
||||
* @param k The swap curve parameter
|
||||
* @param priceLimit The limit of the setting range of the I
|
||||
* @param isOpenTWAP Useless, always false, just for compatible with old version pool
|
||||
*/
|
||||
function init(
|
||||
address maintainer,
|
||||
address admin,
|
||||
address baseTokenAddress,
|
||||
address quoteTokenAddress,
|
||||
uint256 lpFeeRate,
|
||||
uint256 mtFeeRate,
|
||||
uint256 i,
|
||||
uint256 k,
|
||||
uint256 priceLimit,
|
||||
bool isOpenTWAP
|
||||
) external {
|
||||
// GSP can only be initialized once
|
||||
require(!_GSP_INITIALIZED_, "GSP_INITIALIZED");
|
||||
// _GSP_INITIALIZED_ is set to true after initialization
|
||||
_GSP_INITIALIZED_ = true;
|
||||
// baseTokenAddress and quoteTokenAddress should not be the same
|
||||
require(baseTokenAddress != quoteTokenAddress, "BASE_QUOTE_CAN_NOT_BE_SAME");
|
||||
// _BASE_TOKEN_ and _QUOTE_TOKEN_ should be valid ERC20 tokens
|
||||
_BASE_TOKEN_ = IERC20(baseTokenAddress);
|
||||
_QUOTE_TOKEN_ = IERC20(quoteTokenAddress);
|
||||
|
||||
// i should be greater than 0 and less than 10**36
|
||||
require(i > 0 && i <= 10**36);
|
||||
_I_ = i;
|
||||
// k should be greater than 0 and less than 10**18
|
||||
require(k <= 10**18);
|
||||
_K_ = k;
|
||||
|
||||
// _LP_FEE_RATE_ is set when initialization
|
||||
_LP_FEE_RATE_ = lpFeeRate;
|
||||
// _MT_FEE_RATE_ is set when initialization
|
||||
_MT_FEE_RATE_ = mtFeeRate;
|
||||
// _MAINTAINER_ is set when initialization, the address receives the fee
|
||||
_MAINTAINER_ = maintainer;
|
||||
_ADMIN_ = admin;
|
||||
|
||||
_PRICE_LIMIT_ = priceLimit;
|
||||
// _IS_OPEN_TWAP_ is always false
|
||||
_IS_OPEN_TWAP_ = false;
|
||||
|
||||
|
||||
string memory connect = "_";
|
||||
string memory suffix = "GSP";
|
||||
// name of the shares is the combination of suffix, connect and string of the GSP
|
||||
name = string(abi.encodePacked(suffix, connect, addressToShortString(address(this))));
|
||||
// symbol of the shares is GLP
|
||||
symbol = "GLP";
|
||||
// decimals of the shares is the same as the base token decimals
|
||||
decimals = IERC20Metadata(baseTokenAddress).decimals();
|
||||
// initialize DOMAIN_SEPARATOR
|
||||
buildDomainSeparator();
|
||||
// ==========================================================================
|
||||
}
|
||||
|
||||
// ============================== Permit ====================================
|
||||
/**
|
||||
* @notice DOMAIN_SEPARATOR is used for approve by signature
|
||||
*/
|
||||
function buildDomainSeparator() public returns (bytes32){
|
||||
string memory connect = "_";
|
||||
string memory suffix = "GSP";
|
||||
// name of the shares is the combination of suffix, connect and string of the GSP
|
||||
string memory name = string(abi.encodePacked(suffix, connect, addressToShortString(address(this))));
|
||||
|
||||
DOMAIN_SEPARATOR = keccak256(
|
||||
abi.encode(
|
||||
// keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
|
||||
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
|
||||
keccak256(bytes(name)),
|
||||
keccak256(bytes("1")),
|
||||
block.chainid,
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
return DOMAIN_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Convert the address to a shorter string
|
||||
* @param _addr The address to convert
|
||||
* @return A string representation of _addr in hexadecimal
|
||||
*/
|
||||
function addressToShortString(address _addr) public pure returns (string memory) {
|
||||
bytes32 value = bytes32(uint256(uint160(_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 ============
|
||||
/**
|
||||
* @notice Return the version of DODOGasSavingPool
|
||||
* @return The current version is 1.0.1
|
||||
*/
|
||||
function version() external pure returns (string memory) {
|
||||
return "GSP 1.0.1";
|
||||
}
|
||||
}
|
||||
153
contracts/DODOGasSavingPool/GasSavingPool/impl/GSPFunding.sol
Normal file
153
contracts/DODOGasSavingPool/GasSavingPool/impl/GSPFunding.sol
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
|
||||
import {GSPVault} from "./GSPVault.sol";
|
||||
import {DecimalMath} from "../../lib/DecimalMath.sol";
|
||||
import {IDODOCallee} from "../../intf/IDODOCallee.sol";
|
||||
|
||||
/// @notice this part focus on Lp tokens, mint and burn
|
||||
contract GSPFunding is GSPVault {
|
||||
// ============ Events ============
|
||||
|
||||
event BuyShares(address to, uint256 increaseShares, uint256 totalShares);
|
||||
|
||||
event SellShares(address payer, address to, uint256 decreaseShares, uint256 totalShares);
|
||||
|
||||
// ============ Buy & Sell Shares ============
|
||||
|
||||
/// @notice User mint Lp token and deposit tokens, the result is rounded down
|
||||
/// @dev User first transfer baseToken and quoteToken to GSP, then call buyShares
|
||||
/// @param to The address will receive shares
|
||||
/// @return shares The amount of shares user will receive
|
||||
/// @return baseInput The amount of baseToken user transfer to GSP
|
||||
/// @return quoteInput The amount of quoteToken user transfer to GSP
|
||||
function buyShares(address to)
|
||||
external
|
||||
nonReentrant
|
||||
returns (
|
||||
uint256 shares,
|
||||
uint256 baseInput,
|
||||
uint256 quoteInput
|
||||
)
|
||||
{
|
||||
// The balance of baseToken and quoteToken should be the balance minus the fee
|
||||
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_;
|
||||
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_;
|
||||
// The reserve of baseToken and quoteToken
|
||||
uint256 baseReserve = _BASE_RESERVE_;
|
||||
uint256 quoteReserve = _QUOTE_RESERVE_;
|
||||
|
||||
// The amount of baseToken and quoteToken user transfer to GSP
|
||||
baseInput = baseBalance - baseReserve;
|
||||
quoteInput = quoteBalance - quoteReserve;
|
||||
|
||||
// BaseToken should be transferred to GSP before calling buyShares
|
||||
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
|
||||
require(quoteBalance > 0, "ZERO_QUOTE_AMOUNT");
|
||||
// The shares will be minted to user
|
||||
shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_)
|
||||
? DecimalMath.divFloor(quoteBalance, _I_)
|
||||
: baseBalance;
|
||||
// The target will be updated
|
||||
_BASE_TARGET_ = uint112(shares);
|
||||
_QUOTE_TARGET_ = uint112(DecimalMath.mulFloor(shares, _I_));
|
||||
require(_QUOTE_TARGET_ > 0, "QUOTE_TARGET_IS_ZERO");
|
||||
// Lock 1001 shares permanently in first deposit
|
||||
require(shares > 2001, "MINT_AMOUNT_NOT_ENOUGH");
|
||||
_mint(address(0), 1001);
|
||||
shares -= 1001;
|
||||
} 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;
|
||||
// The shares will be minted to user
|
||||
shares = DecimalMath.mulFloor(totalSupply, mintRatio);
|
||||
|
||||
// The target will be updated
|
||||
_BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) + (DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio)));
|
||||
_QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) + (DecimalMath.mulFloor(uint256(_QUOTE_TARGET_), mintRatio)));
|
||||
}
|
||||
// The shares will be minted to user
|
||||
// The reserve will be updated
|
||||
_mint(to, shares);
|
||||
_setReserve(baseBalance, quoteBalance);
|
||||
emit BuyShares(to, shares, _SHARES_[to]);
|
||||
}
|
||||
|
||||
/// @notice User burn their lp and withdraw their tokens, the result is rounded down
|
||||
/// @dev User call sellShares, the calculated baseToken and quoteToken amount should geater than minBaseToken and minQuoteToken
|
||||
/// @param shareAmount The amount of shares user want to sell
|
||||
/// @param to The address will receive baseToken and quoteToken
|
||||
/// @param baseMinAmount The minimum amount of baseToken user want to receive
|
||||
/// @param quoteMinAmount The minimum amount of quoteToken user want to receive
|
||||
/// @param data The data will be passed to callee contract
|
||||
/// @param deadline The deadline of this transaction
|
||||
function sellShares(
|
||||
uint256 shareAmount,
|
||||
address to,
|
||||
uint256 baseMinAmount,
|
||||
uint256 quoteMinAmount,
|
||||
bytes calldata data,
|
||||
uint256 deadline
|
||||
) external nonReentrant returns (uint256 baseAmount, uint256 quoteAmount) {
|
||||
// The deadline should be greater than current timestamp
|
||||
require(deadline >= block.timestamp, "TIME_EXPIRED");
|
||||
// The amount of shares user want to sell should be less than user's balance
|
||||
require(shareAmount <= _SHARES_[msg.sender], "GLP_NOT_ENOUGH");
|
||||
|
||||
// The balance of baseToken and quoteToken should be the balance minus the fee
|
||||
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_;
|
||||
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_;
|
||||
// The total shares of GSP
|
||||
uint256 totalShares = totalSupply;
|
||||
|
||||
// The amount of baseToken and quoteToken user will receive is calculated by the ratio of user's shares to total shares
|
||||
baseAmount = baseBalance * shareAmount / totalShares;
|
||||
quoteAmount = quoteBalance * shareAmount / totalShares;
|
||||
|
||||
// The target will be updated
|
||||
_BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) - DecimalMath._divCeil((uint256(_BASE_TARGET_) * (shareAmount)), totalShares));
|
||||
_QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) - DecimalMath._divCeil((uint256(_QUOTE_TARGET_) * (shareAmount)), totalShares));
|
||||
|
||||
// The calculated baseToken and quoteToken amount should geater than minBaseToken and minQuoteToken
|
||||
require(
|
||||
baseAmount >= baseMinAmount && quoteAmount >= quoteMinAmount,
|
||||
"WITHDRAW_NOT_ENOUGH"
|
||||
);
|
||||
|
||||
// The shares will be burned from user
|
||||
// The baseToken and quoteToken will be transferred to user
|
||||
// The reserve will be synced
|
||||
_burn(msg.sender, shareAmount);
|
||||
_transferBaseOut(to, baseAmount);
|
||||
_transferQuoteOut(to, quoteAmount);
|
||||
_sync();
|
||||
|
||||
// If the data is not empty, the callee contract will be called
|
||||
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]);
|
||||
}
|
||||
}
|
||||
148
contracts/DODOGasSavingPool/GasSavingPool/impl/GSPStorage.sol
Normal file
148
contracts/DODOGasSavingPool/GasSavingPool/impl/GSPStorage.sol
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
|
||||
import {DODOMath} from "../../lib/DODOMath.sol";
|
||||
import {DecimalMath} from "../../lib/DecimalMath.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
||||
import {PMMPricing} from "../../lib/PMMPricing.sol";
|
||||
|
||||
/// @notice this contract is used for store state and read state
|
||||
contract GSPStorage is ReentrancyGuard {
|
||||
|
||||
// ============ Storage for Setup ============
|
||||
// _GSP_INITIALIZED_ will be set to true when the init function is called
|
||||
bool internal _GSP_INITIALIZED_;
|
||||
// GSP does not open TWAP by default
|
||||
// _IS_OPEN_TWAP_ can be set to true when the init function is called
|
||||
bool public _IS_OPEN_TWAP_ = false;
|
||||
|
||||
// ============ Core Address ============
|
||||
// _MAINTAINER_ is the maintainer of GSP
|
||||
address public _MAINTAINER_;
|
||||
// _ADMIN_ can set price
|
||||
address public _ADMIN_;
|
||||
// _BASE_TOKEN_ and _QUOTE_TOKEN_ should be ERC20 token
|
||||
IERC20 public _BASE_TOKEN_;
|
||||
IERC20 public _QUOTE_TOKEN_;
|
||||
// _BASE_RESERVE_ and _QUOTE_RESERVE_ are the current reserves of the GSP
|
||||
uint112 public _BASE_RESERVE_;
|
||||
uint112 public _QUOTE_RESERVE_;
|
||||
// _BLOCK_TIMESTAMP_LAST_ is used when calculating TWAP
|
||||
uint32 public _BLOCK_TIMESTAMP_LAST_;
|
||||
// _BASE_PRICE_CUMULATIVE_LAST_ is used when calculating TWAP
|
||||
uint256 public _BASE_PRICE_CUMULATIVE_LAST_;
|
||||
|
||||
// _BASE_TARGET_ and _QUOTE_TARGET_ are recalculated when the pool state changes
|
||||
uint112 public _BASE_TARGET_;
|
||||
uint112 public _QUOTE_TARGET_;
|
||||
// _RState_ is the current R state of the GSP
|
||||
uint32 public _RState_;
|
||||
|
||||
// ============ Shares (ERC20) ============
|
||||
// symbol is the symbol of the shares
|
||||
string public symbol;
|
||||
// decimals is the decimals of the shares
|
||||
uint8 public decimals;
|
||||
// name is the name of the shares
|
||||
string public name;
|
||||
// totalSupply is the total supply of the shares
|
||||
uint256 public totalSupply;
|
||||
// _SHARES_ is the mapping from account to share balance, record the share balance of each account
|
||||
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 ============
|
||||
// _MT_FEE_RATE_ is the fee rate of mt fee
|
||||
uint256 public _MT_FEE_RATE_;
|
||||
// _LP_FEE_RATE_ is the fee rate of lp fee
|
||||
uint256 public _LP_FEE_RATE_;
|
||||
uint256 public _K_;
|
||||
uint256 public _I_;
|
||||
// _PRICE_LIMIT_ is used to limit the setting range of I
|
||||
uint256 public _PRICE_LIMIT_;
|
||||
|
||||
// ============ Mt Fee ============
|
||||
// _MT_FEE_BASE_ represents the mt fee in base token
|
||||
uint256 public _MT_FEE_BASE_;
|
||||
// _MT_FEE_QUOTE_ represents the mt fee in quote token
|
||||
uint256 public _MT_FEE_QUOTE_;
|
||||
// _MT_FEE_RATE_MODEL_ is useless, just for compatible with old version pool
|
||||
address public _MT_FEE_RATE_MODEL_ = address(0);
|
||||
|
||||
// ============ Helper Functions ============
|
||||
|
||||
/// @notice Return the PMM state of the pool from inner or outside
|
||||
/// @dev B0 and Q0 are calculated in adjustedTarget
|
||||
/// @return state The current PMM state
|
||||
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);
|
||||
}
|
||||
|
||||
/// @notice Return the PMM state variables used for routeHelpers
|
||||
/// @return i The price index
|
||||
/// @return K The K value
|
||||
/// @return B The base token reserve
|
||||
/// @return Q The quote token reserve
|
||||
/// @return B0 The base token target
|
||||
/// @return Q0 The quote token target
|
||||
/// @return R The R state of the pool
|
||||
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);
|
||||
}
|
||||
|
||||
/// @notice Return the adjusted mid price
|
||||
/// @return midPrice The current mid price
|
||||
function getMidPrice() public view returns (uint256 midPrice) {
|
||||
return PMMPricing.getMidPrice(getPMMState());
|
||||
}
|
||||
|
||||
/// @notice Return the total mt fee maintainer can claim
|
||||
/// @dev The total mt fee is represented in two types: in base token and in quote token
|
||||
/// @return mtFeeBase The total mt fee in base token
|
||||
/// @return mtFeeQuote The total mt fee in quote token
|
||||
function getMtFeeTotal() public view returns (uint256 mtFeeBase, uint256 mtFeeQuote) {
|
||||
mtFeeBase = _MT_FEE_BASE_;
|
||||
mtFeeQuote = _MT_FEE_QUOTE_;
|
||||
}
|
||||
}
|
||||
275
contracts/DODOGasSavingPool/GasSavingPool/impl/GSPTrader.sol
Normal file
275
contracts/DODOGasSavingPool/GasSavingPool/impl/GSPTrader.sol
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
|
||||
import {GSPVault} from "./GSPVault.sol";
|
||||
import {DecimalMath} from "../../lib/DecimalMath.sol";
|
||||
import {PMMPricing} from "../../lib/PMMPricing.sol";
|
||||
import {IDODOCallee} from "../../intf/IDODOCallee.sol";
|
||||
|
||||
/// @notice this contract deal with swap
|
||||
contract GSPTrader is GSPVault {
|
||||
|
||||
// ============ 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 ============
|
||||
/**
|
||||
* @notice User sell base tokens, user pay tokens first. Must be used with a router
|
||||
* @dev The base token balance is the actual balance minus the mt fee
|
||||
* @param to The recipient of the output
|
||||
* @return receiveQuoteAmount Amount of quote token received
|
||||
*/
|
||||
function sellBase(address to) external nonReentrant returns (uint256 receiveQuoteAmount) {
|
||||
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_;
|
||||
uint256 baseInput = baseBalance - uint256(_BASE_RESERVE_);
|
||||
uint256 mtFee;
|
||||
uint256 newBaseTarget;
|
||||
PMMPricing.RState newRState;
|
||||
// calculate the amount of quote token to receive and mt fee
|
||||
(receiveQuoteAmount, mtFee, newRState, newBaseTarget) = querySellBase(tx.origin, baseInput);
|
||||
// transfer quote token to recipient
|
||||
_transferQuoteOut(to, receiveQuoteAmount);
|
||||
// update mt fee in quote token
|
||||
_MT_FEE_QUOTE_ = _MT_FEE_QUOTE_ + mtFee;
|
||||
|
||||
|
||||
// update TARGET
|
||||
if (_RState_ != uint32(newRState)) {
|
||||
require(newBaseTarget <= type(uint112).max, "OVERFLOW");
|
||||
_BASE_TARGET_ = uint112(newBaseTarget);
|
||||
_RState_ = uint32(newRState);
|
||||
emit RChange(newRState);
|
||||
}
|
||||
// update reserve
|
||||
_setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_);
|
||||
|
||||
emit DODOSwap(
|
||||
address(_BASE_TOKEN_),
|
||||
address(_QUOTE_TOKEN_),
|
||||
baseInput,
|
||||
receiveQuoteAmount,
|
||||
msg.sender,
|
||||
to
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice User sell quote tokens, user pay tokens first. Must be used with a router
|
||||
* @param to The recipient of the output
|
||||
* @return receiveBaseAmount Amount of base token received
|
||||
*/
|
||||
function sellQuote(address to) external nonReentrant returns (uint256 receiveBaseAmount) {
|
||||
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_;
|
||||
uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);
|
||||
uint256 mtFee;
|
||||
uint256 newQuoteTarget;
|
||||
PMMPricing.RState newRState;
|
||||
// calculate the amount of base token to receive and mt fee
|
||||
(receiveBaseAmount, mtFee, newRState, newQuoteTarget) = querySellQuote(
|
||||
tx.origin,
|
||||
quoteInput
|
||||
);
|
||||
// transfer base token to recipient
|
||||
_transferBaseOut(to, receiveBaseAmount);
|
||||
// update mt fee in base token
|
||||
_MT_FEE_BASE_ = _MT_FEE_BASE_ + mtFee;
|
||||
|
||||
// update TARGET
|
||||
if (_RState_ != uint32(newRState)) {
|
||||
require(newQuoteTarget <= type(uint112).max, "OVERFLOW");
|
||||
_QUOTE_TARGET_ = uint112(newQuoteTarget);
|
||||
_RState_ = uint32(newRState);
|
||||
emit RChange(newRState);
|
||||
}
|
||||
// update reserve
|
||||
_setReserve((_BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_), quoteBalance);
|
||||
|
||||
emit DODOSwap(
|
||||
address(_QUOTE_TOKEN_),
|
||||
address(_BASE_TOKEN_),
|
||||
quoteInput,
|
||||
receiveBaseAmount,
|
||||
msg.sender,
|
||||
to
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice inner flashloan, pay tokens out first, call external contract and check tokens left
|
||||
* @param baseAmount The base token amount user require
|
||||
* @param quoteAmount The quote token amount user require
|
||||
* @param assetTo The address who uses above tokens
|
||||
* @param data The external contract's callData
|
||||
*/
|
||||
function flashLoan(
|
||||
uint256 baseAmount,
|
||||
uint256 quoteAmount,
|
||||
address assetTo,
|
||||
bytes calldata data
|
||||
) external nonReentrant {
|
||||
_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)) - _MT_FEE_BASE_;
|
||||
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_;
|
||||
|
||||
// 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 - uint256(_QUOTE_RESERVE_);
|
||||
(
|
||||
uint256 receiveBaseAmount,
|
||||
uint256 mtFee,
|
||||
PMMPricing.RState newRState,
|
||||
uint256 newQuoteTarget
|
||||
) = querySellQuote(tx.origin, quoteInput); // revert if quoteBalance<quoteReserve
|
||||
require(
|
||||
(uint256(_BASE_RESERVE_) - baseBalance) <= receiveBaseAmount,
|
||||
"FLASH_LOAN_FAILED"
|
||||
);
|
||||
|
||||
_MT_FEE_BASE_ = _MT_FEE_BASE_ + mtFee;
|
||||
|
||||
if (_RState_ != uint32(newRState)) {
|
||||
require(newQuoteTarget <= type(uint112).max, "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 - uint256(_BASE_RESERVE_);
|
||||
(
|
||||
uint256 receiveQuoteAmount,
|
||||
uint256 mtFee,
|
||||
PMMPricing.RState newRState,
|
||||
uint256 newBaseTarget
|
||||
) = querySellBase(tx.origin, baseInput); // revert if baseBalance<baseReserve
|
||||
require(
|
||||
(uint256(_QUOTE_RESERVE_) - quoteBalance) <= receiveQuoteAmount,
|
||||
"FLASH_LOAN_FAILED"
|
||||
);
|
||||
|
||||
_MT_FEE_QUOTE_ = _MT_FEE_QUOTE_ + mtFee;
|
||||
|
||||
if (_RState_ != uint32(newRState)) {
|
||||
require(newBaseTarget <= type(uint112).max, "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 ============
|
||||
/**
|
||||
* @notice Return swap result, for query, sellBase side.
|
||||
* @param trader Useless, just to keep the same interface with old version pool
|
||||
* @param payBaseAmount The amount of base token user want to sell
|
||||
* @return receiveQuoteAmount The amount of quote token user will receive
|
||||
* @return mtFee The amount of mt fee charged
|
||||
* @return newRState The new RState after swap
|
||||
* @return newBaseTarget The new base target after swap
|
||||
*/
|
||||
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_;
|
||||
mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate);
|
||||
receiveQuoteAmount = receiveQuoteAmount
|
||||
- DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate)
|
||||
- mtFee;
|
||||
newBaseTarget = state.B0;
|
||||
}
|
||||
/**
|
||||
* @notice Return swap result, for query, sellQuote side
|
||||
* @param trader Useless, just for keeping the same interface with old version pool
|
||||
* @param payQuoteAmount The amount of quote token user want to sell
|
||||
* @return receiveBaseAmount The amount of base token user will receive
|
||||
* @return mtFee The amount of mt fee charged
|
||||
* @return newRState The new RState after swap
|
||||
* @return newQuoteTarget The new quote target after swap
|
||||
*/
|
||||
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_;
|
||||
mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate);
|
||||
receiveBaseAmount = receiveBaseAmount
|
||||
- DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate)
|
||||
- mtFee;
|
||||
newQuoteTarget = state.Q0;
|
||||
}
|
||||
}
|
||||
369
contracts/DODOGasSavingPool/GasSavingPool/impl/GSPVault.sol
Normal file
369
contracts/DODOGasSavingPool/GasSavingPool/impl/GSPVault.sol
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
|
||||
import {DecimalMath} from "../../lib/DecimalMath.sol";
|
||||
import {PMMPricing} from "../../lib/PMMPricing.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {GSPStorage} from "./GSPStorage.sol";
|
||||
|
||||
contract GSPVault is GSPStorage {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
// ============ Modifiers ============
|
||||
/// @notice Check whether the caller is maintainer
|
||||
modifier onlyMaintainer() {
|
||||
require(msg.sender == _MAINTAINER_, "ACCESS_DENIED");
|
||||
_;
|
||||
}
|
||||
|
||||
/// @notice Check whether the caller is admin
|
||||
modifier onlyAdmin() {
|
||||
require(msg.sender == _ADMIN_, "ADMIN_ACCESS_DENIED");
|
||||
_;
|
||||
}
|
||||
|
||||
// ============ 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);
|
||||
|
||||
event MtFeeRateChange(uint256 newMtFee);
|
||||
|
||||
event LpFeeRateChange(uint256 newLpFee);
|
||||
|
||||
event IChange(uint256 newI);
|
||||
|
||||
event KChange(uint256 newK);
|
||||
|
||||
event WithdrawMtFee(address indexed token, uint256 amount);
|
||||
|
||||
// ============ View Functions ============
|
||||
/**
|
||||
* @notice Get the reserves of the pool
|
||||
* @return baseReserve The base token reserve
|
||||
* @return quoteReserve The quote token reserve
|
||||
*/
|
||||
function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve) {
|
||||
baseReserve = _BASE_RESERVE_;
|
||||
quoteReserve = _QUOTE_RESERVE_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get the fee rate of the pool
|
||||
* @param user Useless, just keep the same interface with old version pool
|
||||
* @return lpFeeRate The lp fee rate
|
||||
* @return mtFeeRate The mt fee rate
|
||||
*/
|
||||
function getUserFeeRate(address user)
|
||||
external
|
||||
view
|
||||
returns (uint256 lpFeeRate, uint256 mtFeeRate)
|
||||
{
|
||||
lpFeeRate = _LP_FEE_RATE_;
|
||||
mtFeeRate = _MT_FEE_RATE_;
|
||||
}
|
||||
|
||||
// ============ Asset In ============
|
||||
/**
|
||||
* @notice Get the amount of base token transferred in
|
||||
* @dev The amount of base token input should be the base token reserve minus the mt fee in base token
|
||||
* @return input The amount of base token transferred in
|
||||
*/
|
||||
function getBaseInput() public view returns (uint256 input) {
|
||||
return _BASE_TOKEN_.balanceOf(address(this)) - uint256(_BASE_RESERVE_) - uint256(_MT_FEE_BASE_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get the amount of quote token transferred in
|
||||
* @dev The amount of quote token input should be the quote token reserve minus the mt fee in quote token
|
||||
* @return input The amount of quote token transferred in
|
||||
*/
|
||||
function getQuoteInput() public view returns (uint256 input) {
|
||||
return _QUOTE_TOKEN_.balanceOf(address(this)) - uint256(_QUOTE_RESERVE_) - uint256(_MT_FEE_QUOTE_);
|
||||
}
|
||||
|
||||
// ============ Set States ============
|
||||
/**
|
||||
* @notice Set the reserves of the pool, internal use only
|
||||
* @param baseReserve The base token reserve
|
||||
* @param quoteReserve The quote token reserve
|
||||
*/
|
||||
function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal {
|
||||
// the reserves should be less than the max uint112
|
||||
require(baseReserve <= type(uint112).max && quoteReserve <= type(uint112).max, "OVERFLOW");
|
||||
_BASE_RESERVE_ = uint112(baseReserve);
|
||||
_QUOTE_RESERVE_ = uint112(quoteReserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sync the reserves of the pool, internal use only
|
||||
* @dev The balances of the pool should be actual balances minus the mt fee
|
||||
*/
|
||||
function _sync() internal {
|
||||
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - uint256(_MT_FEE_BASE_);
|
||||
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - uint256(_MT_FEE_QUOTE_);
|
||||
// the reserves should be less than the max uint112
|
||||
require(baseBalance <= type(uint112).max && quoteBalance <= type(uint112).max, "OVERFLOW");
|
||||
if (baseBalance != _BASE_RESERVE_) {
|
||||
_BASE_RESERVE_ = uint112(baseBalance);
|
||||
}
|
||||
if (quoteBalance != _QUOTE_RESERVE_) {
|
||||
_QUOTE_RESERVE_ = uint112(quoteBalance);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Sync the reserves of the pool
|
||||
function sync() external nonReentrant {
|
||||
_sync();
|
||||
}
|
||||
|
||||
/// @notice Correct the rState of the pool, details in pmm algorithm
|
||||
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_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice PriceLimit is used for oracle change protection
|
||||
* @notice It sets a ratio where the relative deviation between the new price and the old price cannot exceed this ratio.
|
||||
* @dev The default priceLimit is 1e3, the decimals of priceLimit is 1e6
|
||||
* @param priceLimit The new price limit
|
||||
*/
|
||||
function adjustPriceLimit(uint256 priceLimit) external onlyAdmin {
|
||||
// the default priceLimit is 1e3
|
||||
require(priceLimit <= 1e6, "INVALID_PRICE_LIMIT");
|
||||
_PRICE_LIMIT_ = priceLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Adjust oricle price i, only for admin
|
||||
*/
|
||||
function adjustPrice(uint256 i) external onlyAdmin {
|
||||
// the difference between i and _I_ should be less than priceLimit
|
||||
uint256 offset = i > _I_ ? i - _I_ : _I_ - i;
|
||||
require((offset * 1e6 / _I_) <= _PRICE_LIMIT_, "EXCEED_PRICE_LIMIT");
|
||||
_I_ = i;
|
||||
|
||||
emit IChange(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Adjust mtFee rate, only for maintainer
|
||||
* @dev The decimals of mtFee rate is 1e18
|
||||
* @param mtFeeRate The new mtFee rate
|
||||
*/
|
||||
function adjustMtFeeRate(uint256 mtFeeRate) external onlyMaintainer {
|
||||
require(mtFeeRate <= 10**18, "INVALID_MT_FEE_RATE");
|
||||
_MT_FEE_RATE_ = mtFeeRate;
|
||||
|
||||
emit MtFeeRateChange(mtFeeRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Adjust lpFee rate, only for maintainer
|
||||
* @dev The decimals of lpFee rate is 1e18
|
||||
* @param lpFeeRate The new lpFee rate
|
||||
*/
|
||||
function adjustLpFeeRate(uint256 lpFeeRate) external onlyMaintainer {
|
||||
require(lpFeeRate <= 10**18, "INVALID_LP_FEE_RATE");
|
||||
_LP_FEE_RATE_ = lpFeeRate;
|
||||
|
||||
emit LpFeeRateChange(lpFeeRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Adjust swap curve parameter k, only for maintainer
|
||||
* @dev The decimals of k is 1e18
|
||||
* @param k The new swap curve parameter k
|
||||
*/
|
||||
function adjustK(uint256 k) external onlyMaintainer {
|
||||
require(k <= 10**18, "INVALID_K");
|
||||
_K_ = k;
|
||||
|
||||
emit KChange(k);
|
||||
}
|
||||
|
||||
// ============ Asset Out ============
|
||||
/**
|
||||
* @notice Transfer base token out, internal use only
|
||||
* @param to The address of the receiver
|
||||
* @param amount The amount of base token to transfer out
|
||||
*/
|
||||
function _transferBaseOut(address to, uint256 amount) internal {
|
||||
if (amount > 0) {
|
||||
_BASE_TOKEN_.safeTransfer(to, amount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Transfer quote token out, internal use only
|
||||
* @param to The address of the receiver
|
||||
* @param amount The amount of quote token to transfer out
|
||||
*/
|
||||
function _transferQuoteOut(address to, uint256 amount) internal {
|
||||
if (amount > 0) {
|
||||
_QUOTE_TOKEN_.safeTransfer(to, amount);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Maintainer withdraw mtFee, only for maintainer
|
||||
function withdrawMtFeeTotal() external nonReentrant onlyMaintainer {
|
||||
uint256 mtFeeQuote = _MT_FEE_QUOTE_;
|
||||
uint256 mtFeeBase = _MT_FEE_BASE_;
|
||||
_MT_FEE_QUOTE_ = 0;
|
||||
_transferQuoteOut(_MAINTAINER_, mtFeeQuote);
|
||||
_MT_FEE_BASE_ = 0;
|
||||
_transferBaseOut(_MAINTAINER_, mtFeeBase);
|
||||
|
||||
emit WithdrawMtFee(address(_QUOTE_TOKEN_), mtFeeQuote);
|
||||
emit WithdrawMtFee(address(_BASE_TOKEN_), mtFeeBase);
|
||||
}
|
||||
|
||||
// ============ 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] - (amount);
|
||||
_SHARES_[to] = _SHARES_[to] + 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] - amount;
|
||||
_SHARES_[to] = _SHARES_[to] + amount;
|
||||
_ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender] - 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] + value;
|
||||
totalSupply = totalSupply + value;
|
||||
emit Mint(user, value);
|
||||
emit Transfer(address(0), user, value);
|
||||
}
|
||||
|
||||
function _burn(address user, uint256 value) internal {
|
||||
_SHARES_[user] = _SHARES_[user] - value;
|
||||
totalSupply = totalSupply - 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_GSP_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_GSP_LP: INVALID_SIGNATURE"
|
||||
);
|
||||
_approve(owner, spender, value);
|
||||
}
|
||||
}
|
||||
45
contracts/DODOGasSavingPool/GasSavingPool/intf/IGSP.sol
Normal file
45
contracts/DODOGasSavingPool/GasSavingPool/intf/IGSP.sol
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
|
||||
interface IGSP {
|
||||
function init(
|
||||
address maintainer,
|
||||
address admin,
|
||||
address baseTokenAddress,
|
||||
address quoteTokenAddress,
|
||||
uint256 lpFeeRate,
|
||||
uint256 mtFeeRate,
|
||||
uint256 i,
|
||||
uint256 k,
|
||||
uint256 priceLimit,
|
||||
bool isOpenTWAP
|
||||
) external;
|
||||
|
||||
function _BASE_TOKEN_() external view returns (address);
|
||||
|
||||
function _QUOTE_TOKEN_() external view returns (address);
|
||||
|
||||
function _I_() external view returns (uint256);
|
||||
|
||||
function _MT_FEE_RATE_MODEL_() external view returns (address); // Useless, just for compatibility
|
||||
|
||||
function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve);
|
||||
|
||||
function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate);
|
||||
|
||||
function getMtFeeTotal() external view returns (uint256 mtFeeBase, uint256 mtFeeQuote);
|
||||
|
||||
function sellBase(address to) external returns (uint256);
|
||||
|
||||
function sellQuote(address to) external returns (uint256);
|
||||
|
||||
function buyShares(address to) external returns (uint256 shares, uint256 baseInput, uint256 quoteInput);
|
||||
|
||||
function sellShares(uint256 shareAmount, address to, uint256 baseMinAmount, uint256 quoteMinAmount, bytes calldata data, uint256 deadline) external returns (uint256 baseAmount, uint256 quoteAmount);
|
||||
}
|
||||
59
contracts/DODOGasSavingPool/intf/IDODOCallee.sol
Normal file
59
contracts/DODOGasSavingPool/intf/IDODOCallee.sol
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
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;
|
||||
|
||||
function NFTRedeemCall(
|
||||
address payable assetTo,
|
||||
uint256 quoteAmount,
|
||||
bytes calldata
|
||||
) external;
|
||||
}
|
||||
195
contracts/DODOGasSavingPool/lib/DODOMath.sol
Normal file
195
contracts/DODOGasSavingPool/lib/DODOMath.sol
Normal file
@@ -0,0 +1,195 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity 0.8.16;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {DecimalMath} from "./DecimalMath.sol";
|
||||
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
||||
|
||||
/**
|
||||
* @title DODOMath
|
||||
* @author DODO Breeder
|
||||
*
|
||||
* @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions
|
||||
*/
|
||||
library DODOMath {
|
||||
using Math 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 * (V1 - V2); // i*delta
|
||||
if (k == 0) {
|
||||
return fairAmount / DecimalMath.ONE;
|
||||
}
|
||||
uint256 V0V0V1V2 = DecimalMath.divFloor(V0 * V0 / V1, V2);
|
||||
uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2)
|
||||
return (DecimalMath.ONE - k + penalty) * fairAmount / 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 (k == 0) {
|
||||
return V1 + 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();
|
||||
|
||||
if (V1 == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint256 sqrt;
|
||||
uint256 ki = 4 * k * i;
|
||||
|
||||
if (ki == 0) {
|
||||
sqrt = DecimalMath.ONE;
|
||||
} else if ((ki * delta) / ki == delta) {
|
||||
sqrt =((ki * delta) / V1 + DecimalMath.ONE2).sqrt();
|
||||
} else {
|
||||
sqrt = (ki / V1 * delta + DecimalMath.ONE2).sqrt();
|
||||
}
|
||||
uint256 premium =
|
||||
DecimalMath.divFloor(sqrt - DecimalMath.ONE, k * 2) + 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) {
|
||||
// why v1
|
||||
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 * (delta);
|
||||
if (idelta == 0) {
|
||||
temp = 0;
|
||||
} else if ((idelta * V1) / idelta == V1) {
|
||||
temp = (idelta * V1) / (V0 * V0);
|
||||
} else {
|
||||
temp = delta * (V1) / (V0) * (i) / (V0);
|
||||
}
|
||||
return V1 * (temp) / (temp + (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 * (V0) / (V1) * (V0) + (i * (delta)); // kQ0^2/Q1-i*deltaB
|
||||
uint256 bAbs = (DecimalMath.ONE - k) * (V1); // (1-k)Q1
|
||||
|
||||
bool bSig;
|
||||
if (bAbs >= part2) {
|
||||
bAbs = bAbs - part2;
|
||||
bSig = false;
|
||||
} else {
|
||||
bAbs = part2 - bAbs;
|
||||
bSig = true;
|
||||
}
|
||||
bAbs = bAbs / (DecimalMath.ONE);
|
||||
|
||||
// calculate sqrt
|
||||
uint256 squareRoot = DecimalMath.mulFloor((DecimalMath.ONE - k) * (4), DecimalMath.mulFloor(k, V0) * (V0)); // 4(1-k)kQ0^2
|
||||
squareRoot = Math.sqrt((bAbs * bAbs) + squareRoot); // sqrt(b*b+4(1-k)kQ0*Q0)
|
||||
|
||||
// final res
|
||||
uint256 denominator = (DecimalMath.ONE - k) * 2; // 2(1-k)
|
||||
uint256 numerator;
|
||||
if (bSig) {
|
||||
numerator = squareRoot - bAbs;
|
||||
if (numerator == 0) {
|
||||
revert("DODOMath: should not be 0");
|
||||
}
|
||||
} else {
|
||||
numerator = bAbs + squareRoot;
|
||||
}
|
||||
|
||||
uint256 V2 = DecimalMath.divCeil(numerator, denominator);
|
||||
if (V2 > V1) {
|
||||
return 0;
|
||||
} else {
|
||||
return V1 - V2;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
contracts/DODOGasSavingPool/lib/DecimalMath.sol
Normal file
78
contracts/DODOGasSavingPool/lib/DecimalMath.sol
Normal file
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity 0.8.16;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
||||
|
||||
/**
|
||||
* @title DecimalMath
|
||||
* @author DODO Breeder
|
||||
*
|
||||
* @notice Functions for fixed point number with 18 decimals
|
||||
*/
|
||||
|
||||
library DecimalMath {
|
||||
uint256 internal constant ONE = 10 ** 18;
|
||||
uint256 internal constant ONE2 = 10 ** 36;
|
||||
|
||||
function mul(uint256 target, uint256 d) internal pure returns (uint256) {
|
||||
return target * d / (10 ** 18);
|
||||
}
|
||||
|
||||
function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) {
|
||||
return target * d / (10 ** 18);
|
||||
}
|
||||
|
||||
function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) {
|
||||
return _divCeil(target * d, 10 ** 18);
|
||||
}
|
||||
|
||||
function div(uint256 target, uint256 d) internal pure returns (uint256) {
|
||||
return target * (10 ** 18) / d;
|
||||
}
|
||||
|
||||
function divFloor(uint256 target, uint256 d) internal pure returns (uint256) {
|
||||
return target * (10 ** 18) / d;
|
||||
}
|
||||
|
||||
function divCeil(uint256 target, uint256 d) internal pure returns (uint256) {
|
||||
return _divCeil(target * (10 ** 18), d);
|
||||
}
|
||||
|
||||
function reciprocalFloor(uint256 target) internal pure returns (uint256) {
|
||||
return uint256(10 ** 36) / target;
|
||||
}
|
||||
|
||||
function reciprocalCeil(uint256 target) internal pure returns (uint256) {
|
||||
return _divCeil(uint256(10 ** 36), target);
|
||||
}
|
||||
|
||||
function sqrt(uint256 target) internal pure returns (uint256) {
|
||||
return Math.sqrt(target * ONE);
|
||||
}
|
||||
|
||||
function powFloor(uint256 target, uint256 e) internal pure returns (uint256) {
|
||||
if (e == 0) {
|
||||
return 10 ** 18;
|
||||
} else if (e == 1) {
|
||||
return target;
|
||||
} else {
|
||||
uint256 p = powFloor(target, e / 2);
|
||||
p = p * p / (10 ** 18);
|
||||
if (e % 2 == 1) {
|
||||
p = p * target / (10 ** 18);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
function _divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
uint256 quotient = a / b;
|
||||
uint256 remainder = a - quotient * b;
|
||||
if (remainder > 0) {
|
||||
return quotient + 1;
|
||||
} else {
|
||||
return quotient;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
contracts/DODOGasSavingPool/lib/InitializableOwnable.sol
Normal file
58
contracts/DODOGasSavingPool/lib/InitializableOwnable.sol
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
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);
|
||||
}
|
||||
}
|
||||
266
contracts/DODOGasSavingPool/lib/PMMPricing.sol
Normal file
266
contracts/DODOGasSavingPool/lib/PMMPricing.sol
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.8.16;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {DecimalMath} from "../lib/DecimalMath.sol";
|
||||
import {DODOMath} from "../lib/DODOMath.sol";
|
||||
|
||||
/**
|
||||
* @title Pricing
|
||||
* @author DODO Breeder
|
||||
*
|
||||
* @notice DODO Pricing model
|
||||
*/
|
||||
|
||||
library PMMPricing {
|
||||
|
||||
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 ============
|
||||
/**
|
||||
* @notice Inner calculation based on pmm algorithm, sell base
|
||||
* @param state The current PMM state
|
||||
* @param payBaseAmount The amount of base token user want to sell
|
||||
* @return receiveQuoteAmount The amount of quote token user will receive
|
||||
* @return newR The new R status after swap
|
||||
*/
|
||||
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 - state.B;
|
||||
uint256 backToOneReceiveQuote = state.Q - 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 + (
|
||||
_ROneSellBaseToken(state, (payBaseAmount - backToOnePayBase))
|
||||
);
|
||||
newR = RState.BELOW_ONE;
|
||||
}
|
||||
} else {
|
||||
// state.R == RState.BELOW_ONE
|
||||
// case 3: R<1
|
||||
receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount);
|
||||
newR = RState.BELOW_ONE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Inner calculation based on pmm algorithm, sell quote
|
||||
* @param state The current PMM state
|
||||
* @param payQuoteAmount The amount of quote token user want to sell
|
||||
* @return receiveBaseAmount The amount of base token user will receive
|
||||
* @return newR The new R status after swap
|
||||
*/
|
||||
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 - state.Q;
|
||||
uint256 backToOneReceiveBase = state.B - 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 + (
|
||||
_ROneSellQuoteToken(state, payQuoteAmount - 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 + 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 + 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 - state.B0,
|
||||
state.i,
|
||||
state.K
|
||||
);
|
||||
} else if (state.R == RState.ABOVE_ONE) {
|
||||
state.B0 = DODOMath._SolveQuadraticFunctionForTarget(
|
||||
state.B,
|
||||
state.Q - 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 * state.Q0 / state.Q, state.Q);
|
||||
R = DecimalMath.ONE - state.K + (DecimalMath.mulFloor(state.K, R));
|
||||
return DecimalMath.divFloor(state.i, R);
|
||||
} else {
|
||||
uint256 R = DecimalMath.divFloor(state.B0 * state.B0 / state.B, state.B);
|
||||
R = DecimalMath.ONE - state.K + (DecimalMath.mulFloor(state.K, R));
|
||||
return DecimalMath.mulFloor(state.i, R);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ contract DSPFunding is DSPVault {
|
||||
_BASE_TARGET_ = uint112(shares);
|
||||
_QUOTE_TARGET_ = uint112(DecimalMath.mulFloor(shares, _I_));
|
||||
require(shares > 2001, "MINT_AMOUNT_NOT_ENOUGH");
|
||||
require(_QUOTE_TARGET_ > 0, "QUOTE_TARGET_IS_ZERO");
|
||||
_mint(address(0), 1001);
|
||||
shares -= 1001;
|
||||
} else if (baseReserve > 0 && quoteReserve > 0) {
|
||||
|
||||
@@ -7,7 +7,7 @@ pragma solidity 0.6.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {IDODOAdapter} from "../intf/IDODOAdapter.sol";
|
||||
import {IUniswapV3SwapCallback} from "../intf/IUniswapV3SwapCallback.sol";
|
||||
import {IUniswapV3SwapCallback} from "../intf/IUniswapV3SwapCallBack.sol";
|
||||
import {IUniV3} from "../intf/IUniV3.sol";
|
||||
import {IERC20} from "../../intf/IERC20.sol";
|
||||
import {SafeMath} from "../../lib/SafeMath.sol";
|
||||
|
||||
1676
contracts/SmartRoute/proxies/DODOFeeRouteProxy.sol
Normal file
1676
contracts/SmartRoute/proxies/DODOFeeRouteProxy.sol
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,15 @@ interface IERC20ForCheck {
|
||||
function allowance(address owner, address spender) external view returns (uint256);
|
||||
}
|
||||
|
||||
interface IOldERC20ForCheck {
|
||||
function decimals() external view returns (uint);
|
||||
function name() external view returns (bytes32);
|
||||
function symbol() external view returns (bytes32);
|
||||
|
||||
function balanceOf(address account) external view returns (uint256);
|
||||
function allowance(address owner, address spender) external view returns (uint256);
|
||||
}
|
||||
|
||||
|
||||
contract ERC20Helper {
|
||||
function isERC20(address token, address user, address spender) external view returns(bool isOk, string memory symbol, string memory name, uint decimals, uint256 balance, uint256 allownance) {
|
||||
@@ -27,16 +36,38 @@ contract ERC20Helper {
|
||||
allownance = _allownance;
|
||||
isOk = true;
|
||||
} catch {
|
||||
isOk = false;
|
||||
try this.judgeOldERC20(token, user, spender) returns (bytes32 _symbol, bytes32 _name, uint _decimals, uint256 _balance, uint256 _allownance) {
|
||||
symbol = bytes32ToString(_symbol);
|
||||
name = bytes32ToString(_name);
|
||||
decimals = _decimals;
|
||||
balance = _balance;
|
||||
allownance = _allownance;
|
||||
isOk = true;
|
||||
} catch {
|
||||
isOk = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function judgeERC20(address token, address user, address spender) external view returns(string memory symbol, string memory name, uint decimals, uint256 balance, uint256 allownance) {
|
||||
function judgeERC20(address token, address user, address spender) external view returns(string memory symbol, string memory name, uint decimals, uint256 balance, uint256 allownance) {
|
||||
name = IERC20ForCheck(token).name();
|
||||
symbol = IERC20ForCheck(token).symbol();
|
||||
decimals = IERC20ForCheck(token).decimals();
|
||||
|
||||
balance = IERC20ForCheck(token).balanceOf(user);
|
||||
allownance = IERC20ForCheck(token).allowance(user,spender);
|
||||
}
|
||||
}
|
||||
|
||||
function judgeOldERC20(address token, address user, address spender) external view returns(bytes32 symbol, bytes32 name, uint decimals, uint256 balance, uint256 allownance) {
|
||||
name = IOldERC20ForCheck(token).name();
|
||||
symbol = IOldERC20ForCheck(token).symbol();
|
||||
decimals = IOldERC20ForCheck(token).decimals();
|
||||
|
||||
balance = IOldERC20ForCheck(token).balanceOf(user);
|
||||
allownance = IOldERC20ForCheck(token).allowance(user,spender);
|
||||
}
|
||||
|
||||
function bytes32ToString(bytes32 _bytes) public pure returns (string memory _string) {
|
||||
_string = string(abi.encodePacked(_bytes));
|
||||
}
|
||||
}
|
||||
62
migrations/9_deploy_dvm_chain138.js
Normal file
62
migrations/9_deploy_dvm_chain138.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Deploy only the DVM (DODO Vending Machine) stack for Chain 138.
|
||||
* Run with: truffle migrate -f 9 --to 9 --network chain138
|
||||
* Requires: .env with privKey and RPC_URL_138 (or CHAIN138_RPC_URL)
|
||||
*/
|
||||
const CloneFactory = artifacts.require("CloneFactory");
|
||||
const FeeRateModelTemplate = artifacts.require("FeeRateModel");
|
||||
const FeeRateDIP3 = artifacts.require("FeeRateDIP3Impl");
|
||||
const PermissionManagerTemplate = artifacts.require("PermissionManager");
|
||||
const DvmTemplate = artifacts.require("DVM");
|
||||
const DvmFactory = artifacts.require("DVMFactory");
|
||||
|
||||
module.exports = async function (deployer, network, accounts) {
|
||||
if (network !== "chain138") return;
|
||||
|
||||
const multiSig = accounts[0];
|
||||
const defaultMaintainer = accounts[0];
|
||||
|
||||
console.log("Deploying DVM stack on Chain 138 with maintainer:", multiSig);
|
||||
|
||||
await deployer.deploy(CloneFactory);
|
||||
const CloneFactoryAddress = CloneFactory.address;
|
||||
console.log("CloneFactory:", CloneFactoryAddress);
|
||||
|
||||
await deployer.deploy(FeeRateModelTemplate);
|
||||
const defaultMtFeeRateAddress = FeeRateModelTemplate.address;
|
||||
console.log("FeeRateModel:", defaultMtFeeRateAddress);
|
||||
const feeRateModelInstance = await FeeRateModelTemplate.at(defaultMtFeeRateAddress);
|
||||
await feeRateModelInstance.initOwner(multiSig);
|
||||
|
||||
await deployer.deploy(FeeRateDIP3);
|
||||
const feeRateDIP3Address = FeeRateDIP3.address;
|
||||
console.log("FeeRateDIP3Impl:", feeRateDIP3Address);
|
||||
const feeRateDIP3Instance = await FeeRateDIP3.at(feeRateDIP3Address);
|
||||
await feeRateDIP3Instance.initOwner(multiSig);
|
||||
|
||||
await deployer.deploy(PermissionManagerTemplate);
|
||||
const permissionManagerAddress = PermissionManagerTemplate.address;
|
||||
console.log("PermissionManager:", permissionManagerAddress);
|
||||
const permissionInstance = await PermissionManagerTemplate.at(permissionManagerAddress);
|
||||
await permissionInstance.initOwner(multiSig);
|
||||
|
||||
await deployer.deploy(DvmTemplate);
|
||||
const dvmTemplateAddress = DvmTemplate.address;
|
||||
console.log("DVM template:", dvmTemplateAddress);
|
||||
|
||||
await deployer.deploy(
|
||||
DvmFactory,
|
||||
CloneFactoryAddress,
|
||||
dvmTemplateAddress,
|
||||
defaultMaintainer,
|
||||
defaultMtFeeRateAddress
|
||||
);
|
||||
const dvmFactoryAddress = DvmFactory.address;
|
||||
console.log("DVMFactory:", dvmFactoryAddress);
|
||||
const dvmFactoryInstance = await DvmFactory.at(dvmFactoryAddress);
|
||||
await dvmFactoryInstance.initOwner(multiSig);
|
||||
|
||||
console.log("\n=== Chain 138 DVM deployment summary ===");
|
||||
console.log("DVMFactory (use this with an adapter for createDVM):", dvmFactoryAddress);
|
||||
console.log("Set DODO_VENDING_MACHINE_ADDRESS to the DVMFactoryAdapter address (deploy separately).");
|
||||
};
|
||||
24828
package-lock.json
generated
24828
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@
|
||||
"node": "ganache-cli --port 8545 -l 0x1fffffffffffff -i 5777 -g 1 --allowUnlimitedContractSize"
|
||||
},
|
||||
"dependencies": {
|
||||
"@truffle/hdwallet-provider": "^1.5.1",
|
||||
"@truffle/hdwallet-provider": "^1.7.0",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/es6-promisify": "^6.0.0",
|
||||
"@types/ethereumjs-abi": "^0.6.3",
|
||||
@@ -36,13 +36,15 @@
|
||||
"bignumber.js": "^9.0.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"dotenv-flow": "^3.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv-flow": "^3.3.0",
|
||||
"es6-promisify": "^6.1.1",
|
||||
"ethereumjs-util": "^7.0.7",
|
||||
"ethjs": "^0.4.0",
|
||||
"lodash": "^4.17.20",
|
||||
"mocha": "^7.2.0",
|
||||
"solc": "^0.6.9",
|
||||
"truffle-hdwallet-provider": "^1.0.17",
|
||||
"ts-node": "^8.10.2",
|
||||
"typescript": "^3.9.5",
|
||||
"web3": "^1.2.8",
|
||||
@@ -52,6 +54,7 @@
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"ganache-cli": "^6.9.1",
|
||||
"hardhat": "^2.22.2",
|
||||
"prettier": "^2.0.5",
|
||||
"prettier-plugin-solidity": "^1.0.0-alpha.52",
|
||||
"solidity-coverage": "^0.7.7",
|
||||
|
||||
@@ -46,6 +46,12 @@ describe("Funding", () => {
|
||||
|
||||
it("revert cases", async () => {
|
||||
await ctx.transferBaseToDSP(lp, decimalStr("10"))
|
||||
await truffleAssert.reverts(
|
||||
ctx.DSP.methods.buyShares(lp).send(ctx.sendParam(lp)),
|
||||
"ZERO_QUOTE_AMOUNT"
|
||||
)
|
||||
await ctx.transferBaseToDSP(lp, "100")
|
||||
await ctx.transferQuoteToDSP(lp, "100")
|
||||
await truffleAssert.reverts(
|
||||
ctx.DSP.methods.buyShares(lp).send(ctx.sendParam(lp)),
|
||||
"MINT_AMOUNT_NOT_ENOUGH"
|
||||
@@ -56,7 +62,7 @@ describe("Funding", () => {
|
||||
await ctx.transferBaseToDSP(lp, decimalStr("100"))
|
||||
await ctx.transferQuoteToDSP(lp, decimalStr("100"))
|
||||
await ctx.DSP.methods.buyShares(lp).send(ctx.sendParam(lp));
|
||||
assert.equal(await ctx.DSP.methods.balanceOf(lp).call(), decimalStr("100"))
|
||||
assert.equal(await ctx.DSP.methods.balanceOf(lp).call(), "99999999999999998999")
|
||||
assert.equal(await ctx.DSP.methods.getMidPrice().call(), decimalStr("1"))
|
||||
})
|
||||
|
||||
@@ -129,19 +135,20 @@ describe("Funding", () => {
|
||||
|
||||
describe("sell shares", () => {
|
||||
it("not the last one sell shares", async () => {
|
||||
await ctx.transferBaseToDSP(lp, decimalStr("100"))
|
||||
await ctx.transferQuoteToDSP(lp, decimalStr("100"))
|
||||
await ctx.DSP.methods.buyShares(lp).send(ctx.sendParam(lp))
|
||||
|
||||
|
||||
await ctx.transferBaseToDSP(trader, decimalStr("10"))
|
||||
await ctx.transferQuoteToDSP(trader, decimalStr("10"))
|
||||
await ctx.DSP.methods.buyShares(trader).send(ctx.sendParam(trader))
|
||||
|
||||
await ctx.transferBaseToDSP(lp, decimalStr("100"))
|
||||
await ctx.transferQuoteToDSP(lp, decimalStr("100"))
|
||||
await ctx.DSP.methods.buyShares(lp).send(ctx.sendParam(lp))
|
||||
|
||||
var vaultShares = new BigNumber(await ctx.DSP.methods.balanceOf(lp).call())
|
||||
var bob = ctx.SpareAccounts[5]
|
||||
await ctx.DSP.methods.sellShares(vaultShares.div(2).toFixed(0), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp))
|
||||
assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("50"))
|
||||
assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("50"))
|
||||
assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), "50000000000000000000")
|
||||
assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), "50000000000000000000")
|
||||
|
||||
await ctx.DSP.methods.sellShares(vaultShares.div(2).toFixed(0), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp))
|
||||
assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("100"))
|
||||
@@ -156,8 +163,8 @@ describe("Funding", () => {
|
||||
var vaultShares = await ctx.DSP.methods.balanceOf(lp).call()
|
||||
var bob = ctx.SpareAccounts[5]
|
||||
await ctx.DSP.methods.sellShares(vaultShares, bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp))
|
||||
assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("100"))
|
||||
assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("100"))
|
||||
assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), "99999999999999998999")
|
||||
assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), "99999999999999998999")
|
||||
})
|
||||
|
||||
it("revert cases", async () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* phrase from a file you've .gitignored so it doesn't accidentally become public.
|
||||
*
|
||||
*/
|
||||
|
||||
require("dotenv").config();
|
||||
var HDWalletProvider = require("@truffle/hdwallet-provider");
|
||||
var privKey = process.env.privKey;
|
||||
var infuraId = process.env.infuraId;
|
||||
@@ -41,7 +41,7 @@ module.exports = {
|
||||
*/
|
||||
deploySwitch: {
|
||||
DEPLOY_V1: false,
|
||||
DEPLOY_V2: false,
|
||||
DEPLOY_V2: true,
|
||||
ERC20V3Factory: false,
|
||||
MOCK_TOKEN: false,
|
||||
MOCK_V2_POOL: false,
|
||||
@@ -84,7 +84,7 @@ module.exports = {
|
||||
polygonscan: process.env.POLYGONSCAN_API_KEY,
|
||||
snowtrace: process.env.SNOWTRACE_API_KEY,
|
||||
scroll: "fakekey",
|
||||
calderaexplorer:"fakekey",
|
||||
calderaexplorer: "fakekey",
|
||||
},
|
||||
|
||||
networks: {
|
||||
@@ -245,6 +245,7 @@ module.exports = {
|
||||
// timeoutBlocks: 200,
|
||||
skipDryRun: true
|
||||
},
|
||||
|
||||
base: {
|
||||
networkCheckTimeout: 1000000,
|
||||
provider: () => {
|
||||
@@ -265,6 +266,35 @@ module.exports = {
|
||||
network_id: "10"
|
||||
},
|
||||
|
||||
dodotest: {
|
||||
networkCheckTimeout: 100000,
|
||||
provider: () => {
|
||||
return new HDWalletProvider({
|
||||
privateKeys: [privKey],
|
||||
providerOrUrl: 'https://dodochain-testnet.alt.technology',
|
||||
chainId: 53457
|
||||
})
|
||||
},
|
||||
network_id: '53457',
|
||||
skipDryRun: true
|
||||
},
|
||||
|
||||
chain138: {
|
||||
networkCheckTimeout: 60000,
|
||||
provider: () => {
|
||||
const rpc = process.env.RPC_URL_138 || process.env.CHAIN138_RPC_URL || "http://127.0.0.1:8545";
|
||||
return new HDWalletProvider({
|
||||
privateKeys: [privKey],
|
||||
providerOrUrl: rpc,
|
||||
chainId: 138
|
||||
});
|
||||
},
|
||||
network_id: "138",
|
||||
gas: 8000000,
|
||||
gasPrice: process.env.GAS_PRICE_138 ? parseInt(process.env.GAS_PRICE_138, 10) : 1000000000,
|
||||
skipDryRun: true
|
||||
},
|
||||
|
||||
coverage: {
|
||||
host: "127.0.0.1",
|
||||
port: 6545,
|
||||
|
||||
97
yarn-error.log
Normal file
97
yarn-error.log
Normal file
@@ -0,0 +1,97 @@
|
||||
Arguments:
|
||||
/home/skye/.nvm/versions/node/v18.14.0/bin/node /home/skye/.cache/node/corepack/yarn/1.22.19/bin/yarn.js
|
||||
|
||||
PATH:
|
||||
/home/skye/.vscode-server/bin/e170252f762678dec6ca2cc69aba1570769a5d39/bin/remote-cli:/home/skye/.nvm/versions/node/v18.14.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/Program Files/Git/cmd:/mnt/c/Python311/Scripts/:/mnt/c/Python311/:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/:/mnt/c/WINDOWS/System32/OpenSSH/:/mnt/d/Python/Python310:/mnt/d/Python/Python310/Scripts:/mnt/d/nodejs/:/mnt/c/ProgramData/chocolatey/bin:/mnt/d/Go/bin:/mnt/d/anaconda3:/mnt/d/anaconda3/Scripts:/mnt/d/anaconda3/Library/bin:/mnt/c/Users/王翊倩/AppData/Local/Microsoft/WindowsApps:/mnt/d/Python/Python310/Scripts:/mnt/d/Microsoft VS Code/Microsoft VS Code/bin:/mnt/c/Users/王翊倩/AppData/Roaming/npm:/mnt/c/Users/王翊倩/go/bin:/snap/bin:/home/skye/.foundry/bin:/home/skye/.foundry/bin
|
||||
|
||||
Yarn version:
|
||||
1.22.19
|
||||
|
||||
Node version:
|
||||
18.14.0
|
||||
|
||||
Platform:
|
||||
linux x64
|
||||
|
||||
Trace:
|
||||
Error: https://github.com/web3-js/WebSocket-Node: ESOCKETTIMEDOUT
|
||||
at ClientRequest.<anonymous> (/home/skye/.cache/node/corepack/yarn/1.22.19/lib/cli.js:141517:19)
|
||||
at Object.onceWrapper (node:events:627:28)
|
||||
at ClientRequest.emit (node:events:513:28)
|
||||
at TLSSocket.emitRequestTimeout (node:_http_client:848:9)
|
||||
at Object.onceWrapper (node:events:627:28)
|
||||
at TLSSocket.emit (node:events:525:35)
|
||||
at Socket._onTimeout (node:net:570:8)
|
||||
at listOnTimeout (node:internal/timers:569:17)
|
||||
at process.processTimers (node:internal/timers:512:7)
|
||||
|
||||
npm manifest:
|
||||
{
|
||||
"name": "dodo",
|
||||
"version": "1.0.0",
|
||||
"description": "a kind of bird",
|
||||
"main": "index.js",
|
||||
"author": "dodo breeder",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"dodo",
|
||||
"ethereum",
|
||||
"pmm"
|
||||
],
|
||||
"scripts": {
|
||||
"prettier": "prettier --write **/*.sol",
|
||||
"migrate": "truffle migrate",
|
||||
"compile": "rm -r build && truffle compile",
|
||||
"coverage": "NETWORK_ID=1002 RPC_NODE_URI=http://127.0.0.1:6545 COVERAGE=true truffle run coverage",
|
||||
"test": "truffle compile && truffle test",
|
||||
"test_only": "truffle test",
|
||||
"deploy": "truffle migrate --network=$NETWORK --reset",
|
||||
"deploy_kovan": "NETWORK=kovan npm run deploy",
|
||||
"deploy_mainnet": "NETWORK=mainnet npm run deploy",
|
||||
"deploy_test": "NETWORK=development npm run deploy",
|
||||
"node": "ganache-cli --port 8545 -l 0x1fffffffffffff -i 5777 -g 1 --allowUnlimitedContractSize"
|
||||
},
|
||||
"dependencies": {
|
||||
"@truffle/hdwallet-provider": "^1.7.0",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/es6-promisify": "^6.0.0",
|
||||
"@types/ethereumjs-abi": "^0.6.3",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"assert": "^2.0.0",
|
||||
"axios": "^0.24.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv-flow": "^3.3.0",
|
||||
"es6-promisify": "^6.1.1",
|
||||
"ethereumjs-util": "^7.0.7",
|
||||
"ethjs": "^0.4.0",
|
||||
"lodash": "^4.17.20",
|
||||
"mocha": "^7.2.0",
|
||||
"solc": "^0.6.9",
|
||||
"truffle-hdwallet-provider": "^1.0.17",
|
||||
"ts-node": "^8.10.2",
|
||||
"typescript": "^3.9.5",
|
||||
"web3": "^1.2.8",
|
||||
"web3-core-helpers": "^1.2.8",
|
||||
"web3-eth-contract": "^1.2.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"ganache-cli": "^6.9.1",
|
||||
"prettier": "^2.0.5",
|
||||
"prettier-plugin-solidity": "^1.0.0-alpha.52",
|
||||
"solidity-coverage": "^0.7.7",
|
||||
"truffle-assertions": "^0.9.2",
|
||||
"truffle-plugin-verify": "^0.5.33"
|
||||
}
|
||||
}
|
||||
|
||||
yarn manifest:
|
||||
No manifest
|
||||
|
||||
Lockfile:
|
||||
No lockfile
|
||||
Reference in New Issue
Block a user