@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.
- package/dist/browser/esm/index.js +3103 -358
- package/dist/browser/esm/index.js.map +1 -1
- package/dist/browser/umd/index.js +3106 -356
- package/dist/browser/umd/index.js.map +1 -1
- package/dist/node/cjs/credentials/credential-wallet.js +11 -78
- package/dist/node/cjs/credentials/index.js +1 -0
- package/dist/node/cjs/credentials/status/did-resolver-revocation.js +21 -0
- package/dist/node/cjs/iden3comm/handlers/fetch.js +32 -0
- package/dist/node/cjs/iden3comm/handlers/payment.js +38 -13
- package/dist/node/cjs/storage/blockchain/abi/ERC20.json +1 -0
- package/dist/node/cjs/storage/blockchain/did-resolver-readonly-storage.js +72 -0
- package/dist/node/cjs/storage/blockchain/{erc20-permit-sig.js → erc20-helper.js} +14 -1
- package/dist/node/cjs/storage/blockchain/index.js +3 -1
- package/dist/node/cjs/storage/blockchain/onchain-issuer-adapter/non-merklized/version/v0.0.1/onchain-non-merklized-issuer-adapter.js +245 -0
- package/dist/node/cjs/storage/blockchain/onchain-issuer.js +82 -0
- package/dist/node/cjs/storage/blockchain/onchain-zkp-verifier.js +38 -34
- package/dist/node/cjs/storage/interfaces/index.js +1 -0
- package/dist/node/cjs/storage/interfaces/onchain-issuer.js +2 -0
- package/dist/node/cjs/utils/did-helper.js +3 -3
- package/dist/node/cjs/verifiable/constants.js +1 -0
- package/dist/node/cjs/verifiable/credential.js +86 -0
- package/dist/node/esm/credentials/credential-wallet.js +12 -79
- package/dist/node/esm/credentials/index.js +1 -0
- package/dist/node/esm/credentials/status/did-resolver-revocation.js +17 -0
- package/dist/node/esm/iden3comm/handlers/fetch.js +32 -0
- package/dist/node/esm/iden3comm/handlers/payment.js +38 -14
- package/dist/node/esm/storage/blockchain/abi/ERC20.json +1 -0
- package/dist/node/esm/storage/blockchain/did-resolver-readonly-storage.js +68 -0
- package/dist/node/esm/storage/blockchain/{erc20-permit-sig.js → erc20-helper.js} +14 -2
- package/dist/node/esm/storage/blockchain/index.js +3 -1
- package/dist/node/esm/storage/blockchain/onchain-issuer-adapter/non-merklized/version/v0.0.1/onchain-non-merklized-issuer-adapter.js +241 -0
- package/dist/node/esm/storage/blockchain/onchain-issuer.js +78 -0
- package/dist/node/esm/storage/blockchain/onchain-zkp-verifier.js +38 -34
- package/dist/node/esm/storage/interfaces/index.js +1 -0
- package/dist/node/esm/storage/interfaces/onchain-issuer.js +1 -0
- package/dist/node/esm/utils/did-helper.js +3 -3
- package/dist/node/esm/verifiable/constants.js +1 -0
- package/dist/node/esm/verifiable/credential.js +88 -2
- package/dist/types/credentials/credential-wallet.d.ts +0 -6
- package/dist/types/credentials/index.d.ts +1 -0
- package/dist/types/credentials/models.d.ts +8 -0
- package/dist/types/credentials/status/did-resolver-revocation.d.ts +7 -0
- package/dist/types/iden3comm/handlers/fetch.d.ts +8 -0
- package/dist/types/iden3comm/handlers/payment.d.ts +3 -2
- package/dist/types/storage/blockchain/did-resolver-readonly-storage.d.ts +16 -0
- package/dist/types/storage/blockchain/{erc20-permit-sig.d.ts → erc20-helper.d.ts} +8 -1
- package/dist/types/storage/blockchain/index.d.ts +3 -1
- package/dist/types/storage/blockchain/onchain-issuer-adapter/non-merklized/version/v0.0.1/onchain-non-merklized-issuer-adapter.d.ts +66 -0
- package/dist/types/storage/blockchain/onchain-issuer.d.ts +40 -0
- package/dist/types/storage/blockchain/onchain-zkp-verifier.d.ts +11 -12
- package/dist/types/storage/entities/state.d.ts +2 -0
- package/dist/types/storage/interfaces/index.d.ts +1 -0
- package/dist/types/storage/interfaces/onchain-issuer.d.ts +12 -0
- package/dist/types/verifiable/constants.d.ts +1 -0
- package/dist/types/verifiable/credential.d.ts +15 -2
- package/package.json +3 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DID
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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:
|
|
43
|
-
expirationDate: new Date(data.expirationDate)
|
|
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:
|
|
51
|
-
expirationDate: new Date(data.expirationDate)
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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,
|
|
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
|
+
}
|
|
@@ -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
|
+
}
|