@0xpolygonid/js-sdk
Version:
SDK to work with Polygon ID
144 lines (125 loc) • 4.35 kB
text/typescript
import { RevocationStatus, Issuer } from '../../verifiable';
import { Contract, JsonRpcProvider, Signer, TransactionReceipt, TransactionRequest } from 'ethers';
import { Proof, NodeAuxJSON, Hash } from '@iden3/js-merkletree';
import { EthConnectionConfig } from './state';
import abi from '../blockchain/abi/CredentialStatusResolver.json';
import { ITransactionService, TransactionService } from '../../blockchain';
/**
* OnChainRevocationStore is a class that allows to interact with the onchain contract
* and build the revocation status.
*
* @public
* @class OnChainIssuer
*/
export class OnChainRevocationStorage {
private readonly _contract: Contract;
private readonly _provider: JsonRpcProvider;
private readonly _transactionService: ITransactionService;
/**
*
* Creates an instance of OnChainIssuer.
* @public
* @param {string} - onchain contract address
* @param {string} - rpc url to connect to the blockchain
*/
constructor(
private readonly _config: EthConnectionConfig,
contractAddress: string,
private _signer?: Signer
) {
this._provider = new JsonRpcProvider(_config.url);
let contract = new Contract(contractAddress, abi, this._provider);
if (this._signer) {
this._signer = this._signer.connect(this._provider);
contract = contract.connect(this._signer) as Contract;
}
this._contract = contract;
this._transactionService = new TransactionService(this._provider);
}
/**
* Get revocation status by issuerId, issuerState and nonce from the onchain.
* @public
* @returns Promise<RevocationStatus>
*/
public async getRevocationStatusByIdAndState(
issuerID: bigint,
state: bigint,
nonce: number
): Promise<RevocationStatus> {
const response = await this._contract.getRevocationStatusByIdAndState(issuerID, state, nonce);
const issuer = OnChainRevocationStorage.convertIssuerInfo(response.issuer);
const mtp = OnChainRevocationStorage.convertSmtProofToProof(response.mtp);
return {
issuer,
mtp
};
}
/**
* Get revocation status by nonce from the onchain contract.
* @public
* @returns Promise<RevocationStatus>
*/
public async getRevocationStatus(issuerID: bigint, nonce: number): Promise<RevocationStatus> {
const response = await this._contract.getRevocationStatus(issuerID, nonce);
const issuer = OnChainRevocationStorage.convertIssuerInfo(response.issuer);
const mtp = OnChainRevocationStorage.convertSmtProofToProof(response.mtp);
return {
issuer,
mtp
};
}
public async saveNodes(payload: bigint[][]): Promise<TransactionReceipt> {
if (!this._signer) {
throw new Error('No signer provided');
}
const feeData = await this._provider.getFeeData();
const maxFeePerGas = this._config.maxFeePerGas
? BigInt(this._config.maxFeePerGas)
: feeData.maxFeePerGas;
const maxPriorityFeePerGas = this._config.maxPriorityFeePerGas
? BigInt(this._config.maxPriorityFeePerGas)
: feeData.maxPriorityFeePerGas;
const gasLimit = await this._contract.saveNodes.estimateGas(payload);
const txData = await this._contract.saveNodes.populateTransaction(payload);
const request: TransactionRequest = {
to: txData.to,
data: txData.data,
gasLimit,
maxFeePerGas,
maxPriorityFeePerGas
};
const { txnReceipt } = await this._transactionService.sendTransactionRequest(
this._signer,
request
);
return txnReceipt;
}
private static convertIssuerInfo(issuer: bigint[]): Issuer {
const [state, claimsTreeRoot, revocationTreeRoot, rootOfRoots] = issuer.map((i) =>
Hash.fromBigInt(i).hex()
);
return {
state,
claimsTreeRoot,
revocationTreeRoot,
rootOfRoots
};
}
private static convertSmtProofToProof(mtp: {
existence: boolean;
auxIndex: bigint;
auxValue: bigint;
auxExistence: boolean;
siblings: bigint[];
}): Proof {
let nodeAux: NodeAuxJSON | undefined = undefined;
const siblings = mtp.siblings?.map((s) => s.toString());
if (mtp.auxExistence) {
nodeAux = {
key: mtp.auxIndex.toString(),
value: mtp.auxValue.toString()
};
}
return Proof.fromJSON({ existence: mtp.existence, node_aux: nodeAux, siblings });
}
}