Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

[BAC-119] Use Uniswap V2 and Curve in DAI-USD oracle #498

Merged
merged 13 commits into from
Aug 28, 2020
10 changes: 9 additions & 1 deletion __tests__/modules/TestContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { TestPriceOracle } from '../../build/testing_wrappers/TestPriceOracle';
import { TestMakerOracle } from '../../build/testing_wrappers/TestMakerOracle';
import { TestCurve } from '../../build/testing_wrappers/TestCurve';
import { TestUniswapV2Pair } from '../../build/testing_wrappers/TestUniswapV2Pair';
import { TestUniswapV2Pair2 } from '../../build/testing_wrappers/TestUniswapV2Pair2';
import { TestInterestSetter } from '../../build/testing_wrappers/TestInterestSetter';
import { TestPolynomialInterestSetter }
from '../../build/testing_wrappers/TestPolynomialInterestSetter';
Expand All @@ -53,6 +54,7 @@ import testPriceOracleJson from '../../build/testing_contracts/TestPriceOracle.j
import testMakerOracleJson from '../../build/testing_contracts/TestMakerOracle.json';
import testCurveJson from '../../build/testing_contracts/TestCurve.json';
import testUniswapV2PairJson from '../../build/testing_contracts/TestUniswapV2Pair.json';
import testUniswapV2Pair2Json from '../../build/testing_contracts/TestUniswapV2Pair2.json';
import testPolynomialInterestSetterJson
from '../../build/testing_contracts/TestPolynomialInterestSetter.json';
import testDoubleExponentInterestSetterJson
Expand Down Expand Up @@ -83,6 +85,7 @@ export class TestContracts extends Contracts {
public testMakerOracle: TestMakerOracle;
public testCurve: TestCurve;
public testUniswapV2Pair: TestUniswapV2Pair;
public testUniswapV2Pair2: TestUniswapV2Pair2;
public testPolynomialInterestSetter: TestPolynomialInterestSetter;
public testDoubleExponentInterestSetter: TestDoubleExponentInterestSetter;
public testInterestSetter: TestInterestSetter;
Expand Down Expand Up @@ -115,7 +118,10 @@ export class TestContracts extends Contracts {
this.testCurve = new this.web3.eth.Contract(testCurveJson.abi) as TestCurve;
this.testUniswapV2Pair = new this.web3.eth.Contract(
testUniswapV2PairJson.abi,
) as TestUniswapV2Pair;
) as TestUniswapV2Pair;
this.testUniswapV2Pair2 = new this.web3.eth.Contract(
testUniswapV2Pair2Json.abi,
) as TestUniswapV2Pair2;
this.testInterestSetter = new this.web3.eth.Contract(
testInterestSetterJson.abi) as TestInterestSetter;
this.testPolynomialInterestSetter = new this.web3.eth.Contract(
Expand Down Expand Up @@ -157,6 +163,7 @@ export class TestContracts extends Contracts {
{ contract: this.testMakerOracle, json: testMakerOracleJson },
{ contract: this.testCurve, json: testCurveJson },
{ contract: this.testUniswapV2Pair, json: testUniswapV2PairJson },
{ contract: this.testUniswapV2Pair2, json: testUniswapV2Pair2Json },
{ contract: this.testPolynomialInterestSetter, json: testPolynomialInterestSetterJson },
{ contract: this.testDoubleExponentInterestSetter,
json: testDoubleExponentInterestSetterJson },
Expand Down Expand Up @@ -198,6 +205,7 @@ export class TestContracts extends Contracts {
this.testMakerOracle.options.from = account;
this.testCurve.options.from = account;
this.testUniswapV2Pair.options.from = account;
this.testUniswapV2Pair2.options.from = account;
this.testPolynomialInterestSetter.options.from = account;
this.testDoubleExponentInterestSetter.options.from = account;
this.testInterestSetter.options.from = account;
Expand Down
133 changes: 47 additions & 86 deletions __tests__/oracles/DaiPriceOracle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ let poker: address;
let rando: address;
let marketMaker: address;
const defaultPrice = new BigNumber('1e18');
const defaultEthPrice = new BigNumber('1e20');

describe('DaiPriceOracle', () => {
let snapshotId: string;
Expand All @@ -34,7 +33,6 @@ describe('DaiPriceOracle', () => {
const tokenAmount = new BigNumber('1e19');
await Promise.all([
solo.oracle.daiPriceOracle.setPokerAddress(poker, { from: admin }),
setEthPrice(defaultEthPrice, true),
solo.testing.tokenB.issueTo(tokenAmount, marketMaker),
solo.weth.wrap(marketMaker, tokenAmount),
]);
Expand Down Expand Up @@ -116,17 +114,6 @@ describe('DaiPriceOracle', () => {
);
});

it('Does not update price when stale ETH price', async () => {
await Promise.all([
setEthPrice(defaultEthPrice, false),
setCurvePrice('0.99'),
setUniswapPrice('1.00'),
]);
await expectThrow(
updatePrice(),
);
});

it('Updates timestamp correctly', async () => {
await Promise.all([
setCurvePrice('0.99'),
Expand Down Expand Up @@ -319,20 +306,6 @@ describe('DaiPriceOracle', () => {
});
});

describe('getMedianizerPrice', () => {
it('Fails for stale ETH price', async () => {
await setEthPrice(defaultEthPrice, false);
await expectThrow(
solo.oracle.daiPriceOracle.getMedianizerPrice(),
);
});

it('Succeeds for normal eth price', async () => {
const price = await solo.oracle.daiPriceOracle.getMedianizerPrice();
expect(price).toEqual(defaultEthPrice);
});
});

describe('getCurvePrice', () => {
it('Returns the price, adjusting for the fee', async () => {
await setCurvePrice('1.05');
Expand All @@ -351,61 +324,44 @@ describe('DaiPriceOracle', () => {
);
});

it('Gets the right price for DAI = ETH', async () => {
const ethAmt = new BigNumber(4444);
const daiAmt = new BigNumber(4444);
await setUniswapBalances(ethAmt, daiAmt);
it('Gets the right price for ETH-DAI = ETH-USDC', async () => {
await setUniswapPrice(1);
const price = await solo.oracle.daiPriceOracle.getUniswapPrice();
expect(price).toEqual(defaultEthPrice);
expect(price).toEqual(defaultPrice);
});

it('Gets the right price for ETH > DAI', async () => {
const ethAmt = new BigNumber(4321);
const daiAmt = new BigNumber(1234);
await setUniswapBalances(ethAmt, daiAmt);
it('Gets the right price for ETH-DAI > ETH-USDC', async () => {
await setUniswapPrice(0.975);
const price = await solo.oracle.daiPriceOracle.getUniswapPrice();
const expected = defaultEthPrice.times(ethAmt).div(daiAmt).integerValue(BigNumber.ROUND_DOWN);
expect(price).toEqual(expected);
expect(price).toEqual(new BigNumber(0.975).shiftedBy(18));
});

it('Gets the right price for DAI > ETH', async () => {
const ethAmt = new BigNumber(1234);
const daiAmt = new BigNumber(4321);
await setUniswapBalances(ethAmt, daiAmt);
it('Gets the right price for ETH-DAI < ETH-USDC', async () => {
await setUniswapPrice(1.025);
const price = await solo.oracle.daiPriceOracle.getUniswapPrice();
const expected = defaultEthPrice.times(ethAmt).div(daiAmt).integerValue(BigNumber.ROUND_DOWN);
expect(price).toEqual(expected);
expect(price).toEqual(new BigNumber(1.025).shiftedBy(18));
});

it('Gets the right price for different ETH prices', async () => {
const ethAmt = new BigNumber(1500);
const daiAmt = new BigNumber(400000);
await setUniswapBalances(ethAmt, daiAmt);
const price1 = defaultPrice;
const price2 = defaultPrice.times(2);
const [daiPrice1, daiPrice2] = await Promise.all([
solo.oracle.daiPriceOracle.getUniswapPrice(price1),
solo.oracle.daiPriceOracle.getUniswapPrice(price2),
it('Does not overflow when the pools hold on the order of $100B in value', async () => {
await Promise.all([
// Suppose ETH has a price of $1.
setUniswapEthDaiBalances(
new BigNumber(100e9).shiftedBy(18), // ethAmt
new BigNumber(100e9).shiftedBy(18), // daiAmt
),
setUniswapEthUsdcBalances(
new BigNumber(100e9).shiftedBy(18), // ethAmt
new BigNumber(100e9).shiftedBy(6), // usdcAmt
),
]);
expect(new BigNumber(daiPrice2)).toEqual(new BigNumber(daiPrice1).times(2));
const price = await solo.oracle.daiPriceOracle.getUniswapPrice();
expect(price).toEqual(defaultPrice);
});
});
});

// ============ Helper Functions ============

async function setEthPrice(
price: BigNumber,
valid: boolean,
) {
await solo.contracts.send(
solo.contracts.testMakerOracle.methods.setValues(
price.toFixed(0),
valid,
),
);
}

async function setCurvePrice(
price: BigNumberable,
) {
Expand All @@ -432,36 +388,41 @@ async function setCurvePrice(
async function setUniswapPrice(
price: BigNumberable,
) {
await setUniswapBalances(
new BigNumber('1e18'),
getNumberFromPrice('1e18', price),
);
const ethPrice = new BigNumber(100);
await Promise.all([
// Apply an arbitrary constant factor to the balances of each pool.
setUniswapEthDaiBalances(
new BigNumber(1).times(1.23).shiftedBy(18), // ethAmt
ethPrice.times(1.23).shiftedBy(18), // daiAmt
),
setUniswapEthUsdcBalances(
new BigNumber(1).times(2.34).shiftedBy(18), // ethAmt
ethPrice.times(price).times(2.34).shiftedBy(6), // usdcAmt
),
]);
}

async function setUniswapBalances(
ethAmt: BigNumber,
daiAmt: BigNumber,
async function setUniswapEthDaiBalances(
ethAmt: BigNumberable,
daiAmt: BigNumberable,
) {
await solo.contracts.send(
solo.contracts.testUniswapV2Pair.methods.setReserves(
daiAmt.toFixed(0),
ethAmt.toFixed(0),
new BigNumber(daiAmt).toFixed(0),
new BigNumber(ethAmt).toFixed(0),
),
);
}

function getNumberFromPrice(
multiplier: BigNumberable,
price: BigNumberable,
async function setUniswapEthUsdcBalances(
ethAmt: BigNumberable,
usdcAmt: BigNumberable,
) {
return new BigNumber(multiplier).times(
defaultEthPrice,
).div(
defaultPrice,
).div(
price,
).integerValue(
BigNumber.ROUND_DOWN,
await solo.contracts.send(
solo.contracts.testUniswapV2Pair2.methods.setReserves(
new BigNumber(usdcAmt).toFixed(0),
new BigNumber(ethAmt).toFixed(0),
),
);
}

Expand Down
Loading