Skip to content

Commit

Permalink
Add CurvePool for stETH to ETH selling
Browse files Browse the repository at this point in the history
  • Loading branch information
elenadimitrova committed Mar 16, 2021
1 parent 7d53386 commit d9c0972
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 17 deletions.
36 changes: 36 additions & 0 deletions contracts/infrastructure/dapp/CurveFilter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (C) 2021 Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.s

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.6.12;

import "./BaseFilter.sol";

contract CurveFilter is BaseFilter {
bytes4 private constant EXCHANGE = bytes4(keccak256("exchange(int128,int128,uint256,uint256)"));
bytes4 private constant ERC20_APPROVE = bytes4(keccak256("approve(address,uint256)"));

function isValid(address /*_wallet*/, address _spender, address _to, bytes calldata _data) external view override returns (bool) {
if (_data.length < 4) {
return false;
}
bytes4 methodId = getMethod(_data);
if(_spender == _to) {
return (methodId == EXCHANGE);
} else {
return (methodId == ERC20_APPROVE);
}
}
}
4 changes: 1 addition & 3 deletions contracts/infrastructure/dapp/LidoFilter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ contract LidoFilter is BaseFilter {
}
if(_spender == _to && _data.length >= 4) {
bytes4 methodId = getMethod(_data);
if (methodId == SUBMIT) {
return true;
}
return (methodId == SUBMIT);
}
}
}
6 changes: 6 additions & 0 deletions deployment/7_deploy_2.5.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const VatFilter = artifacts.require("VatFilter");
const ScdMcdMigration = artifacts.require("ScdMcdMigration");
const UniswapV2Filter = artifacts.require("UniswapV2UniZapFilter");
const LidoFilter = artifacts.require("LidoFilter");
const CurveFilter = artifacts.require("CurveFilter");

const deployManager = require("../utils/deploy-manager.js");
const MultisigExecutor = require("../utils/multisigexecutor.js");
Expand Down Expand Up @@ -185,6 +186,11 @@ const main = async () => {
const LidoFilterWrapper = await LidoFilter.new();
console.log(`Deployed LidoFilter at ${LidoFilterWrapper.address}`);
await DappRegistryWrapper.addDapp(0, config.defi.lido.contract, LidoFilterWrapper.address);
// Curve Pool for stETH -> ETH
console.log("Deploying CurveFilter");
const CurveFilterWrapper = await CurveFilter.new();
console.log(`Deployed CurveFilter at ${CurveFilterWrapper.address}`);
await DappRegistryWrapper.addDapp(0, config.defi.lido.stETHCurvePool, CurveFilterWrapper.address);

// Setting timelock
console.log(`Setting Timelock to ${config.settings.timelockPeriod}`);
Expand Down
8 changes: 7 additions & 1 deletion lib/paraswap/lib/curve/ICurve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ interface IPool {
}

interface ICurvePool {
event TokenExchange(
address indexed buyer,
int128 sold_id,
uint256 tokens_sold,
int128 bought_id,
uint256 tokens_bought);

function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 minDy) external;

function exchange(int128 i, int128 j, uint256 dx, uint256 minDy) external;

}

interface ICompoundPool {
Expand Down
65 changes: 53 additions & 12 deletions test-integration/filter-lido.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const chai = require("chai");
const BN = require("bn.js");
const bnChai = require("bn-chai");

const { assert } = chai;
const { expect } = chai;
chai.use(bnChai(BN));

// Argent
Expand All @@ -17,7 +17,9 @@ const GuardianStorage = artifacts.require("GuardianStorage");
const ArgentModule = artifacts.require("ArgentModule");
const DappRegistry = artifacts.require("DappRegistry");
const LidoFilter = artifacts.require("LidoFilter");
const CurveFilter = artifacts.require("CurveFilter");
const ILido = artifacts.require("ILido");
const ICurvePool = artifacts.require("ICurvePool");

// UniswapV2
const UniswapV2Factory = artifacts.require("UniswapV2FactoryMock");
Expand Down Expand Up @@ -48,15 +50,18 @@ contract("Lido Filter", (accounts) => {
let module;
let wallet;
let walletImplementation;
let filter;
let lidoFilter;
let curveFilter;
let dappRegistry;
let uniswapRouter;

let lido;
let curve;

before(async () => {
// Lido contract on mainnet
lido = await ILido.at("0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84");
curve = await ICurvePool.at("0xdc24316b9ae028f1497c275eb9192a3ea0f67022");

const uniswapFactory = await UniswapV2Factory.new(ZERO_ADDRESS);
const weth = await WETH.new();
Expand All @@ -78,8 +83,10 @@ contract("Lido Filter", (accounts) => {
RECOVERY_PERIOD,
LOCK_PERIOD);
await registry.registerModule(module.address, ethers.utils.formatBytes32String("ArgentModule"));
filter = await LidoFilter.new();
await dappRegistry.addDapp(0, lido.address, filter.address);
lidoFilter = await LidoFilter.new();
curveFilter = await CurveFilter.new();
await dappRegistry.addDapp(0, lido.address, lidoFilter.address);
await dappRegistry.addDapp(0, curve.address, curveFilter.address);
await dappRegistry.addDapp(0, relayer, ZERO_ADDRESS);
walletImplementation = await BaseWallet.new();
manager = new RelayManager(guardianStorage.address, ZERO_ADDRESS);
Expand Down Expand Up @@ -115,7 +122,7 @@ contract("Lido Filter", (accounts) => {
assert.isTrue(success, `deposit failed: "${error}"`);

const walletBalance = await lido.balanceOf(wallet.address);
assert.equal(walletBalance.toNumber(), 99);
expect(walletBalance).to.eq.BN(99);
});

it("should allow staking from wallet via submit", async () => {
Expand All @@ -136,14 +143,17 @@ contract("Lido Filter", (accounts) => {
assert.isTrue(success, `deposit failed: "${error}"`);

const walletBalance = await lido.balanceOf(wallet.address);
assert.equal(walletBalance.toNumber(), 99);
expect(walletBalance).to.eq.BN(99);
});
});

it("should not allow calling forbidden staking pool methods", async () => {
const data = lido.contract.methods.approve(accounts[5], 10).encodeABI();
const transaction = encodeTransaction(lido.address, 0, data);
describe("Selling via CurvePool", () => {
beforeEach(async () => {
// Stake some funds to use to test selling
const data = lido.contract.methods.submit(accounts[5]).encodeABI();
const transaction = encodeTransaction(lido.address, 100, data);

const txReceipt = await manager.relay(
await manager.relay(
module,
"multiCall",
[wallet.address, [transaction]],
Expand All @@ -152,10 +162,41 @@ contract("Lido Filter", (accounts) => {
1,
ETH_TOKEN,
relayer);
});

it("should allow selling stETH via Curve", async () => {
const transactions = [];
let data = lido.contract.methods.approve(curve.address, 99).encodeABI();
let transaction = encodeTransaction(lido.address, 0, data);
transactions.push(transaction);
data = curve.contract.methods.exchange(1, 0, 99, 95).encodeABI();
transaction = encodeTransaction(curve.address, 0, data);
transactions.push(transaction);

const before = await utils.getBalance(wallet.address);
const txReceipt = await manager.relay(
module,
"multiCall",
[wallet.address, transactions],
wallet,
[owner],
0,
ZERO_ADDRESS,
ZERO_ADDRESS);

const { success, error } = utils.parseRelayReceipt(txReceipt);
assert.isFalse(success);
assert.equal(error, "TM: call not authorised");
assert.isTrue(success, `exchange failed: "${error}"`);

const event = await utils.getEvent(txReceipt, curve, "TokenExchange");
assert.equal(event.args.tokens_sold, 99); // Sold stETH
assert.equal(event.args.tokens_bought, 96); // Got ETH
// Check ETH was received
const after = await utils.getBalance(wallet.address);
expect(after.sub(before)).to.eq.BN(96);

// Check only dust stETH left
const walletBalance = await lido.balanceOf(wallet.address);
expect(walletBalance).to.eq.BN(1);
});
});
});
5 changes: 4 additions & 1 deletion utils/config/development.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@
],
"wethPools": ["0xe1237aA7f535b0CC33Fd973D66cBf830354D16c7"]
},
"lido": { "contract": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" }
"lido": {
"contract": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84",
"stETHCurvePool": "0xdc24316b9ae028f1497c275eb9192a3ea0f67022"
}
},
"contracts": {
"MultiSigWallet": "0xACF05e2a9b757e70C2900432c9b4E08Fdc54dD53",
Expand Down

0 comments on commit d9c0972

Please sign in to comment.