@merkl/api 0.15.45 → 0.15.46

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.
@@ -1643,28 +1643,6 @@ declare const eden: {
1643
1643
  200: void;
1644
1644
  }>>;
1645
1645
  };
1646
- pendings: {
1647
- post: (body: {
1648
- data: {
1649
- reason: string;
1650
- pending: string;
1651
- recipient: string;
1652
- }[];
1653
- distributionChainId: number;
1654
- campaignId: string;
1655
- root: string;
1656
- rewardToken: string;
1657
- }, options: {
1658
- headers: {
1659
- authorization: string;
1660
- };
1661
- query?: Record<string, unknown> | undefined;
1662
- fetch?: RequestInit | undefined;
1663
- }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
1664
- [x: string]: any;
1665
- 200: any;
1666
- }>>;
1667
- };
1668
1646
  };
1669
1647
  unclaim: {
1670
1648
  index: {
@@ -4765,28 +4743,6 @@ declare const eden: {
4765
4743
  200: void;
4766
4744
  }>>;
4767
4745
  };
4768
- pendings: {
4769
- post: (body: {
4770
- data: {
4771
- reason: string;
4772
- pending: string;
4773
- recipient: string;
4774
- }[];
4775
- distributionChainId: number;
4776
- campaignId: string;
4777
- root: string;
4778
- rewardToken: string;
4779
- }, options: {
4780
- headers: {
4781
- authorization: string;
4782
- };
4783
- query?: Record<string, unknown> | undefined;
4784
- fetch?: RequestInit | undefined;
4785
- }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
4786
- [x: string]: any;
4787
- 200: any;
4788
- }>>;
4789
- };
4790
4746
  };
4791
4747
  unclaim: {
4792
4748
  index: {
@@ -8886,33 +8842,6 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
8886
8842
  };
8887
8843
  };
8888
8844
  };
8889
- } & {
8890
- engine: {
8891
- pendings: {
8892
- post: {
8893
- body: {
8894
- data: {
8895
- reason: string;
8896
- pending: string;
8897
- recipient: string;
8898
- }[];
8899
- distributionChainId: number;
8900
- campaignId: string;
8901
- root: string;
8902
- rewardToken: string;
8903
- };
8904
- params: {};
8905
- query: unknown;
8906
- headers: {
8907
- authorization: string;
8908
- };
8909
- response: {
8910
- [x: string]: any;
8911
- 200: any;
8912
- };
8913
- };
8914
- };
8915
- };
8916
8845
  } & {
8917
8846
  count: {
8918
8847
  chain: {
@@ -13365,28 +13294,6 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
13365
13294
  200: void;
13366
13295
  }>>;
13367
13296
  };
13368
- pendings: {
13369
- post: (body: {
13370
- data: {
13371
- reason: string;
13372
- pending: string;
13373
- recipient: string;
13374
- }[];
13375
- distributionChainId: number;
13376
- campaignId: string;
13377
- root: string;
13378
- rewardToken: string;
13379
- }, options: {
13380
- headers: {
13381
- authorization: string;
13382
- };
13383
- query?: Record<string, unknown> | undefined;
13384
- fetch?: RequestInit | undefined;
13385
- }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
13386
- [x: string]: any;
13387
- 200: any;
13388
- }>>;
13389
- };
13390
13297
  };
13391
13298
  unclaim: {
13392
13299
  index: {
@@ -16487,28 +16394,6 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
16487
16394
  200: void;
16488
16395
  }>>;
16489
16396
  };
16490
- pendings: {
16491
- post: (body: {
16492
- data: {
16493
- reason: string;
16494
- pending: string;
16495
- recipient: string;
16496
- }[];
16497
- distributionChainId: number;
16498
- campaignId: string;
16499
- root: string;
16500
- rewardToken: string;
16501
- }, options: {
16502
- headers: {
16503
- authorization: string;
16504
- };
16505
- query?: Record<string, unknown> | undefined;
16506
- fetch?: RequestInit | undefined;
16507
- }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
16508
- [x: string]: any;
16509
- 200: any;
16510
- }>>;
16511
- };
16512
16397
  };
16513
16398
  unclaim: {
16514
16399
  index: {
@@ -1944,33 +1944,6 @@ declare const app: Elysia<"", false, {
1944
1944
  };
1945
1945
  };
1946
1946
  };
1947
- } & {
1948
- engine: {
1949
- pendings: {
1950
- post: {
1951
- body: {
1952
- data: {
1953
- reason: string;
1954
- pending: string;
1955
- recipient: string;
1956
- }[];
1957
- distributionChainId: number;
1958
- campaignId: string;
1959
- root: string;
1960
- rewardToken: string;
1961
- };
1962
- params: {};
1963
- query: unknown;
1964
- headers: {
1965
- authorization: string;
1966
- };
1967
- response: {
1968
- [x: string]: any;
1969
- 200: any;
1970
- };
1971
- };
1972
- };
1973
- };
1974
1947
  } & {
1975
1948
  count: {
1976
1949
  chain: {
@@ -3,7 +3,7 @@ if (!process.env.ENV || !process.env.FILENAME)
3
3
  throw new Error("[ENV]: missing variable");
4
4
  import { log } from "../../utils/logger";
5
5
  import { apiDbClient } from "../../utils/prisma";
6
- import { withRetry } from "@sdk";
6
+ import { NETWORK_LABELS, withRetry } from "@sdk";
7
7
  import { S3Client } from "bun";
8
8
  import moment from "moment";
9
9
  // ─── Constants ───────────────────────────────────────────────
@@ -36,12 +36,14 @@ const extract = async () => {
36
36
  }
37
37
  if (data.length < MAX_BATCH_SIZE)
38
38
  continue;
39
+ // Retry up to 5 times with a 10s delay
39
40
  const promise = withRetry(load, [data], 5, 10_000);
40
41
  loadPromises.push(promise);
41
42
  await promise;
42
43
  data = [];
43
44
  }
44
45
  // Final batch
46
+ // Retry up to 5 times with a 10s delay
45
47
  const promise = withRetry(load, [data], 5, 10_000);
46
48
  loadPromises.push(promise);
47
49
  await promise;
@@ -61,7 +63,7 @@ const extract = async () => {
61
63
  };
62
64
  // ─── Transform & Load ────────────────────────────────────────────────────────
63
65
  const load = async (pendings) => {
64
- log.info(`pushing ${pendings.length} pendings`);
66
+ log.info(`pushing ${pendings.length} pendings for token ${pendings[0].rewardToken} on ${NETWORK_LABELS[chainId]}`);
65
67
  const { updated, created } = await updatePendings({
66
68
  distributionChainId: chainId,
67
69
  rewardToken: pendings[0].rewardToken, // sometimes undefined
@@ -75,25 +77,12 @@ const load = async (pendings) => {
75
77
  const updatePendings = async (data) => {
76
78
  const rewardTokenId = Bun.hash(`${data.distributionChainId}${data.rewardToken}`).toString();
77
79
  const campaignId = Bun.hash(`${data.distributionChainId}${data.campaignId}`).toString();
78
- const rewardUniques = {};
79
- // Adds a record to the Reward row where pendings need to be updated
80
- const updateRewardUniques = (recipient, pending, previousPending) => {
81
- Bun.hash(`${root}${recipient}${rewardTokenId}`).toString();
82
- const rewardId = Bun.hash(`${data.root}${recipient}${rewardTokenId}`).toString();
83
- if (!rewardUniques[rewardId]) {
84
- rewardUniques[rewardId] = { pending: "0", recipient: recipient };
85
- }
86
- rewardUniques[rewardId].pending = (BigInt(rewardUniques[rewardId].pending) +
87
- BigInt(pending) -
88
- BigInt(previousPending ?? "0")) // Store the delta
89
- .toString();
90
- };
80
+ // List of identifiers of breakdowns that will be touched by this jobd
91
81
  const breakdownUniques = await data.data.map(({ recipient, reason }) => {
92
82
  const rewardId = Bun.hash(`${data.root}${recipient}${rewardTokenId}`).toString();
93
83
  return { rewardId, reason, campaignId };
94
84
  });
95
- const breakdownToUpdate = [];
96
- const breakdownToCreate = [];
85
+ // Check if these breakdowns already exist or not
97
86
  const breakdownExists = await apiDbClient.$transaction(breakdownUniques.map(x => apiDbClient.rewardBreakdown.findUnique({
98
87
  select: {
99
88
  pending: true,
@@ -106,20 +95,35 @@ const updatePendings = async (data) => {
106
95
  },
107
96
  },
108
97
  })));
98
+ // We want to get all current pendings for the rewardIds that will be modified
99
+ // Should contain rewardId => { pendingIncrease, recipient }
100
+ const rewardIdToPendingIncrease = {};
101
+ const breakdownToUpdate = [];
102
+ const breakdownToCreate = [];
103
+ let totalBreakdownIncrease = 0n;
104
+ // For all point to update
105
+ // - compute the delta with what's in database, add it to rewardIdToPendingIncrease
106
+ // - add the point to breakdownToUpdate or breakdownToCreate
109
107
  for (const [pointIndex, point] of data.data.entries()) {
110
- updateRewardUniques(point.recipient, point.pending, breakdownExists[pointIndex]?.pending);
111
- if (!!breakdownExists[pointIndex]) {
112
- if (point.pending !== breakdownExists[pointIndex].pending) {
108
+ const rewardId = Bun.hash(`${data.root}${point.recipient}${rewardTokenId}`).toString();
109
+ if (!rewardIdToPendingIncrease[rewardId]) {
110
+ rewardIdToPendingIncrease[rewardId] = { pendingIncrease: 0n, recipient: point.recipient };
111
+ }
112
+ const delta = BigInt(point.pending) - BigInt(breakdownExists[pointIndex]?.pending ?? "0");
113
+ totalBreakdownIncrease += delta;
114
+ if (delta > 0n) {
115
+ rewardIdToPendingIncrease[rewardId].pendingIncrease += delta; // Store the delta
116
+ if (!!breakdownExists[pointIndex]) {
113
117
  breakdownToUpdate.push(point);
114
118
  }
115
- }
116
- else {
117
- breakdownToCreate.push(point);
119
+ else {
120
+ breakdownToCreate.push(point);
121
+ }
118
122
  }
119
123
  }
120
- const toUpdate = [];
121
- const toCreate = [];
122
- const exists = await apiDbClient.$transaction(Object.keys(rewardUniques).map(x => apiDbClient.reward.findUnique({
124
+ const rewardToUpdate = [];
125
+ const rewardToCreate = [];
126
+ const RewardExists = await apiDbClient.$transaction(Object.keys(rewardIdToPendingIncrease).map(x => apiDbClient.reward.findUnique({
123
127
  select: {
124
128
  pending: true,
125
129
  },
@@ -127,25 +131,29 @@ const updatePendings = async (data) => {
127
131
  id: x,
128
132
  },
129
133
  })));
130
- for (const [pointIndex, point] of Object.values(rewardUniques).entries()) {
131
- if (!!exists[pointIndex]) {
132
- if (point.pending !== "0") {
133
- toUpdate.push({
134
- ...point,
135
- pending: (BigInt(exists[pointIndex].pending) + BigInt(point.pending)).toString(), // Store the delta
134
+ for (const [pointIndex, point] of Object.values(rewardIdToPendingIncrease).entries()) {
135
+ if (BigInt(point.pendingIncrease) > 0n) {
136
+ if (!!RewardExists[pointIndex]) {
137
+ rewardToUpdate.push({
138
+ recipient: point.recipient,
139
+ pending: BigInt(RewardExists[pointIndex].pending) + BigInt(point.pendingIncrease),
140
+ });
141
+ }
142
+ else {
143
+ rewardToCreate.push({
144
+ recipient: point.recipient,
145
+ pending: BigInt(point.pendingIncrease),
136
146
  });
137
147
  }
138
- }
139
- else {
140
- toCreate.push(point);
141
148
  }
142
149
  }
143
- console.log(`rewards to create: ${toCreate.length}, to update: ${toUpdate.length}`);
144
- if (toCreate.length > 0) {
145
- const users = toCreate.map(x => x.recipient);
150
+ const totalRewardIncrease = Object.values(rewardIdToPendingIncrease).reduce((prev, current) => prev + BigInt(current.pendingIncrease), 0n);
151
+ log.info(`rewards to create: ${rewardToCreate.length}, to update: ${rewardToUpdate.length}, total reward increase: ${totalRewardIncrease.toString()}`);
152
+ if (rewardToCreate.length > 0) {
153
+ const users = rewardToCreate.map(x => x.recipient);
146
154
  await apiDbClient.user.createMany({ data: users.map(x => ({ address: x, tags: [] })), skipDuplicates: true });
147
155
  await apiDbClient.reward.createMany({
148
- data: toCreate.map(x => {
156
+ data: rewardToCreate.map(x => {
149
157
  const rewardId = Bun.hash(`${root}${x.recipient}${rewardTokenId}`).toString();
150
158
  return {
151
159
  id: rewardId,
@@ -154,26 +162,26 @@ const updatePendings = async (data) => {
154
162
  rewardTokenId,
155
163
  proofs: [],
156
164
  amount: "0",
157
- pending: x.pending,
165
+ pending: x.pending.toString(),
158
166
  claimed: "0",
159
167
  };
160
168
  }),
161
169
  });
162
170
  }
163
- if (toUpdate.length > 0) {
164
- await apiDbClient.$transaction(toUpdate.map(x => {
171
+ if (rewardToUpdate.length > 0) {
172
+ await apiDbClient.$transaction(rewardToUpdate.map(x => {
165
173
  const rewardId = Bun.hash(`${data.root}${x.recipient}${rewardTokenId}`).toString();
166
174
  return apiDbClient.reward.update({
167
175
  where: {
168
176
  id: rewardId,
169
177
  },
170
178
  data: {
171
- pending: x.pending,
179
+ pending: x.pending.toString(),
172
180
  },
173
181
  });
174
182
  }));
175
183
  }
176
- console.log(`breakdowns to create: ${breakdownToCreate.length}, to update: ${breakdownToUpdate.length}`);
184
+ log.info(`breakdowns to create: ${breakdownToCreate.length}, to update: ${breakdownToUpdate.length}, total breakdown increase: ${totalBreakdownIncrease.toString()}`);
177
185
  if (breakdownToUpdate.length > 0) {
178
186
  await apiDbClient.$transaction(breakdownToUpdate.map(x => {
179
187
  return apiDbClient.rewardBreakdown.update({
@@ -284,33 +284,6 @@ export declare const RewardController: Elysia<"/rewards", false, {
284
284
  };
285
285
  };
286
286
  };
287
- } & {
288
- engine: {
289
- pendings: {
290
- post: {
291
- body: {
292
- data: {
293
- reason: string;
294
- pending: string;
295
- recipient: string;
296
- }[];
297
- distributionChainId: number;
298
- campaignId: string;
299
- root: string;
300
- rewardToken: string;
301
- };
302
- params: {};
303
- query: unknown;
304
- headers: {
305
- authorization: string;
306
- };
307
- response: {
308
- [x: string]: any;
309
- 200: any;
310
- };
311
- };
312
- };
313
- };
314
287
  } & {
315
288
  count: {
316
289
  chain: {
@@ -5,7 +5,7 @@ import Elysia from "elysia";
5
5
  import { ChainDto } from "../accounting";
6
6
  import { CampaignService } from "../campaign";
7
7
  import { TokenService } from "../token";
8
- import { CampaignIdDto, CampaignIdWithoutPageDto, CampaignRewardsDto, CreateManyBreakdownDto, CreateManyRewardDto, RegisterClaimsDto, TokenIdDto, UpdatePendingDto, } from "./reward.model";
8
+ import { CampaignIdDto, CampaignIdWithoutPageDto, CampaignRewardsDto, CreateManyBreakdownDto, CreateManyRewardDto, RegisterClaimsDto, TokenIdDto, } from "./reward.model";
9
9
  import { RewardService } from "./reward.service";
10
10
  // ─── Rewards Controller ──────────────────────────────────────────────────────
11
11
  export const RewardController = new Elysia({ prefix: "/rewards", detail: { tags: ["Rewards"] } })
@@ -100,13 +100,6 @@ export const RewardController = new Elysia({ prefix: "/rewards", detail: { tags:
100
100
  }
101
101
  },
102
102
  detail: { hide: true },
103
- })
104
- // ─── Create Many Pending Rewards ─────────────────────────────────────
105
- .post("/pendings", async ({ body }) => await RewardService.updatePendings(body), {
106
- headers: AuthorizationHeadersDto,
107
- body: UpdatePendingDto,
108
- beforeHandle: EngineGuard,
109
- detail: { hide: true },
110
103
  }));
111
104
  })
112
105
  // ─── Get Reward Count By Chain And Root ──────────────────────────────
@@ -1814,33 +1814,6 @@ export declare const v4: Elysia<"/v4", false, {
1814
1814
  };
1815
1815
  };
1816
1816
  };
1817
- } & {
1818
- engine: {
1819
- pendings: {
1820
- post: {
1821
- body: {
1822
- data: {
1823
- reason: string;
1824
- pending: string;
1825
- recipient: string;
1826
- }[];
1827
- distributionChainId: number;
1828
- campaignId: string;
1829
- root: string;
1830
- rewardToken: string;
1831
- };
1832
- params: {};
1833
- query: unknown;
1834
- headers: {
1835
- authorization: string;
1836
- };
1837
- response: {
1838
- [x: string]: any;
1839
- 200: any;
1840
- };
1841
- };
1842
- };
1843
- };
1844
1817
  } & {
1845
1818
  count: {
1846
1819
  chain: {