Issuer - @energyweb/issuer
Overview
The Issuer package contains smart contracts and interfaces that handle the management of certificates on the blockchain.
The package has four components:
- Smart Contracts: The smart contracts for Certificate management on the blockchain. Smart contracts are documented below. All smart contracts are written in Solidity.
- Migrations: Methods for deploying the smart contracts to the blockchain using the OpenZeppelin Truffle Upgrades API.
- Blockchain-facade: Interfaces and strongly-typed classes with methods to interact with Issuer smart contracts. Blockchain facades are documented below.
- Utilities
- Events
- Precise Proof Utilities
Components
Smart Contracts
IERC1888
Interface for IERC-1888 Certificate/Claim.
Registry
This contract implements the ERC-1888 and ERC-1155 methods in the context of the Origin platform. You can read more about ERC-1888 and ERC-1155 here.
As its name suggests, the Registry contract stores and manages certificates. It handles the issuing, minting, transferring and claiming of certificates, and returns certificate data and certificate owner’s claimed balances for a given certificate(s).
Certificates are stored in the certificateStorage map in this contract, and are accessed by their Certificate Id.
Claimed balances (the balance of units of energy claimed) are stored in the claimedBalances map in this contract, and are accessed by tokenId and owner address.
Claiming Certificates
To claim a certificate is to retire it, or remove it from circulation, for reporting purposes. This is the final stage in the certificate lifecyle.
When a certificate is claimed, the Certificate blockchain facade calls the safeTransferAndClaimFrom method on the Registry contract with the certificate id, the volume to be claimed and the claim data.
return registryWithSigner.safeTransferAndClaimFrom(
fromAddress,
claimAddress,
this.id,
amount ?? ownedVolume,
encodedClaimData
);
The user's claim balance is updated in the Registry contract, and the certificate is burned using the ERC1155 burn method:
/// @notice Burn certificates after they've been claimed, and increase the claimed balance.
function _burn(address _from, uint256 _id, uint256 _value) internal override {
ERC1155._burn(_from, _id, _value);
claimedBalances[_id][_from] = claimedBalances[_id][_from] + _value;
}
Once a certificate is burned, you can no longer perform operations on it (transfer, withdraw, deposit onto exchange, etc.)
When the burn occurs, a ClaimSingle (or ClaimBatch) event is emitted. The Issuer API's on-chain Certificate listener processes this event by updating the certificate repository with the new claim event. This creates parity between the on-chain certificate and the representation of the certificate in the database. This data is used for claims reporting.
const onChainCert = await new OnChainCertificate(
certificate.id,
certificate.blockchain.wrap()
).sync();
try {
const updateResult = await this.repository.update(certificate.id, {
owners: onChainCert.owners,
claimers: onChainCert.claimers,
claims: await onChainCert.getClaimedData()
});
RegistryExtended
The methods in this contract handle batch issuance, batch transfer and batch transfer and claim for multiple _to and _from addresses. (Batch methods in the Registry.sol contract only support issuing and transferring certificates for one address.)
Issuer
This smart contract contains the methods for the Certificate request and approval workflow for issuing ERC-1888 Transferable Certificates, including:
- Requesting certification from the issuer
- Approving certification requests
- Revoking certificate requests requests
- Revoking issued certificates
The Issuer smart contract is dependent on the Registry smart contract for issuing certificates and minting energy production values for certificates.
When new Certificates are issued, they are stored in Registry’s certificate storage map and accessed by their Certificate Id.
Blockchain Facade
The Blockchain facade exposes methods that call the public methods of the Issuer module's smart contracts. The facade methods use the ethers.js API to interact with the smart contracts. If you are unfamiliar with how API client libraries connect to and interact with the blockchain, you can read more in our documentation here.
Implementing Facades in the Issuer API
Facades are imported and instantiated in the Issuer API, where the facade's methods are called to interact with smart contracts on the blockchain. See the below implementation in the Issue Certificate Handler (source code here).
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
//Import facade
import { Certificate as CertificateFacade } from '@energyweb/issuer';
import { BigNumber, ContractTransaction } from 'ethers';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { IssueCertificateCommand } from '../commands/issue-certificate.command';
import { BlockchainPropertiesService } from '../../blockchain/blockchain-properties.service';
import { UnminedCommitment } from '../unmined-commitment.entity';
@CommandHandler(IssueCertificateCommand)
export class IssueCertificateHandler implements ICommandHandler<IssueCertificateCommand> {
constructor(
@InjectRepository(UnminedCommitment)
private readonly unminedCommitmentRepository: Repository<UnminedCommitment>,
private readonly blockchainPropertiesService: BlockchainPropertiesService
) {}
async execute({
to,
energy,
fromTime,
toTime,
deviceId,
isPrivate,
metadata
}: IssueCertificateCommand): Promise<ContractTransaction> {
const blockchainProperties = await this.blockchainPropertiesService.get();
if (!isPrivate) {
return await CertificateFacade.create(
to,
BigNumber.from(energy),
fromTime,
toTime,
deviceId,
blockchainProperties.wrap(),
metadata
);
}
//call facade method:
const { tx, proof } = await CertificateFacade.createPrivate(
to,
BigNumber.from(energy),
fromTime,
toTime,
deviceId,
blockchainProperties.wrap(),
metadata
);
await this.unminedCommitmentRepository.save({
txHash: tx.hash.toLowerCase(),
commitment: proof
});
return tx;
}
}