@verii/endpoints-event-processing 1.0.0-pre.1752076816

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 (37) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +1 -0
  3. package/README.md +42 -0
  4. package/package.json +64 -0
  5. package/src/config/abi.json +1 -0
  6. package/src/config/config.js +138 -0
  7. package/src/controllers/events-processing/controller.js +115 -0
  8. package/src/controllers/health-probes/autohooks.js +6 -0
  9. package/src/controllers/health-probes/controller.js +117 -0
  10. package/src/entities/burned-coupons/index.js +3 -0
  11. package/src/entities/burned-coupons/repo.js +29 -0
  12. package/src/entities/health-probes/domains/health-states.js +8 -0
  13. package/src/entities/health-probes/domains/index.js +3 -0
  14. package/src/entities/health-probes/index.js +3 -0
  15. package/src/entities/index.js +23 -0
  16. package/src/entities/oauth/index.js +19 -0
  17. package/src/entities/oauth/scopes.js +21 -0
  18. package/src/entities/purchases/domain/constants.js +64 -0
  19. package/src/entities/purchases/domain/index.js +3 -0
  20. package/src/entities/purchases/index.js +4 -0
  21. package/src/entities/purchases/repo.js +39 -0
  22. package/src/entities/transactions/domain/constants.js +31 -0
  23. package/src/entities/transactions/domain/index.js +3 -0
  24. package/src/entities/transactions/index.js +3 -0
  25. package/src/event-processing-endpoints.js +57 -0
  26. package/src/handlers/handle-coupons-burned-logging-event.js +58 -0
  27. package/src/handlers/handle-coupons-burned-verification-event.js +188 -0
  28. package/src/handlers/handle-coupons-minted-logging-event.js +73 -0
  29. package/src/handlers/handle-credential-issued-logging-event.js +70 -0
  30. package/src/handlers/handle-credential-issued-rewards-event.js +218 -0
  31. package/src/handlers/index.js +7 -0
  32. package/src/helpers/document-functions.js +28 -0
  33. package/src/helpers/event-decoding.js +20 -0
  34. package/src/helpers/index.js +5 -0
  35. package/src/helpers/map-coupon-burned.js +32 -0
  36. package/src/index.js +22 -0
  37. package/src/init-server.js +65 -0
@@ -0,0 +1,188 @@
1
+ const { map, zip } = require('lodash/fp');
2
+ const { initVerificationCoupon } = require('@verii/metadata-registration');
3
+
4
+ const { batchOperations } = require('@verii/fineract-client');
5
+ const { getDidAndAliases } = require('@verii/did-doc');
6
+
7
+ const task = 'coupons-burned-verification';
8
+
9
+ const { initDocumentFunctions, mapCouponBurned } = require('../helpers');
10
+
11
+ const initReadEventsFromBlock = async (context) => {
12
+ const { config } = context;
13
+ const { pullBurnCouponEvents } = await initVerificationCoupon(
14
+ {
15
+ contractAddress: config.couponContractAddress,
16
+ rpcProvider: context.rpcProvider,
17
+ },
18
+ context
19
+ );
20
+
21
+ return async (block) => {
22
+ return pullBurnCouponEvents(block);
23
+ };
24
+ };
25
+ const syncBurnsWithFineract = async (
26
+ { burnerDidToBundleMap, organizationsMap, burnEvents },
27
+ context
28
+ ) => {
29
+ const { log } = context;
30
+ if (burnerDidToBundleMap.size === 0) {
31
+ return [];
32
+ }
33
+
34
+ const voucherQuantitiesToBurn = map((burnEvent) => {
35
+ const orgOfBurnEvent = organizationsMap.get(burnEvent.burnerDid);
36
+ return {
37
+ clientId: orgOfBurnEvent.ids.fineractClientId,
38
+ quantity: 1,
39
+ submittedOnDate: burnEvent.burnTime,
40
+ };
41
+ }, burnEvents);
42
+
43
+ log.info({ task, voucherQuantitiesToBurn });
44
+ const batchResponses = await batchOperations(
45
+ { clientVoucherBurns: voucherQuantitiesToBurn, transactionalBatch: false },
46
+ context
47
+ );
48
+
49
+ for (const [payload, batchResponse] of zip(
50
+ voucherQuantitiesToBurn,
51
+ batchResponses
52
+ )) {
53
+ if (batchResponse.statusCode >= 400)
54
+ log.warn({
55
+ code: 'failed-burn-sync',
56
+ batchResponse,
57
+ payload,
58
+ });
59
+ }
60
+
61
+ return batchResponses;
62
+ };
63
+
64
+ const writeBurnsToDatabase = async (
65
+ { burnerDidToBundleMap, organizationsMap },
66
+ context
67
+ ) => {
68
+ const { repos } = context;
69
+ if (burnerDidToBundleMap.size === 0) {
70
+ return [];
71
+ }
72
+ const now = new Date();
73
+ const burnedCouponsProms = [];
74
+ for (const [burnerDid, bundleToBurnsMap] of burnerDidToBundleMap.entries()) {
75
+ for (const [bundleId, count] of bundleToBurnsMap.entries()) {
76
+ const prom = async () => {
77
+ const purchase = await repos.purchases.findOne({
78
+ filter: {
79
+ 'couponBundle.couponBundleId': bundleId,
80
+ $expr: { $lt: ['$couponBundle.used', '$couponBundle.quantity'] },
81
+ },
82
+ });
83
+
84
+ if (!purchase) return Promise.resolve();
85
+
86
+ await repos.purchases.collection().findOneAndUpdate(
87
+ { _id: purchase._id },
88
+ {
89
+ $inc: {
90
+ 'couponBundle.used': count,
91
+ },
92
+ }
93
+ );
94
+
95
+ return repos.burnedCoupons.insert({
96
+ purchaseId: purchase.purchaseId,
97
+ used: count,
98
+ at: now,
99
+ clientId: organizationsMap.get(burnerDid)?.ids.brokerClientId,
100
+ });
101
+ };
102
+ burnedCouponsProms.push(prom());
103
+ }
104
+ }
105
+ return Promise.all(burnedCouponsProms);
106
+ };
107
+
108
+ const processEventGenerator = async ({ eventsCursor }, context) => {
109
+ const burnerDidToBundleMap = new Map();
110
+ let numberOfEventsRead = 0;
111
+ let burnEvents = [];
112
+ for await (const selectedEvents of eventsCursor()) {
113
+ const mappedEvents = map(
114
+ (evt) => mapCouponBurned(evt, context),
115
+ selectedEvents
116
+ );
117
+ burnEvents = [...burnEvents, ...mappedEvents];
118
+ numberOfEventsRead += selectedEvents.length;
119
+ for (const evt of mappedEvents) {
120
+ const { burnerDid, bundleIdHex: bundleId } = evt;
121
+ // eslint-disable-next-line max-depth
122
+ if (!burnerDidToBundleMap.has(burnerDid)) {
123
+ burnerDidToBundleMap.set(burnerDid, new Map());
124
+ }
125
+ const bundleToBurnsMap = burnerDidToBundleMap.get(burnerDid);
126
+ // eslint-disable-next-line max-depth
127
+ if (!bundleToBurnsMap.has(bundleId)) {
128
+ bundleToBurnsMap.set(bundleId, 0);
129
+ }
130
+ bundleToBurnsMap.set(bundleId, bundleToBurnsMap.get(bundleId) + 1);
131
+ }
132
+ }
133
+ return {
134
+ burnEvents,
135
+ numberOfEventsRead,
136
+ burnerDidToBundleMap,
137
+ };
138
+ };
139
+
140
+ const handleCouponsBurnedVerificationEvent = async (context) => {
141
+ const { log, repos } = context;
142
+ const { readLastSuccessfulBlock, writeLastSuccessfulBlock } =
143
+ initDocumentFunctions({ eventName: task }, context);
144
+
145
+ const readEventsFromBlock = await initReadEventsFromBlock(context);
146
+
147
+ const lastReadBlock = await readLastSuccessfulBlock();
148
+ log.info({ task, lastReadBlock });
149
+ const initialBlockNumber = lastReadBlock + 1;
150
+ const { eventsCursor, latestBlock } = await readEventsFromBlock(
151
+ initialBlockNumber
152
+ );
153
+
154
+ const { numberOfEventsRead, burnEvents, burnerDidToBundleMap } =
155
+ await processEventGenerator({ eventsCursor }, context);
156
+ log.info({
157
+ task,
158
+ lastReadBlock,
159
+ numberOfEventsRead,
160
+ });
161
+
162
+ const organizations = await repos.organizations.findByDids(
163
+ Array.from(burnerDidToBundleMap.keys())
164
+ );
165
+ const organizationsMap = new Map();
166
+ for (const organization of organizations) {
167
+ const ids = getDidAndAliases(organization?.didDoc);
168
+ for (const id of ids) {
169
+ organizationsMap.set(id, organization);
170
+ }
171
+ }
172
+
173
+ await writeBurnsToDatabase(
174
+ { burnerDidToBundleMap, organizationsMap },
175
+ context
176
+ );
177
+ await syncBurnsWithFineract(
178
+ { burnerDidToBundleMap, organizationsMap, burnEvents },
179
+ context
180
+ );
181
+
182
+ log.info({ task, latestBlock });
183
+ await writeLastSuccessfulBlock(latestBlock);
184
+ };
185
+
186
+ module.exports = {
187
+ handleCouponsBurnedVerificationEvent,
188
+ };
@@ -0,0 +1,73 @@
1
+ const { map, forEach } = require('lodash/fp');
2
+ const { toHexString } = require('@verii/blockchain-functions');
3
+ const { initVerificationCoupon } = require('@verii/metadata-registration');
4
+ const { initDocumentFunctions } = require('../helpers');
5
+
6
+ const task = 'coupons-minted-logging';
7
+
8
+ const mapEvent = (event) => ({
9
+ blockNumber: event.blockNumber,
10
+ blockHash: event.blockHash,
11
+ transactionIndex: event.transactionIndex,
12
+ transactionHash: event.transactionHash,
13
+ event: event.fragment.name,
14
+ owner: event.args[0],
15
+ bundleId: `${event.args[1]}`,
16
+ bundleIdHex: toHexString(event.args[1]),
17
+ expirationTime: new Date(Number(`${event.args[2]}`) * 1000),
18
+ quantity: `${event.args[3]}`,
19
+ eventTraceId: event.args[4],
20
+ ownerDid: event.args[5],
21
+ });
22
+
23
+ const initReadEventsFromBlock = async (context) => {
24
+ const { config } = context;
25
+ const { pullMintCouponBundleEvents } = await initVerificationCoupon(
26
+ {
27
+ contractAddress: config.couponContractAddress,
28
+ rpcProvider: context.rpcProvider,
29
+ },
30
+ context
31
+ );
32
+
33
+ return async (block) => {
34
+ return pullMintCouponBundleEvents(block);
35
+ };
36
+ };
37
+
38
+ const handleCouponsMintedLoggingEvent = async (context) => {
39
+ const { log } = context;
40
+ const { readLastSuccessfulBlock, writeLastSuccessfulBlock } =
41
+ initDocumentFunctions({ eventName: task }, context);
42
+
43
+ const readEventsFromBlock = await initReadEventsFromBlock(context);
44
+
45
+ const lastReadBlock = await readLastSuccessfulBlock();
46
+
47
+ log.info({ task, lastReadBlock });
48
+
49
+ const { eventsCursor, latestBlock } = await readEventsFromBlock(
50
+ lastReadBlock + 1
51
+ );
52
+ let numberOfEventsRead = 0;
53
+ for await (const events of eventsCursor()) {
54
+ numberOfEventsRead += events.length;
55
+ const mappedEvents = map(mapEvent, events);
56
+ forEach((event) => {
57
+ log.info(event);
58
+ }, mappedEvents);
59
+ }
60
+
61
+ log.info({
62
+ task,
63
+ lastReadBlock,
64
+ numberOfEventsRead,
65
+ });
66
+
67
+ log.info({ task, latestBlock });
68
+ await writeLastSuccessfulBlock(latestBlock);
69
+ };
70
+
71
+ module.exports = {
72
+ handleCouponsMintedLoggingEvent,
73
+ };
@@ -0,0 +1,70 @@
1
+ const { map, forEach } = require('lodash/fp');
2
+ const { initMetadataRegistry } = require('@verii/metadata-registration');
3
+ const { initDocumentFunctions, decodeIssuerVc } = require('../helpers');
4
+
5
+ const task = 'credential-issued-logging';
6
+
7
+ const mapEvent = (event) => ({
8
+ blockNumber: event.blockNumber,
9
+ blockHash: event.blockHash,
10
+ transactionIndex: event.transactionIndex,
11
+ transactionHash: event.transactionHash,
12
+ event: event.fragment.name,
13
+ sender: event.args[0],
14
+ issuerDid: decodeIssuerVc(event.args[1]),
15
+ listId: `${event.args[2]}`,
16
+ credentialType: event.args[3],
17
+ index: `${event.args[4]}`,
18
+ eventTraceId: event.args[5],
19
+ caoDid: event.args[6],
20
+ });
21
+
22
+ const initReadEventsFromBlock = async (context) => {
23
+ const { config } = context;
24
+ const { pullAddedCredentialMetadataEvents } = await initMetadataRegistry(
25
+ {
26
+ contractAddress: config.metadataRegistryContractAddress,
27
+ rpcProvider: context.rpcProvider,
28
+ },
29
+ context
30
+ );
31
+
32
+ return async (block) => {
33
+ return pullAddedCredentialMetadataEvents(block);
34
+ };
35
+ };
36
+
37
+ const handleCredentialIssuedLoggingEvent = async (context) => {
38
+ const { log } = context;
39
+ const { readLastSuccessfulBlock, writeLastSuccessfulBlock } =
40
+ initDocumentFunctions({ eventName: task }, context);
41
+
42
+ const readEventsFromBlock = await initReadEventsFromBlock(context);
43
+
44
+ const lastReadBlock = await readLastSuccessfulBlock();
45
+
46
+ log.info({ task, lastReadBlock });
47
+
48
+ const { eventsCursor, latestBlock } = await readEventsFromBlock(
49
+ lastReadBlock + 1
50
+ );
51
+ let numberOfEventsRead = 0;
52
+ for await (const events of eventsCursor()) {
53
+ numberOfEventsRead += events.length;
54
+ const mappedEvents = map(mapEvent, events);
55
+ forEach((event) => log.info(event), mappedEvents);
56
+ }
57
+
58
+ log.info({
59
+ task,
60
+ lastReadBlock,
61
+ numberOfEventsRead,
62
+ });
63
+
64
+ log.info({ task, latestBlock });
65
+ await writeLastSuccessfulBlock(latestBlock);
66
+ };
67
+
68
+ module.exports = {
69
+ handleCredentialIssuedLoggingEvent,
70
+ };
@@ -0,0 +1,218 @@
1
+ const {
2
+ partition,
3
+ reduce,
4
+ map,
5
+ size,
6
+ isEmpty,
7
+ uniq,
8
+ flow,
9
+ find,
10
+ } = require('lodash/fp');
11
+ const { initMetadataRegistry } = require('@verii/metadata-registration');
12
+
13
+ const { get2BytesHash } = require('@verii/crypto');
14
+ const { batchTransferCredits } = require('@verii/fineract-client');
15
+
16
+ const { getDidAndAliases } = require('@verii/did-doc');
17
+ const { TransactionReasons } = require('../entities');
18
+ const { decodeIssuerVc, initDocumentFunctions } = require('../helpers');
19
+
20
+ const task = 'credential-issued-rewards';
21
+
22
+ const initReadEventsFromBlock = async (context) => {
23
+ const { config } = context;
24
+ const { pullAddedCredentialMetadataEvents } = await initMetadataRegistry(
25
+ {
26
+ contractAddress: config.metadataRegistryContractAddress,
27
+ rpcProvider: context.rpcProvider,
28
+ },
29
+ context
30
+ );
31
+
32
+ return async (block) => {
33
+ return pullAddedCredentialMetadataEvents(block);
34
+ };
35
+ };
36
+ const executeTransfers = async (
37
+ { rewardsDictionary, organizations },
38
+ context
39
+ ) => {
40
+ const {
41
+ config: { vnfRewardDispersalAccountId },
42
+ log,
43
+ } = context;
44
+ if (isEmpty(rewardsDictionary)) {
45
+ return [];
46
+ }
47
+ const transfers = reduce(
48
+ (acc, nextOrganization) => {
49
+ const dids = getDidAndAliases(nextOrganization?.didDoc);
50
+ for (const id of dids) {
51
+ const caoKey = `${id}.caoReward`;
52
+ if (rewardsDictionary[caoKey]) {
53
+ acc.push({
54
+ fromAccount: vnfRewardDispersalAccountId,
55
+ toAccount: nextOrganization.ids.tokenAccountId,
56
+ amount: rewardsDictionary[caoKey],
57
+ description: TransactionReasons.CAO_ISSUING_REWARD,
58
+ });
59
+ }
60
+ const issuerKey = `${id}.issuerReward`;
61
+ if (rewardsDictionary[issuerKey]) {
62
+ acc.push({
63
+ fromAccount: vnfRewardDispersalAccountId,
64
+ toAccount: nextOrganization.ids.tokenAccountId,
65
+ amount: rewardsDictionary[issuerKey],
66
+ description: TransactionReasons.ISSUER_ISSUING_REWARD,
67
+ });
68
+ }
69
+ }
70
+
71
+ return acc;
72
+ },
73
+ [],
74
+ organizations
75
+ );
76
+
77
+ log.info({ task, transfers });
78
+
79
+ return batchTransferCredits({ transfers }, context);
80
+ };
81
+
82
+ const eventsToOrganizationTransactions = async (
83
+ { eventsCursor, credentialTypes },
84
+ context
85
+ ) => {
86
+ const {
87
+ config: { issuerRewardAmount, caoRewardAmount },
88
+ } = context;
89
+ const rewardsDictionary = {};
90
+ const dids = [];
91
+ const calcIssuerReward = (issuerKey) => {
92
+ const currentIssuerReward = rewardsDictionary[issuerKey] ?? 0;
93
+ return currentIssuerReward + issuerRewardAmount;
94
+ };
95
+
96
+ const calcCaoReward = (caoKey) => {
97
+ const currentCaoReward = rewardsDictionary[caoKey] ?? 0;
98
+ return currentCaoReward + caoRewardAmount;
99
+ };
100
+ let numberOfEventsRead = 0;
101
+ for await (const events of eventsCursor()) {
102
+ numberOfEventsRead += events.length;
103
+ const validEvents = getRewardableEvents(
104
+ { events, credentialTypes },
105
+ context
106
+ );
107
+ for (const { issuerKey, caoKey, issuerDid, caoDid } of validEvents) {
108
+ dids.push(issuerDid);
109
+ dids.push(caoDid);
110
+ rewardsDictionary[issuerKey] = calcIssuerReward(issuerKey);
111
+ rewardsDictionary[caoKey] = calcCaoReward(caoKey);
112
+ }
113
+ }
114
+ return { rewardsDictionary, numberOfEventsRead, dids: uniq(dids) };
115
+ };
116
+
117
+ const handleCredentialIssuedRewardsEvent = async (context) => {
118
+ const { log, repos } = context;
119
+ const credentialTypes = await getCredentialTypes(context);
120
+ const { readLastSuccessfulBlock, writeLastSuccessfulBlock } =
121
+ initDocumentFunctions({ eventName: task }, context);
122
+
123
+ const readEventsFromBlock = await initReadEventsFromBlock(context);
124
+
125
+ const lastReadBlock = await readLastSuccessfulBlock();
126
+ log.info({ task, lastReadBlock });
127
+ const { eventsCursor, latestBlock } = await readEventsFromBlock(
128
+ lastReadBlock + 1
129
+ );
130
+ const { rewardsDictionary, numberOfEventsRead, dids } =
131
+ await eventsToOrganizationTransactions(
132
+ { eventsCursor, credentialTypes },
133
+ context
134
+ );
135
+ log.info({
136
+ task,
137
+ lastReadBlock,
138
+ numberOfEventsRead,
139
+ rewardsDictionary,
140
+ });
141
+
142
+ const allOrganizations = await repos.organizations.findByDids(dids);
143
+
144
+ const [organizations, rejectedOrganizations] = partition(
145
+ 'ids.tokenAccountId',
146
+ allOrganizations
147
+ );
148
+
149
+ const rejectOrgsCounts = size(rejectedOrganizations);
150
+ if (rejectOrgsCounts > 0) {
151
+ log.warn('organizations were rejected due to missing ids.tokenAccountId');
152
+ log.warn({
153
+ rejectedOrganizations: map('didDoc.id', rejectedOrganizations),
154
+ });
155
+ }
156
+
157
+ await executeTransfers({ rewardsDictionary, organizations }, context);
158
+
159
+ log.info({ task, latestBlock });
160
+ await writeLastSuccessfulBlock(latestBlock);
161
+ };
162
+
163
+ const getRewardableEvents = ({ events, credentialTypes }, context) => {
164
+ const [rewardableEvents, unRewardableEvents] = flow(
165
+ map(mapEvent),
166
+ partition(isRewardedEvent(credentialTypes))
167
+ )(events);
168
+ context.log.info({
169
+ rewardableEvents,
170
+ unRewardableEvents,
171
+ events,
172
+ credentialTypes,
173
+ });
174
+ return rewardableEvents;
175
+ };
176
+
177
+ const isRewardedEvent = (credentialTypes) => (event) => {
178
+ const issuerDid = decodeIssuerVc(event.args[1]);
179
+ const caoDid = event.args[6];
180
+ const credentialType = find((ct) => {
181
+ return ct.credentialType2BytesHash === event.credentialType2BytesHash;
182
+ }, credentialTypes);
183
+
184
+ return issuerDid != null && caoDid != null && credentialType?.layer1;
185
+ };
186
+
187
+ const mapEvent = (event) => {
188
+ const issuerDid = decodeIssuerVc(event.args[1]);
189
+ const caoDid = event.args[6];
190
+ const credentialType2BytesHash = event.args[3];
191
+ const issuerKey = `${issuerDid}.issuerReward`;
192
+ const caoKey = `${caoDid}.caoReward`;
193
+ return {
194
+ issuerDid,
195
+ caoDid,
196
+ issuerKey,
197
+ caoKey,
198
+ credentialType2BytesHash,
199
+ ...event,
200
+ };
201
+ };
202
+
203
+ const getCredentialTypes = async (context) => {
204
+ const { repos } = context;
205
+ const credentialTypes = await repos.credentialSchemas.find({});
206
+ return map(
207
+ ({ credentialType, layer1 }) => ({
208
+ credentialType,
209
+ credentialType2BytesHash: get2BytesHash(credentialType),
210
+ layer1,
211
+ }),
212
+ credentialTypes
213
+ );
214
+ };
215
+
216
+ module.exports = {
217
+ handleCredentialIssuedRewardsEvent,
218
+ };
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ ...require('./handle-credential-issued-rewards-event'),
3
+ ...require('./handle-coupons-burned-verification-event'),
4
+ ...require('./handle-coupons-minted-logging-event'),
5
+ ...require('./handle-coupons-burned-logging-event'),
6
+ ...require('./handle-credential-issued-logging-event'),
7
+ };
@@ -0,0 +1,28 @@
1
+ const { initReadDocument, initWriteDocument } = require('@verii/aws-clients');
2
+
3
+ const initDocumentFunctions = ({ eventName }, context) => {
4
+ const readDocument = initReadDocument(context.config);
5
+ const writeDocument = initWriteDocument(context.config);
6
+ const readLastSuccessfulBlock = async () => {
7
+ const result = await readDocument(context.config.dynamoDbTableEventBlock, {
8
+ EventName: eventName,
9
+ });
10
+ context.log.info({ result });
11
+
12
+ if (!result || !result.Item) {
13
+ return -1;
14
+ }
15
+ return result.Item.BlockNumber;
16
+ };
17
+ const writeLastSuccessfulBlock = (blockNumber) =>
18
+ writeDocument(context.config.dynamoDbTableEventBlock, {
19
+ EventName: eventName,
20
+ BlockNumber: blockNumber,
21
+ });
22
+
23
+ return { readLastSuccessfulBlock, writeLastSuccessfulBlock };
24
+ };
25
+
26
+ module.exports = {
27
+ initDocumentFunctions,
28
+ };
@@ -0,0 +1,20 @@
1
+ const { jwtDecode } = require('@verii/jwt');
2
+
3
+ const decodeBigIntToNumber = (bigInt) => Number(bigInt.hex);
4
+
5
+ const decodeBigIntToDate = (bigInt) => new Date(Number(bigInt.hex) * 1000);
6
+
7
+ const hexStringToUtfString = (hexString) =>
8
+ Buffer.from(hexString, 'hex').toString();
9
+
10
+ const decodeIssuerVc = (issuerVc) => {
11
+ const { payload } = jwtDecode(hexStringToUtfString(issuerVc.slice(2)));
12
+
13
+ return payload?.iss;
14
+ };
15
+
16
+ module.exports = {
17
+ decodeBigIntToNumber,
18
+ decodeBigIntToDate,
19
+ decodeIssuerVc,
20
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ ...require('./document-functions'),
3
+ ...require('./event-decoding'),
4
+ ...require('./map-coupon-burned'),
5
+ };
@@ -0,0 +1,32 @@
1
+ const { toHexString, toNumber } = require('@verii/blockchain-functions');
2
+
3
+ const mapCouponBurned = (evt, { log }) => {
4
+ const expirationTime = new Date(toNumber(evt.args[6]) * 1000);
5
+ let burnTime = new Date();
6
+ try {
7
+ burnTime = new Date(toNumber(evt.args[7]) * 1000);
8
+ } catch (err) {
9
+ log.info({ err });
10
+ log.info('burnTime not present on event');
11
+ }
12
+ return {
13
+ blockNumber: evt.blockNumber,
14
+ blockHash: evt.blockHash,
15
+ transactionIndex: evt.transactionIndex,
16
+ transactionHash: evt.transactionHash,
17
+ event: evt.fragment.name,
18
+ owner: evt.args[0],
19
+ bundleId: `${evt.args[1]}`,
20
+ bundleIdHex: evt.args[1] ? toHexString(evt.args[1]) : undefined,
21
+ eventTraceId: evt.args[2],
22
+ caoDid: evt.args[3],
23
+ burnerDid: evt.args[4],
24
+ balance: toNumber(evt.args[5]),
25
+ expirationTime,
26
+ burnTime,
27
+ };
28
+ };
29
+
30
+ module.exports = {
31
+ mapCouponBurned,
32
+ };
package/src/index.js ADDED
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Copyright 2025 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ /* eslint-disable global-require */
19
+ module.exports = {
20
+ ...require('./event-processing-endpoints'),
21
+ ...require('./config/config'),
22
+ };