This project is bootstrapped with Hardhat, a development environment for Ethereum smart contracts.
-
Install dependencies
yarn
-
Compile contracts
yarn compile
-
Test contracts
yarn test
-
Contracts can be deployed on any EVM network. To deploy contracts, you need to set up a
.env
file with the following variablesHARDHAT_PRIVATE_KEY=0x...
-
Deploy contracts
Note
Currently arguments for deployment are hardcoded in the scripts. You can change them in the scripts before deploying.
Script | Description | Arguments |
---|---|---|
deploy-all |
Deploy all contracts | |
deploy-crecy |
Deploy CRECY ERC20 contract | totalSupply, initialSupply |
deploy-fcw |
Deploy FCW ERC721 contract | |
deploy-timelock |
Deploy timelock contract | crecy address |
Network | Description |
---|---|
localhost |
Hardhat localnet |
celo |
CELO mainnet |
alfajores |
CELO testnet |
yarn hardhat run scripts/<script>.ts --network <network>
Verify smart contract codes on explorer
yarn hardhat verify --network <network> <address>
Contract Name | Network | Address |
---|---|---|
cRECY | Celo | 0x34C11A932853Ae24E845Ad4B633E3cEf91afE583 |
DeTrashCertificate | Polygon | 0xbc68c4ec4182e1d2c73b5e58bd92be9871db2230 |
TimeLock | Celo | 0x2B7D8711E26E78218791B85EdB1ff4EFf1A8BF54 |
RecyCertificate | Celo | 0xd574e0605c20Fc683feDBDD01ce745fF2C1a6f40 |
Contract Name | Address |
---|---|
CRecy | 0x004c368A3fb45b0CD601e2203Fd2948D9d695a3b |
FCWStatus | 0xbf10E8d903bB4fcc705740d37d5668e2d5A6CBbC |
RecyCertificate | 0x56A396a452f4F44412f089Efc3c4bF27aE6B5423 |
TimeLock (deprecated) |
This document describes how to interact with the Lock Contract in the cRecy ecosystem. This implementation considers the existence of the following contracts:
- TimeLock: the contract responsible for keeping tokens during lock period and for emitting NFT certificates.
- CRecy: the ERC-20 CRecy token
- RecyCertificate: the NFT for each certificate
- FCWStatus: the Friends of the Clean World contract
The CRecy and RecyCertificate addresses are passed as parameters during the TimeLock initialization. It supports the following roles:
- ADMIN_ROLE
- OPERATOR_ROLE
- PAUSER_ROLE
The current version of the contract is published on Celo Network in the following address: 0x2B7D8711E26E78218791B85EdB1ff4EFf1A8BF54.
Every certificate submitted to the Lock Contract needs to be signed according to the EIP-712. Each certificate should be an object with the following data and types:
CertificateAuthorization: [
{ name: "institution", type: "bytes32" },
{ name: "tons", type: "uint8" },
{ name: "baseYear", type: "uint16" },
{ name: "baseMonth", type: "uint8" },
{ name: "timespan", type: "uint8" },
{ name: "signer", type: "address" },
{ name: "authorization", type: "bytes32" },
{ name: "deadline", type: "uint256" },
]
The first 5 fields are related to the certificate itself, while the last 3 are used to validate the signature on-chain, as follows:
signer
: address of the account that will sign the certificate. Should haveADMIN_ROLE
orOPERATOR_ROLE
in the Lock Contractauthorization
: a unique control number in hex format for each authorizationdeadline
: the date until that certificate can be presented to the Lock Contract
Each signature should also contain the message, the CertificateAuthorization
types object, and the following domain:
{
chainId: 31337,
name: "GenericTypedMessage",
version: "1",
verifyingContract: "0x2B7D8711E26E78218791B85EdB1ff4EFf1A8BF54"
}
The signature process requires an account with ADMIN_ROLE
or OPERATOR_ROLE
to generate the signed message. It can be accomplished via a script or via a DApp, where signers can connect their EOA using solutions such as Metamask, Wallet Connect or any other equivalent solution.
Here, we provide an example using Ethers.js with a randomly created account:
import moment from "moment";
import { HDNodeWallet } from "@ethersproject/wallet"
import { fromRpcSig } from 'ethereumjs-util';
import { doLock } from "./lockUtils.js"
certTypedMessage = {
domain: {
chainId: 31337,
name: "GenericTypedMessage",
version: "1",
verifyingContract: "0x2B7D8711E26E78218791B85EdB1ff4EFf1A8BF54"
},
message: {
institution: ethers.encodeBytes32String("Acme Inc."),
tons: '3',
baseYear: '2024',
baseMonth: '1',
timespan: '12',
signer: admin.address,
authorization: `0x1`,
deadline: moment().add(1, 'day').format('X')
},
types: {
CertificateAuthorization: [
{ name: "institution", type: "bytes32" },
{ name: "tons", type: "uint8" },
{ name: "baseYear", type: "uint16" },
{ name: "baseMonth", type: "uint8" },
{ name: "timespan", type: "uint8" },
{ name: "signer", type: "address" },
{ name: "authorization", type: "bytes32" },
{ name: "deadline", type: "uint256" },
],
},
};
const wallet = HDNodeWallet.createRandom();
const amount = 100; // A value to be defined
wallet.signTypedData(
certTypedMessage.domain,
certTypedMessage.types,
certTypedMessage.message
).then( async (signedCertificate) => {
const rsvSignedCertificate = fromRpcSig(signedCertificate);
await doLock(amount, certTypedMessage.message, rsvSignedCertificate);
});
To lock a certificate in the Lock Contract, it is necessary to meet the following conditions:
- Having a signed, not used, not expired, certificate
{ r, s, v}
components - Approval from certificate receiving account for the Lock Contract to move at least the amount of CRecy tokens to be locked on their behalf
Any account can call a lock procedure in the Lock Contract passing the lock amount, the certificate data and the signature. Ahead, we provide the lockUtils.js
script that exports a function to lock an amount in CRecy and a certificate in the contract.
import { Contract } from "@ethersproject/contract";
import lockContractAbi from "./lockContractAbi.json";
export const doLock (amount, certificate, signature) => {
const contract = new Contract("0x2B7D8711E26E78218791B85EdB1ff4EFf1A8BF54", lockContractAbi);
contract.lock(amount, certificate, signature);
}