add withdraw functionnality

This commit is contained in:
zapaz.eth 2021-10-16 11:21:41 +02:00
parent a12320f8fd
commit dffd14c2fe
9 changed files with 609 additions and 252 deletions

22
.prettierrc Normal file
View File

@ -0,0 +1,22 @@
{
"printWidth": 120,
"singleQuote": false,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "none",
"semi": true,
"plugins": ["./node_modules/prettier-plugin-solidity"],
"overrides": [
{
"files": "*.sol",
"options": {
"printWidth": 80,
"tabWidth": 4,
"useTabs": true,
"singleQuote": false,
"bracketSpacing": true,
"explicitTypes": "always"
}
}
]
}

View File

@ -6,9 +6,19 @@ contract Events {
address indexed userAddress, address indexed userAddress,
address indexed token, address indexed token,
uint256 amount, uint256 amount,
uint256 indexed bondingShareId,
uint256 lpAmount, uint256 lpAmount,
uint256 durationWeeks, uint256 durationWeeks,
uint256 getId,
uint256 setId
);
event Withdraw(
address indexed userAddress,
uint256 indexed bondingShareId, uint256 indexed bondingShareId,
uint256 lpAmount,
uint256 endBlock,
address indexed token,
uint256 amount,
uint256 getId, uint256 getId,
uint256 setId uint256 setId
); );

View File

@ -1,40 +1,55 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.7.6; pragma solidity 0.7.6;
import {TokenInterface} from "../../common/interfaces.sol"; import { Basic } from "../../common/basic.sol";
import {DSMath} from "../../common/math.sol"; import { IUbiquityAlgorithmicDollarManager } from "./interfaces.sol";
import {Basic} from "../../common/basic.sol";
abstract contract Helpers is DSMath, Basic { abstract contract Helpers is Basic {
/** /**
* @dev Ubiquity Algorithmic Dollar Manager Address * @dev Ubiquity Algorithmic Dollar Manager
*/ */
address internal constant UbiquityAlgorithmicDollarManager = IUbiquityAlgorithmicDollarManager internal constant ubiquityManager =
0x4DA97a8b831C345dBe6d16FF7432DF2b7b776d98; IUbiquityAlgorithmicDollarManager(
0x4DA97a8b831C345dBe6d16FF7432DF2b7b776d98
);
/** /**
* @dev DAI Address * @dev DAI Address
*/ */
address internal constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address internal constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
/** /**
* @dev USDC Address * @dev USDC Address
*/ */
address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
/** /**
* @dev USDT Address * @dev USDT Address
*/ */
address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
/** /**
* @dev Curve 3CRV Token Address * @dev Curve 3CRV Token Address
*/ */
address internal constant CRV3 = 0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490; address internal constant CRV3 = 0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490;
/** /**
* @dev Curve 3Pool Address * @dev Curve 3Pool Address
*/ */
address internal constant Pool3 = address internal constant Pool3 =
0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7; 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
/**
* @dev Ubiquity Algorithmic Dollar Address
*/
function getUAD() internal returns (address) {
return ubiquityManager.dollarTokenAddress();
}
/**
* @dev Ubiquity Metapool uAD / 3CRV Address
*/
function getUADCRV3() internal returns (address) {
return ubiquityManager.stableSwapMetaPoolAddress();
}
} }

View File

@ -1,31 +1,63 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.7.6; pragma solidity 0.7.6;
pragma abicoder v2;
import {TokenInterface} from "../../common/interfaces.sol";
interface IUbiquityBondingV2 { interface IUbiquityBondingV2 {
function deposit(uint256 lpAmount, uint256 durationWeeks) struct Bond {
external address minter;
returns (uint256 bondingShareId); uint256 lpFirstDeposited;
uint256 creationBlock;
uint256 lpRewardDebt;
uint256 endBlock;
uint256 lpAmount;
}
function deposit(uint256 lpAmount, uint256 durationWeeks)
external
returns (uint256 bondingShareId);
function removeLiquidity(uint256 lpAmount, uint256 bondId) external;
function holderTokens(address) external view returns (uint256[] memory);
function totalLP() external view returns (uint256);
function totalSupply() external view returns (uint256);
function getBond(uint256 bondId) external returns (Bond memory bond);
} }
interface IUbiquityMetaPool { interface IUbiquityMetaPool {
function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount) function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)
external external
returns (uint256); returns (uint256);
function remove_liquidity_one_coin(
uint256 lpAmount,
int128 i,
uint256 min_amount
) external returns (uint256);
} }
interface IUbiquity3Pool { interface I3Pool {
function add_liquidity( function add_liquidity(
uint256[3] calldata _amounts, uint256[3] calldata _amounts,
uint256 _min_mint_amount uint256 _min_mint_amount
) external; ) external;
function remove_liquidity_one_coin(
uint256 lpAmount,
int128 i,
uint256 min_amount
) external;
} }
interface IUbiquityAlgorithmicDollarManager { interface IUbiquityAlgorithmicDollarManager {
function dollarTokenAddress() external returns (address); function dollarTokenAddress() external returns (address);
function stableSwapMetaPoolAddress() external returns (address); function stableSwapMetaPoolAddress() external returns (address);
function bondingContractAddress() external returns (address); function bondingContractAddress() external returns (address);
function bondingShareAddress() external returns (address);
} }

View File

@ -7,131 +7,214 @@ pragma abicoder v2;
* @dev Ubiquity Dollar (uAD). * @dev Ubiquity Dollar (uAD).
*/ */
import {TokenInterface, MemoryInterface} from "../../common/interfaces.sol"; import { TokenInterface } from "../../common/interfaces.sol";
import {Stores} from "../../common/stores.sol"; import { IUbiquityBondingV2, IUbiquityMetaPool, I3Pool } from "./interfaces.sol";
import {SafeMath} from "../../common/math.sol"; import { Helpers } from "./helpers.sol";
import {IUbiquityBondingV2, IUbiquityMetaPool, IUbiquity3Pool, IUbiquityAlgorithmicDollarManager} from "./interfaces.sol"; import { Events } from "./events.sol";
import {Helpers} from "./helpers.sol";
import {Events} from "./events.sol";
contract ConnectV2Ubiquity is Helpers, Events { contract ConnectV2Ubiquity is Helpers, Events {
string public constant name = "Ubiquity-v1"; string public constant name = "Ubiquity-v1";
/** /**
* @dev Deposit into Ubiquity protocol * @dev Deposit into Ubiquity protocol
* @notice 3POOL (DAI / USDC / USDT) => METAPOOL (3CRV / uAD) => uAD3CRV-f => Ubiquity BondingShare * @notice 3POOL (DAI / USDC / USDT) => METAPOOL (3CRV / uAD) => uAD3CRV-f => Ubiquity BondingShare
* @notice STEP 1 : 3POOL (DAI / USDC / USDT) => 3CRV * @notice STEP 1 : 3POOL (DAI / USDC / USDT) => 3CRV
* @notice STEP 2 : METAPOOL(3CRV / UAD) => uAD3CRV-f * @notice STEP 2 : METAPOOL(3CRV / UAD) => uAD3CRV-f
* @notice STEP 3 : uAD3CRV-f => Ubiquity BondingShare * @notice STEP 3 : uAD3CRV-f => Ubiquity BondingShare
* @param token Token deposited : DAI, USDC, USDT, 3CRV, uAD or uAD3CRV-f * @param token Token deposited : DAI, USDC, USDT, 3CRV, uAD or uAD3CRV-f
* @param amount Amount of tokens to deposit (For max: `uint256(-1)`) * @param amount Amount of tokens to deposit (For max: `uint256(-1)`)
* @param durationWeeks Duration in weeks tokens will be locked (4-208) * @param durationWeeks Duration in weeks tokens will be locked (4-208)
* @param getId ID to retrieve amt. * @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens deposited. * @param setId ID stores the bonding share id of tokens deposited.
*/ */
function deposit( function deposit(
address token, address token,
uint256 amount, uint256 amount,
uint256 durationWeeks, uint256 durationWeeks,
uint256 getId, uint256 getId,
uint256 setId uint256 setId
) )
external external
payable payable
returns (string memory _eventName, bytes memory _eventParam) returns (string memory _eventName, bytes memory _eventParam)
{ {
address UAD = IUbiquityAlgorithmicDollarManager( address UAD = getUAD();
UbiquityAlgorithmicDollarManager address UAD3CRVf = getUADCRV3();
).dollarTokenAddress();
address UAD3CRVf = IUbiquityAlgorithmicDollarManager(
UbiquityAlgorithmicDollarManager
).stableSwapMetaPoolAddress();
require( require(
token == DAI || token == DAI ||
token == USDC || token == USDC ||
token == USDT || token == USDT ||
token == UAD || token == UAD ||
token == CRV3 || token == CRV3 ||
token == UAD3CRVf, token == UAD3CRVf,
"Invalid token: must be DAI, USDC, USDT, uAD, 3CRV or uAD3CRV-f" "Invalid token: must be DAI, USDC, USDT, uAD, 3CRV or uAD3CRV-f"
); );
uint256 _amount = getUint(getId, amount); uint256 _amount = getUint(getId, amount);
uint256 _lpAmount; uint256 lpAmount;
// Full balance if amount = -1 // Full balance if amount = -1
if (_amount == uint256(-1)) { if (_amount == uint256(-1)) {
_amount = getTokenBal(TokenInterface(token)); _amount = getTokenBal(TokenInterface(token));
} }
// STEP 1 : SwapTo3CRV : Deposit DAI, USDC or USDT into 3Pool to get 3Crv LPs // STEP 1 : SwapTo3CRV : Deposit DAI, USDC or USDT into 3Pool to get 3Crv LPs
if (token == DAI || token == USDC || token == USDT) { if (token == DAI || token == USDC || token == USDT) {
uint256[3] memory amounts1; uint256[3] memory amounts1;
if (token == DAI) amounts1[0] = _amount; if (token == DAI) amounts1[0] = _amount;
else if (token == USDC) amounts1[1] = _amount; else if (token == USDC) amounts1[1] = _amount;
else if (token == USDT) amounts1[2] = _amount; else if (token == USDT) amounts1[2] = _amount;
approve(TokenInterface(token), Pool3, _amount); approve(TokenInterface(token), Pool3, _amount);
IUbiquity3Pool(Pool3).add_liquidity(amounts1, 0); I3Pool(Pool3).add_liquidity(amounts1, 0);
} }
// STEP 2 : ProvideLiquidityToMetapool : Deposit in uAD3CRV pool to get uAD3CRV-f LPs // STEP 2 : ProvideLiquidityToMetapool : Deposit in uAD3CRV pool to get uAD3CRV-f LPs
if ( if (
token == DAI || token == DAI ||
token == USDC || token == USDC ||
token == USDT || token == USDT ||
token == UAD || token == UAD ||
token == CRV3 token == CRV3
) { ) {
uint256[2] memory amounts2; uint256[2] memory amounts2;
address token2 = token; address token2 = token;
uint256 _amount2; uint256 _amount2;
if (token == UAD) { if (token == UAD) {
_amount2 = _amount; _amount2 = _amount;
amounts2[0] = _amount2; amounts2[0] = _amount2;
} else { } else {
if (token == CRV3) { if (token == CRV3) {
_amount2 = _amount; _amount2 = _amount;
} else { } else {
token2 = CRV3; token2 = CRV3;
_amount2 = getTokenBal(TokenInterface(token2)); _amount2 = getTokenBal(TokenInterface(token2));
} }
amounts2[1] = _amount2; amounts2[1] = _amount2;
} }
approve(TokenInterface(token2), UAD3CRVf, _amount2); approve(TokenInterface(token2), UAD3CRVf, _amount2);
_lpAmount = IUbiquityMetaPool(UAD3CRVf).add_liquidity(amounts2, 0); lpAmount = IUbiquityMetaPool(UAD3CRVf).add_liquidity(amounts2, 0);
} }
// STEP 3 : Farm/ApeIn : Deposit uAD3CRV-f LPs into UbiquityBondingV2 and get Ubiquity Bonding Shares // STEP 3 : Farm/ApeIn : Deposit uAD3CRV-f LPs into UbiquityBondingV2 and get Ubiquity Bonding Shares
if (token == UAD3CRVf) { if (token == UAD3CRVf) {
_lpAmount = _amount; lpAmount = _amount;
} }
address Bonding = IUbiquityAlgorithmicDollarManager( address bonding = ubiquityManager.bondingContractAddress();
UbiquityAlgorithmicDollarManager approve(TokenInterface(UAD3CRVf), bonding, lpAmount);
).bondingContractAddress(); uint256 bondingShareId = IUbiquityBondingV2(bonding).deposit(
approve(TokenInterface(UAD3CRVf), Bonding, _lpAmount); lpAmount,
uint256 bondingShareId = IUbiquityBondingV2(Bonding).deposit( durationWeeks
_lpAmount, );
durationWeeks
);
setUint(setId, bondingShareId); setUint(setId, bondingShareId);
_eventName = "Deposit(address,address,uint256,uint256,uint256,uint256,uint256,uint256)"; _eventName = "Deposit(address,address,uint256,uint256,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode( _eventParam = abi.encode(
address(this), address(this),
token, token,
amount, amount,
_lpAmount, bondingShareId,
durationWeeks, lpAmount,
bondingShareId, durationWeeks,
getId, getId,
setId setId
); );
} }
/**
* @dev Withdraw from Ubiquity protocol
* @notice Ubiquity BondingShare => uAD3CRV-f => METAPOOL (3CRV / uAD) => 3POOL (DAI / USDC / USDT)
* @notice STEP 1 : Ubiquity BondingShare => uAD3CRV-f
* @notice STEP 2 : uAD3CRV-f => METAPOOL(3CRV / UAD)
* @notice STEP 3 : 3CRV => 3POOL (DAI / USDC / USDT)
* @param bondingShareId Bonding Share Id to withdraw
* @param token Token to withdraw to : DAI, USDC, USDT, 3CRV, uAD or uAD3CRV-f
* @param getId ID
* @param setId ID
*/
function withdraw(
uint256 bondingShareId,
address token,
uint256 getId,
uint256 setId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
address UAD3CRVf = getUADCRV3();
bool[6] memory tok = [
token == DAI, // 0
token == USDC, // 1
token == USDT, // 2
token == CRV3, // 3
token == getUAD(), // 4
token == UAD3CRVf // 5
];
require(
// DAI / USDC / USDT / CRV3 / UAD / UAD3CRVF
tok[0] || tok[1] || tok[2] || tok[3] || tok[4] || tok[5],
"Invalid token: must be DAI, USDC, USDT, uAD, 3CRV or uAD3CRV-f"
);
uint256 _bondingShareId = getUint(getId, bondingShareId);
// Get Bond
IUbiquityBondingV2.Bond memory bond = IUbiquityBondingV2(
ubiquityManager.bondingShareAddress()
).getBond(_bondingShareId);
require(address(this) == bond.minter, "Not bond owner");
// STEP 1 : Withdraw Ubiquity Bonding Shares to get back uAD3CRV-f LPs
address bonding = ubiquityManager.bondingContractAddress();
IUbiquityBondingV2(bonding).removeLiquidity(
bond.lpAmount,
_bondingShareId
);
// STEP 2 : Withdraw uAD3CRV-f LPs to get back uAD or 3Crv
// DAI / USDC / USDT / CRV3 / UAD
if (tok[0] || tok[1] || tok[2] || tok[3] || tok[4]) {
uint256 amount2 = getTokenBal(TokenInterface(UAD3CRVf));
IUbiquityMetaPool(UAD3CRVf).remove_liquidity_one_coin(
amount2,
tok[4] ? 0 : 1,
0
);
}
// STEP 3 : Withdraw 3Crv LPs from 3Pool to get back DAI, USDC or USDT
// DAI / USDC / USDT
if (tok[0] || tok[1] || tok[2]) {
uint256 amount1 = getTokenBal(TokenInterface(CRV3));
I3Pool(Pool3).remove_liquidity_one_coin(
amount1,
tok[0] ? 0 : (tok[1] ? 1 : 2),
0
);
}
uint256 amount = getTokenBal(TokenInterface(token));
setUint(setId, amount);
_eventName = "Withdraw(address,uint256,uint256,uint256,address,uint256,uint256,uint256)";
_eventParam = abi.encode(
address(this),
_bondingShareId,
bond.lpAmount,
bond.endBlock,
token,
amount,
getId,
setId
);
}
} }

View File

@ -28,63 +28,63 @@ module.exports = {
settings: { settings: {
optimizer: { optimizer: {
enabled: true, enabled: true,
runs: 200, runs: 200
}, }
}, }
}, },
{ {
version: "0.6.0", version: "0.6.0"
}, },
{ {
version: "0.6.2", version: "0.6.2"
}, },
{ {
version: "0.6.5", version: "0.6.5"
}, }
], ]
}, },
networks: { networks: {
kovan: { kovan: {
url: `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_ID}`, url: `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_ID}`,
accounts: [`0x${PRIVATE_KEY}`], accounts: [`0x${PRIVATE_KEY}`]
}, },
mainnet: { mainnet: {
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`, url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
accounts: [`0x${PRIVATE_KEY}`], accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000, timeout: 150000,
gasPrice: parseInt(utils.parseUnits("30", "gwei")), gasPrice: parseInt(utils.parseUnits("30", "gwei"))
}, },
rinkeby: { rinkeby: {
url: `https://eth-rinkeby.alchemyapi.io/v2/${ALCHEMY_ID}`, url: `https://eth-rinkeby.alchemyapi.io/v2/${ALCHEMY_ID}`,
accounts: [`0x${PRIVATE_KEY}`], accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000, timeout: 150000
}, },
hardhat: { hardhat: {
forking: { forking: {
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`, url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
blockNumber: 12696000, blockNumber: 13097100
}, },
blockGasLimit: 12000000, blockGasLimit: 12000000,
gasPrice: parseInt(utils.parseUnits("300", "gwei")), gasPrice: parseInt(utils.parseUnits("300", "gwei"))
}, },
local: { local: {
url: "http://127.0.0.1:8545", url: "http://127.0.0.1:8545"
}, },
matic: { matic: {
url: "https://rpc-mainnet.maticvigil.com/", url: "https://rpc-mainnet.maticvigil.com/",
accounts: [`0x${PRIVATE_KEY}`], accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000, timeout: 150000,
gasPrice: parseInt(utils.parseUnits("1", "gwei")), gasPrice: parseInt(utils.parseUnits("1", "gwei"))
}, },
arbitrum: { arbitrum: {
chainId: 42161, chainId: 42161,
url: `https://arb-mainnet.g.alchemy.com/v2/${ALCHEMY_ID}`, url: `https://arb-mainnet.g.alchemy.com/v2/${ALCHEMY_ID}`,
accounts: [`0x${PRIVATE_KEY}`], accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000, timeout: 150000,
gasPrice: parseInt(utils.parseUnits("2", "gwei")), gasPrice: parseInt(utils.parseUnits("2", "gwei"))
}, },
avax: { avax: {
url: 'https://api.avax.network/ext/bc/C/rpc', url: "https://api.avax.network/ext/bc/C/rpc",
chainId: 43114, chainId: 43114,
accounts: [`0x${PRIVATE_KEY}`], accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000, timeout: 150000,
@ -92,13 +92,14 @@ module.exports = {
} }
}, },
etherscan: { etherscan: {
apiKey: ETHERSCAN_API_KEY, apiKey: ETHERSCAN_API_KEY
}, },
tenderly: { tenderly: {
project: process.env.TENDERLY_PROJECT, project: process.env.TENDERLY_PROJECT,
username: process.env.TENDERLY_USERNAME, username: process.env.TENDERLY_USERNAME
}, },
mocha: { mocha: {
timeout: 100 * 1000, timeout: 100 * 1000,
}, bail: true
}; }
};

View File

@ -22,33 +22,36 @@
}, },
"homepage": "https://github.com/InstaDApp/dsa-connectors-new#readme", "homepage": "https://github.com/InstaDApp/dsa-connectors-new#readme",
"dependencies": { "dependencies": {
"@openzeppelin/contracts": "^3.4.0-solc-0.7", "@openzeppelin/contracts": "^3.4.2",
"@uniswap/v3-core": "^1.0.0", "@uniswap/v3-core": "^1.0.0",
"@uniswap/v3-periphery": "^1.1.1", "@uniswap/v3-periphery": "^1.2.1",
"chalk": "^4.0.0", "chalk": "^4.1.2",
"commander": "^7.1.0", "commander": "^7.2.0",
"dotenv": "^7.0.0", "dotenv": "^7.0.0",
"hardhat-docgen": "^1.1.1", "hardhat-docgen": "^1.1.2",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"solc": "^0.7.0" "solc": "^0.7.6"
}, },
"devDependencies": { "devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^2.1.6", "@nomiclabs/hardhat-etherscan": "^2.1.6",
"@nomiclabs/hardhat-waffle": "^2.0.1", "@nomiclabs/hardhat-waffle": "^2.0.1",
"@nomiclabs/hardhat-web3": "^2.0.0", "@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/test-helpers": "^0.5.12", "@openzeppelin/test-helpers": "^0.5.15",
"@studydefi/money-legos": "^2.3.7", "@studydefi/money-legos": "^2.4.2",
"@tenderly/hardhat-tenderly": "^1.0.12", "@tenderly/hardhat-tenderly": "^1.0.12",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"ethereum-waffle": "^3.4.0", "ethereum-waffle": "^3.4.0",
"ethers": "^5.4.4", "ethers": "^5.4.7",
"hardhat": "^2.6.4", "hardhat": "^2.6.6",
"hardhat-deploy": "^0.9.1", "hardhat-deploy": "^0.9.3",
"hardhat-deploy-ethers": "^0.3.0-beta.10", "hardhat-deploy-ethers": "^0.3.0-beta.11",
"husky": "^6.0.0", "husky": "^6.0.0",
"prettier": "^2.4.1",
"prettier-plugin-solidity": "^1.0.0-beta.18",
"sol-merger": "^2.0.1", "sol-merger": "^2.0.1",
"solhint": "^3.3.6",
"solidity-coverage": "0.5.11", "solidity-coverage": "0.5.11",
"web3": "^1.3.6" "web3": "^1.6.0"
} }
} }

View File

@ -2,7 +2,7 @@ const { expect } = require("chai");
const hre = require("hardhat"); const hre = require("hardhat");
const { waffle, ethers } = hre; const { waffle, ethers } = hre;
const { provider } = waffle; const { provider } = waffle;
const { BigNumber } = ethers; const { BigNumber, utils } = ethers;
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js"); const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js");
const buildDSAv2 = require("../../scripts/buildDSAv2"); const buildDSAv2 = require("../../scripts/buildDSAv2");
@ -10,14 +10,14 @@ const encodeSpells = require("../../scripts/encodeSpells");
const addresses = require("../../scripts/constant/addresses"); const addresses = require("../../scripts/constant/addresses");
const abis = require("../../scripts/constant/abis"); const abis = require("../../scripts/constant/abis");
const impersonate = require("../../scripts/impersonate"); const impersonate = require("../../scripts/impersonate");
const { forkReset, sendEth } = require("./utils"); const { forkReset, sendEth, mineNBlock } = require("./utils");
const connectV2UbiquityArtifacts = require("../../artifacts/contracts/mainnet/connectors/ubiquity/main.sol/ConnectV2Ubiquity.json"); const connectV2UbiquityArtifacts = require("../../artifacts/contracts/mainnet/connectors/ubiquity/main.sol/ConnectV2Ubiquity.json");
const { abi: implementationsABI } = require("../../scripts/constant/abi/core/InstaImplementations.json") const { abi: implementationsABI } = require("../../scripts/constant/abi/core/InstaImplementations.json");
const implementationsMappingAddr = "0xCBA828153d3a85b30B5b912e1f2daCac5816aE9D" const implementationsMappingAddr = "0xCBA828153d3a85b30B5b912e1f2daCac5816aE9D";
describe.only("Ubiquity", function () { describe("Ubiquity", function () {
const ubiquityTest = "UBIQUITY-TEST-A"; const ubiquityTest = "UBIQUITY-TEST-A";
const BOND = "0x2dA07859613C14F6f05c97eFE37B9B4F212b5eF5"; const BOND = "0x2dA07859613C14F6f05c97eFE37B9B4F212b5eF5";
@ -43,7 +43,7 @@ describe.only("Ubiquity", function () {
"function add_liquidity(uint256[3],uint256) returns (uint256)", "function add_liquidity(uint256[3],uint256) returns (uint256)",
"function approve(address, uint256) external", "function approve(address, uint256) external",
"function holderTokens(address) view returns (uint256[])", "function holderTokens(address) view returns (uint256[])",
"function getBond(uint256) view returns (tuple(address,uint256,uint256,uint256,uint256,uint256))", "function getBond(uint256) view returns (tuple(address,uint256,uint256,uint256,uint256,uint256))"
]; ];
let dsa; let dsa;
let POOL3Contract; let POOL3Contract;
@ -62,19 +62,40 @@ describe.only("Ubiquity", function () {
let uadWhale; let uadWhale;
const bondingShareLpAmount = async function (address) { const bondingShare = async function (address) {
let LP = 0; let lpAmount = BigNumber.from(0);
const bondId = await BONDContract.holderTokens(address); let lpAmountTotal = BigNumber.from(0);
if (bondId.length){ let bondId = -1;
const bond = await BONDContract.getBond(bondId[0]);
LP = bond[5];
}
// console.log("LP", ethers.utils.formatEther(LP.toString()));
return LP;
}
beforeEach(async () => { const bondIds = await BONDContract.holderTokens(address);
await forkReset(blockFork); const bondN = bondIds?.length || 0;
if (bondN) {
for await (_bondId of bondIds) {
lpAmountTotal = lpAmountTotal.add((await BONDContract.getBond(_bondId))[5]);
}
bondId = Number(bondIds[bondN - 1]);
lpAmount = (await BONDContract.getBond(bondId))[5];
}
return { bondId, bondN, lpAmount, lpAmountTotal };
};
const depositAndGetOneBond = async function () {
await dsaDepositUAD3CRVf(100);
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "deposit",
args: [UAD3CRVF, one.mul(100), 1, 0, 0]
}
]),
uadWhaleAddress
);
};
before(async () => {
// await forkReset(blockFork);
[uadWhale] = await impersonate([uadWhaleAddress]); [uadWhale] = await impersonate([uadWhaleAddress]);
const [ethWhale] = await impersonate([ethWhaleAddress]); const [ethWhale] = await impersonate([ethWhaleAddress]);
@ -90,6 +111,7 @@ describe.only("Ubiquity", function () {
BONDContract = new ethers.Contract(BOND, ABI, uadWhale); BONDContract = new ethers.Contract(BOND, ABI, uadWhale);
dsa = (await buildDSAv2(uadWhaleAddress)).connect(uadWhale); dsa = (await buildDSAv2(uadWhaleAddress)).connect(uadWhale);
await sendEth(ethWhale, dsa.address, 100); await sendEth(ethWhale, dsa.address, 100);
await sendEth(ethWhale, uadWhaleAddress, 100);
instaIndex = new ethers.Contract(addresses.core.instaIndex, abis.core.instaIndex, ethWhale); instaIndex = new ethers.Contract(addresses.core.instaIndex, abis.core.instaIndex, ethWhale);
@ -100,19 +122,36 @@ describe.only("Ubiquity", function () {
instaConnectorsV2 = new ethers.Contract(addresses.core.connectorsV2, abis.core.connectorsV2); instaConnectorsV2 = new ethers.Contract(addresses.core.connectorsV2, abis.core.connectorsV2);
instaImplementationsMapping = await ethers.getContractAt(implementationsABI, implementationsMappingAddr); instaImplementationsMapping = await ethers.getContractAt(implementationsABI, implementationsMappingAddr);
InstaAccountV2DefaultImpl = await ethers.getContractFactory("InstaDefaultImplementation") InstaAccountV2DefaultImpl = await ethers.getContractFactory("InstaDefaultImplementation");
instaAccountV2DefaultImpl = await InstaAccountV2DefaultImpl.deploy(addresses.core.instaIndex); instaAccountV2DefaultImpl = await InstaAccountV2DefaultImpl.deploy(addresses.core.instaIndex);
await instaAccountV2DefaultImpl.deployed() await instaAccountV2DefaultImpl.deployed();
await (await instaImplementationsMapping.connect(master).setDefaultImplementation(instaAccountV2DefaultImpl.address)).wait(); await (
await instaImplementationsMapping.connect(master).setDefaultImplementation(instaAccountV2DefaultImpl.address)
).wait();
connector = await deployAndEnableConnector({ connector = await deployAndEnableConnector({
connectorName: ubiquityTest, connectorName: ubiquityTest,
contractArtifact: connectV2UbiquityArtifacts, contractArtifact: connectV2UbiquityArtifacts,
signer: master, signer: master,
connectors: instaConnectorsV2, connectors: instaConnectorsV2
}); });
}); });
const logAll = async function () {
console.log("dsa eth", utils.formatEther(await ethers.provider.getBalance(dsa.address)));
console.log("dsa dai", utils.formatEther(await DAIContract.balanceOf(dsa.address)));
console.log("dsa usdc", utils.formatUnits(await USDCContract.balanceOf(dsa.address), 6));
console.log("dsa usdt", utils.formatUnits(await USDTContract.balanceOf(dsa.address), 6));
console.log("dsa uad", utils.formatEther(await uADContract.balanceOf(dsa.address)));
console.log("dsa 3CRV", utils.formatEther(await CRV3Contract.balanceOf(dsa.address)));
console.log("dsa uad3CRV-f", utils.formatEther(await uAD3CRVfContract.balanceOf(dsa.address)));
const { bondId, bondN, lpAmount, lpAmountTotal } = await bondingShare(dsa.address);
console.log("dsa n bonds", utils.formatEther(lpAmountTotal), bondN);
console.log("dsa last bond", utils.formatEther(lpAmount), bondId);
};
afterEach(logAll);
const dsaDepositUAD3CRVf = async (amount) => { const dsaDepositUAD3CRVf = async (amount) => {
await uAD3CRVfContract.transfer(dsa.address, one.mul(amount)); await uAD3CRVfContract.transfer(dsa.address, one.mul(amount));
}; };
@ -128,17 +167,29 @@ describe.only("Ubiquity", function () {
}; };
const dsaDepositDAI = async (amount) => { const dsaDepositDAI = async (amount) => {
await uAD3CRVfContract.remove_liquidity_one_coin(one.mul(amount).mul(120).div(100), 1, one.mul(amount).mul(110).div(100)); await uAD3CRVfContract.remove_liquidity_one_coin(
one.mul(amount).mul(120).div(100),
1,
one.mul(amount).mul(110).div(100)
);
await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 0, one.mul(amount)); await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 0, one.mul(amount));
await DAIContract.transfer(dsa.address, one.mul(amount)); await DAIContract.transfer(dsa.address, one.mul(amount));
}; };
const dsaDepositUSDC = async (amount) => { const dsaDepositUSDC = async (amount) => {
await uAD3CRVfContract.remove_liquidity_one_coin(one.mul(amount).mul(120).div(100), 1, one.mul(amount).mul(110).div(100)); await uAD3CRVfContract.remove_liquidity_one_coin(
one.mul(amount).mul(120).div(100),
1,
one.mul(amount).mul(110).div(100)
);
await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 1, onep.mul(amount)); await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 1, onep.mul(amount));
await USDCContract.transfer(dsa.address, onep.mul(amount)); await USDCContract.transfer(dsa.address, onep.mul(amount));
}; };
const dsaDepositUSDT = async (amount) => { const dsaDepositUSDT = async (amount) => {
await uAD3CRVfContract.remove_liquidity_one_coin(one.mul(amount).mul(120).div(100), 1, one.mul(amount).mul(110).div(100)); await uAD3CRVfContract.remove_liquidity_one_coin(
one.mul(amount).mul(120).div(100),
1,
one.mul(amount).mul(110).div(100)
);
await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 2, onep.mul(amount)); await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 2, onep.mul(amount));
await USDTContract.transfer(dsa.address, onep.mul(amount)); await USDTContract.transfer(dsa.address, onep.mul(amount));
}; };
@ -184,113 +235,221 @@ describe.only("Ubiquity", function () {
}); });
}); });
describe("Main", function () { describe.only("Withdraw", function () {
let bondId = -1;
before(async () => {
await depositAndGetOneBond();
await depositAndGetOneBond();
await depositAndGetOneBond();
await depositAndGetOneBond();
await depositAndGetOneBond();
await depositAndGetOneBond();
({ bondId } = await bondingShare(dsa.address));
await logAll();
console.log("Mining 50 000 blocks for more than one week, please wait...");
await mineNBlock(50000);
});
it("Should deposit and withdraw DAI", async function () {
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "withdraw",
args: [bondId, DAI, 0, 0]
}
]),
uadWhaleAddress
)
).to.be.not.reverted;
});
it("Should deposit and withdraw USDC", async function () {
// await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "withdraw",
args: [bondId - 1, USDC, 0, 0]
}
]),
uadWhaleAddress
);
// ).to.be.not.reverted;
});
it("Should deposit and withdraw USDT", async function () {
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "withdraw",
args: [bondId - 2, USDT, 0, 0]
}
]),
uadWhaleAddress
)
).to.be.not.reverted;
});
it("Should deposit and withdraw UAD", async function () {
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "withdraw",
args: [bondId - 3, UAD, 0, 0]
}
]),
uadWhaleAddress
)
).to.be.not.reverted;
});
it("Should deposit and withdraw CRV3", async function () {
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "withdraw",
args: [bondId - 4, CRV3, 0, 0]
}
]),
uadWhaleAddress
)
).to.be.not.reverted;
});
it("Should deposit and withdraw UAD3CRVF", async function () {
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "withdraw",
args: [bondId - 5, UAD3CRVF, 0, 0]
}
]),
uadWhaleAddress
)
).to.be.not.reverted;
});
});
describe("Deposit", function () {
it("should deposit uAD3CRVf to get Ubiquity Bonding Shares", async function () { it("should deposit uAD3CRVf to get Ubiquity Bonding Shares", async function () {
await dsaDepositUAD3CRVf(100); await dsaDepositUAD3CRVf(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.equal(0);
await expect( await expect(
dsa.cast( dsa.cast(
...encodeSpells([ ...encodeSpells([
{ {
connector: ubiquityTest, connector: ubiquityTest,
method: "deposit", method: "deposit",
args: [UAD3CRVF, one, 4, 0, 0], args: [UAD3CRVF, one, 4, 0, 0]
}, }
]), ]),
uadWhaleAddress uadWhaleAddress
) )
).to.be.not.reverted; ).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0);
}); });
it("should deposit uAD to get Ubiquity Bonding Shares", async function () { it("should deposit uAD to get Ubiquity Bonding Shares", async function () {
await dsaDepositUAD(100); await dsaDepositUAD(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.equal(0);
await expect( await expect(
dsa.cast( dsa.cast(
...encodeSpells([ ...encodeSpells([
{ {
connector: ubiquityTest, connector: ubiquityTest,
method: "deposit", method: "deposit",
args: [UAD, one, 4, 0, 0], args: [UAD, one, 4, 0, 0]
}, }
]), ]),
uadWhaleAddress uadWhaleAddress
) )
).to.be.not.reverted; ).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0);
}); });
it("should deposit 3CRV to get Ubiquity Bonding Shares", async function () { it("should deposit 3CRV to get Ubiquity Bonding Shares", async function () {
await dsaDepositCRV3(100); await dsaDepositCRV3(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.equal(0);
await expect( await expect(
dsa.cast( dsa.cast(
...encodeSpells([ ...encodeSpells([
{ {
connector: ubiquityTest, connector: ubiquityTest,
method: "deposit", method: "deposit",
args: [CRV3, one, 4, 0, 0], args: [CRV3, one, 4, 0, 0]
}, }
]), ]),
uadWhaleAddress uadWhaleAddress
) )
).to.be.not.reverted; ).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0);
}); });
it("should deposit DAI to get Ubiquity Bonding Shares", async function () { it("should deposit DAI to get Ubiquity Bonding Shares", async function () {
await dsaDepositDAI(100); await dsaDepositDAI(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.equal(0);
await expect( await expect(
dsa.cast( dsa.cast(
...encodeSpells([ ...encodeSpells([
{ {
connector: ubiquityTest, connector: ubiquityTest,
method: "deposit", method: "deposit",
args: [DAI, one.mul(100), 4, 0, 0], args: [DAI, one.mul(100), 4, 0, 0]
}, }
]), ]),
uadWhaleAddress uadWhaleAddress
) )
).to.be.not.reverted; ).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0);
}); });
it("should deposit USDC to get Ubiquity Bonding Shares", async function () { it("should deposit USDC to get Ubiquity Bonding Shares", async function () {
await dsaDepositUSDC(100); await dsaDepositUSDC(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.equal(0);
await expect( await expect(
dsa.cast( dsa.cast(
...encodeSpells([ ...encodeSpells([
{ {
connector: ubiquityTest, connector: ubiquityTest,
method: "deposit", method: "deposit",
args: [USDC, onep.mul(100), 4, 0, 0], args: [USDC, onep.mul(100), 4, 0, 0]
}, }
]), ]),
uadWhaleAddress uadWhaleAddress
) )
).to.be.not.reverted; ).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0);
}); });
it("should deposit USDT to get Ubiquity Bonding Shares", async function () { it("should deposit USDT to get Ubiquity Bonding Shares", async function () {
await dsaDepositUSDT(100); await dsaDepositUSDT(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.equal(0);
await expect( await expect(
dsa.cast( dsa.cast(
...encodeSpells([ ...encodeSpells([
{ {
connector: ubiquityTest, connector: ubiquityTest,
method: "deposit", method: "deposit",
args: [USDT, onep.mul(100), 4, 0, 0], args: [USDT, onep.mul(100), 4, 0, 0]
}, }
]), ]),
uadWhaleAddress uadWhaleAddress
) )
).to.be.not.reverted; ).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0); expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0);
}); });
}); });

View File

@ -8,17 +8,49 @@ async function forkReset(blockNumber) {
{ {
forking: { forking: {
jsonRpcUrl: hardhatConfig.networks.hardhat.forking.url, jsonRpcUrl: hardhatConfig.networks.hardhat.forking.url,
blockNumber, blockNumber
}, }
}, }
], ]
});
}
async function sendEth(from, to, amount) {
await from.sendTransaction({
to: to,
value: ethers.BigNumber.from(10).pow(18).mul(amount),
}); });
} }
module.exports = { forkReset, sendEth }; async function mineBlock(timestamp) {
await network.provider.request({
method: "evm_mine",
params: [timestamp]
});
}
async function sendEth(from, to, amount) {
await from.sendTransaction({
to: to,
value: ethers.BigNumber.from(10).pow(18).mul(amount)
});
}
async function mineNBlock(blockCount, secondsBetweenBlock) {
const blockBefore = await ethers.provider.getBlock("latest");
const maxMinedBlockPerBatch = 1000;
let blockToMine = blockCount;
let blockTime = blockBefore.timestamp;
while (blockToMine > maxMinedBlockPerBatch) {
// eslint-disable-next-line @typescript-eslint/no-loop-func
const minings = [...Array(maxMinedBlockPerBatch).keys()].map((_v, i) => {
const newTs = blockTime + i + (secondsBetweenBlock || 1);
return mineBlock(newTs);
});
// eslint-disable-next-line no-await-in-loop
await Promise.all(minings);
blockToMine -= maxMinedBlockPerBatch;
blockTime = blockTime + maxMinedBlockPerBatch - 1 + maxMinedBlockPerBatch * (secondsBetweenBlock || 1);
}
const minings = [...Array(blockToMine).keys()].map((_v, i) => {
const newTs = blockTime + i + (secondsBetweenBlock || 1);
return mineBlock(newTs);
});
// eslint-disable-next-line no-await-in-loop
await Promise.all(minings);
}
module.exports = { forkReset, sendEth, mineNBlock };