@0xpolygonid/js-sdk 1.23.0 → 1.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/browser/esm/index.js +3103 -358
  2. package/dist/browser/esm/index.js.map +1 -1
  3. package/dist/browser/umd/index.js +3106 -356
  4. package/dist/browser/umd/index.js.map +1 -1
  5. package/dist/node/cjs/credentials/credential-wallet.js +11 -78
  6. package/dist/node/cjs/credentials/index.js +1 -0
  7. package/dist/node/cjs/credentials/status/did-resolver-revocation.js +21 -0
  8. package/dist/node/cjs/iden3comm/handlers/fetch.js +32 -0
  9. package/dist/node/cjs/iden3comm/handlers/payment.js +38 -13
  10. package/dist/node/cjs/storage/blockchain/abi/ERC20.json +1 -0
  11. package/dist/node/cjs/storage/blockchain/did-resolver-readonly-storage.js +72 -0
  12. package/dist/node/cjs/storage/blockchain/{erc20-permit-sig.js → erc20-helper.js} +14 -1
  13. package/dist/node/cjs/storage/blockchain/index.js +3 -1
  14. package/dist/node/cjs/storage/blockchain/onchain-issuer-adapter/non-merklized/version/v0.0.1/onchain-non-merklized-issuer-adapter.js +245 -0
  15. package/dist/node/cjs/storage/blockchain/onchain-issuer.js +82 -0
  16. package/dist/node/cjs/storage/blockchain/onchain-zkp-verifier.js +38 -34
  17. package/dist/node/cjs/storage/interfaces/index.js +1 -0
  18. package/dist/node/cjs/storage/interfaces/onchain-issuer.js +2 -0
  19. package/dist/node/cjs/utils/did-helper.js +3 -3
  20. package/dist/node/cjs/verifiable/constants.js +1 -0
  21. package/dist/node/cjs/verifiable/credential.js +86 -0
  22. package/dist/node/esm/credentials/credential-wallet.js +12 -79
  23. package/dist/node/esm/credentials/index.js +1 -0
  24. package/dist/node/esm/credentials/status/did-resolver-revocation.js +17 -0
  25. package/dist/node/esm/iden3comm/handlers/fetch.js +32 -0
  26. package/dist/node/esm/iden3comm/handlers/payment.js +38 -14
  27. package/dist/node/esm/storage/blockchain/abi/ERC20.json +1 -0
  28. package/dist/node/esm/storage/blockchain/did-resolver-readonly-storage.js +68 -0
  29. package/dist/node/esm/storage/blockchain/{erc20-permit-sig.js → erc20-helper.js} +14 -2
  30. package/dist/node/esm/storage/blockchain/index.js +3 -1
  31. package/dist/node/esm/storage/blockchain/onchain-issuer-adapter/non-merklized/version/v0.0.1/onchain-non-merklized-issuer-adapter.js +241 -0
  32. package/dist/node/esm/storage/blockchain/onchain-issuer.js +78 -0
  33. package/dist/node/esm/storage/blockchain/onchain-zkp-verifier.js +38 -34
  34. package/dist/node/esm/storage/interfaces/index.js +1 -0
  35. package/dist/node/esm/storage/interfaces/onchain-issuer.js +1 -0
  36. package/dist/node/esm/utils/did-helper.js +3 -3
  37. package/dist/node/esm/verifiable/constants.js +1 -0
  38. package/dist/node/esm/verifiable/credential.js +88 -2
  39. package/dist/types/credentials/credential-wallet.d.ts +0 -6
  40. package/dist/types/credentials/index.d.ts +1 -0
  41. package/dist/types/credentials/models.d.ts +8 -0
  42. package/dist/types/credentials/status/did-resolver-revocation.d.ts +7 -0
  43. package/dist/types/iden3comm/handlers/fetch.d.ts +8 -0
  44. package/dist/types/iden3comm/handlers/payment.d.ts +3 -2
  45. package/dist/types/storage/blockchain/did-resolver-readonly-storage.d.ts +16 -0
  46. package/dist/types/storage/blockchain/{erc20-permit-sig.d.ts → erc20-helper.d.ts} +8 -1
  47. package/dist/types/storage/blockchain/index.d.ts +3 -1
  48. package/dist/types/storage/blockchain/onchain-issuer-adapter/non-merklized/version/v0.0.1/onchain-non-merklized-issuer-adapter.d.ts +66 -0
  49. package/dist/types/storage/blockchain/onchain-issuer.d.ts +40 -0
  50. package/dist/types/storage/blockchain/onchain-zkp-verifier.d.ts +11 -12
  51. package/dist/types/storage/entities/state.d.ts +2 -0
  52. package/dist/types/storage/interfaces/index.d.ts +1 -0
  53. package/dist/types/storage/interfaces/onchain-issuer.d.ts +12 -0
  54. package/dist/types/verifiable/constants.d.ts +1 -0
  55. package/dist/types/verifiable/credential.d.ts +15 -2
  56. package/package.json +3 -2
@@ -1,4 +1,4 @@
1
- import { DID, getChainId } from '@iden3/js-iden3-core';
1
+ import { DID } from '@iden3/js-iden3-core';
2
2
  import { W3CCredential, VerifiableConstants, CredentialStatusType, DisplayMethodType } from './../verifiable';
3
3
  import * as uuid from 'uuid';
4
4
  import { CredentialStatusResolverRegistry } from './status/resolver';
@@ -31,40 +31,18 @@ export class CredentialWallet {
31
31
  if (!schema.$metadata.uris['jsonLdContext']) {
32
32
  throw new Error('jsonLdContext is missing is the schema');
33
33
  }
34
- request.context = request.context ?? [];
35
- if (request.displayMethod?.type === DisplayMethodType.Iden3BasicDisplayMethodV1 &&
36
- !request.context.includes(VerifiableConstants.JSONLD_SCHEMA.IDEN3_DISPLAY_METHOD)) {
37
- request.context.push(VerifiableConstants.JSONLD_SCHEMA.IDEN3_DISPLAY_METHOD);
34
+ // do copy of request to avoid mutation
35
+ const r = { ...request };
36
+ r.context = r.context ?? [];
37
+ if (r.displayMethod?.type === DisplayMethodType.Iden3BasicDisplayMethodV1 &&
38
+ !r.context.includes(VerifiableConstants.JSONLD_SCHEMA.IDEN3_DISPLAY_METHOD)) {
39
+ r.context.push(VerifiableConstants.JSONLD_SCHEMA.IDEN3_DISPLAY_METHOD);
38
40
  }
39
- const context = [
40
- VerifiableConstants.JSONLD_SCHEMA.W3C_CREDENTIAL_2018,
41
- ...request.context,
42
- VerifiableConstants.JSONLD_SCHEMA.IDEN3_CREDENTIAL,
43
- schema.$metadata.uris['jsonLdContext']
44
- ];
45
- const credentialType = [
46
- VerifiableConstants.CREDENTIAL_TYPE.W3C_VERIFIABLE_CREDENTIAL,
47
- request.type
48
- ];
49
- const expirationDate = !request.expiration || request.expiration == 0 ? null : request.expiration;
50
- const credentialSubject = request.credentialSubject;
51
- credentialSubject['type'] = request.type;
52
- const cr = new W3CCredential();
53
- cr.id = `urn:${uuid.v4()}`;
54
- cr['@context'] = context;
55
- cr.type = credentialType;
56
- cr.expirationDate = expirationDate ? new Date(expirationDate * 1000).toISOString() : undefined;
57
- cr.refreshService = request.refreshService;
58
- cr.displayMethod = request.displayMethod;
59
- cr.issuanceDate = new Date().toISOString();
60
- cr.credentialSubject = credentialSubject;
61
- cr.issuer = issuer.string();
62
- cr.credentialSchema = {
63
- id: request.credentialSchema,
64
- type: VerifiableConstants.JSON_SCHEMA_VALIDATOR
65
- };
66
- cr.credentialStatus = this.buildCredentialStatus(request, issuer);
67
- return cr;
41
+ r.context.push(schema.$metadata.uris['jsonLdContext']);
42
+ r.expiration = r.expiration ? r.expiration * 1000 : undefined;
43
+ r.id = r.id ? r.id : `urn:${uuid.v4()}`;
44
+ r.issuanceDate = r.issuanceDate ? r.issuanceDate * 1000 : Date.now();
45
+ return W3CCredential.fromCredentialRequest(issuer, r);
68
46
  };
69
47
  // if no credential status resolvers are provided
70
48
  // register default issuer resolver
@@ -134,51 +112,6 @@ export class CredentialWallet {
134
112
  }
135
113
  return statusResolver.resolve(credStatus, credentialStatusResolveOptions);
136
114
  }
137
- /**
138
- * Builds credential status
139
- * @param {CredentialRequest} request
140
- * @returns `CredentialStatus`
141
- */
142
- buildCredentialStatus(request, issuer) {
143
- const credentialStatus = {
144
- id: request.revocationOpts.id,
145
- type: request.revocationOpts.type,
146
- revocationNonce: request.revocationOpts.nonce
147
- };
148
- switch (request.revocationOpts.type) {
149
- case CredentialStatusType.SparseMerkleTreeProof:
150
- return {
151
- ...credentialStatus,
152
- id: `${credentialStatus.id.replace(/\/$/, '')}/${credentialStatus.revocationNonce}`
153
- };
154
- case CredentialStatusType.Iden3ReverseSparseMerkleTreeProof:
155
- return {
156
- ...credentialStatus,
157
- id: request.revocationOpts.issuerState
158
- ? `${credentialStatus.id.replace(/\/$/, '')}/node?state=${request.revocationOpts.issuerState}`
159
- : `${credentialStatus.id.replace(/\/$/, '')}`
160
- };
161
- case CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023: {
162
- const issuerId = DID.idFromDID(issuer);
163
- const chainId = getChainId(DID.blockchainFromId(issuerId), DID.networkIdFromId(issuerId));
164
- const searchParams = [
165
- ['revocationNonce', request.revocationOpts.nonce?.toString() || ''],
166
- ['contractAddress', `${chainId}:${request.revocationOpts.id}`],
167
- ['state', request.revocationOpts.issuerState || '']
168
- ]
169
- .filter(([, value]) => Boolean(value))
170
- .map(([key, value]) => `${key}=${value}`)
171
- .join('&');
172
- return {
173
- ...credentialStatus,
174
- // `[did]:[methodid]:[chain]:[network]:[id]/credentialStatus?(revocationNonce=value)&[contractAddress=[chainID]:[contractAddress]]&(state=issuerState)`
175
- id: `${issuer.string()}/credentialStatus?${searchParams}`
176
- };
177
- }
178
- default:
179
- return credentialStatus;
180
- }
181
- }
182
115
  /**
183
116
  * {@inheritDoc ICredentialWallet.findById}
184
117
  */
@@ -4,6 +4,7 @@ export * from './status/sparse-merkle-tree';
4
4
  export * from './status/resolver';
5
5
  export * from './status/agent-revocation';
6
6
  export * from './status/credential-status-publisher';
7
+ export * from './status/did-resolver-revocation';
7
8
  export * from './credential-wallet';
8
9
  export * from './rhs';
9
10
  export * from './utils';
@@ -0,0 +1,17 @@
1
+ export class DidDocumentCredentialStatusResolver {
2
+ constructor(didResolverUrl) {
3
+ this.didResolverUrl = didResolverUrl;
4
+ }
5
+ async resolve(credentialStatus, opts) {
6
+ if (!opts?.issuerDID) {
7
+ throw new Error('IssuerDID is not set in options');
8
+ }
9
+ const url = `${this.didResolverUrl}/1.0/credential-status/${encodeURIComponent(opts.issuerDID.string())}`;
10
+ const resp = await fetch(url, {
11
+ method: 'POST',
12
+ body: JSON.stringify(credentialStatus)
13
+ });
14
+ const data = await resp.json();
15
+ return data;
16
+ }
17
+ }
@@ -47,10 +47,34 @@ export class FetchHandler extends AbstractMessageHandler {
47
47
  return this.handleFetchRequest(message);
48
48
  case PROTOCOL_MESSAGE_TYPE.CREDENTIAL_ISSUANCE_RESPONSE_MESSAGE_TYPE:
49
49
  return this.handleIssuanceResponseMsg(message);
50
+ case PROTOCOL_MESSAGE_TYPE.CREDENTIAL_ONCHAIN_OFFER_MESSAGE_TYPE: {
51
+ const result = await this.handleOnchainOfferMessage(message);
52
+ if (Array.isArray(result)) {
53
+ const credWallet = this.opts?.credentialWallet;
54
+ if (!credWallet)
55
+ throw new Error('Credential wallet is not provided');
56
+ await credWallet.saveAll(result);
57
+ return null;
58
+ }
59
+ return result;
60
+ }
50
61
  default:
51
62
  return super.handle(message, ctx);
52
63
  }
53
64
  }
65
+ async handleOnchainOfferMessage(offerMessage) {
66
+ if (!this.opts?.onchainIssuer) {
67
+ throw new Error('onchain issuer is not provided');
68
+ }
69
+ const credentials = [];
70
+ for (const credentialInfo of offerMessage.body.credentials) {
71
+ const issuerDID = DID.parse(offerMessage.from);
72
+ const userDID = DID.parse(offerMessage.to);
73
+ const credential = await this.opts.onchainIssuer.getCredential(issuerDID, userDID, BigInt(credentialInfo.id));
74
+ credentials.push(credential);
75
+ }
76
+ return credentials;
77
+ }
54
78
  async handleOfferMessage(offerMessage, ctx) {
55
79
  if (!ctx.mediaType) {
56
80
  ctx.mediaType = MediaType.ZKPMessage;
@@ -135,6 +159,14 @@ export class FetchHandler extends AbstractMessageHandler {
135
159
  }
136
160
  throw new Error('invalid protocol message response');
137
161
  }
162
+ /**
163
+ * Handles only messages with credentials/1.0/onchain-offer type
164
+ * @beta
165
+ */
166
+ async handleOnchainOffer(offer) {
167
+ const offerMessage = await FetchHandler.unpackMessage(this._packerMgr, offer, PROTOCOL_MESSAGE_TYPE.CREDENTIAL_ONCHAIN_OFFER_MESSAGE_TYPE);
168
+ return this.handleOnchainOfferMessage(offerMessage);
169
+ }
138
170
  async handleFetchRequest(msgRequest) {
139
171
  if (!msgRequest.to) {
140
172
  throw new Error("failed request. empty 'to' field");
@@ -5,8 +5,8 @@ import * as uuid from 'uuid';
5
5
  import { proving } from '@iden3/js-jwz';
6
6
  import { byteEncoder } from '../../utils';
7
7
  import { AbstractMessageHandler } from './message-handler';
8
- import { PaymentFeatures, PaymentRequestDataType, PaymentType, SupportedPaymentProofType } from '../../verifiable';
9
- import { ethers } from 'ethers';
8
+ import { PaymentFeatures, PaymentRequestDataType, PaymentType, SupportedCurrencies, SupportedPaymentProofType } from '../../verifiable';
9
+ import { ethers, parseEther, parseUnits } from 'ethers';
10
10
  import { verifyExpiresTime } from './common';
11
11
  /**
12
12
  * @beta
@@ -36,19 +36,20 @@ export function createPaymentRequest(sender, receiver, agent, payments, opts) {
36
36
  return request;
37
37
  }
38
38
  export async function verifyEIP712TypedData(data, resolver) {
39
+ const convertedAmount = await convertPaymentAmount(data.amount, data.currency);
39
40
  const paymentData = data.type === PaymentRequestDataType.Iden3PaymentRailsRequestV1
40
41
  ? {
41
42
  recipient: data.recipient,
42
- amount: data.amount,
43
- expirationDate: new Date(data.expirationDate).getTime(),
43
+ amount: convertedAmount,
44
+ expirationDate: getUnixTimestamp(new Date(data.expirationDate)),
44
45
  nonce: data.nonce,
45
46
  metadata: '0x'
46
47
  }
47
48
  : {
48
49
  tokenAddress: data.tokenAddress,
49
50
  recipient: data.recipient,
50
- amount: data.amount,
51
- expirationDate: new Date(data.expirationDate).getTime(),
51
+ amount: convertedAmount,
52
+ expirationDate: getUnixTimestamp(new Date(data.expirationDate)),
52
53
  nonce: data.nonce,
53
54
  metadata: '0x'
54
55
  };
@@ -71,6 +72,30 @@ export async function verifyEIP712TypedData(data, resolver) {
71
72
  }
72
73
  throw new Error(`failed request. no matching verificationMethod`);
73
74
  }
75
+ export async function convertPaymentAmount(amount, currency) {
76
+ let convertedAmount = 0n;
77
+ switch (currency) {
78
+ case SupportedCurrencies.ETH:
79
+ case SupportedCurrencies.POL:
80
+ case SupportedCurrencies.MATIC:
81
+ convertedAmount = parseEther(amount.toString());
82
+ break;
83
+ case SupportedCurrencies.ETH_WEI:
84
+ convertedAmount = parseUnits(amount.toString(), 'wei');
85
+ break;
86
+ case SupportedCurrencies.ETH_GWEI:
87
+ convertedAmount = parseUnits(amount.toString(), 'gwei');
88
+ break;
89
+ case SupportedCurrencies.USDC:
90
+ case SupportedCurrencies.USDT: {
91
+ convertedAmount = parseUnits(amount.toString(), 6);
92
+ break;
93
+ }
94
+ default:
95
+ throw new Error(`failed request. unsupported currency ${currency}`);
96
+ }
97
+ return convertedAmount;
98
+ }
74
99
  /**
75
100
  * @beta
76
101
  * createPayment is a function to create protocol payment message
@@ -268,26 +293,25 @@ export class PaymentHandler extends AbstractMessageHandler {
268
293
  if (type === PaymentRequestDataType.Iden3PaymentRailsERC20RequestV1 && !tokenAddress) {
269
294
  throw new Error(`failed request. no token address for currency ${currency}`);
270
295
  }
271
- const expirationTime = expirationDate
272
- ? new Date(expirationDate).getTime()
273
- : new Date(new Date().setHours(new Date().getHours() + 1)).getTime();
296
+ const expirationDateRequired = expirationDate ?? new Date(new Date().setHours(new Date().getHours() + 1));
274
297
  const typeUrl = `https://schema.iden3.io/core/json/${type}.json`;
275
298
  const typesFetchResult = await fetch(typeUrl);
276
299
  const types = await typesFetchResult.json();
277
300
  delete types.EIP712Domain;
301
+ const convertedAmount = await convertPaymentAmount(amount, currency);
278
302
  const paymentData = type === PaymentRequestDataType.Iden3PaymentRailsRequestV1
279
303
  ? {
280
304
  recipient,
281
- amount,
282
- expirationDate: expirationTime,
305
+ amount: convertedAmount,
306
+ expirationDate: getUnixTimestamp(expirationDateRequired),
283
307
  nonce,
284
308
  metadata: '0x'
285
309
  }
286
310
  : {
287
311
  tokenAddress,
288
312
  recipient,
289
- amount,
290
- expirationDate: expirationTime,
313
+ amount: convertedAmount,
314
+ expirationDate: getUnixTimestamp(expirationDateRequired),
291
315
  nonce,
292
316
  metadata: '0x'
293
317
  };
@@ -321,7 +345,7 @@ export class PaymentHandler extends AbstractMessageHandler {
321
345
  recipient,
322
346
  amount: amount.toString(),
323
347
  currency,
324
- expirationDate: new Date(expirationTime).toISOString(),
348
+ expirationDate: expirationDateRequired.toISOString(),
325
349
  nonce: nonce.toString(),
326
350
  metadata: '0x',
327
351
  proof
@@ -0,0 +1 @@
1
+ [{ "inputs": [{ "internalType": "uint256", "name": "initialSupply", "type": "uint256" }], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [{ "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "allowance", "type": "uint256" }, { "internalType": "uint256", "name": "needed", "type": "uint256" }], "name": "ERC20InsufficientAllowance", "type": "error" }, { "inputs": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint256", "name": "balance", "type": "uint256" }, { "internalType": "uint256", "name": "needed", "type": "uint256" }], "name": "ERC20InsufficientBalance", "type": "error" }, { "inputs": [{ "internalType": "address", "name": "approver", "type": "address" }], "name": "ERC20InvalidApprover", "type": "error" }, { "inputs": [{ "internalType": "address", "name": "receiver", "type": "address" }], "name": "ERC20InvalidReceiver", "type": "error" }, { "inputs": [{ "internalType": "address", "name": "sender", "type": "address" }], "name": "ERC20InvalidSender", "type": "error" }, { "inputs": [{ "internalType": "address", "name": "spender", "type": "address" }], "name": "ERC20InvalidSpender", "type": "error" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" }], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" }], "name": "Transfer", "type": "event" }, { "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }, { "internalType": "address", "name": "spender", "type": "address" }], "name": "allowance", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }], "name": "approve", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], "name": "balanceOf", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "decimals", "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "name", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalSupply", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }], "name": "transfer", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }], "name": "transferFrom", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }]
@@ -0,0 +1,68 @@
1
+ import { Hash } from '@iden3/js-merkletree';
2
+ import { resolveDidDocument } from '../../utils';
3
+ import { DID, Id } from '@iden3/js-iden3-core';
4
+ import { JsonRpcProvider } from 'ethers';
5
+ export class DidResolverStateReadonlyStorage {
6
+ constructor(resolverUrl) {
7
+ this.resolverUrl = resolverUrl;
8
+ }
9
+ async getLatestStateById(id) {
10
+ return this.getStateInfo(id);
11
+ }
12
+ async getStateInfoByIdAndState(id, state) {
13
+ return this.getStateInfo(id, state);
14
+ }
15
+ async getGISTProof(id) {
16
+ const { didDocument } = await resolveDidDocument(DID.parseFromId(Id.fromBigInt(id)), this.resolverUrl);
17
+ const { global } = this.getIden3StateInfo2023(didDocument);
18
+ if (!global) {
19
+ throw new Error('GIST root not found');
20
+ }
21
+ const { proof } = global;
22
+ if (!proof) {
23
+ throw new Error('GIST proof not found');
24
+ }
25
+ return {
26
+ root: global.root,
27
+ existence: proof.existence,
28
+ siblings: proof.siblings?.map((sibling) => BigInt(sibling)),
29
+ index: BigInt(0),
30
+ value: BigInt(0),
31
+ auxExistence: !!proof.node_aux,
32
+ auxIndex: proof.node_aux ? BigInt(proof.node_aux.key) : BigInt(0),
33
+ auxValue: proof.node_aux ? BigInt(proof.node_aux.value) : BigInt(0)
34
+ };
35
+ }
36
+ async getGISTRootInfo(root, userId) {
37
+ const { didDocument } = await resolveDidDocument(DID.parseFromId(Id.fromBigInt(userId)), this.resolverUrl, {
38
+ gist: Hash.fromBigInt(root)
39
+ });
40
+ const { global } = this.getIden3StateInfo2023(didDocument);
41
+ if (!global) {
42
+ throw new Error('GIST root not found');
43
+ }
44
+ return global;
45
+ }
46
+ getRpcProvider() {
47
+ return new JsonRpcProvider();
48
+ }
49
+ publishState() {
50
+ throw new Error('publishState method not implemented.');
51
+ }
52
+ publishStateGeneric() {
53
+ throw new Error('publishStateGeneric method not implemented.');
54
+ }
55
+ async getStateInfo(id, state) {
56
+ const opts = state ? { state: Hash.fromBigInt(state) } : undefined;
57
+ const { didDocument } = await resolveDidDocument(DID.parseFromId(Id.fromBigInt(id)), this.resolverUrl, opts);
58
+ const { info } = this.getIden3StateInfo2023(didDocument);
59
+ return { ...info };
60
+ }
61
+ getIden3StateInfo2023(didDocument) {
62
+ const vm = didDocument.verificationMethod?.find((i) => i.type === 'Iden3StateInfo2023');
63
+ if (!vm) {
64
+ throw new Error('Iden3StateInfo2023 verification method not found');
65
+ }
66
+ return vm;
67
+ }
68
+ }
@@ -1,5 +1,6 @@
1
1
  import { Contract } from 'ethers';
2
- import abi from './abi/ERC20Permit.json';
2
+ import permitAbi from './abi/ERC20Permit.json';
3
+ import erc20Abi from './abi/ERC20.json';
3
4
  /**
4
5
  * @beta
5
6
  * getPermitSignature is a function to create EIP712 Permit signature
@@ -11,10 +12,11 @@ import abi from './abi/ERC20Permit.json';
11
12
  * @returns {Promise<PaymentRequestMessage>}
12
13
  */
13
14
  export async function getPermitSignature(signer, tokenAddress, spender, value, deadline) {
14
- const erc20PermitContract = new Contract(tokenAddress, abi, signer);
15
+ const erc20PermitContract = new Contract(tokenAddress, permitAbi, signer);
15
16
  const nonce = await erc20PermitContract.nonces(await signer.getAddress());
16
17
  const domainData = await erc20PermitContract.eip712Domain();
17
18
  const domain = {
19
+ name: domainData[1],
18
20
  version: domainData[2],
19
21
  chainId: domainData[3],
20
22
  verifyingContract: tokenAddress
@@ -37,3 +39,13 @@ export async function getPermitSignature(signer, tokenAddress, spender, value, d
37
39
  };
38
40
  return signer.signTypedData(domain, types, message);
39
41
  }
42
+ /**
43
+ * @beta
44
+ * getERC20Decimals is a function to retrieve the number of decimals of an ERC20 token
45
+ * @param {string} tokenAddress - Token address
46
+ * @param {ethers.ContractRunner} runner - Contract runner
47
+ */
48
+ export async function getERC20Decimals(tokenAddress, runner) {
49
+ const erc20Contract = new Contract(tokenAddress, erc20Abi, runner);
50
+ return erc20Contract.decimals();
51
+ }
@@ -1,4 +1,6 @@
1
1
  export * from './state';
2
2
  export * from './onchain-zkp-verifier';
3
3
  export * from './onchain-revocation';
4
- export * from './erc20-permit-sig';
4
+ export * from './onchain-issuer';
5
+ export * from './did-resolver-readonly-storage';
6
+ export * from './erc20-helper';
@@ -0,0 +1,241 @@
1
+ import { Claim, DID, Id } from '@iden3/js-iden3-core';
2
+ import { NonMerklizedIssuerBase__factory } from '@iden3/onchain-non-merklized-issuer-base-abi';
3
+ import { CredentialStatusType, DisplayMethodType, Iden3SparseMerkleTreeProof, W3CCredential } from '../../../../../../verifiable';
4
+ import { Merklizer, Path } from '@iden3/js-jsonld-merklization';
5
+ import { XSDNS } from '../../../../../../circuits';
6
+ import { Hash, Proof } from '@iden3/js-merkletree';
7
+ import { ethers } from 'ethers';
8
+ import { getDateFromUnixTimestamp } from '@iden3/js-iden3-core';
9
+ var NonMerklizedIssuerInterfaces;
10
+ (function (NonMerklizedIssuerInterfaces) {
11
+ NonMerklizedIssuerInterfaces["InterfaceDetection"] = "0x01ffc9a7";
12
+ NonMerklizedIssuerInterfaces["InterfaceNonMerklizedIssuer"] = "0x58874949";
13
+ NonMerklizedIssuerInterfaces["InterfaceGetCredential"] = "0x5d1ca631";
14
+ })(NonMerklizedIssuerInterfaces || (NonMerklizedIssuerInterfaces = {}));
15
+ var ValueHashes;
16
+ (function (ValueHashes) {
17
+ ValueHashes["BooleanTrue"] = "18586133768512220936620570745912940619677854269274689475585506675881198879027";
18
+ ValueHashes["BooleanFalse"] = "19014214495641488759237505126948346942972912379615652741039992445865937985820";
19
+ })(ValueHashes || (ValueHashes = {}));
20
+ /**
21
+ * `OnchainNonMerklizedIssuerAdapter` provides functionality to interact with a non-merklized on-chain credential issuer.
22
+ * This adapter enables interface detection, credential retrieval, and conversion to the W3C Verifiable Credential format.
23
+ *
24
+ * @public
25
+ * @beta
26
+ * @class OnchainNonMerklizedIssuerAdapter
27
+ */
28
+ export class OnchainNonMerklizedIssuerAdapter {
29
+ /**
30
+ * Initializes an instance of `OnchainNonMerklizedIssuerAdapter`.
31
+ *
32
+ * @param ethConnectionConfig The configuration for the Ethereum connection.
33
+ * @param issuerDid The decentralized identifier (DID) of the issuer.
34
+ * @param merklizationOptions Optional settings for merklization.
35
+ */
36
+ constructor(ethConnectionConfig, issuerDid, options) {
37
+ if (!ethConnectionConfig.chainId) {
38
+ throw new Error('Chain ID is required');
39
+ }
40
+ this._chainId = ethConnectionConfig.chainId;
41
+ this._contractAddress = ethers.getAddress(ethers.hexlify(Id.ethAddressFromId(DID.idFromDID(issuerDid))));
42
+ this._contract = NonMerklizedIssuerBase__factory.connect(this._contractAddress, new ethers.JsonRpcProvider(ethConnectionConfig.url));
43
+ this._issuerDid = issuerDid;
44
+ this._merklizationOptions = options?.merklizationOptions;
45
+ }
46
+ /**
47
+ * Checks if the contract supports required interfaces.
48
+ * Throws an error if any required interface is unsupported.
49
+ *
50
+ * @throws Error - If required interfaces are not supported.
51
+ */
52
+ async isInterfaceSupported() {
53
+ const supportedInterfaces = [
54
+ {
55
+ name: 'Interface detection ERC-165',
56
+ value: NonMerklizedIssuerInterfaces.InterfaceDetection
57
+ },
58
+ {
59
+ name: 'Interface non-merklized issuer',
60
+ value: NonMerklizedIssuerInterfaces.InterfaceNonMerklizedIssuer
61
+ },
62
+ {
63
+ name: 'Interface get credential',
64
+ value: NonMerklizedIssuerInterfaces.InterfaceGetCredential
65
+ }
66
+ ];
67
+ const unsupportedInterfaces = await Promise.all(supportedInterfaces.map(async (interfaceObj) => {
68
+ const isSupported = await this._contract.supportsInterface(interfaceObj.value);
69
+ return isSupported ? null : interfaceObj.name;
70
+ }));
71
+ const unsupportedInterfacesFiltered = unsupportedInterfaces.filter((interfaceName) => interfaceName !== null);
72
+ if (unsupportedInterfacesFiltered.length > 0) {
73
+ throw new Error(`Unsupported interfaces: ${unsupportedInterfacesFiltered.join(', ')}`);
74
+ }
75
+ }
76
+ /**
77
+ * Retrieves a credential from the on-chain non-merklized contract.
78
+ * @param userId The user's core.Id.
79
+ * @param credentialId The unique identifier of the credential.
80
+ */
81
+ async getCredential(userId, credentialId) {
82
+ const [credentialData, coreClaimBigInts, credentialSubjectFields] = await this._contract.getCredential(userId.bigInt(), credentialId);
83
+ return { credentialData, coreClaimBigInts, credentialSubjectFields };
84
+ }
85
+ /**
86
+ * Retrieves the credential IDs of a user.
87
+ * @param userId The user's core.Id.
88
+ * @returns An array of credential IDs.
89
+ */
90
+ async getUserCredentialsIds(userId) {
91
+ return this._contract.getUserCredentialIds(userId.bigInt());
92
+ }
93
+ /**
94
+ * Converts on-chain credential to a verifiable credential.
95
+ *
96
+ * @param credentialData Data structure of the credential from the contract.
97
+ * @param coreClaimBigInts Claim data in bigint format.
98
+ * @param credentialSubjectFields Subject fields of the credential.
99
+ */
100
+ async convertOnChainInfoToW3CCredential(credentialData, coreClaimBigInts, credentialSubjectFields) {
101
+ const c = new Claim().unMarshalJson(JSON.stringify(coreClaimBigInts.map((b) => b.toString())));
102
+ const credentialSubject = await this.convertCredentialSubject(c, credentialData.context, credentialData._type, credentialSubjectFields);
103
+ const credentialRequest = {
104
+ id: this.credentialId(credentialData.id),
105
+ credentialSchema: credentialData.credentialSchema.id,
106
+ type: credentialData._type,
107
+ credentialSubject: credentialSubject,
108
+ expiration: c.getExpirationDate()?.getTime(),
109
+ displayMethod: this.convertDisplayMethod(credentialData.displayMethod),
110
+ context: credentialData.context,
111
+ revocationOpts: {
112
+ id: this._contractAddress,
113
+ nonce: Number(c.getRevocationNonce()),
114
+ type: CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023
115
+ },
116
+ issuanceDate: getDateFromUnixTimestamp(Number(credentialData.issuanceDate)).getTime()
117
+ };
118
+ const existenceProof = await this.existenceProof(c);
119
+ const w3c = W3CCredential.fromCredentialRequest(this._issuerDid, credentialRequest);
120
+ w3c.proof = [existenceProof];
121
+ return w3c;
122
+ }
123
+ credentialId(id) {
124
+ return `urn:iden3:onchain:${this._chainId}:${this._contractAddress}:${id}`;
125
+ }
126
+ async convertCredentialSubject(coreClaim, contractContexts, credentialType, credentialSubjectFields) {
127
+ const contractContextsStr = JSON.stringify({
128
+ '@context': contractContexts
129
+ });
130
+ const credentialSubject = {};
131
+ for (const f of credentialSubjectFields) {
132
+ const dataType = await Path.newTypeFromContext(contractContextsStr, `${credentialType}.${f.key}`, this._merklizationOptions);
133
+ switch (dataType) {
134
+ case XSDNS.Boolean: {
135
+ switch (f.rawValue.toString()) {
136
+ case ValueHashes.BooleanTrue:
137
+ credentialSubject[f.key] = true;
138
+ break;
139
+ case ValueHashes.BooleanFalse:
140
+ credentialSubject[f.key] = false;
141
+ break;
142
+ }
143
+ break;
144
+ }
145
+ case (XSDNS.NonNegativeInteger,
146
+ XSDNS.NonPositiveInteger,
147
+ XSDNS.NegativeInteger,
148
+ XSDNS.PositiveInteger):
149
+ {
150
+ credentialSubject[f.key] = f.value.toString();
151
+ break;
152
+ }
153
+ case XSDNS.Integer: {
154
+ credentialSubject[f.key] = Number(f.value);
155
+ break;
156
+ }
157
+ case XSDNS.String: {
158
+ this.validateSourceValue(dataType, f.value, f.rawValue);
159
+ credentialSubject[f.key] = f.rawValue;
160
+ break;
161
+ }
162
+ case XSDNS.DateTime: {
163
+ const timestamp = BigInt(f.rawValue);
164
+ const sourceTimestamp = getDateFromUnixTimestamp(Number(timestamp)).toISOString();
165
+ this.validateSourceValue(dataType, f.value, sourceTimestamp);
166
+ credentialSubject[f.key] = sourceTimestamp;
167
+ break;
168
+ }
169
+ case XSDNS.Double: {
170
+ const rawFloat = Number(f.rawValue);
171
+ this.validateSourceValue(dataType, f.value, rawFloat);
172
+ credentialSubject[f.key] = rawFloat;
173
+ break;
174
+ }
175
+ default: {
176
+ throw new Error(`Unsupported data type ${dataType}`);
177
+ }
178
+ }
179
+ }
180
+ credentialSubject['type'] = credentialType;
181
+ const subjectId = coreClaim.getId();
182
+ const subjectDid = DID.parseFromId(subjectId);
183
+ credentialSubject['id'] = subjectDid.string();
184
+ return credentialSubject;
185
+ }
186
+ async existenceProof(coreClaim) {
187
+ const [mtpProof, stateInfo] = await this._contract.getClaimProofWithStateInfo(coreClaim.hIndex());
188
+ if (!mtpProof.existence) {
189
+ throw new Error('Claim does not exist');
190
+ }
191
+ const latestStateHash = Hash.fromBigInt(stateInfo.state);
192
+ const latestClaimsOfRootHash = Hash.fromBigInt(stateInfo.claimsRoot);
193
+ const latestRevocationOfRootHash = Hash.fromBigInt(stateInfo.revocationsRoot);
194
+ const latestRootsOfRootHash = Hash.fromBigInt(stateInfo.rootsRoot);
195
+ const p = new Proof({
196
+ siblings: mtpProof.siblings.map((s) => Hash.fromBigInt(s)),
197
+ existence: mtpProof.existence,
198
+ nodeAux: mtpProof.auxExistence
199
+ ? {
200
+ key: Hash.fromBigInt(mtpProof.auxIndex),
201
+ value: Hash.fromBigInt(mtpProof.auxValue)
202
+ }
203
+ : undefined
204
+ });
205
+ return new Iden3SparseMerkleTreeProof({
206
+ issuerData: {
207
+ id: this._issuerDid,
208
+ state: {
209
+ value: latestStateHash,
210
+ claimsTreeRoot: latestClaimsOfRootHash,
211
+ revocationTreeRoot: latestRevocationOfRootHash,
212
+ rootOfRoots: latestRootsOfRootHash
213
+ }
214
+ },
215
+ mtp: p,
216
+ coreClaim: coreClaim
217
+ });
218
+ }
219
+ async validateSourceValue(dataType, originHash, source) {
220
+ const sourceHash = await Merklizer.hashValue(dataType, source);
221
+ if (sourceHash !== originHash) {
222
+ throw new Error(`Invalid source value for ${dataType} type`);
223
+ }
224
+ }
225
+ convertDisplayMethod(onchainDisplayMethod) {
226
+ if (!onchainDisplayMethod.id || !onchainDisplayMethod._type) {
227
+ return undefined;
228
+ }
229
+ switch (onchainDisplayMethod._type) {
230
+ case DisplayMethodType.Iden3BasicDisplayMethodV1: {
231
+ return {
232
+ id: onchainDisplayMethod.id,
233
+ type: DisplayMethodType.Iden3BasicDisplayMethodV1
234
+ };
235
+ }
236
+ default: {
237
+ throw new Error(`Unsupported display method type ${onchainDisplayMethod._type}`);
238
+ }
239
+ }
240
+ }
241
+ }