Retire Credits From a Specific Carbon Credit Project

GigaHierz
7 min readOct 25, 2023

--

Learn how to retire carbon credits from a specific region (maybe your country?)

In the last two parts of our 5 part series, we have learned what it means to retire carbon credits using Toucan’s tooling on a high level and then looked at the OffsetHelper that abstracts interactions with Toucan’s main contracts.

In this part of the Toucan Developer Toolings Series by GigaHierz (me), you will learn how to retire carbon credits from a specific project. We will use our knowledge from the last two articles to create customized functions. After going through this tutorial, you will be able to create a function that retires carbon credits from a specific project. You can find all of the code in the Toucan Quickstart Repo.

Okay, let’s start by defining our main function that we will call retireSpecificProject() and will cover the following steps:

  • Redeem the pool token for a specific TCO2 token
  • Retire the TCO2 token

To find more information on the projects that are available, their methodology, region and supply, you can check out this board on Footprint. If you want to learn even more about the project, you can look it up on Verra using the projectId. If a project is called “VCS-191” the projected would be “191”.

Footprint board on projects by ID and supply

In case you are new to retiring carbon credits with Toucan, please refer to these articles.

A little reminder here: The following steps are combined in each of the following “auto offset” methods implemented in OffsetHelper to allow a retirement within one transaction.

  1. Buy pool tokens (on a DEX) with any ERC20 token, e.g., cUSD on Celo.
  2. Redeem the pool token for a TCO2, a token that holds a direct reference to a real-world asset.
  3. Retire that TCO2 token.

Disclaimer: if you are working on testnet, the transaction may revert without a clear error message. This is likely related to the low liquidity of the pools. But no worries, you can fork the mainnet for testing.

Swapping

The swapping won't be an official part of this tutorial, but if you want to create a better user experience, you should check out the SingleSwap from Uniswap, and follow the very easy tutorial to implement a swap. And feel free to create a PR once you do that.

Redeeming pool tokens for TCO2

As mentioned above, you can use The Graph or a Data Dashboard, e.g., on Footprint to find out about the projects and their supply on-chain. Once you've got the address of that project, let’s get ready. You can use the Toucan Quickstart Repo which already comes with an interface to call functions from Toucan’s contracts.

For our redeemProject() function, we call redeemMany() from the pool token contract.

  • This function allows users to redeem Toucan pool tokens for TCO2 tokens.
  • Users need to approve the pool token before calling this function.
import "./interfaces/IToucanPoolToken.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract RetireSpecificProject {
using SafeERC20 for IERC20;

function redeemProject(
address _poolToken,
address[] memory _tco2s,
uint256[] memory _amounts
) public returns (address[] memory tco2s, uint256[] memory amounts) {
// instantiate pool token (NCT or BCT)
IToucanPoolToken PoolTokenImplementation = IToucanPoolToken(_poolToken);


// sends pool tokens to the contract
IERC20(_poolToken).safeTransferFrom(
msg.sender,
address(this),
_amount
);

tco2s = _tco2s;
amounts = _amounts;

// redeem pool token for TCO2 with that address; will transfer the TCO2 to this contract
PoolTokenImplementation.redeemMany(tco2s, amounts);
}

First, we are creating our instance of the pool contract. We need to transfer the amount of pool tokens to the contract so they can be redeemed in the contract. When implementing the function, you need to approve the token spending before that. When calling redeemMany() , we need an array of TCO2s and the corresponding amounts. This means you can retire tokens from different projects in one transaction. Finally, we return the amounts and the TCO2 amounts to use these variables in the retirement step.

Important here to know is that Toucan takes fees if you redeem your pool tokens for specific carbon credits. for NCTs that would be a fee of 10%. In the OffsetHelper we are using autoRedeem() to redeem pool tokens for TCO2. This won’t cost us a fee, but you will automatically redeem for the oldest TCO2. If you want to redeem for a specific project, you have to pay a fee. For NCTs, this will 10% of their token price. To properly calculate how many NCTs we will receive, we will have to call IToucanPoolToken.calculateRedeemFees().

import "./interfaces/IToucanPoolToken.sol";   


contract RetireSpecificProject {

function redeemProject() {
// redeem functions

// to calculate the amount you will be able to retire
IToucanPoolToken(_poolToken).calculateRedeemFees(tco2s, amounts);
}
}

Before we can start with the retirement process, we need to subtract the amount of the calculated protocol fees from the amount of pool tokens that we’ve got from the redeeming process. Below, you can see the whole function. In this function, we have now removed the transfer function, as we used it for testing the function.

    function redeemProject(
address _poolToken,
address[] memory _tco2s,
uint256[] memory _amounts
) internal returns (address[] memory tco2s, uint256[] memory amounts) {
require(
IERC20(_poolToken).balanceOf(address(this)) >= _amounts[0],
"Insufficient NCT/BCT balance"
);

// instantiate pool token (NCT or BCT)
IToucanPoolToken PoolTokenImplementation = IToucanPoolToken(_poolToken);

tco2s = _tco2s;
amounts = _amounts;

// redeem pool token for TCO2 with that address; will transfer the TCO2 to this contract
PoolTokenImplementation.redeemMany(tco2s, amounts);

// the amount that we return needs to be the actuall amount of TCO2s that
// is received after taking away the reddem fee
amounts[0] =
amounts[0] -
PoolTokenImplementation.calculateRedeemFees(tco2s, amounts);
}

Amazing. Now, you are the proud holder of a TCO2, an on-chain carbon credit that references a real-life asset.

The last step will be to retire that carbon credit on-chain. One of the benefits of using the blockchain is how we can relate every person directly to retirement (including all the information on the projects. This will bring new opportunities for transparency to the VCM (Voluntary Carbon Market).

Retire Carbon Credits

This part is pretty straightforward. To retire the carbon credits, we create a retireProjects() function.

  1. retireProjects():
  • This function is responsible for retiring TCO2 tokens to achieve carbon offsetting.
  • It retires the specified amount of TCO2 tokens

We will call the retire() function from the ToucanCarbonOffset contract, which burns the TCO2 and creates the retirements with a reference to your address. For this step, we need the TCO2 addresses and the amounts we want to retire.

import "./interfaces/IToucanCarbonOffsets.sol";

function retire(address[] memory _tco2s, uint256[] memory _amounts) public {
IToucanCarbonOffsets(_tco2s[0]).retire(_amounts[0]);
}

Let’s add some checks and a loop for the arrays to the function. We also have to transfer the TCO2s to the smart contract so it can retire them for us. Again, this is for testing the function and will be removed, when we bring all of the functions together.

import "./interfaces/IToucanCarbonOffsets.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract RetireSpecificProject {
using SafeERC20 for IERC20;

function retire(address[] memory _tco2s, uint256[] memory _amounts) public {
uint256 tco2sLen = _tco2s.length;
require(tco2sLen != 0, "Array empty");
require(tco2sLen == _amounts.length, "Arrays unequal");



for (uint i = 0; i < tco2sLen; i++) {
if (_amounts[i] == 0) {
continue;
}
// sends tco2s tokens to the contract
IERC20(_tco2s[i]).safeTransferFrom(
msg.sender,
address(this),
_amount
);

IToucanCarbonOffsets(_tco2s[i]).retire(_amounts[i]);
}
}
}

Congratulations, you have retired your first carbon credits on chain for the project of your choice! ✨

Okay, now that we have all the pieces, let’s bring them together in our
retireSpecificProjectInToken(). Make sure you remove the transfer functions from the redeemProject() as well as the retire() function, as the first transfer of tokens is happening in this function.

  function retireSpecificProject(
address _poolToken,
address _tco2,
uint256 _amountToSwap
) public returns (address[] memory tco2s, uint256[] memory amounts) {
// deposit pool token from user to this contract
IERC20(_poolToken).safeTransferFrom(
msg.sender,
address(this),
_amounts[0]
);

// redeem BCT / NCT for a sepcific TCO2
(tco2s, amounts) = redeemProject(_poolToken, _tco2, amountToOffset);

// retire the TCO2s to achieve offset
retire(tco2s, amounts);
}

And that’s it. Now after deploying the smart contract, we can call the function in our frontend. Check the full code in the toucan-quickstartrepository.

Step 5: Interacting with the Contract

Once deployed, users can interact with the contract functions. In the react-app folder of the toucan-quickstartrepository, find the index.tsx file and call the function.

  • Approve the spending of pool tokens for the contract.
  • Call retireSpecificProject() to offset carbon emissions
 const retire = async () => {
// approve the spending of the ERC20 tokens, e.g. cUSD
await (await poolContract.approve(retirementHelperAddress, amount)).wait();

await retirementHelper.retireSpecificProject(
poolAddress,
tco2Address,
amount,
{
gasLimit: 5000000,
}
);
};

That was fun. Now, you have the tools to explore the tools that Toucan provides and build the most fun things.

Building fun things that will help us live a bit longer

Let’s build!

Well, let’s get your hands dirty and start building with Toucan

--

--