Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion contracts/OffsetHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,9 @@ contract OffsetHelper is OffsetHelperStorage {
balances[msg.sender][_tco2s[i]] >= _amounts[i],
"Insufficient TCO2 balance"
);

if (_amounts[i] == 0) {
continue;
}
balances[msg.sender][_tco2s[i]] -= _amounts[i];

IToucanCarbonOffsets(_tco2s[i]).retire(_amounts[i]);
Expand Down
85 changes: 76 additions & 9 deletions test/OffsetHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import {

import * as hardhatContracts from "../utils/toucanContracts.json";
import * as poolContract from "../artifacts/contracts/interfaces/IToucanPoolToken.sol/IToucanPoolToken.json";
import * as carbonOffsetsContract from "../artifacts/contracts/interfaces/IToucanCarbonOffsets.sol/IToucanCarbonOffsets.json";
import {
IToucanPoolToken,
OffsetHelper,
OffsetHelper__factory,
Swapper,
Swapper__factory,
} from "../typechain";
import addresses from "../utils/addresses";
import addresses, { whaleAddresses } from "../utils/addresses";
import { Contract } from "ethers";
import { usdcABI, wethABI, wmaticABI } from "../utils/ABIs";

Expand Down Expand Up @@ -85,13 +86,31 @@ describe("Offset Helper - autoOffset", function () {
]
);

await Promise.all(
addrs.map(async (addr) => {
await addr.sendTransaction({
to: addr2.address,
value: (await addr.getBalance()).sub(parseEther("1.0")),
});
})
// Transfer a large amount of MATIC and NCT to the test account via account impersonation.
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [whaleAddresses.matic],
});
const maticWhale = ethers.provider.getSigner(whaleAddresses.matic);
await maticWhale.sendTransaction({
to: addr2.address,
value: (await maticWhale.getBalance()).sub(parseEther("1.0")),
});

// Note: The swapper fails when trying to exchange such a large amount of NCT.
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [whaleAddresses.nct],
});
const addrNctWhale = ethers.provider.getSigner(whaleAddresses.nct);
const nctWhaleSigner = new ethers.Contract(
addresses.nct,
hardhatContracts.contracts.NatureCarbonTonne.abi,
addrNctWhale
);
await nctWhaleSigner.transfer(
addr2.address,
await nctWhaleSigner.balanceOf(whaleAddresses.nct)
);

await swapper.swap(addresses.weth, parseEther("20.0"), {
Expand Down Expand Up @@ -298,7 +317,7 @@ describe("Offset Helper - autoOffset", function () {
).to.be.revertedWith("Insufficient TCO2 balance");
});

it("Should retire using an NCT deposit", async function () {
it("Should retire using a BCT deposit", async function () {
await (await bct.approve(offsetHelper.address, parseEther("1.0"))).wait();

await (
Expand All @@ -319,6 +338,54 @@ describe("Offset Helper - autoOffset", function () {

await expect(offsetHelper.autoRetire(tco2s, amounts)).to.not.be.reverted;
});

it("Should retire using an NCT deposit, even if the first scored TCO2 is not in pool", async function () {
const scoredTCO2s = await nct.getScoredTCO2s();
const lowestScoredTCO2 = new ethers.Contract(
scoredTCO2s[0],
carbonOffsetsContract.abi,
addr2
);
const lowestScoredTCO2Balance = await lowestScoredTCO2.balanceOf(
addresses.nct
);

// Skip setup if the oldest tco2's balance in the pool is already 0.
if (formatEther(lowestScoredTCO2Balance) !== "0.0") {
// Setup: If the oldest tco2 balance is non-zero, remove all its tokens from the pool via a redeem.
// Ensure that addr2 has enough NCT to redeem all of the lowestScoredTCO2 or setup will fail.
expect(await nct.balanceOf(addr2.address)).to.be.above(
await lowestScoredTCO2.balanceOf(addresses.nct)
);

await nct.approve(offsetHelper.address, lowestScoredTCO2Balance);

await offsetHelper.deposit(addresses.nct, lowestScoredTCO2Balance);

await offsetHelper.autoRedeem(addresses.nct, lowestScoredTCO2Balance);
}

// Ensure the test condition is met.
expect(await lowestScoredTCO2.balanceOf(addresses.nct)).to.equal(0);

await nct.approve(offsetHelper.address, parseEther("1.0"));

await offsetHelper.deposit(addresses.nct, parseEther("0.0005"));

const redeemReceipt = await (
await offsetHelper.autoRedeem(addresses.nct, parseEther("0.0005"))
).wait();

if (!redeemReceipt.events) {
return;
}
const tco2s =
redeemReceipt.events[redeemReceipt.events.length - 1].args?.tco2s;
const amounts =
redeemReceipt.events[redeemReceipt.events.length - 1].args?.amounts;

await expect(offsetHelper.autoRetire(tco2s, amounts)).to.not.be.reverted;
});
});

describe("Testing deposit() and withdraw()", function () {
Expand Down
11 changes: 10 additions & 1 deletion utils/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ interface IfcAddresses {
wmatic: string;
}

const addresses: IfcAddresses = {
interface IfcWhaleAddresses {
matic: string;
nct: string;
}
export const addresses: IfcAddresses = {
myAddress: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
bct: "0x2F800Db0fdb5223b3C3f354886d907A671414A7F",
nct: "0xD838290e877E0188a4A44700463419ED96c16107",
Expand All @@ -16,6 +20,11 @@ const addresses: IfcAddresses = {
wmatic: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
};

export const whaleAddresses: IfcWhaleAddresses = {
matic: "0xe7804c37c13166fF0b37F5aE0BB07A3aEbb6e245",
nct: "0x4b3ebae392e8b90a9b13068e90b27d9c41abc3c8",
};

export const mumbaiAddresses: IfcAddresses = {
myAddress: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
bct: "0xf2438A14f668b1bbA53408346288f3d7C71c10a1",
Expand Down