diff --git a/contracts/connectors/curve.sol b/contracts/connectors/curve.sol index ee1a318..08b1702 100644 --- a/contracts/connectors/curve.sol +++ b/contracts/connectors/curve.sol @@ -60,7 +60,7 @@ contract CurveProtocol is Stores, DSMath { event LogBuy(address sellAddr, address buyAddr, uint256 sellAmount, uint256 buyAmount); event LogAddLiquidity(uint256[4] amounts, uint256 mintAmount); event LogRemoveLiquidityImbalance(uint256[4] amounts, uint256 burnAmount); - event LogRemoveLiquidityOneCoin(address receiveCoin, uint256 amount); + event LogRemoveLiquidityOneCoin(address receiveCoin, uint256 withdrawnAmount); address public constant sCurveSwap = address(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); address public constant sCurveToken = address(0xC25a3A3b969415c80451098fa907EC722572917F); @@ -74,18 +74,16 @@ contract CurveProtocol is Stores, DSMath { mapping (int128 => address) addresses; constructor() public { - addresses[0] = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); - addresses[1] = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); - addresses[2] = address(0xdAC17F958D2ee523a2206206994597C13D831ec7); - addresses[3] = address(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); + addresses[0] = DAI; + addresses[1] = USDC; + addresses[2] = USDT; + addresses[3] = sUSD; } - function get_virtual_price() external returns (uint256) { - ICurve curve = ICurve(sCurveSwap); - return curve.get_virtual_price(); + function calc_token_amount(uint256[4] memory amounts, bool deposit) public returns (uint256 amount) { + return ICurve(sCurveSwap).calc_token_amount(amounts, deposit); } - function get_dy(int128 i, int128 j, uint256 dx) public returns(uint256) { ICurve curve = ICurve(sCurveSwap); return curve.get_dy(i, j, dx); diff --git a/package.json b/package.json index d67ff4e..3fd1c2e 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "truffle-verify": "^1.0.8" }, "devDependencies": { + "chai-bn": "^0.2.1", "sol-merger": "^2.0.1", "solidity-coverage": "0.5.11", "solium": "1.2.3" diff --git a/test/CurveProtocol.js b/test/CurveProtocol.js index 217ab9c..512af0e 100644 --- a/test/CurveProtocol.js +++ b/test/CurveProtocol.js @@ -2,8 +2,13 @@ const CurveProtocol = artifacts.require('CurveProtocol') const daiABI = require('./abi/dai'); const erc20 = require('./abi/erc20') const swap_abi = require('./abi/swap') -const { BN, ether, balance } = require('openzeppelin-test-helpers'); -const { asyncForEach } = require('./utils'); +const { ether, balance } = require('openzeppelin-test-helpers'); + +const BN = require('bn.js') + +const chai = require('chai') +const expect = chai.expect +chai.use(require('chai-bn')(BN)); // userAddress must be unlocked using --unlock ADDRESS const userAddress = '0x9eb7f2591ed42dee9315b6e2aaf21ba85ea69f8c'; @@ -19,7 +24,6 @@ const swapContract = new web3.eth.Contract(swap_abi, swap) const swapToken = '0xC25a3A3b969415c80451098fa907EC722572917F' const tokenContract = new web3.eth.Contract(erc20, swapToken) -//console.log(CurveProtocol); contract('Curve Protocol', async accounts => { @@ -35,19 +39,20 @@ contract('Curve Protocol', async accounts => { value: ether('0.1') }); const ethBalance = await balance.current(userAddress); - //expect(new BN(ethBalance)).to.be.bignumber.least(new BN(ether('0.1'))); + expect(+ethBalance).to.be.at.least(+ether('0.1')) }); - it('should mint DAI for our first 5 generated accounts', async () => { + it('should transfer DAI to CurveProtocol', async () => { let account = accounts[0] let contract = await CurveProtocol.deployed() // Get 100 DAI for first 5 accounts // daiAddress is passed to ganache-cli with flag `--unlock` // so we can use the `transfer` method await daiContract.methods - .transfer(contract.address, ether('10').toString()) + .transfer(contract.address, ether('100').toString()) .send({ from: userAddress, gasLimit: 800000 }); const daiBalance = await daiContract.methods.balanceOf(contract.address).call(); + expect(+daiBalance).to.be.at.least(+ether('100')) }); it('should approve DAI to CurveProtocol', async() => { @@ -55,8 +60,10 @@ contract('Curve Protocol', async accounts => { let contract = await CurveProtocol.deployed() await daiContract.methods - .approve(contract.address, "1000000000000000000000000000") + .approve(contract.address, ether('100').toString()) .send({ from: account, gasLimit: 800000 }); + const daiAllowance = await daiContract.methods.allowance(account, contract.address).call() + expect(+daiAllowance).to.be.at.least(+ether('100')) }); it('should exchange', async () => { @@ -64,29 +71,52 @@ contract('Curve Protocol', async accounts => { let contract = await CurveProtocol.deployed() // Get 100 DAI for first 5 accounts - let receipt = await contract.exchange(0, 1, "1000000000000000000", 1, { from: account }); - console.log(receipt.logs[0].args.sellAmount.toString(), receipt.logs[0].args.buyAmount.toString()) + let get_dy = await contract.get_dy.call(0, 1, ether('1').toString()) + let min_dy = +get_dy * 0.99 + let receipt = await contract.exchange(0, 1, ether('1').toString(), 1, { from: account }) + let buyAmount = receipt.logs[0].args.buyAmount.toString() + expect(+buyAmount).to.be.at.least(min_dy); }); it('should add liquidity', async () => { let account = accounts[0] let contract = await CurveProtocol.deployed() - let receipt = await contract.add_liquidity(["1000000000000000000", 0, 0, 0], 1, { from: account }) - console.log(receipt.logs[0].args.amounts.map(amount=>amount.toString()), receipt.logs[0].args.mintAmount.toString()) + + let amounts = [ether('1').toString(), 0, 0, 0] + let token_amount = await contract.calc_token_amount.call(amounts, true) + + let receipt = await contract.add_liquidity(amounts, 1, { from: account }) + let mintAmount = receipt.logs[0].args.mintAmount.toString() + expect(+mintAmount).to.be.at.least(+mintAmount) }) it('should remove liquidity imbalance', async () => { let account = accounts[0] let contract = await CurveProtocol.deployed() + + let tokenBalance = await tokenContract.methods.balanceOf(contract.address).call() let receipt = await contract.remove_liquidity_imbalance(["100000000000", 0, 0, 0], { from: account }) - console.log(receipt.logs[0].args.amounts.map(amount=>amount.toString()), receipt.logs[0].args.burnAmount.toString()) + let burnAmount = receipt.logs[0].args.burnAmount.toString() + let tokenBalanceAfter = await tokenContract.methods.balanceOf(contract.address).call() + + //weird Ganache errors sometimes "cannot decode event" + console.log(+tokenBalance, +tokenBalanceAfter, +burnAmount) + //expect(BN(tokenBalance)).to.be.a.bignumber.equal(BN(tokenBalanceAfter).add(burnAmount)) + }) it('should remove liquidity in one coin', async() => { let account = accounts[0] let contract = await CurveProtocol.deployed() + + let daiBalance = await daiContract.methods.balanceOf(contract.address).call() let receipt = await contract.remove_liquidity_one_coin("100000000000", 0, 1, { from: account }) - console.log(receipt.logs[0].args.receiveCoin, receipt.logs[0].args.amount.toString()) + let withdrawnAmount = receipt.logs[0].args.withdrawnAmount.toString() + let daiBalanceAfter = await daiContract.methods.balanceOf(contract.address).call() + + //weird Ganache errors sometimes "cannot decode event" + console.log(+daiBalance, +daiBalanceAfter, +withdrawnAmount) + //expect(BN(daiBalance)).to.be.a.bignumber.equal(BN(daiBalanceAfter).sub(withdrawnAmount)); }) }); \ No newline at end of file