Skip to content

Commit 472f1ec

Browse files
committed
fix: calculate token amount method
While working on PRT-198 I realised we need a method to calculate the token amount needed to swap to NCT and redeem+retire in the same manner we have the howMuchETHShouldISendToSwap method that calculates the amount of ETH to send. While working on this PR (PRT-212) I realised there was an issue with the swap. Because there was not method to calculate the needed tokens, the code could break since approval and SafeTransferFrom in the swap method was done blindly. It really only passed tests because I was using weth so the approved amount was always larger than needed, but it could have created cases were bugs happened. P.S.: I have opened PRT-214 to refactor this as it's quite messy, but I am focused on the SDK right now. P.P.S.: I have opened PRT-215 to write USDC and WMATIC tests.
1 parent fbb084f commit 472f1ec

File tree

4 files changed

+84
-26
lines changed

4 files changed

+84
-26
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ A collection of examples that implement, integrate with or otherwise use Toucan'
66

77
| Contract | Polygon | Mumbai |
88
| ------------ | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
9-
| OffsetHelper | [0x2E730e699D6c5A9F7dF40E6D7cbB82638d56dF6B](https://polygonscan.com/address/0x2E730e699D6c5A9F7dF40E6D7cbB82638d56dF6B) | [0x1b5e0afaDAcC6D4631C94bF35D3e3F4d60ad8323](https://mumbai.polygonscan.com/address/0x1b5e0afaDAcC6D4631C94bF35D3e3F4d60ad8323) |
9+
| OffsetHelper | [0x7229F708d2d1C29b1508E35695a3070F55BbA479](https://polygonscan.com/address/0x7229F708d2d1C29b1508E35695a3070F55BbA479) | [0xE0a1D62C84f7Ca4611C0ada6cfC3E9187a7A97e6](https://mumbai.polygonscan.com/address/0xE0a1D62C84f7Ca4611C0ada6cfC3E9187a7A97e6) |
1010

1111
## OffsetHelper
1212

@@ -28,9 +28,9 @@ If you call the `autoOffset()` method specifying only 2 params, it will be payab
2828

2929
The first param is the pool token you want the contract to use (could be NCT or BCT) and the second is the amount of TCO2 to retire.
3030

31-
In case you send too much MATIC in the `msg.value`, the contract is programed to send leftover MATIC back to the user. But, I suggest you use the `howMuchETHShouldISendToSwap()` method of the contract before calling `autoOffset()` in this case.
31+
In case you send too much MATIC in the `msg.value`, the contract is programed to send leftover MATIC back to the user. But, I suggest you use the `calculateNeededETHAmount()` method of the contract before calling `autoOffset()` in this case.
3232

33-
We'll discuss the `howMuchETHShouldISendToSwap()` method below.
33+
We'll discuss the `calculateNeededETHAmount()` method below.
3434

3535
### `autoOffsetUsingPoolToken(address _poolToken, uint256 _amountToOffset)`
3636

@@ -42,7 +42,7 @@ The first parameter is the pool token you will deposit to do the offset (could b
4242

4343
You will want to approve the `OffsetHelper` from the token you wish to deposit before calling `autoOffsetUsingPoolToken()` in this case.
4444

45-
### `howMuchETHShouldISendToSwap(address _toToken, uint256 _amount)`
45+
### `calculateNeededETHAmount(address _toToken, uint256 _amount)`
4646

4747
This is a view method that allows you to see how much MATIC it would cost you to get a certain amount of BCT / NCT.
4848

contracts/OffsetHelper.sol

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,36 @@ contract OffsetHelper is OffsetHelperStorage {
120120
return false;
121121
}
122122

123+
// @description tells user how much of _fromToken is required to swap for an amount of pool tokens
124+
// @param _fromToken the token the user wants to swap for pool token
125+
// @param _toToken token to swap for (should be NCT or BCT)
126+
// @param _amount amount of NCT / BCT wanted
127+
// @returns uint256 representing the required ETH / MATIC to get the amount of NCT / BCT
128+
function calculateNeededTokenAmount(
129+
address _fromToken,
130+
address _toToken,
131+
uint256 _amount
132+
) public view returns (uint256) {
133+
// check tokens
134+
require(
135+
isSwapable(_fromToken) && isRedeemable(_toToken),
136+
"Can't swap this token"
137+
);
138+
139+
// instantiate sushi router
140+
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);
141+
142+
// establish path
143+
address[] memory path = new address[](3);
144+
path[0] = _fromToken;
145+
path[1] = eligibleTokenAddresses["USDC"];
146+
path[2] = _toToken;
147+
148+
// swap input token for pool token
149+
uint256[] memory amountsIn = routerSushi.getAmountsIn(_amount, path);
150+
return amountsIn[0];
151+
}
152+
123153
// @description uses SushiSwap to exchange eligible tokens for BCT / NCT
124154
// @param _fromToken token to deposit and swap
125155
// @param _toToken token to swap for (will be held within contract)
@@ -130,17 +160,21 @@ contract OffsetHelper is OffsetHelperStorage {
130160
address _toToken,
131161
uint256 _amount
132162
) public {
133-
// check tokens
134-
require(
135-
isSwapable(_fromToken) && isRedeemable(_toToken),
136-
"Can't swap this token"
163+
uint256 neededAmountToSend = calculateNeededTokenAmount(
164+
_fromToken,
165+
_toToken,
166+
_amount
137167
);
138168

139169
// transfer token from user to this contract
140-
IERC20(_fromToken).safeTransferFrom(msg.sender, address(this), _amount);
170+
IERC20(_fromToken).safeTransferFrom(
171+
msg.sender,
172+
address(this),
173+
neededAmountToSend
174+
);
141175

142176
// approve sushi router
143-
IERC20(_fromToken).approve(sushiRouterAddress, _amount);
177+
IERC20(_fromToken).approve(sushiRouterAddress, neededAmountToSend);
144178

145179
// instantiate sushi router
146180
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);
@@ -171,11 +205,11 @@ contract OffsetHelper is OffsetHelperStorage {
171205

172206
receive() external payable {}
173207

174-
// @description tells user how much ETH/MATIC is required to swap for an amount of specified tokens
208+
// @description tells user how much ETH/MATIC is required to swap for an amount of pool tokens
175209
// @param _toToken token to swap for (should be NCT or BCT)
176210
// @param _amount amount of NCT / BCT wanted
177211
// @returns uint256 representing the required ETH / MATIC to get the amount of NCT / BCT
178-
function howMuchETHShouldISendToSwap(address _toToken, uint256 _amount)
212+
function calculateNeededETHAmount(address _toToken, uint256 _amount)
179213
public
180214
view
181215
returns (uint256)
@@ -187,7 +221,6 @@ contract OffsetHelper is OffsetHelperStorage {
187221
IUniswapV2Router02 routerSushi = IUniswapV2Router02(sushiRouterAddress);
188222

189223
// establish path;
190-
// sushi router expects path[0] == WMATIC, but otherwise the path will resemble the one above
191224
address[] memory path = new address[](3);
192225
path[0] = eligibleTokenAddresses["WMATIC"];
193226
path[1] = eligibleTokenAddresses["USDC"];

contracts/test/Swapper.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ contract Swapper {
2020
}
2121
}
2222

23-
function howMuchETHShouldISendToSwap(address _toToken, uint256 _amount)
23+
function calculateNeededETHAmount(address _toToken, uint256 _amount)
2424
public
2525
view
2626
returns (uint256)

test/OffsetHelper.ts

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,21 @@ describe("Offset Helper - autoOffset", function () {
9393
);
9494

9595
await swapper.swap(addresses.weth, parseEther("20.0"), {
96-
value: await swapper.howMuchETHShouldISendToSwap(
96+
value: await swapper.calculateNeededETHAmount(
9797
addresses.weth,
9898
parseEther("20.0")
9999
),
100100
});
101101

102102
await swapper.swap(addresses.bct, parseEther("50.0"), {
103-
value: await swapper.howMuchETHShouldISendToSwap(
103+
value: await swapper.calculateNeededETHAmount(
104104
addresses.bct,
105105
parseEther("50.0")
106106
),
107107
});
108108

109109
await swapper.swap(addresses.nct, parseEther("50.0"), {
110-
value: await swapper.howMuchETHShouldISendToSwap(
110+
value: await swapper.calculateNeededETHAmount(
111111
addresses.nct,
112112
parseEther("50.0")
113113
),
@@ -117,7 +117,14 @@ describe("Offset Helper - autoOffset", function () {
117117
describe("Testing autoOffset()", function () {
118118
it("Should retire using a WETH swap and NCT redemption", async function () {
119119
await (
120-
await weth.approve(offsetHelper.address, parseEther("1.0"))
120+
await weth.approve(
121+
offsetHelper.address,
122+
await offsetHelper.calculateNeededTokenAmount(
123+
addresses.weth,
124+
addresses.nct,
125+
parseEther("1.0")
126+
)
127+
)
121128
).wait();
122129

123130
await expect(
@@ -130,7 +137,7 @@ describe("Offset Helper - autoOffset", function () {
130137
});
131138

132139
it("Should retire using a MATIC swap and NCT redemption", async function () {
133-
const maticToSend = await offsetHelper.howMuchETHShouldISendToSwap(
140+
const maticToSend = await offsetHelper.calculateNeededETHAmount(
134141
addresses.nct,
135142
parseEther("1.0")
136143
);
@@ -152,7 +159,14 @@ describe("Offset Helper - autoOffset", function () {
152159

153160
it("Should retire using a WETH swap and BCT redemption", async function () {
154161
await (
155-
await weth.approve(offsetHelper.address, parseEther("1.0"))
162+
await weth.approve(
163+
offsetHelper.address,
164+
await offsetHelper.calculateNeededTokenAmount(
165+
addresses.weth,
166+
addresses.bct,
167+
parseEther("1.0")
168+
)
169+
)
156170
).wait();
157171

158172
await expect(
@@ -327,7 +341,14 @@ describe("Offset Helper - autoOffset", function () {
327341
const initialBalance = await nct.balanceOf(offsetHelper.address);
328342

329343
await (
330-
await weth.approve(offsetHelper.address, parseEther("1.0"))
344+
await weth.approve(
345+
offsetHelper.address,
346+
await offsetHelper.calculateNeededTokenAmount(
347+
addresses.weth,
348+
addresses.nct,
349+
parseEther("1.0")
350+
)
351+
)
331352
).wait();
332353

333354
await (
@@ -351,7 +372,7 @@ describe("Offset Helper - autoOffset", function () {
351372
});
352373

353374
it("Should swap MATIC for 1.0 NCT", async function () {
354-
const maticToSend = await offsetHelper.howMuchETHShouldISendToSwap(
375+
const maticToSend = await offsetHelper.calculateNeededETHAmount(
355376
addresses.nct,
356377
parseEther("1.0")
357378
);
@@ -375,7 +396,7 @@ describe("Offset Helper - autoOffset", function () {
375396
offsetHelper.address
376397
);
377398

378-
const maticToSend = await offsetHelper.howMuchETHShouldISendToSwap(
399+
const maticToSend = await offsetHelper.calculateNeededETHAmount(
379400
addresses.nct,
380401
parseEther("1.0")
381402
);
@@ -422,9 +443,13 @@ describe("Offset Helper - autoOffset", function () {
422443
it("Should swap WETH for 1.0 BCT", async function () {
423444
const initialBalance = await bct.balanceOf(offsetHelper.address);
424445

425-
await (
426-
await weth.approve(offsetHelper.address, parseEther("1.0"))
427-
).wait();
446+
const neededAmount = await offsetHelper.calculateNeededTokenAmount(
447+
addresses.weth,
448+
addresses.bct,
449+
parseEther("1.0")
450+
);
451+
452+
await (await weth.approve(offsetHelper.address, neededAmount)).wait();
428453

429454
await (
430455
await offsetHelper["swap(address,address,uint256)"](

0 commit comments

Comments
 (0)