Skip to content

Commit a0be56e

Browse files
author
Joey Santoro
committed
router
1 parent 1d95312 commit a0be56e

File tree

7 files changed

+408
-1
lines changed

7 files changed

+408
-1
lines changed

contracts/mock/MockUniswapIncentive.sol

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pragma solidity ^0.6.0;
22
pragma experimental ABIEncoderV2;
33

44
import "./MockIncentive.sol";
5+
import "../external/Decimal.sol";
56

67
contract MockUniswapIncentive is MockIncentive {
78

@@ -10,6 +11,7 @@ contract MockUniswapIncentive is MockIncentive {
1011
public {}
1112

1213
bool isParity = false;
14+
bool isExempt = false;
1315

1416
function isIncentiveParity() external view returns (bool) {
1517
return isParity;
@@ -19,5 +21,32 @@ contract MockUniswapIncentive is MockIncentive {
1921
isParity = _isParity;
2022
}
2123

24+
function isExemptAddress(address account) public returns (bool) {
25+
return isExempt;
26+
}
27+
28+
function setExempt(bool exempt) public {
29+
isExempt = exempt;
30+
}
31+
32+
function updateOracle() external returns(bool) {
33+
return true;
34+
}
35+
2236
function setExemptAddress(address account, bool isExempt) external {}
37+
38+
function getBuyIncentive(uint amount) external returns(uint,
39+
uint32 weight,
40+
Decimal.D256 memory initialDeviation,
41+
Decimal.D256 memory finalDeviation
42+
) {
43+
return (amount * 10 / 100, weight, initialDeviation, finalDeviation);
44+
}
45+
46+
function getSellPenalty(uint amount) external returns(uint,
47+
Decimal.D256 memory initialDeviation,
48+
Decimal.D256 memory finalDeviation)
49+
{
50+
return (amount * 10 / 100, initialDeviation, finalDeviation);
51+
}
2352
}

contracts/mock/MockWeth.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,9 @@ contract MockWeth is MockERC20 {
99
function deposit() external payable {
1010
mint(msg.sender, msg.value);
1111
}
12+
13+
function withdraw(uint amount) external payable {
14+
_burn(msg.sender, amount);
15+
_msgSender().transfer(amount);
16+
}
1217
}

contracts/router/FeiRouter.sol

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
pragma solidity ^0.6.0;
2+
pragma experimental ABIEncoderV2;
3+
4+
import "./UniswapSingleEthRouter.sol";
5+
import "../refs/IOracleRef.sol";
6+
import "../token/IUniswapIncentive.sol";
7+
8+
contract FeiRouter is UniswapSingleEthRouter {
9+
10+
IUniswapIncentive public immutable INCENTIVE;
11+
constructor(
12+
address pair,
13+
address weth,
14+
address incentive
15+
) public UniswapSingleEthRouter(pair, weth) {
16+
INCENTIVE = IUniswapIncentive(incentive);
17+
}
18+
19+
function buyFei(
20+
uint minReward,
21+
uint amountOutMin,
22+
address to,
23+
uint deadline
24+
) external payable {
25+
IOracleRef(address(INCENTIVE)).updateOracle();
26+
27+
uint reward = 0;
28+
if (!INCENTIVE.isExemptAddress(to)) {
29+
(reward,,,) = INCENTIVE.getBuyIncentive(amountOutMin);
30+
}
31+
require(reward >= minReward, "FeiRouter: Not enough reward");
32+
swapExactETHForTokens(amountOutMin, to, deadline);
33+
}
34+
35+
function sellFei(
36+
uint maxPenalty,
37+
uint amountIn,
38+
uint amountOutMin,
39+
address to,
40+
uint deadline
41+
) external {
42+
IOracleRef(address(INCENTIVE)).updateOracle();
43+
44+
uint penalty = 0;
45+
if (!INCENTIVE.isExemptAddress(to)) {
46+
(penalty,,) = INCENTIVE.getSellPenalty(amountIn);
47+
}
48+
require(penalty <= maxPenalty, "FeiRouter: Penalty too high");
49+
swapExactTokensForETH(amountIn, amountOutMin, to, deadline);
50+
}
51+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
pragma solidity ^0.6.0;
2+
3+
import "@uniswap/v2-periphery/contracts/libraries/UniswapV2Library.sol";
4+
import "@uniswap/v2-periphery/contracts/interfaces/IWETH.sol";
5+
import "@uniswap/lib/contracts/libraries/TransferHelper.sol";
6+
7+
contract UniswapSingleEthRouter {
8+
IWETH public immutable WETH;
9+
IUniswapV2Pair public immutable PAIR;
10+
11+
constructor(
12+
address pair,
13+
address weth
14+
) public {
15+
PAIR = IUniswapV2Pair(pair);
16+
WETH = IWETH(weth);
17+
}
18+
19+
receive() external payable {
20+
assert(msg.sender == address(WETH)); // only accept ETH via fallback from the WETH contract
21+
}
22+
23+
modifier ensure(uint deadline) {
24+
require(deadline >= block.timestamp, 'UniswapSingleEthRouter: EXPIRED');
25+
_;
26+
}
27+
28+
function _getReserves() internal view returns(uint reservesETH, uint reservesOther, bool isETH0) {
29+
(uint reserves0, uint reserves1, ) = PAIR.getReserves();
30+
isETH0 = PAIR.token0() == address(WETH);
31+
return isETH0 ? (reserves0, reserves1, isETH0) : (reserves1, reserves0, isETH0);
32+
}
33+
34+
function swapExactETHForTokens(uint amountOutMin, address to, uint deadline)
35+
public
36+
payable
37+
ensure(deadline)
38+
returns (uint amountOut)
39+
{
40+
(uint reservesETH, uint reservesOther, bool isETH0) = _getReserves();
41+
42+
uint amountIn = msg.value;
43+
amountOut = UniswapV2Library.getAmountOut(amountIn, reservesETH, reservesOther);
44+
require(amountOut >= amountOutMin, 'UniswapSingleEthRouter: INSUFFICIENT_OUTPUT_AMOUNT');
45+
IWETH(WETH).deposit{value: amountIn}();
46+
assert(IWETH(WETH).transfer(address(PAIR), amountIn));
47+
48+
(uint amount0Out, uint amount1Out) = isETH0 ? (uint(0), amountOut) : (amountOut, uint(0));
49+
PAIR.swap(amount0Out, amount1Out, to, new bytes(0));
50+
}
51+
52+
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address to, uint deadline)
53+
public
54+
ensure(deadline)
55+
returns (uint amountOut)
56+
{
57+
(uint reservesETH, uint reservesOther, bool isETH0) = _getReserves();
58+
amountOut = UniswapV2Library.getAmountOut(amountIn, reservesOther, reservesETH);
59+
60+
require(amountOut >= amountOutMin, 'UniswapSingleEthRouter: INSUFFICIENT_OUTPUT_AMOUNT');
61+
62+
address token = isETH0 ? PAIR.token1() : PAIR.token0();
63+
TransferHelper.safeTransferFrom(
64+
token, msg.sender, address(PAIR), amountIn
65+
);
66+
67+
(uint amount0Out, uint amount1Out) = isETH0 ? (amountOut, uint(0)) : (uint(0), amountOut);
68+
PAIR.swap(amount0Out, amount1Out, address(this), new bytes(0));
69+
70+
IWETH(WETH).withdraw(amountOut);
71+
72+
TransferHelper.safeTransferETH(to, amountOut);
73+
}
74+
}

migrations/2_deploy.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ const IDOOrchestrator = artifacts.require("IDOOrchestrator");
66
const GenesisOrchestrator = artifacts.require("GenesisOrchestrator");
77
const GovernanceOrchestrator = artifacts.require("GovernanceOrchestrator");
88
const PCVDepositOrchestrator = artifacts.require("PCVDepositOrchestrator");
9+
const FeiRouter = artifacts.require("FeiRouter");
910

1011
module.exports = function(deployer, network, accounts) {
11-
var pcvo, bc, incentive, controller, ido, genesis, gov, core;
12+
var pcvo, bc, incentive, controller, ido, genesis, gov, core, ethPair, ui, weth;
1213

1314
deployer.then(function() {
1415
return deployer.deploy(ControllerOrchestrator);
@@ -73,5 +74,16 @@ module.exports = function(deployer, network, accounts) {
7374
return core.initGenesis();
7475
}).then(function(instance) {
7576
return core.initGovernance();
77+
}).then(function(instance) {
78+
return core.ethFeiPair();
79+
}).then(function(instance) {
80+
ethPair = instance;
81+
return core.WETH();
82+
}).then(function(instance) {
83+
weth = instance;
84+
return core.uniswapIncentive();
85+
}).then(function(instance) {
86+
ui = instance;
87+
return deployer.deploy(FeiRouter, ethPair, weth, ui);
7688
});
7789
}

scripts/routerTest.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
const { BN } = require("@openzeppelin/test-helpers/src/setup");
2+
const { MAX_UINT256 } = require("@openzeppelin/test-helpers/src/constants");
3+
4+
const CoreOrchestrator = artifacts.require("CoreOrchestrator");
5+
const Core = artifacts.require("Core");
6+
const Fei = artifacts.require("Fei");
7+
const IUniswapV2Pair = artifacts.require("IUniswapV2Pair");
8+
const EthBondingCurve = artifacts.require("EthBondingCurve");
9+
const UniswapIncentive = artifacts.require("UniswapIncentive");
10+
const FeiRouter = artifacts.require("FeiRouter");
11+
12+
module.exports = async function(callback) {
13+
let accounts = await web3.eth.getAccounts();
14+
let co = await CoreOrchestrator.deployed();
15+
let core = await Core.at(await co.core());
16+
let fei = await Fei.at(await core.fei());
17+
18+
let ethPair = await IUniswapV2Pair.at(await co.ethFeiPair());
19+
let ui = await UniswapIncentive.at(await co.uniswapIncentive());
20+
21+
let ethAmount = new BN('100000000000000000000000');
22+
let feiTotal = ethAmount.mul(new BN('1000000'));
23+
let router = await FeiRouter.deployed();
24+
await core.grantMinter(accounts[0], {from: accounts[0]});
25+
await core.grantBurner(accounts[0], {from: accounts[0]});
26+
27+
await fei.mint(accounts[0], feiTotal);
28+
await fei.approve(router.address, feiTotal, {from: accounts[0]});
29+
await ui.updateOracle();
30+
31+
console.log('Current');
32+
33+
let reserves = await ui.getReserves();
34+
let price = reserves[0].div(reserves[1]);
35+
let peg = await ui.peg();
36+
let pegBN = new BN(peg.value).div(new BN('1000000000000000000'));
37+
console.log(price);
38+
console.log(pegBN);
39+
40+
console.log('Sync');
41+
let targetFei = reserves[1].mul(pegBN);
42+
let currentFei = await fei.balanceOf(ethPair.address);
43+
44+
await fei.burnFrom(ethPair.address, currentFei, {from: accounts[0]});
45+
await fei.mint(ethPair.address, targetFei, {from: accounts[0]});
46+
await ethPair.sync();
47+
48+
await ui.setExemptAddress(accounts[0], true, {from: accounts[0]});
49+
50+
console.log('Selling At');
51+
reserves = await ui.getReserves()
52+
let twoPercent = reserves[0].div(new BN('50'));
53+
let maxBurn = twoPercent;
54+
let sell = await router.sellFei(maxBurn, twoPercent, 0, accounts[0], MAX_UINT256, {from: accounts[0]});
55+
console.log(sell);
56+
57+
console.log('Buying Below');
58+
reserves = await ui.getReserves();
59+
let buy = await router.buyFei(0, 0, accounts[0], MAX_UINT256, {value: twoPercent.mul(new BN('2')), from: accounts[0]});
60+
console.log(buy);
61+
62+
console.log('Buying Above');
63+
reserves = await ui.getReserves();
64+
buy = await router.buyFei(0, 0, accounts[0], MAX_UINT256, {value: twoPercent, from: accounts[0]});
65+
console.log(buy);
66+
67+
console.log('Selling Above');
68+
reserves = await ui.getReserves();
69+
sell = await router.sellFei(maxBurn, twoPercent, 0, accounts[0], MAX_UINT256, {from: accounts[0]});
70+
console.log(sell);
71+
72+
callback();
73+
}
74+
75+
function sleep(ms) {
76+
return new Promise(resolve => setTimeout(resolve, ms));
77+
}
78+
79+
function stringify(bn) {
80+
let decimals = new BN('1000000000000000000');
81+
return bn.div(decimals).toString();
82+
}

0 commit comments

Comments
 (0)