Skip to content

Commit

Permalink
Refactored smart contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
ethan-crypto committed Jan 13, 2022
1 parent 499992f commit b5c7fa3
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 46 deletions.
6 changes: 3 additions & 3 deletions src/abis/DexAggregator.json
Original file line number Diff line number Diff line change
Expand Up @@ -22132,12 +22132,12 @@
"1": {
"events": {},
"links": {},
"address": "0x2d9c8D48757B6f208364A4D4040f84c8D5045Fb4",
"transactionHash": "0x4fef4bc3fb5182c7151e43a65c69d6961c781f63152257c19d9e28d77eaaaf33"
"address": "0xF8C0e98f2C998804223f3CB687217Df0B46733cE",
"transactionHash": "0x1035bd281ac24c7ea9d507c175e27e76fb3658c08b0f185eb01714d1784a5cbe"
}
},
"schemaVersion": "3.4.3",
"updatedAt": "2022-01-11T00:27:47.531Z",
"updatedAt": "2022-01-11T17:06:59.836Z",
"networkType": "ethereum",
"devdoc": {
"kind": "dev",
Expand Down
6 changes: 3 additions & 3 deletions src/abis/Migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -2311,8 +2311,8 @@
"1": {
"events": {},
"links": {},
"address": "0xDac60886Aa25E4E95ffd8490e970343A071D2b0B",
"transactionHash": "0x88f8ca781aaceb27076b7b28070972e705eae286e4e90f484f75b2ebf4a543dc"
"address": "0x4b34dC17290630e8a38F40143aA044471e9A6bBf",
"transactionHash": "0xefe0d855dd76b479a7d7309871e3145e1b29ad355c339570949e399d382ddec7"
},
"1640296764925": {
"events": {},
Expand All @@ -2322,7 +2322,7 @@
}
},
"schemaVersion": "3.4.3",
"updatedAt": "2022-01-11T00:27:47.536Z",
"updatedAt": "2022-01-11T17:06:59.840Z",
"networkType": "ethereum",
"devdoc": {
"kind": "dev",
Expand Down
5 changes: 3 additions & 2 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import './App.css'
import { futureTime } from '../helpers'

const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
const wethAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
const exchangeDataReset = {
outputsLoading: false,
uniOutput: '0',
Expand Down Expand Up @@ -69,7 +70,7 @@ class App extends Component {

buyUsdc = (etherAmount) => {
this.setState({ loading: true })
this.state.dexAggregator.methods.buyUSDCAtBestPrice(futureTime(15)).send({ value: etherAmount, from: this.state.account }).on('transactionHash', (hash) => {
this.state.dexAggregator.methods.buyUSDCAtBestPrice(futureTime(15), [wethAddress, usdcAddress]).send({ value: etherAmount, from: this.state.account }).on('transactionHash', (hash) => {
this.setState({ loading: false })
this.setState({exchangeData : exchangeDataReset})
})
Expand All @@ -79,7 +80,7 @@ class App extends Component {
this.setState({ loading: true })
console.log(this.state.dexAggregator)
await this.state.usdc.methods.approve(this.state.dexAddress, usdcAmount).send({ from: this.state.account })
await this.state.dexAggregator.methods.sellUSDCAtBestPrice(usdcAmount, futureTime(15)).send({ from: this.state.account })
await this.state.dexAggregator.methods.sellUSDCAtBestPrice(usdcAmount, futureTime(15), [usdcAddress, wethAddress]).send({ from: this.state.account })
this.setState({ loading: false })
this.setState({exchangeData : exchangeDataReset})
}
Expand Down
50 changes: 18 additions & 32 deletions src/contracts/DexAggregator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ contract DexAggregator {
address public immutable sushiAddress;
IERC20 public immutable usdc;
address public immutable wethAddress;

/// @notice USDCBought event emitted on successful ETH to USDC swap.
event USDCBought(
uint256 usdcAmountBought,
Expand All @@ -46,62 +45,49 @@ contract DexAggregator {
// Needed to receive refunded eth from exchange
receive() payable external {}
// function that fetches the return amounts from each exchange for a given swap.
function getReturnAmounts(uint _amountIn, address[] memory _tradingPair) public view returns(uint _uniReturn, uint _sushiReturn){
function getReturnAmounts(uint _amountIn, address[] calldata _tradingPair) public view returns(uint _uniReturn, uint _sushiReturn){
uint[] memory _uniAmounts = IRouter(uniAddress).getAmountsOut(_amountIn, _tradingPair);
uint[] memory _sushiAmounts = IRouter(sushiAddress).getAmountsOut(_amountIn, _tradingPair);
_uniReturn = _uniAmounts[1];
_sushiReturn = _sushiAmounts[1];
}
/// @notice Function that swaps the users ETH to USDC using the exchange that offers the best price.
function buyUSDCAtBestPrice(uint _deadline) external payable {
// Get traiding pair
address[] memory _pair = getTradingPair(wethAddress, address(usdc));
function buyUSDCAtBestPrice(uint _deadline, address[] calldata _pair) external payable {
require(_pair[0] == wethAddress && _pair[1] == address(usdc), "Wrong token pair array");
// Fetch the USDC return amount for each exchange
(uint _uniUSDCAmount, uint _sushiUSDCAmount) = getReturnAmounts(msg.value, _pair);
// Determine which exchange offers the greater return then route the swap to that exchange.
if(_uniUSDCAmount > _sushiUSDCAmount) {
routeSwap(uniAddress, _uniUSDCAmount, msg.value, _deadline, _pair);
IRouter(uniAddress).swapETHForExactTokens{ value: msg.value }(_uniUSDCAmount, _pair,msg.sender, _deadline);
emit USDCBought(_uniUSDCAmount, msg.value, uniAddress, _sushiUSDCAmount);
} else {
routeSwap(sushiAddress, _sushiUSDCAmount, msg.value, _deadline, _pair);
IRouter(sushiAddress).swapETHForExactTokens{ value: msg.value }(_sushiUSDCAmount, _pair,msg.sender, _deadline);
emit USDCBought(_sushiUSDCAmount, msg.value, sushiAddress, _uniUSDCAmount);
}
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
function sellUSDCAtBestPrice(uint _USDCAmount, uint _deadline) external {
/// @notice Function that swaps the users USDC to ETH using the exchange that offers the best price.
function sellUSDCAtBestPrice(uint _USDCAmount, uint _deadline, address[] calldata _pair) external {
require(_pair[1] == wethAddress && _pair[0] == address(usdc), "Wrong token pair array");

require(usdc.balanceOf(msg.sender) >= _USDCAmount, "Error, can't sell more USDC than owned");
// Transfer the usdc amount from the user to this contract. User must approve this contract to transfer usdc on their behalf.
require(usdc.transferFrom(msg.sender, address(this), _USDCAmount), 'transferFrom failed.');
// Get traiding pair
address[] memory _pair = getTradingPair(address(usdc), wethAddress);
// Fetch the ETH return amount for each exchange
(uint _uniETHAmount, uint _sushiETHAmount) = getReturnAmounts(_USDCAmount, _pair);
// Determine which exchange offers the greater return then route the swap to that exchange.
if(_uniETHAmount > _sushiETHAmount) {
routeSwap(uniAddress, _uniETHAmount, _USDCAmount, _deadline, _pair);
// approve Uni to spend USDC tokens
require(usdc.approve(uniAddress, _USDCAmount), 'approve failed.');
IRouter(uniAddress).swapTokensForExactETH(_uniETHAmount, _USDCAmount,_pair, msg.sender, _deadline);
emit USDCSold(_uniETHAmount, _USDCAmount, uniAddress, _sushiETHAmount);
} else {
routeSwap(sushiAddress, _sushiETHAmount, _USDCAmount, _deadline, _pair);
// approve Sushi to spend USDC tokens
require(usdc.approve(sushiAddress, _USDCAmount), 'approve failed.');
IRouter(uniAddress).swapTokensForExactETH(_uniETHAmount, _USDCAmount,_pair, msg.sender, _deadline);
emit USDCSold(_sushiETHAmount, _USDCAmount, sushiAddress, _uniETHAmount);
}
}
/// Routes the swap to either the Uniswap or Sushiswap dex using their common interface.
function routeSwap(address _router, uint _amountOut, uint _amountIn, uint _deadline, address[] memory _pair) internal {
if(_pair[0] == address(usdc)){
// approve router to spend _amountIn tokens
require(usdc.approve(_router, _amountIn), 'approve failed.');
IRouter(_router).swapTokensForExactETH(_amountOut, _amountIn,_pair, msg.sender, _deadline);

} else {
IRouter(_router).swapETHForExactTokens{ value: msg.value }(_amountOut,_pair,msg.sender, _deadline);
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
}
function getTradingPair(address _input, address _output) internal pure returns(address[] memory _path) {
// get trading path for router and getReturnAmount
_path = new address[](2);
_path[0] = _input;
_path[1] = _output;
}
}
13 changes: 9 additions & 4 deletions test/DexAggregator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ contract('DexAggregator', ([deployer, user]) => {
sushiUSDCReturn = returnAmounts[1]

//swap 1 ETH for USDC
result = await dexAggregator.buyUSDCAtBestPrice(futureTime(15), {value: ethAmountSold, from: user })
result = await dexAggregator.buyUSDCAtBestPrice(futureTime(15), [wethAddress, usdcAddress], {value: ethAmountSold, from: user })

// Compare return values and find the highest return
uniUSDCReturn > sushiUSDCReturn
? highestUSDCReturn = {amount: uniUSDCReturn, router: uniAddress}
: highestUSDCReturn = {amount: sushiUSDCReturn, router: sushiAddress}
})
it('successfully routes ETH to USDC swap to the exchange with highest return', async () => {
it('Routes ETH to USDC swap to the exchange with highest return', async () => {
console.log(`START ETH BALANCE: ${fromWei(ethBalance)}`)
console.log(`START USDC BALANCE: ${fromUSDC(usdcBalance)}`)
// Users new USDC balance should increase by the highestUSDCReturn
Expand All @@ -119,6 +119,8 @@ contract('DexAggregator', ([deployer, user]) => {
const ethSubtracted = +ethBalance.toString() - +ethAmountSold.toString()
expect(+(newEthBalance.toString())).to.be.lessThan(ethSubtracted)
console.log(`${fromWei(newEthBalance).toString()} is approx ${fromWei(ethSubtracted).toString()}`)
// fail case: reverts when the user inputs the wrong traiding pair array
await dexAggregator.buyUSDCAtBestPrice(futureTime(15), [usdcAddress, wethAddress], {value: 1, from: user }).should.be.rejected;
})
it('successfully refunds leftover ETH from the swap', async () => {
// Aggregator Eth balance should be zero
Expand Down Expand Up @@ -163,14 +165,14 @@ contract('DexAggregator', ([deployer, user]) => {
await usdcRef.methods.approve(dexAggregator.address, usdcAmountSold).send({from: user})

//swap 1000 USDC for ETH
result = await dexAggregator.sellUSDCAtBestPrice(usdcAmountSold, futureTime(15), {from: user })
result = await dexAggregator.sellUSDCAtBestPrice(usdcAmountSold, futureTime(15), [usdcAddress, wethAddress], {from: user })

// Compare return values and find the highest return
uniETHReturn > sushiETHReturn
? highestETHReturn = {amount: uniETHReturn, router: uniAddress}
: highestETHReturn = {amount: sushiETHReturn, router: sushiAddress}
})
it('successfully routes USDC to ETH swap to the exchange with highest return', async () => {
it('Routes USDC to ETH swap to the exchange with highest return', async () => {
console.log(`START ETH BALANCE: ${fromWei(ethBalance)}`)
console.log(`START USDC BALANCE: ${fromUSDC(usdcBalance)}`)
// Users new USDC balance should decrease by the usdcAmountSold
Expand All @@ -187,6 +189,9 @@ contract('DexAggregator', ([deployer, user]) => {
console.log(`${fromWei(newEthBalance).toString()} is approx ${fromWei(ethAdded).toString()}`)
// fail case: users can't sell more usdc than they have
await dexAggregator.sellUSDCAtBestPrice((newUsdcBalance + 1), futureTime(15), { from: user }).should.be.rejected;
// fail case: reverts when the user inputs the wrong traiding pair array
await usdcRef.methods.approve(dexAggregator.address, 1).send({from: user})
await dexAggregator.buyUSDCAtBestPrice(1 ,futureTime(15), [wethAddress, usdcAddress], { from: user }).should.be.rejected;
})
it('emits a "USDCSold" event', () => {
const log = result.logs[0]
Expand Down
2 changes: 0 additions & 2 deletions truffle-config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require('babel-register');
require('babel-polyfill');

module.exports = {
networks: {
Expand Down

0 comments on commit b5c7fa3

Please sign in to comment.