Skip to content

Commit

Permalink
refactored smart contract
Browse files Browse the repository at this point in the history
  • Loading branch information
ethan-crypto committed Jan 20, 2022
1 parent 02d8bcf commit 44ab7a2
Show file tree
Hide file tree
Showing 9 changed files with 20,942 additions and 17,943 deletions.
2 changes: 1 addition & 1 deletion migrations/2_deploy_contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ const sushiAddress = "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F"
const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
const wethAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
module.exports = function (deployer) {
deployer.deploy(DexAggregator, sushiAddress, usdcAddress, wethAddress)
deployer.deploy(DexAggregator, sushiAddress, usdcAddress)
};
25,055 changes: 13,737 additions & 11,318 deletions src/abis/DexAggregator.json

Large diffs are not rendered by default.

13,583 changes: 7,069 additions & 6,514 deletions src/abis/IDex.json

Large diffs are not rendered by default.

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": "0x7a03F2Ab01ce33f66Bae50912c722b565318015F",
"transactionHash": "0xc8c7e16cb230c020a4bae7497e9294c695732abfa4fe7742dd1aa55594fe24b6"
"address": "0x0740d4C5416569A011E47B98190835261E995033",
"transactionHash": "0xd795c6d195bc53229c514c52f58d503b7ee7cafd90c6dd2d33c026eaed441305"
},
"1640296764925": {
"events": {},
Expand All @@ -2322,7 +2322,7 @@
}
},
"schemaVersion": "3.4.3",
"updatedAt": "2022-01-14T18:01:13.238Z",
"updatedAt": "2022-01-20T22:28:08.673Z",
"networkType": "ethereum",
"devdoc": {
"kind": "dev",
Expand Down
12 changes: 8 additions & 4 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
const wethAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
const exchangeDataReset = {
outputsLoading: false,
dexIndexWithBestPrice: '0',
uniOutput: '0',
sushiOutput: '0',
}
Expand Down Expand Up @@ -88,14 +89,17 @@ class App extends Component {
let data = this.state.exchangeData
if(input !== '0' ){
this.setState({ exchangeData: {...data, outputsLoading: true }})
const outputs = await this.state.dexAggregator.methods.getReturnAmounts(input, pairArray).call()
console.log(outputs)
const result = await this.state.dexAggregator.methods.getOutputAmounts(input, pairArray).call()
const index = result[0].toString()
const amounts = result[1]
console.log(result)
this.setState({
exchangeData: {
...data,
outputsLoading: false,
uniOutput: outputs[0],
sushiOutput: outputs[1]
dexIndexWithBestPrice: index,
uniOutput: index === "0" ? amounts[0] : amounts[1],
sushiOutput: index === "0" ? amounts[1] : amounts[0]
}
})
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/components/BuyForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class BuyForm extends Component {
render() {
const {
outputsLoading,
dexIndexWithBestPrice,
uniOutput,
sushiOutput
} = this.props.exchangeData
Expand Down Expand Up @@ -147,7 +148,7 @@ class BuyForm extends Component {
</span>
</div>
<div>
{window.web3.utils.fromWei(uniOutput, "ether") > window.web3.utils.fromWei(sushiOutput, "ether")
{ dexIndexWithBestPrice === "0"
? renderOutputForms(uniOutput, sushiOutput, this.state.etherAmount, outputsLoading, ['Uniswap', 'SushiSwap'])
: renderOutputForms(sushiOutput, uniOutput, this.state.etherAmount, outputsLoading, ['SushiSwap', 'Uniswap'])
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/SellForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class SellForm extends Component {
render() {
const {
outputsLoading,
dexIndexWithBestPrice,
uniOutput,
sushiOutput
} = this.props.exchangeData
Expand Down Expand Up @@ -147,7 +148,7 @@ class SellForm extends Component {
</span>
</div>
<div>
{(uniOutput > sushiOutput)
{ dexIndexWithBestPrice === "0"
? renderOutputForms(uniOutput, sushiOutput, this.state.usdcAmount, outputsLoading, ['Uniswap', 'SushiSwap'])
: renderOutputForms(sushiOutput, uniOutput, this.state.usdcAmount, outputsLoading, ['SushiSwap', 'Uniswap'])
}
Expand Down
87 changes: 44 additions & 43 deletions src/contracts/DexAggregator.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
pragma solidity ^0.8.11;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
//Code for the shared dex interface of sushiSwap and uniSwap.
interface IDex {
function WETH() external pure returns (address);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
Expand All @@ -13,69 +14,69 @@ interface IDex {
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
}
contract DexAggregator {
address public immutable sushiAddress;
address public immutable usdcAddress;
// Uniswap at index 0 and sushiswap at index 1
IDex[2] public Dexes;
IERC20 public immutable usdc;
address public immutable wethAddress;
address public constant UNI_ADDRESS = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
/// @notice USDCBought event emitted on successful ETH to USDC swap.
event USDCBought(
uint256 usdcAmountBought,
uint256 ethAmountSold,
address dex,
uint256 nextBestUsdcReturn
uint256 nextBestUsdcOutput
);
/// @notice USDCSold event emitted on successful USDC to ETH swap.
event USDCSold(
uint256 ethAmountBought,
uint256 usdcAmountSold,
address dex,
uint256 nextBestEthReturn
uint256 nextBestEthOutput
);
constructor (address _sushiAddress, address _usdcAddress, address _wethAddress) {
sushiAddress = _sushiAddress;
usdcAddress = _usdcAddress;
wethAddress = _wethAddress;
// Don't need to pass uni address to constructor b/c its the same across all networks.
constructor (address _sushiAddress, address _usdcAddress) {
// Don't need to pass uni address to constructor b/c its the same across all networks.
Dexes[0] = IDex(address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D));
Dexes[1] = IDex(_sushiAddress);
wethAddress = Dexes[0].WETH();
usdc = IERC20(_usdcAddress);
}
// important to recieve refunded ETH from either dex
receive() external payable {}
function getReturnAmounts(uint _amountIn, address[] calldata _path) public view returns(uint _uniAmount, uint _sushiAmount){
uint[] memory _uniAmounts = IDex(UNI_ADDRESS).getAmountsOut(_amountIn, _path);
uint[] memory _sushiAmounts = IDex(sushiAddress).getAmountsOut(_amountIn, _path);
_uniAmount = _uniAmounts[1];
_sushiAmount = _sushiAmounts[1];
// Function that returns the index of the exchange with the highest output amount and the output amount from each exchange in an array
function getOutputAmounts(uint _amountIn, address[] calldata _path) public view returns(uint8 _bestPriceDex, uint[] memory _amounts){
//Create new in memory array of length 2
_amounts = new uint[] (2);
// fetch output amounts from each exchange
_amounts[0] = (Dexes[0].getAmountsOut(_amountIn, _path))[1];
_amounts[1] = (Dexes[1].getAmountsOut(_amountIn, _path))[1];
// If sushi amount is greater than uni amount, swap order of amounts array and set dex which offers the greater output index 0 (sushi)
if(_amounts[1] > _amounts[0]) {
_amounts[0] = _amounts[1];
_amounts[1] = (Dexes[0].getAmountsOut(_amountIn, _path))[1];
_bestPriceDex = 1;
}
}
function buyUSDCAtBestPrice(uint _deadline,address[] calldata _path) external payable {
require(_path[0] == wethAddress && _path[1] == usdcAddress, "Wrong token pair array");
// get USDC return amounts for each exchange
(uint _uniUSDCAmount, uint _sushiUSDCAmount) = getReturnAmounts(msg.value, _path);
// determine which exchange offers the greater return then route the swap to that exchange.
if(_uniUSDCAmount > _sushiUSDCAmount) {
IDex(UNI_ADDRESS).swapETHForExactTokens{ value: msg.value }(_uniUSDCAmount, _path, msg.sender, _deadline);
emit USDCBought(_uniUSDCAmount, msg.value, UNI_ADDRESS, _sushiUSDCAmount);
} else {
IDex(sushiAddress).swapETHForExactTokens{ value: msg.value }(_sushiUSDCAmount, _path, msg.sender, _deadline);
emit USDCBought(_sushiUSDCAmount, msg.value, sushiAddress, _uniUSDCAmount);
}
require(_path[0] == wethAddress && _path[1] == address(usdc), "Wrong token pair array");
// get dex with best USDC price and output amounts for each exchange
(uint8 _dex, uint[] memory _USDCAmounts) = getOutputAmounts(msg.value, _path);
// Route trade to dex with best USDC price
Dexes[_dex].swapETHForExactTokens{ value: msg.value }(_USDCAmounts[0], _path, msg.sender, _deadline);
emit USDCBought(_USDCAmounts[0], msg.value, address(Dexes[_dex]), _USDCAmounts[1]);
// refund leftover ETH to user
payable(msg.sender).transfer(address(this).balance);
}
function sellUSDCAtBestPrice(uint _USDCAmount, uint _deadline, address[] calldata _path) external {
require(_path[1] == wethAddress && _path[0] == usdcAddress, "Wrong token pair array");
require(IERC20(usdcAddress).balanceOf(msg.sender) >= _USDCAmount, "Error, can't sell more USDC than owned");
require(_path[1] == wethAddress && _path[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.
require(IERC20(usdcAddress).transferFrom(msg.sender, address(this), _USDCAmount));
// get ETH return amounts for each exchange
(uint _uniETHAmount, uint _sushiETHAmount) = getReturnAmounts(_USDCAmount, _path);
// determine which exchange offers the greater return then route the swap to that exchange.
if(_uniETHAmount > _sushiETHAmount) {
// approve Uni to spend USDC tokens
require(IERC20(usdcAddress).approve(UNI_ADDRESS, _USDCAmount), 'approve failed.');
IDex(UNI_ADDRESS).swapTokensForExactETH(_uniETHAmount, _USDCAmount, _path, msg.sender, _deadline);
emit USDCSold(_uniETHAmount, _USDCAmount, UNI_ADDRESS, _sushiETHAmount);
} else {
// approve Sushi to spend USDC tokens
require(IERC20(usdcAddress).approve(sushiAddress, _USDCAmount), 'approve failed.');
IDex(sushiAddress).swapTokensForExactETH(_sushiETHAmount, _USDCAmount, _path, msg.sender, _deadline);
emit USDCSold(_sushiETHAmount, _USDCAmount, sushiAddress, _uniETHAmount);
}
require(usdc.transferFrom(msg.sender, address(this), _USDCAmount));
// get dex with best ETH price and output amounts for each exchange
(uint8 _dex, uint[] memory _ETHAmounts) = getOutputAmounts(_USDCAmount, _path);
// approve dex with best ETH price to spend USDC tokens
require(usdc.approve(address(Dexes[_dex]), _USDCAmount), 'approve failed.');
// Route trade to dex with best ETH price
Dexes[_dex].swapTokensForExactETH(_ETHAmounts[0], _USDCAmount, _path, msg.sender, _deadline);
emit USDCSold(_ETHAmounts[0], _USDCAmount, address(Dexes[_dex]), _ETHAmounts[1]);
}
}
Loading

0 comments on commit 44ab7a2

Please sign in to comment.