ubiquity connector after rebase main

This commit is contained in:
zapaz.eth 2021-10-11 18:47:51 +02:00
parent fd5e2dcba8
commit a12320f8fd
7 changed files with 567 additions and 0 deletions

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
contract Events {
event Deposit(
address indexed userAddress,
address indexed token,
uint256 amount,
uint256 lpAmount,
uint256 durationWeeks,
uint256 indexed bondingShareId,
uint256 getId,
uint256 setId
);
}

View File

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

View File

@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
import {TokenInterface} from "../../common/interfaces.sol";
interface IUbiquityBondingV2 {
function deposit(uint256 lpAmount, uint256 durationWeeks)
external
returns (uint256 bondingShareId);
}
interface IUbiquityMetaPool {
function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)
external
returns (uint256);
}
interface IUbiquity3Pool {
function add_liquidity(
uint256[3] calldata _amounts,
uint256 _min_mint_amount
) external;
}
interface IUbiquityAlgorithmicDollarManager {
function dollarTokenAddress() external returns (address);
function stableSwapMetaPoolAddress() external returns (address);
function bondingContractAddress() external returns (address);
}

View File

@ -0,0 +1,137 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma abicoder v2;
/**
* @title Ubiquity.
* @dev Ubiquity Dollar (uAD).
*/
import {TokenInterface, MemoryInterface} from "../../common/interfaces.sol";
import {Stores} from "../../common/stores.sol";
import {SafeMath} from "../../common/math.sol";
import {IUbiquityBondingV2, IUbiquityMetaPool, IUbiquity3Pool, IUbiquityAlgorithmicDollarManager} from "./interfaces.sol";
import {Helpers} from "./helpers.sol";
import {Events} from "./events.sol";
contract ConnectV2Ubiquity is Helpers, Events {
string public constant name = "Ubiquity-v1";
/**
* @dev Deposit into Ubiquity protocol
* @notice 3POOL (DAI / USDC / USDT) => METAPOOL (3CRV / uAD) => uAD3CRV-f => Ubiquity BondingShare
* @notice STEP 1 : 3POOL (DAI / USDC / USDT) => 3CRV
* @notice STEP 2 : METAPOOL(3CRV / UAD) => uAD3CRV-f
* @notice STEP 3 : uAD3CRV-f => Ubiquity BondingShare
* @param token Token deposited : DAI, USDC, USDT, 3CRV, uAD or uAD3CRV-f
* @param amount Amount of tokens to deposit (For max: `uint256(-1)`)
* @param durationWeeks Duration in weeks tokens will be locked (4-208)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens deposited.
*/
function deposit(
address token,
uint256 amount,
uint256 durationWeeks,
uint256 getId,
uint256 setId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
address UAD = IUbiquityAlgorithmicDollarManager(
UbiquityAlgorithmicDollarManager
).dollarTokenAddress();
address UAD3CRVf = IUbiquityAlgorithmicDollarManager(
UbiquityAlgorithmicDollarManager
).stableSwapMetaPoolAddress();
require(
token == DAI ||
token == USDC ||
token == USDT ||
token == UAD ||
token == CRV3 ||
token == UAD3CRVf,
"Invalid token: must be DAI, USDC, USDT, uAD, 3CRV or uAD3CRV-f"
);
uint256 _amount = getUint(getId, amount);
uint256 _lpAmount;
// Full balance if amount = -1
if (_amount == uint256(-1)) {
_amount = getTokenBal(TokenInterface(token));
}
// STEP 1 : SwapTo3CRV : Deposit DAI, USDC or USDT into 3Pool to get 3Crv LPs
if (token == DAI || token == USDC || token == USDT) {
uint256[3] memory amounts1;
if (token == DAI) amounts1[0] = _amount;
else if (token == USDC) amounts1[1] = _amount;
else if (token == USDT) amounts1[2] = _amount;
approve(TokenInterface(token), Pool3, _amount);
IUbiquity3Pool(Pool3).add_liquidity(amounts1, 0);
}
// STEP 2 : ProvideLiquidityToMetapool : Deposit in uAD3CRV pool to get uAD3CRV-f LPs
if (
token == DAI ||
token == USDC ||
token == USDT ||
token == UAD ||
token == CRV3
) {
uint256[2] memory amounts2;
address token2 = token;
uint256 _amount2;
if (token == UAD) {
_amount2 = _amount;
amounts2[0] = _amount2;
} else {
if (token == CRV3) {
_amount2 = _amount;
} else {
token2 = CRV3;
_amount2 = getTokenBal(TokenInterface(token2));
}
amounts2[1] = _amount2;
}
approve(TokenInterface(token2), UAD3CRVf, _amount2);
_lpAmount = IUbiquityMetaPool(UAD3CRVf).add_liquidity(amounts2, 0);
}
// STEP 3 : Farm/ApeIn : Deposit uAD3CRV-f LPs into UbiquityBondingV2 and get Ubiquity Bonding Shares
if (token == UAD3CRVf) {
_lpAmount = _amount;
}
address Bonding = IUbiquityAlgorithmicDollarManager(
UbiquityAlgorithmicDollarManager
).bondingContractAddress();
approve(TokenInterface(UAD3CRVf), Bonding, _lpAmount);
uint256 bondingShareId = IUbiquityBondingV2(Bonding).deposit(
_lpAmount,
durationWeeks
);
setUint(setId, bondingShareId);
_eventName = "Deposit(address,address,uint256,uint256,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(
address(this),
token,
amount,
_lpAmount,
durationWeeks,
bondingShareId,
getId,
setId
);
}
}

View File

@ -65,6 +65,10 @@ module.exports = {
blockNumber: 12696000, blockNumber: 12696000,
}, },
blockGasLimit: 12000000, blockGasLimit: 12000000,
gasPrice: parseInt(utils.parseUnits("300", "gwei")),
},
local: {
url: "http://127.0.0.1:8545",
}, },
matic: { matic: {
url: "https://rpc-mainnet.maticvigil.com/", url: "https://rpc-mainnet.maticvigil.com/",

View File

@ -0,0 +1,316 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { waffle, ethers } = hre;
const { provider } = waffle;
const { BigNumber } = ethers;
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js");
const buildDSAv2 = require("../../scripts/buildDSAv2");
const encodeSpells = require("../../scripts/encodeSpells");
const addresses = require("../../scripts/constant/addresses");
const abis = require("../../scripts/constant/abis");
const impersonate = require("../../scripts/impersonate");
const { forkReset, sendEth } = require("./utils");
const connectV2UbiquityArtifacts = require("../../artifacts/contracts/mainnet/connectors/ubiquity/main.sol/ConnectV2Ubiquity.json");
const { abi: implementationsABI } = require("../../scripts/constant/abi/core/InstaImplementations.json")
const implementationsMappingAddr = "0xCBA828153d3a85b30B5b912e1f2daCac5816aE9D"
describe.only("Ubiquity", function () {
const ubiquityTest = "UBIQUITY-TEST-A";
const BOND = "0x2dA07859613C14F6f05c97eFE37B9B4F212b5eF5";
const UAD = "0x0F644658510c95CB46955e55D7BA9DDa9E9fBEc6";
const DAI = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
const CRV3 = "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490";
const POOL3 = "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7";
const UAD3CRVF = "0x20955CB69Ae1515962177D164dfC9522feef567E";
const ethWhaleAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
const uadWhaleAddress = "0xefC0e701A824943b469a694aC564Aa1efF7Ab7dd";
const blockFork = 13097100;
const one = BigNumber.from(10).pow(18);
const onep = BigNumber.from(10).pow(6);
const ABI = [
"function balanceOf(address owner) view returns (uint256)",
"function allowance(address owner, address spender) external view returns (uint256)",
"function transfer(address to, uint amount) returns (boolean)",
"function remove_liquidity_one_coin(uint256 _burn_amount, int128 i, uint256 _min_received) external returns (uint256)",
"function add_liquidity(uint256[3],uint256) returns (uint256)",
"function approve(address, uint256) external",
"function holderTokens(address) view returns (uint256[])",
"function getBond(uint256) view returns (tuple(address,uint256,uint256,uint256,uint256,uint256))",
];
let dsa;
let POOL3Contract;
let CRV3Contract;
let uAD3CRVfContract;
let uADContract;
let DAIContract;
let USDCContract;
let USDTContract;
let BONDContract;
let instaIndex;
let instaConnectorsV2;
let connector;
let instaImplementationsMapping;
let InstaAccountV2DefaultImpl;
let uadWhale;
const bondingShareLpAmount = async function (address) {
let LP = 0;
const bondId = await BONDContract.holderTokens(address);
if (bondId.length){
const bond = await BONDContract.getBond(bondId[0]);
LP = bond[5];
}
// console.log("LP", ethers.utils.formatEther(LP.toString()));
return LP;
}
beforeEach(async () => {
await forkReset(blockFork);
[uadWhale] = await impersonate([uadWhaleAddress]);
const [ethWhale] = await impersonate([ethWhaleAddress]);
await sendEth(ethWhale, uadWhaleAddress, 100);
POOL3Contract = new ethers.Contract(POOL3, ABI, uadWhale);
CRV3Contract = new ethers.Contract(CRV3, ABI, uadWhale);
uAD3CRVfContract = new ethers.Contract(UAD3CRVF, ABI, uadWhale);
uADContract = new ethers.Contract(UAD, ABI, uadWhale);
DAIContract = new ethers.Contract(DAI, ABI, uadWhale);
USDCContract = new ethers.Contract(USDC, ABI, uadWhale);
USDTContract = new ethers.Contract(USDT, ABI, uadWhale);
BONDContract = new ethers.Contract(BOND, ABI, uadWhale);
dsa = (await buildDSAv2(uadWhaleAddress)).connect(uadWhale);
await sendEth(ethWhale, dsa.address, 100);
instaIndex = new ethers.Contract(addresses.core.instaIndex, abis.core.instaIndex, ethWhale);
const masterAddress = await instaIndex.master();
const [master] = await impersonate([masterAddress]);
await sendEth(ethWhale, masterAddress, 100);
instaConnectorsV2 = new ethers.Contract(addresses.core.connectorsV2, abis.core.connectorsV2);
instaImplementationsMapping = await ethers.getContractAt(implementationsABI, implementationsMappingAddr);
InstaAccountV2DefaultImpl = await ethers.getContractFactory("InstaDefaultImplementation")
instaAccountV2DefaultImpl = await InstaAccountV2DefaultImpl.deploy(addresses.core.instaIndex);
await instaAccountV2DefaultImpl.deployed()
await (await instaImplementationsMapping.connect(master).setDefaultImplementation(instaAccountV2DefaultImpl.address)).wait();
connector = await deployAndEnableConnector({
connectorName: ubiquityTest,
contractArtifact: connectV2UbiquityArtifacts,
signer: master,
connectors: instaConnectorsV2,
});
});
const dsaDepositUAD3CRVf = async (amount) => {
await uAD3CRVfContract.transfer(dsa.address, one.mul(amount));
};
const dsaDepositUAD = async (amount) => {
await uAD3CRVfContract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 0, one.mul(amount));
await uADContract.transfer(dsa.address, one.mul(amount));
};
const dsaDepositCRV3 = async (amount) => {
await uAD3CRVfContract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 1, one.mul(amount));
await CRV3Contract.transfer(dsa.address, one.mul(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 POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 0, one.mul(amount));
await DAIContract.transfer(dsa.address, one.mul(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 POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 1, onep.mul(amount));
await USDCContract.transfer(dsa.address, onep.mul(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 POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 2, onep.mul(amount));
await USDTContract.transfer(dsa.address, onep.mul(amount));
};
describe("DSA wallet setup", function () {
it("Should have contracts deployed.", async function () {
expect(POOL3Contract.address).to.be.properAddress;
expect(CRV3Contract.address).to.be.properAddress;
expect(uADContract.address).to.be.properAddress;
expect(uAD3CRVfContract.address).to.be.properAddress;
expect(DAIContract.address).to.be.properAddress;
expect(USDCContract.address).to.be.properAddress;
expect(USDTContract.address).to.be.properAddress;
expect(BONDContract.address).to.be.properAddress;
expect(instaIndex.address).to.be.properAddress;
expect(instaConnectorsV2.address).to.be.properAddress;
expect(connector.address).to.be.properAddress;
expect(dsa.address).to.be.properAddress;
});
it("Should deposit uAD3CRVf into DSA wallet", async function () {
await dsaDepositUAD3CRVf(100);
expect(await uAD3CRVfContract.balanceOf(dsa.address)).to.be.gte(one.mul(100));
});
it("Should deposit uAD into DSA wallet", async function () {
await dsaDepositUAD(100);
expect(await uADContract.balanceOf(dsa.address)).to.be.gte(one.mul(100));
});
it("Should deposit 3CRV into DSA wallet", async function () {
await dsaDepositCRV3(100);
expect(await CRV3Contract.balanceOf(dsa.address)).to.be.gte(one.mul(100));
});
it("Should deposit DAI into DSA wallet", async function () {
await dsaDepositDAI(100);
expect(await DAIContract.balanceOf(dsa.address)).to.be.gte(one.mul(100));
});
it("Should deposit USDC into DSA wallet", async function () {
await dsaDepositUSDC(100);
expect(await USDCContract.balanceOf(dsa.address)).to.be.gte(onep.mul(100));
});
it("Should deposit USDT into DSA wallet", async function () {
await dsaDepositUSDT(100);
expect(await USDTContract.balanceOf(dsa.address)).to.be.gte(onep.mul(100));
});
});
describe("Main", function () {
it("should deposit uAD3CRVf to get Ubiquity Bonding Shares", async function () {
await dsaDepositUAD3CRVf(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0);
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "deposit",
args: [UAD3CRVF, one, 4, 0, 0],
},
]),
uadWhaleAddress
)
).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0);
});
it("should deposit uAD to get Ubiquity Bonding Shares", async function () {
await dsaDepositUAD(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0);
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "deposit",
args: [UAD, one, 4, 0, 0],
},
]),
uadWhaleAddress
)
).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0);
});
it("should deposit 3CRV to get Ubiquity Bonding Shares", async function () {
await dsaDepositCRV3(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0);
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "deposit",
args: [CRV3, one, 4, 0, 0],
},
]),
uadWhaleAddress
)
).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0);
});
it("should deposit DAI to get Ubiquity Bonding Shares", async function () {
await dsaDepositDAI(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0);
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "deposit",
args: [DAI, one.mul(100), 4, 0, 0],
},
]),
uadWhaleAddress
)
).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0);
});
it("should deposit USDC to get Ubiquity Bonding Shares", async function () {
await dsaDepositUSDC(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0);
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "deposit",
args: [USDC, onep.mul(100), 4, 0, 0],
},
]),
uadWhaleAddress
)
).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0);
});
it("should deposit USDT to get Ubiquity Bonding Shares", async function () {
await dsaDepositUSDT(100);
expect(await bondingShareLpAmount(dsa.address)).to.be.equal(0);
await expect(
dsa.cast(
...encodeSpells([
{
connector: ubiquityTest,
method: "deposit",
args: [USDT, onep.mul(100), 4, 0, 0],
},
]),
uadWhaleAddress
)
).to.be.not.reverted;
expect(await bondingShareLpAmount(dsa.address)).to.be.gt(0);
});
});
describe("3Pool test", function () {
it("Should add DAI liquidity to 3Pool", async function () {
const n = 100;
await dsaDepositDAI(n);
const amount = one.mul(n);
const [dsaSigner] = await impersonate([dsa.address]);
expect(await DAIContract.balanceOf(dsa.address)).to.be.equal(amount);
expect(await CRV3Contract.balanceOf(dsa.address)).to.be.equal(0);
await (await DAIContract.connect(dsaSigner).approve(POOL3, amount)).wait();
await (await POOL3Contract.connect(dsaSigner).add_liquidity([amount, 0, 0], amount.mul(98).div(100))).wait();
expect(await DAIContract.balanceOf(dsa.address)).to.be.equal(0);
expect(await CRV3Contract.balanceOf(dsa.address))
.to.be.gte(amount.mul(98).div(100))
.to.be.lte(amount.mul(102).div(100));
});
});
});

24
test/ubiquity/utils.js Normal file
View File

@ -0,0 +1,24 @@
const hre = require("hardhat");
const hardhatConfig = require("../../hardhat.config");
async function forkReset(blockNumber) {
await hre.network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: hardhatConfig.networks.hardhat.forking.url,
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 };