Skip to content

Commit

Permalink
fix: USDC & WETH swap
Browse files Browse the repository at this point in the history
After implementing tests for USDC & WETH I found and fixed an issue regarding dynamic swap path creation
  • Loading branch information
mauricedesaxe committed Jun 6, 2022
1 parent 54cad5b commit c6d2b9f
Show file tree
Hide file tree
Showing 5 changed files with 2,113 additions and 698 deletions.
59 changes: 36 additions & 23 deletions contracts/OffsetHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,8 @@ contract OffsetHelper is OffsetHelperStorage {
// instantiate router
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);

// establish path
address[] memory path = new address[](3);
path[0] = _fromToken;
path[1] = eligibleTokenAddresses["USDC"];
path[2] = _toToken;
// generate path
address[] memory path = generatePath(_fromToken, _toToken);

// get expected amountsIn
uint256[] memory amountsIn = routerSushi.getAmountsIn(_amount, path);
Expand All @@ -169,11 +166,8 @@ contract OffsetHelper is OffsetHelperStorage {
// instantiate router
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);

// establish path
address[] memory path = new address[](3);
path[0] = _fromToken;
path[1] = eligibleTokenAddresses["USDC"];
path[2] = _toToken;
// generate path
address[] memory path = generatePath(_fromToken, _toToken);

// estimate amountsIn
uint256[] memory expectedAmountsIn = routerSushi.getAmountsIn(
Expand All @@ -194,14 +188,14 @@ contract OffsetHelper is OffsetHelperStorage {
// swap
routerSushi.swapTokensForExactTokens(
_amount,
expectedAmountsIn[2],
expectedAmountsIn[0],
path,
address(this),
block.timestamp
);

// update balances
balances[msg.sender][path[2]] += _amount;
balances[msg.sender][_toToken] += _amount;
}

// apparently I need a fallback and a receive method to fix the situation where transfering dust MATIC
Expand All @@ -225,11 +219,11 @@ contract OffsetHelper is OffsetHelperStorage {
// instantiate router
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);

// establish path
address[] memory path = new address[](3);
path[0] = eligibleTokenAddresses["WMATIC"];
path[1] = eligibleTokenAddresses["USDC"];
path[2] = _toToken;
// generate path
address[] memory path = generatePath(
eligibleTokenAddresses["WMATIC"],
_toToken
);

// get expectedAmountsIn
uint256[] memory amounts = routerSushi.getAmountsIn(_amount, path);
Expand All @@ -247,11 +241,11 @@ contract OffsetHelper is OffsetHelperStorage {
// instantiate router
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);

// estabilish path
address[] memory path = new address[](3);
path[0] = eligibleTokenAddresses["WMATIC"];
path[1] = eligibleTokenAddresses["USDC"];
path[2] = _toToken;
// generate path
address[] memory path = generatePath(
eligibleTokenAddresses["WMATIC"],
_toToken
);

// estimate amountsIn
uint256[] memory expectedAmountsIn = routerSushi.getAmountsIn(
Expand All @@ -278,7 +272,7 @@ contract OffsetHelper is OffsetHelperStorage {
}

// update balances
balances[msg.sender][path[2]] += _amount;
balances[msg.sender][_toToken] += _amount;
}

// @description allow users to withdraw tokens they have deposited
Expand Down Expand Up @@ -360,6 +354,25 @@ contract OffsetHelper is OffsetHelperStorage {
}
}

function generatePath(address _fromToken, address _toToken)
internal
view
returns (address[] memory)
{
if (_fromToken == eligibleTokenAddresses["USDC"]) {
address[] memory path = new address[](2);
path[0] = _fromToken;
path[1] = _toToken;
return path;
} else {
address[] memory path = new address[](3);
path[0] = _fromToken;
path[1] = eligibleTokenAddresses["USDC"];
path[2] = _toToken;
return path;
}
}

// ----------------------------------------
// Admin methods
// ----------------------------------------
Expand Down
35 changes: 27 additions & 8 deletions contracts/test/Swapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ contract Swapper {
{
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);

address[] memory path = new address[](3);
path[0] = tokenAddresses["WMATIC"];
path[1] = tokenAddresses["USDC"];
path[2] = _toToken;
address[] memory path = generatePath(
tokenAddresses["WMATIC"],
_toToken
);

uint256[] memory amounts = routerSushi.getAmountsIn(_amount, path);
return amounts[0];
Expand All @@ -39,10 +39,10 @@ contract Swapper {
function swap(address _toToken, uint256 _amount) public payable {
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);

address[] memory path = new address[](3);
path[0] = tokenAddresses["WMATIC"];
path[1] = tokenAddresses["USDC"];
path[2] = _toToken;
address[] memory path = generatePath(
tokenAddresses["WMATIC"],
_toToken
);

uint256[] memory amounts = routerSushi.swapETHForExactTokens{
value: msg.value
Expand All @@ -60,6 +60,25 @@ contract Swapper {
}
}

function generatePath(address _fromToken, address _toToken)
internal
view
returns (address[] memory)
{
if (_toToken == tokenAddresses["USDC"]) {
address[] memory path = new address[](2);
path[0] = _fromToken;
path[1] = _toToken;
return path;
} else {
address[] memory path = new address[](3);
path[0] = _fromToken;
path[1] = tokenAddresses["USDC"];
path[2] = _toToken;
return path;
}
}

fallback() external payable {}

receive() external payable {}
Expand Down
65 changes: 60 additions & 5 deletions test/OffsetHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
FormatTypes,
Interface,
parseEther,
parseUnits,
} from "ethers/lib/utils";

import * as hardhatContracts from "../utils/toucanContracts.json";
Expand All @@ -19,13 +20,16 @@ import {
} from "../typechain";
import addresses from "../utils/addresses";
import { Contract } from "ethers";
import { usdcABI, wethABI, wmaticABI } from "../utils/ABIs";

describe("Offset Helper - autoOffset", function () {
let offsetHelper: OffsetHelper;
let swapper: Swapper;
let bct: IToucanPoolToken;
let nct: IToucanPoolToken;
let weth: Contract;
let wmatic: Contract;
let usdc: Contract;
let addr1: SignerWithAddress;
let addr2: SignerWithAddress;
let addrs: SignerWithAddress[];
Expand All @@ -48,11 +52,9 @@ describe("Offset Helper - autoOffset", function () {
]
);

const iface = new Interface(
'[{"inputs":[{"internalType":"address","name":"childChainManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr2","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"address payable","name":"relayerAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"functionSignature","type":"bytes"}],"name":"MetaTransactionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"CHILD_CHAIN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHILD_CHAIN_ID_BYTES","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSITOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC712_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_CHAIN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_CHAIN_ID_BYTES","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr2","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes","name":"depositData","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"bytes","name":"functionSignature","type":"bytes"},{"internalType":"bytes32","name":"sigR","type":"bytes32"},{"internalType":"bytes32","name":"sigS","type":"bytes32"},{"internalType":"uint8","name":"sigV","type":"uint8"}],"name":"executeMetaTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getDomainSeperator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]'
);
iface.format(FormatTypes.full);
weth = new ethers.Contract(addresses.weth, iface, addr2);
weth = new ethers.Contract(addresses.weth, wethABI, addr2);
wmatic = new ethers.Contract(addresses.wmatic, wmaticABI, addr2);
usdc = new ethers.Contract(addresses.usdc, usdcABI, addr2);

// @ts-ignore
nct = new ethers.Contract(
Expand Down Expand Up @@ -99,6 +101,13 @@ describe("Offset Helper - autoOffset", function () {
),
});

await swapper.swap(addresses.usdc, parseUnits("20.0", 6), {
value: await swapper.calculateNeededETHAmount(
addresses.usdc,
parseUnits("20.0", 6)
),
});

await swapper.swap(addresses.bct, parseEther("50.0"), {
value: await swapper.calculateNeededETHAmount(
addresses.bct,
Expand Down Expand Up @@ -177,6 +186,52 @@ describe("Offset Helper - autoOffset", function () {
)
).to.not.be.reverted;
});

it("Should retire using a USDC swap and NCT redemption", async function () {
await (
await usdc.approve(
offsetHelper.address,
await offsetHelper.calculateNeededTokenAmount(
addresses.usdc,
addresses.nct,
parseEther("1.0")
)
)
).wait();

await expect(
offsetHelper.autoOffsetUsingToken(
addresses.usdc,
addresses.nct,
parseEther("1.0")
)
).to.not.be.reverted;
});

it("Should retire using a WMATIC swap and NCT redemption", async function () {
await wmatic.deposit({
value: parseEther("20.0"),
});

await (
await wmatic.approve(
offsetHelper.address,
await offsetHelper.calculateNeededTokenAmount(
addresses.wmatic,
addresses.nct,
parseEther("1.0")
)
)
).wait();

await expect(
offsetHelper.autoOffsetUsingToken(
addresses.wmatic,
addresses.nct,
parseEther("1.0")
)
).to.not.be.reverted;
});
});

describe("Testing autoRedeem()", function () {
Expand Down
Loading

0 comments on commit c6d2b9f

Please sign in to comment.