@show-karma/karma-gap-sdk 0.4.15 → 0.4.16

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 (188) hide show
  1. package/.cursorrules +43 -0
  2. package/core/abi/AirdropNFT.json +1 -1
  3. package/core/abi/Allo.json +860 -860
  4. package/core/abi/AlloRegistry.json +578 -578
  5. package/core/abi/CommunityResolverABI.json +506 -506
  6. package/core/abi/Donations.json +251 -251
  7. package/core/abi/EAS.json +1 -1
  8. package/core/abi/MultiAttester.json +746 -746
  9. package/core/abi/ProjectResolver.json +574 -574
  10. package/core/abi/SchemaRegistry.json +1 -1
  11. package/core/abi/index.ts +21 -0
  12. package/core/class/AllGapSchemas.ts +21 -0
  13. package/core/class/Attestation.ts +429 -0
  14. package/core/class/Fetcher.ts +224 -0
  15. package/core/class/GAP.ts +481 -0
  16. package/core/class/GapSchema.ts +93 -0
  17. package/core/class/Gelato/{Gelato.js → Gelato.ts} +23 -0
  18. package/core/class/GrantProgramRegistry/Allo.ts +188 -0
  19. package/core/class/GrantProgramRegistry/AlloRegistry.ts +101 -0
  20. package/core/class/GraphQL/AxiosGQL.ts +29 -0
  21. package/core/class/GraphQL/EASClient.ts +34 -0
  22. package/core/class/GraphQL/GapEasClient.ts +869 -0
  23. package/core/class/Schema.ts +659 -0
  24. package/core/class/SchemaError.ts +42 -0
  25. package/core/class/contract/GapContract.ts +457 -0
  26. package/core/class/entities/Community.ts +148 -0
  27. package/core/class/entities/ContributorProfile.ts +108 -0
  28. package/core/class/entities/Grant.ts +321 -0
  29. package/core/class/entities/GrantUpdate.ts +187 -0
  30. package/core/class/entities/MemberOf.ts +52 -0
  31. package/core/class/entities/Milestone.ts +898 -0
  32. package/core/class/entities/Project.ts +672 -0
  33. package/core/class/entities/ProjectImpact.ts +170 -0
  34. package/core/class/entities/ProjectMilestone.ts +254 -0
  35. package/core/class/entities/ProjectPointer.ts +39 -0
  36. package/core/class/entities/ProjectUpdate.ts +176 -0
  37. package/core/class/entities/Track.ts +32 -0
  38. package/core/class/karma-indexer/GapIndexerClient.ts +383 -0
  39. package/core/class/karma-indexer/api/GapIndexerApi.ts +446 -0
  40. package/core/class/karma-indexer/api/types.ts +313 -0
  41. package/core/class/remote-storage/IpfsStorage.ts +76 -0
  42. package/core/class/remote-storage/RemoteStorage.ts +65 -0
  43. package/core/class/types/allo.ts +93 -0
  44. package/core/class/types/attestations.ts +223 -0
  45. package/core/consts.ts +775 -0
  46. package/core/scripts/create-grant.ts +102 -0
  47. package/core/scripts/create-program.ts +43 -0
  48. package/core/scripts/create-schemas.ts +65 -0
  49. package/core/scripts/deploy.ts +65 -0
  50. package/core/scripts/index.ts +1 -0
  51. package/core/scripts/milestone-multi-grants.ts +125 -0
  52. package/core/shared/types.ts +13 -0
  53. package/core/types.ts +224 -0
  54. package/core/utils/gelato/send-gelato-txn.ts +114 -0
  55. package/core/utils/gelato/sponsor-handler.ts +77 -0
  56. package/core/utils/gelato/watch-gelato-txn.ts +67 -0
  57. package/core/utils/get-date.ts +3 -0
  58. package/core/utils/get-ipfs-data.ts +13 -0
  59. package/core/utils/get-web3-provider.ts +18 -0
  60. package/core/utils/gql-queries.ts +133 -0
  61. package/core/utils/map-filter.ts +21 -0
  62. package/core/utils/serialize-bigint.ts +7 -0
  63. package/core/utils/to-unix.ts +18 -0
  64. package/create-community-example.ts +119 -0
  65. package/csv-upload/README.md +74 -0
  66. package/csv-upload/config.ts +41 -0
  67. package/csv-upload/example.csv +2 -0
  68. package/csv-upload/keys.example.json +8 -0
  69. package/csv-upload/scripts/run.ts +417 -0
  70. package/csv-upload/types.ts +39 -0
  71. package/docs/.gitkeep +0 -0
  72. package/docs/images/attestation-architecture.png +0 -0
  73. package/docs/images/dfd-get-projects.png +0 -0
  74. package/gap-schema.yaml +155 -0
  75. package/milestone-workflow-example.ts +353 -0
  76. package/package.json +45 -39
  77. package/readme.md +872 -0
  78. package/schemas/.gitkeep +0 -0
  79. package/schemas/GAP-schemas-1692135812877.json +33 -0
  80. package/test-file-indexer-api.ts +25 -0
  81. package/tsconfig.json +26 -0
  82. package/core/abi/index.d.ts +0 -1114
  83. package/core/abi/index.js +0 -26
  84. package/core/class/AllGapSchemas.d.ts +0 -9
  85. package/core/class/AllGapSchemas.js +0 -19
  86. package/core/class/Attestation.d.ts +0 -173
  87. package/core/class/Attestation.js +0 -333
  88. package/core/class/Fetcher.d.ts +0 -175
  89. package/core/class/Fetcher.js +0 -13
  90. package/core/class/GAP.d.ts +0 -254
  91. package/core/class/GAP.js +0 -289
  92. package/core/class/GapSchema.d.ts +0 -34
  93. package/core/class/GapSchema.js +0 -62
  94. package/core/class/GrantProgramRegistry/Allo.d.ts +0 -17
  95. package/core/class/GrantProgramRegistry/Allo.js +0 -137
  96. package/core/class/GrantProgramRegistry/AlloRegistry.d.ts +0 -15
  97. package/core/class/GrantProgramRegistry/AlloRegistry.js +0 -70
  98. package/core/class/GraphQL/AxiosGQL.d.ts +0 -6
  99. package/core/class/GraphQL/AxiosGQL.js +0 -25
  100. package/core/class/GraphQL/EASClient.d.ts +0 -16
  101. package/core/class/GraphQL/EASClient.js +0 -26
  102. package/core/class/GraphQL/GapEasClient.d.ts +0 -71
  103. package/core/class/GraphQL/GapEasClient.js +0 -451
  104. package/core/class/GraphQL/index.js +0 -19
  105. package/core/class/Schema.d.ts +0 -233
  106. package/core/class/Schema.js +0 -488
  107. package/core/class/SchemaError.d.ts +0 -30
  108. package/core/class/SchemaError.js +0 -39
  109. package/core/class/contract/GapContract.d.ts +0 -102
  110. package/core/class/contract/GapContract.js +0 -285
  111. package/core/class/entities/Community.d.ts +0 -34
  112. package/core/class/entities/Community.js +0 -109
  113. package/core/class/entities/ContributorProfile.d.ts +0 -41
  114. package/core/class/entities/ContributorProfile.js +0 -69
  115. package/core/class/entities/Grant.d.ts +0 -54
  116. package/core/class/entities/Grant.js +0 -223
  117. package/core/class/entities/GrantUpdate.d.ts +0 -40
  118. package/core/class/entities/GrantUpdate.js +0 -114
  119. package/core/class/entities/MemberOf.d.ts +0 -11
  120. package/core/class/entities/MemberOf.js +0 -33
  121. package/core/class/entities/Milestone.d.ts +0 -168
  122. package/core/class/entities/Milestone.js +0 -657
  123. package/core/class/entities/Project.d.ts +0 -92
  124. package/core/class/entities/Project.js +0 -418
  125. package/core/class/entities/ProjectImpact.d.ts +0 -50
  126. package/core/class/entities/ProjectImpact.js +0 -112
  127. package/core/class/entities/ProjectMilestone.d.ts +0 -60
  128. package/core/class/entities/ProjectMilestone.js +0 -174
  129. package/core/class/entities/ProjectPointer.d.ts +0 -12
  130. package/core/class/entities/ProjectPointer.js +0 -22
  131. package/core/class/entities/ProjectUpdate.d.ts +0 -50
  132. package/core/class/entities/ProjectUpdate.js +0 -110
  133. package/core/class/entities/Track.d.ts +0 -16
  134. package/core/class/entities/Track.js +0 -21
  135. package/core/class/entities/index.js +0 -26
  136. package/core/class/index.js +0 -26
  137. package/core/class/karma-indexer/GapIndexerClient.d.ts +0 -66
  138. package/core/class/karma-indexer/GapIndexerClient.js +0 -207
  139. package/core/class/karma-indexer/api/GapIndexerApi.d.ts +0 -73
  140. package/core/class/karma-indexer/api/GapIndexerApi.js +0 -256
  141. package/core/class/karma-indexer/api/types.d.ts +0 -295
  142. package/core/class/karma-indexer/api/types.js +0 -2
  143. package/core/class/remote-storage/IpfsStorage.d.ts +0 -23
  144. package/core/class/remote-storage/IpfsStorage.js +0 -56
  145. package/core/class/remote-storage/RemoteStorage.d.ts +0 -41
  146. package/core/class/remote-storage/RemoteStorage.js +0 -38
  147. package/core/class/types/allo.d.ts +0 -78
  148. package/core/class/types/allo.js +0 -2
  149. package/core/class/types/attestations.d.ts +0 -168
  150. package/core/class/types/attestations.js +0 -66
  151. package/core/consts.d.ts +0 -48
  152. package/core/consts.js +0 -641
  153. package/core/index.js +0 -24
  154. package/core/shared/types.d.ts +0 -6
  155. package/core/shared/types.js +0 -2
  156. package/core/types.d.ts +0 -131
  157. package/core/types.js +0 -13
  158. package/core/utils/gelato/index.js +0 -19
  159. package/core/utils/gelato/send-gelato-txn.d.ts +0 -55
  160. package/core/utils/gelato/send-gelato-txn.js +0 -100
  161. package/core/utils/gelato/sponsor-handler.d.ts +0 -9
  162. package/core/utils/gelato/sponsor-handler.js +0 -60
  163. package/core/utils/gelato/watch-gelato-txn.d.ts +0 -7
  164. package/core/utils/gelato/watch-gelato-txn.js +0 -63
  165. package/core/utils/get-date.d.ts +0 -1
  166. package/core/utils/get-date.js +0 -7
  167. package/core/utils/get-ipfs-data.d.ts +0 -1
  168. package/core/utils/get-ipfs-data.js +0 -20
  169. package/core/utils/get-web3-provider.d.ts +0 -2
  170. package/core/utils/get-web3-provider.js +0 -18
  171. package/core/utils/gql-queries.d.ts +0 -12
  172. package/core/utils/gql-queries.js +0 -90
  173. package/core/utils/index.js +0 -23
  174. package/core/utils/map-filter.d.ts +0 -8
  175. package/core/utils/map-filter.js +0 -20
  176. package/core/utils/serialize-bigint.d.ts +0 -1
  177. package/core/utils/serialize-bigint.js +0 -8
  178. package/core/utils/to-unix.d.ts +0 -1
  179. package/core/utils/to-unix.js +0 -25
  180. package/index.js +0 -17
  181. /package/core/class/GraphQL/{index.d.ts → index.ts} +0 -0
  182. /package/core/class/entities/{index.d.ts → index.ts} +0 -0
  183. /package/core/class/{index.d.ts → index.ts} +0 -0
  184. /package/core/{index.d.ts → index.ts} +0 -0
  185. /package/core/utils/gelato/{index.d.ts → index.ts} +0 -0
  186. /package/core/utils/{index.d.ts → index.ts} +0 -0
  187. /package/{core/class/Gelato/Gelato.d.ts → csv-upload/.gitkeep} +0 -0
  188. /package/{index.d.ts → index.ts} +0 -0
@@ -0,0 +1,869 @@
1
+ import { Attestation } from "../Attestation";
2
+ import { gqlQueries } from "../../utils/gql-queries";
3
+ import { toUnix } from "../../utils/to-unix";
4
+ import {
5
+ AttestationRes,
6
+ AttestationsRes,
7
+ EASNetworkConfig,
8
+ Hex,
9
+ IAttestation,
10
+ SchemaRes,
11
+ SchemataRes,
12
+ TNetwork,
13
+ TSchemaName,
14
+ } from "../../types";
15
+ import {
16
+ CommunityDetails,
17
+ GrantDetails,
18
+ Grantee,
19
+ MemberDetails,
20
+ MilestoneCompleted,
21
+ ProjectDetails,
22
+ } from "../types/attestations";
23
+ import { GapSchema } from "../GapSchema";
24
+ import { Schema } from "../Schema";
25
+ import { SchemaError } from "../SchemaError";
26
+ import { Grant, Milestone, IProject, Project, MemberOf } from "../entities";
27
+ import { Community } from "../entities/Community";
28
+ import { mapFilter } from "../../utils";
29
+ import { Fetcher } from "../Fetcher";
30
+ import { Networks } from "../../consts";
31
+ import { GrantUpdate } from "../entities/GrantUpdate";
32
+ import { ISummaryProject } from "../karma-indexer/api/types";
33
+
34
+ interface EASClientProps {
35
+ network: TNetwork;
36
+ }
37
+
38
+ // TODO: Split this class into small ones
39
+ export class GapEasClient extends Fetcher {
40
+ network: EASNetworkConfig & { name: TNetwork };
41
+
42
+ constructor(args: EASClientProps) {
43
+ const { network } = args;
44
+ super(Networks[network].url);
45
+
46
+ this.network = { ...Networks[network], name: network };
47
+ }
48
+
49
+ /**
50
+ * Fetches all the schemas deployed by an owner
51
+ * @param owner
52
+ */
53
+ async schemas(owner: Hex): Promise<GapSchema[]> {
54
+ const query = gqlQueries.schemata(owner);
55
+ const { schemata } = await this.query<SchemataRes>(query);
56
+
57
+ return schemata.map(
58
+ (schema) =>
59
+ new GapSchema(
60
+ {
61
+ name: "",
62
+ schema: Schema.rawToObject(schema.schema),
63
+ uid: schema.uid,
64
+ },
65
+ this.gap
66
+ )
67
+ );
68
+ }
69
+
70
+ async attestation<T = unknown>(uid: Hex) {
71
+ const query = gqlQueries.attestation(uid);
72
+ const { attestation } = await this.query<AttestationRes>(query);
73
+
74
+ return Attestation.fromInterface<Attestation<T>>(
75
+ [attestation],
76
+ this.network.name
77
+ )[0];
78
+ }
79
+
80
+ async attestations(
81
+ schemaName: TSchemaName,
82
+ search?: string
83
+ ): Promise<IAttestation[]> {
84
+ const schema = this.gap.findSchema(schemaName);
85
+
86
+ const query = gqlQueries.attestationsOf(schema.uid, search);
87
+ const {
88
+ schema: { attestations },
89
+ } = await this.query<SchemaRes>(query);
90
+
91
+ return attestations;
92
+ }
93
+
94
+ async attestationsOf(
95
+ schemaName: TSchemaName,
96
+ recipient: Hex
97
+ ): Promise<IAttestation[]> {
98
+ const schema = this.gap.findSchema(schemaName);
99
+ const query = gqlQueries.attestationsOf(schema.uid, recipient);
100
+ const {
101
+ schema: { attestations },
102
+ } = await this.query<SchemaRes>(query);
103
+
104
+ return attestations;
105
+ }
106
+
107
+ async attestationsTo(
108
+ schemaName: TSchemaName,
109
+ recipient: Hex
110
+ ): Promise<IAttestation[]> {
111
+ const schema = this.gap.findSchema(schemaName);
112
+ const query = gqlQueries.attestationsTo(schema.uid, recipient);
113
+ const {
114
+ schema: { attestations },
115
+ } = await this.query<SchemaRes>(query);
116
+
117
+ return attestations;
118
+ }
119
+
120
+ /**
121
+ * Fetch all dependent attestations of a parent schema.
122
+ * @param parentSchema the schema name to get dependents of.
123
+ * @param parentUid the parent uid to get dependents of.
124
+ */
125
+ async dependentsOf(
126
+ parentSchema: TSchemaName,
127
+ parentUid: Hex
128
+ ): Promise<Attestation[]> {
129
+ const parent = this.gap.findSchema(parentSchema);
130
+ const children = parent.children.map((c) => c.uid);
131
+
132
+ if (!children.length)
133
+ throw new SchemaError(
134
+ "INVALID_REFERENCE",
135
+ `Schema ${parentSchema} has no children.`
136
+ );
137
+
138
+ const query = gqlQueries.dependentsOf(parentUid, children);
139
+ const { attestations } = await this.query<AttestationsRes>(query);
140
+
141
+ return Attestation.fromInterface(attestations, this.network.name);
142
+ }
143
+
144
+ async communities(search?: string) {
145
+ const [community, communityDetails] = this.gap.findManySchemas([
146
+ "Community",
147
+ "CommunityDetails",
148
+ ]);
149
+
150
+ const query = gqlQueries.attestationsOf(community.uid, search);
151
+ const {
152
+ schema: { attestations },
153
+ } = await this.query<SchemaRes>(query);
154
+
155
+ const communities = Attestation.fromInterface<Community>(
156
+ attestations,
157
+ this.network.name
158
+ );
159
+
160
+ if (!communities.length) return [];
161
+
162
+ return this.communitiesDetails(communities);
163
+ }
164
+
165
+ async communitiesOf(address?: Hex) {
166
+ const [community] = this.gap.findManySchemas(["Community"]);
167
+
168
+ const query = gqlQueries.attestationsTo(community.uid, address);
169
+ const {
170
+ schema: { attestations },
171
+ } = await this.query<SchemaRes>(query);
172
+
173
+ const communities = Attestation.fromInterface<Community>(
174
+ attestations,
175
+ this.network.name
176
+ );
177
+
178
+ if (!communities.length) return [];
179
+
180
+ return this.communitiesDetails(communities);
181
+ }
182
+
183
+ async communitiesAdminOf(address?: Hex) {
184
+ const [community] = this.gap.findManySchemas(["Community"]);
185
+
186
+ const query = gqlQueries.attestationsTo(community.uid, address);
187
+ const {
188
+ schema: { attestations },
189
+ } = await this.query<SchemaRes>(query);
190
+
191
+ const communities = Attestation.fromInterface<Community>(
192
+ attestations,
193
+ this.network.name
194
+ );
195
+
196
+ if (!communities.length) return [];
197
+
198
+ return this.communitiesDetails(communities);
199
+ }
200
+
201
+ async communitiesByIds(uids: Hex[]) {
202
+ if (!uids.length) return [];
203
+ const communityDetails = this.gap.findSchema("CommunityDetails");
204
+
205
+ const communityQuery = gqlQueries.attestationsIn(uids);
206
+ const detailsQuery = gqlQueries.dependentsOf(uids, [communityDetails.uid]);
207
+ try {
208
+ const [communities, details] = await Promise.all([
209
+ this.query<AttestationsRes>(communityQuery),
210
+ this.query<AttestationsRes>(detailsQuery),
211
+ ]);
212
+
213
+ const communitiesAttestations = Attestation.fromInterface<Community>(
214
+ communities.attestations || [],
215
+ this.network.name
216
+ );
217
+
218
+ const detailsAttestations = Attestation.fromInterface<CommunityDetails>(
219
+ details.attestations || [],
220
+ this.network.name
221
+ );
222
+
223
+ communitiesAttestations.forEach((community) => {
224
+ community.details = detailsAttestations.find(
225
+ (d) => d.refUID === community.uid
226
+ );
227
+ });
228
+
229
+ return communitiesAttestations;
230
+ } catch (error) {
231
+ throw error;
232
+ }
233
+ }
234
+
235
+ async communitiesDetails(communities: Community[]) {
236
+ const [project, communityDetails] = this.gap.findManySchemas([
237
+ "Project",
238
+ "CommunityDetails",
239
+ ]);
240
+
241
+ const ref = gqlQueries.dependentsOf(
242
+ communities.map((c) => c.uid),
243
+ [communityDetails.uid]
244
+ );
245
+
246
+ const results = await this.query<AttestationsRes>(ref);
247
+ const deps = Attestation.fromInterface(
248
+ results.attestations || [],
249
+ this.network.name
250
+ );
251
+
252
+ return communities.map((community) => {
253
+ community.projects = <Project[]>(
254
+ deps.filter(
255
+ (ref) =>
256
+ ref.schema.uid === project.uid && ref.refUID === community.uid
257
+ )
258
+ );
259
+
260
+ community.details = <CommunityDetails>(
261
+ deps.find(
262
+ (ref) =>
263
+ ref.schema.uid === communityDetails.uid &&
264
+ ref.refUID === community.uid
265
+ )
266
+ );
267
+
268
+ return community;
269
+ });
270
+ }
271
+
272
+ async communityBySlug(slug: string) {
273
+ const communitySchema = this.gap.findSchema("CommunityDetails");
274
+
275
+ const query = gqlQueries.attestationsOf(
276
+ communitySchema.uid,
277
+ this.getSearchFieldString("slug", slug)
278
+ );
279
+
280
+ try {
281
+ const {
282
+ schema: { attestations },
283
+ } = await this.query<SchemaRes>(query);
284
+
285
+ if (!attestations.length) throw new Error("Community not found.");
286
+
287
+ const communities = mapFilter(
288
+ Attestation.fromInterface<CommunityDetails>(
289
+ attestations,
290
+ this.network.name
291
+ ),
292
+ (details) => !!details.name,
293
+ (details) => {
294
+ const community = new Community({
295
+ data: { community: true },
296
+ uid: details.refUID,
297
+ schema: communitySchema,
298
+ recipient: details.recipient,
299
+ });
300
+
301
+ community.details = details;
302
+ return community;
303
+ }
304
+ );
305
+
306
+ const [withDetails] = await this.communitiesDetails(communities);
307
+
308
+ if (!withDetails) throw new Error("Community not found.");
309
+
310
+ const grants = await this.grantsByCommunity(withDetails.uid);
311
+ withDetails.grants = grants;
312
+
313
+ return withDetails;
314
+ } catch (error) {
315
+ throw error;
316
+ }
317
+ }
318
+
319
+ async communityById(uid: Hex) {
320
+ const query = gqlQueries.attestation(uid);
321
+ const { attestation } = await this.query<AttestationRes>(query);
322
+
323
+ if (!attestation) throw new Error("Community not found.");
324
+
325
+ const communities = Attestation.fromInterface<Community>(
326
+ [attestation],
327
+ this.network.name
328
+ ).map((c) => new Community(c));
329
+
330
+ const [withDetails] = await this.communitiesDetails(communities);
331
+ if (!withDetails) throw new Error("Community not found.");
332
+ const grants = await this.grantsByCommunity(uid);
333
+ withDetails.grants = grants;
334
+ return withDetails;
335
+ }
336
+
337
+ /**
338
+ * Fetch the details for a set of
339
+ * projects with project grants,
340
+ * members, milestones, and tags.
341
+ * @param projects
342
+ */
343
+ async projectsDetails(projects: Project[]) {
344
+ // Get projects array and fetch details, members, grants, etc then append to the project and return the array.
345
+
346
+ const [projectDetails] = this.gap.findManySchemas(["ProjectDetails"]);
347
+
348
+ const refQuery = gqlQueries.dependentsOf(
349
+ projects.map((p) => p.uid),
350
+ [projectDetails.uid]
351
+ );
352
+
353
+ const [result, members, grants] = await Promise.all([
354
+ this.query<AttestationsRes>(refQuery),
355
+ this.membersOf(projects),
356
+ this.grantsFor(projects, true),
357
+ ]);
358
+
359
+ const deps = Attestation.fromInterface(
360
+ result.attestations || [],
361
+ this.network.name
362
+ );
363
+
364
+ return projects.map((project) => {
365
+ project.details = <ProjectDetails>(
366
+ deps.find(
367
+ (ref) =>
368
+ ref.schema.uid === projectDetails.uid && ref.refUID === project.uid
369
+ )
370
+ );
371
+
372
+ project.members = members.filter((m) => m.refUID === project.uid);
373
+
374
+ project.grants = grants.filter((g) => g.refUID === project.uid);
375
+
376
+ return project;
377
+ });
378
+ }
379
+
380
+ async projectById(uid: Hex) {
381
+ const query = gqlQueries.attestation(uid);
382
+ const { attestation } = await this.query<AttestationRes>(query);
383
+
384
+ if (!attestation) throw new Error("Project not found.");
385
+
386
+ const projectAttestation = Attestation.fromInterface<Project>(
387
+ [attestation],
388
+ this.network.name
389
+ )[0];
390
+
391
+ const [result] = await this.projectsDetails([
392
+ new Project(projectAttestation),
393
+ ]);
394
+
395
+ return result;
396
+ }
397
+
398
+ async projectBySlug(slug: string) {
399
+ const projectDetails = this.gap.findSchema("ProjectDetails");
400
+
401
+ const query = gqlQueries.attestationsOf(
402
+ projectDetails.uid,
403
+ this.getSearchFieldString("slug", slug)
404
+ );
405
+
406
+ const {
407
+ schema: { attestations },
408
+ } = await this.query<SchemaRes>(query);
409
+
410
+ const projectAttestations = Attestation.fromInterface<ProjectDetails>(
411
+ attestations,
412
+ this.network.name
413
+ ).filter((p) => p.title);
414
+
415
+ if (!projectAttestations.length) throw new Error("Project not found.");
416
+
417
+ const project = new Project({
418
+ data: { project: true },
419
+ uid: projectAttestations[0].refUID,
420
+ schema: this.gap.findSchema("Project"),
421
+ recipient: projectAttestations[0].recipient,
422
+ });
423
+ const [withDetails] = await this.projectsDetails([project]);
424
+
425
+ if (!withDetails) throw new Error("Project not found.");
426
+
427
+ return withDetails;
428
+ }
429
+
430
+ async slugExists(slug: string) {
431
+ const details = this.gap.findSchema("ProjectDetails");
432
+
433
+ const query = gqlQueries.attestationsOf(details.uid, "slug");
434
+ const {
435
+ schema: { attestations },
436
+ } = await this.query<SchemaRes>(query);
437
+
438
+ return attestations.some((a) => a.decodedDataJson.includes(slug));
439
+ }
440
+
441
+ search(
442
+ query: string
443
+ ): Promise<{ projects: Project[]; communities: Community[] }> {
444
+ throw new Error("Method not implemented.");
445
+ }
446
+
447
+ searchProjects(query: string): Promise<Project[]> {
448
+ throw new Error("Method not implemented.");
449
+ }
450
+
451
+ async projects(name?: string): Promise<Project[]> {
452
+ const result = await this.attestations("Project", name);
453
+
454
+ if (!result.length) return [];
455
+ const projects = Attestation.fromInterface<Project>(
456
+ result,
457
+ this.network.name
458
+ );
459
+ return this.projectsDetails(projects);
460
+ }
461
+
462
+ async projectsOf(grantee: Hex): Promise<Project[]> {
463
+ const result = await this.attestationsTo("Project", grantee);
464
+
465
+ if (!result.length) return [];
466
+ const projects = Attestation.fromInterface<Project>(
467
+ result,
468
+ this.network.name
469
+ );
470
+ return this.projectsDetails(projects);
471
+ }
472
+
473
+ async grantee(address: Hex): Promise<Grantee> {
474
+ const projects = await this.projectsOf(address);
475
+
476
+ return new Grantee(address, projects);
477
+ }
478
+
479
+ async grantees(): Promise<Grantee[]> {
480
+ const projects = await this.projects();
481
+
482
+ return projects.reduce(
483
+ (acc, item) => {
484
+ const hasGrantee = acc.find((g) => g.address === item.recipient);
485
+
486
+ if (hasGrantee) hasGrantee.projects.push(item);
487
+ else acc.push(new Grantee(item.recipient, [item]));
488
+ return acc;
489
+ },
490
+ <Grantee[]>[]
491
+ );
492
+ }
493
+
494
+ async grantsOf(grantee: Hex, withCommunity?: boolean): Promise<Grant[]> {
495
+ const [grant, grantDetails, grantVerified] = this.gap.findManySchemas([
496
+ "Grant",
497
+ "GrantDetails",
498
+ "GrantVerified",
499
+ ]);
500
+
501
+ const query = gqlQueries.attestationsTo(grant.uid, grantee);
502
+ const {
503
+ schema: { attestations },
504
+ } = await this.query<SchemaRes>(query);
505
+
506
+ const grants = Attestation.fromInterface<Grant>(
507
+ attestations,
508
+ this.network.name
509
+ );
510
+
511
+ if (!grants.length) return [];
512
+
513
+ const ref = gqlQueries.dependentsOf(
514
+ grants.map((g) => g.uid),
515
+ [grantDetails.uid, grantVerified.uid],
516
+ grants.map((g) => g.recipient)
517
+ );
518
+
519
+ const results = await this.query<AttestationsRes>(ref);
520
+ const deps = Attestation.fromInterface(
521
+ results.attestations || [],
522
+ this.network.name
523
+ );
524
+
525
+ const milestones = await this.milestonesOf(grants);
526
+ const communities = withCommunity
527
+ ? await this.communitiesByIds(
528
+ mapFilter(
529
+ grants,
530
+ (g) => !!g.communityUID,
531
+ (g) => g.communityUID
532
+ )
533
+ )
534
+ : [];
535
+
536
+ const withDetails = grants.map((grant) => {
537
+ const refs = deps.filter((ref) => ref.refUID === grant.uid);
538
+
539
+ grant.verified = !!refs.find(
540
+ (ref) =>
541
+ ref.schema.uid === grantVerified.uid && ref.refUID === grant.uid
542
+ );
543
+
544
+ grant.details = <GrantDetails>(
545
+ refs.find(
546
+ (ref) =>
547
+ ref.schema.uid === grantDetails.uid &&
548
+ ref.refUID === grant.uid &&
549
+ typeof (ref as Milestone).endsAt === "undefined"
550
+ )
551
+ );
552
+
553
+ grant.milestones = milestones.filter(
554
+ (m) => m.refUID === grant.uid && typeof m.endsAt !== "undefined"
555
+ );
556
+
557
+ grant.community = communities.find((c) => c.uid === grant.communityUID);
558
+
559
+ return grant;
560
+ });
561
+
562
+ return this.grantsUpdates(withDetails);
563
+ }
564
+
565
+ async grantsUpdates(grants: Grant[]) {
566
+ const details = this.gap.findSchema("GrantDetails");
567
+
568
+ const query = gqlQueries.attestationsOf(
569
+ details.uid,
570
+ this.getSearchFieldString("type", "grant-update"),
571
+ grants.map((g) => g.uid)
572
+ );
573
+
574
+ const {
575
+ schema: { attestations },
576
+ } = await this.query<SchemaRes>(query);
577
+
578
+ const updates = Attestation.fromInterface<GrantUpdate>(
579
+ attestations,
580
+ this.network.name
581
+ );
582
+
583
+ return grants.map((grant) => {
584
+ grant.updates = updates.filter((u) => u.refUID === grant.uid);
585
+ return grant;
586
+ });
587
+ }
588
+
589
+ async grantsByCommunity(uid: Hex) {
590
+ const [grant, grantDetails, project, projectDetails] =
591
+ this.gap.findManySchemas([
592
+ "Grant",
593
+ "GrantDetails",
594
+ "Project",
595
+ "ProjectDetails",
596
+ ]);
597
+
598
+ const query = gqlQueries.attestations(grant.uid, uid);
599
+ const {
600
+ schema: { attestations },
601
+ } = await this.query<SchemaRes>(query);
602
+
603
+ const grants = Attestation.fromInterface<Grant>(
604
+ attestations,
605
+ this.network.name
606
+ ).map((g) => new Grant(g));
607
+
608
+ if (!grants.length) return [];
609
+
610
+ const refs = gqlQueries.dependentsOf(
611
+ grants.map((g) => [g.uid, g.refUID]).flat(),
612
+ [grantDetails.uid, project.uid]
613
+ );
614
+
615
+ const results = await this.query<AttestationsRes>(refs);
616
+
617
+ const deps = Attestation.fromInterface(
618
+ results.attestations || [],
619
+ this.network.name
620
+ );
621
+
622
+ const projectsQuery = gqlQueries.attestationsIn(
623
+ grants.map((g) => g.refUID)
624
+ );
625
+
626
+ const { attestations: projectAttestations } =
627
+ await this.query<AttestationsRes>(projectsQuery);
628
+
629
+ const projects = Attestation.fromInterface<Project>(
630
+ projectAttestations,
631
+ this.network.name
632
+ );
633
+
634
+ const milestones = await this.milestonesOf(grants);
635
+
636
+ const getSummaryProject = (project: Project): ISummaryProject => ({
637
+ title: project.details?.title,
638
+ uid: project.uid,
639
+ slug: project.details?.slug,
640
+ });
641
+
642
+ return grants
643
+ .map((grant) => {
644
+ grant.project = getSummaryProject(
645
+ <Project>projects.find((p) => p.uid === grant.refUID)
646
+ );
647
+ grant.details = <GrantDetails>(
648
+ deps.find(
649
+ (d) =>
650
+ d.refUID === grant.uid &&
651
+ d.schema.uid === grantDetails.uid &&
652
+ typeof (d as GrantDetails).amount !== undefined &&
653
+ typeof (d as Milestone).endsAt === "undefined" &&
654
+ typeof (d as GrantUpdate).data.type === "undefined"
655
+ )
656
+ );
657
+
658
+ grant.milestones = milestones
659
+ .filter(
660
+ (m) => m.refUID === grant.uid && typeof m.endsAt !== "undefined"
661
+ )
662
+ .sort((a, b) => a.endsAt - b.endsAt);
663
+
664
+ grant.updates = deps.filter(
665
+ (d: GrantUpdate) => d.data.type && d.refUID === grant.uid
666
+ ) as GrantUpdate[];
667
+
668
+ return grant;
669
+ })
670
+ .filter((g) => !!g.project);
671
+ }
672
+
673
+ async grantsFor(
674
+ projects: Project[],
675
+ withCommunity?: boolean
676
+ ): Promise<Grant[]> {
677
+ const [grant, grantDetails] = this.gap.findManySchemas([
678
+ "Grant",
679
+ "GrantDetails",
680
+ "Milestone",
681
+ "MilestoneApproved",
682
+ "MilestoneCompleted",
683
+ ]);
684
+
685
+ const query = gqlQueries.dependentsOf(
686
+ projects.map((p) => p.uid),
687
+ [grant.uid]
688
+ );
689
+
690
+ const { attestations: grants } = await this.query<AttestationsRes>(query);
691
+
692
+ const grantsWithDetails = Attestation.fromInterface<Grant>(
693
+ grants,
694
+ this.network.name
695
+ ).map((g) => new Grant(g));
696
+
697
+ const ref = gqlQueries.dependentsOf(
698
+ grants.map((g) => g.uid),
699
+ [grantDetails.uid]
700
+ );
701
+
702
+ const { attestations } = await this.query<AttestationsRes>(ref);
703
+
704
+ const milestones = await this.milestonesOf(grantsWithDetails);
705
+
706
+ const deps = Attestation.fromInterface(attestations, this.network.name);
707
+
708
+ // TODO unify this with grantsOf
709
+ grantsWithDetails.forEach((grant) => {
710
+ grant.details = <GrantDetails>(
711
+ deps.find(
712
+ (d) =>
713
+ d.refUID === grant.uid &&
714
+ d.schema.uid === grantDetails.uid &&
715
+ typeof (d as GrantDetails).amount !== undefined &&
716
+ typeof (d as Milestone).endsAt === "undefined" &&
717
+ typeof (d as GrantUpdate).data.type === "undefined"
718
+ )
719
+ );
720
+ grant.milestones = milestones
721
+ .filter(
722
+ (m) => m.refUID === grant.uid && typeof m.endsAt !== "undefined"
723
+ )
724
+ .sort((a, b) => a.endsAt - b.endsAt);
725
+ });
726
+
727
+ const communities = withCommunity
728
+ ? await this.communitiesByIds(
729
+ mapFilter(
730
+ grantsWithDetails,
731
+ (g) => !!g.communityUID,
732
+ (g) => g.communityUID
733
+ )
734
+ )
735
+ : [];
736
+
737
+ grantsWithDetails.forEach((grant) => {
738
+ grant.community = communities.find((c) => c.uid === grant.communityUID);
739
+ });
740
+
741
+ const grantsWithUpdates = await this.grantsUpdates(grantsWithDetails);
742
+
743
+ return grantsWithUpdates.sort(
744
+ (a, b) =>
745
+ a.milestones?.at(-1)?.endsAt - b.milestones?.at(-1)?.endsAt ||
746
+ a.createdAt.getTime() - b.createdAt.getTime()
747
+ );
748
+ }
749
+
750
+ async milestonesOf(grants: Grant[]): Promise<Milestone[]> {
751
+ const [milestone, milestoneApproved, milestoneCompleted] =
752
+ this.gap.findManySchemas([
753
+ "Milestone",
754
+ "MilestoneApproved",
755
+ "MilestoneCompleted",
756
+ ]);
757
+
758
+ const query = gqlQueries.dependentsOf(
759
+ grants.map((g) => g.uid),
760
+ [milestone.uid]
761
+ );
762
+
763
+ const { attestations } = await this.query<AttestationsRes>(query);
764
+
765
+ const milestones = Attestation.fromInterface<Milestone>(
766
+ attestations,
767
+ this.network.name
768
+ )
769
+ .map((milestone) => new Milestone(milestone))
770
+ .filter((m) => typeof m.endsAt !== "undefined");
771
+
772
+ if (!milestones.length) return [];
773
+
774
+ const ref = gqlQueries.dependentsOf(
775
+ milestones.map((m) => m.uid),
776
+ [milestoneApproved.uid, milestoneCompleted.uid]
777
+ );
778
+
779
+ const results = await this.query<AttestationsRes>(ref);
780
+
781
+ const deps = Attestation.fromInterface<MilestoneCompleted>(
782
+ results.attestations || [],
783
+ this.network.name
784
+ );
785
+
786
+ return milestones.map((milestone) => {
787
+ const refs = deps.filter((ref) => ref.refUID === milestone.uid);
788
+
789
+ milestone.endsAt = toUnix(milestone.endsAt);
790
+
791
+ milestone.completed = refs.find(
792
+ (dep) => dep.type === "completed" && dep.refUID === milestone.uid
793
+ );
794
+
795
+ milestone.approved = refs.find(
796
+ (dep) => dep.type === "approved" && dep.refUID === milestone.uid
797
+ );
798
+
799
+ milestone.rejected = refs.find(
800
+ (dep) => dep.type === "rejected" && dep.refUID === milestone.uid
801
+ );
802
+
803
+ return milestone;
804
+ });
805
+ }
806
+
807
+ async membersOf(projects: Project[]): Promise<MemberOf[]> {
808
+ const [member, memberDetails] = this.gap.findManySchemas([
809
+ "MemberOf",
810
+ "MemberDetails",
811
+ ]);
812
+
813
+ if (!projects.length) return [];
814
+
815
+ const query = gqlQueries.dependentsOf(
816
+ projects.map((p) => p.uid),
817
+ [member.uid],
818
+ projects.map((p) => p.attester)
819
+ );
820
+
821
+ const results = await this.query<AttestationsRes>(query);
822
+
823
+ const members = Attestation.fromInterface<MemberOf>(
824
+ results.attestations || [],
825
+ this.network.name
826
+ );
827
+
828
+ if (members.length) {
829
+ const ref = gqlQueries.dependentsOf(
830
+ members.map((a) => a.uid),
831
+ [memberDetails.uid],
832
+ members.map((a) => a.attester)
833
+ );
834
+
835
+ const detailsResult = await this.query<AttestationsRes>(ref);
836
+ const detailsRef = Attestation.fromInterface<MemberDetails>(
837
+ detailsResult.attestations || [],
838
+ this.network.name
839
+ );
840
+
841
+ members.forEach((member) => {
842
+ member.details = detailsRef.find((d) => d.refUID === member.uid);
843
+ });
844
+ }
845
+
846
+ return members;
847
+ }
848
+
849
+ /**
850
+ * Returns a string to be used to search by a value in `decodedDataJson`.
851
+ * @param field
852
+ * @param value
853
+ */
854
+ private getSearchFieldString(field: string, value: string) {
855
+ return [
856
+ String.raw`\\\\\"${field}\\\\\":\\\\\"${value}\\\\\"`,
857
+ String.raw`\\\\\"${field}\\\\\": \\\\\"${value}\\\\\"`,
858
+ ];
859
+ }
860
+
861
+ async grantsForExtProject(projectExtId: string): Promise<Grant[]> {
862
+ console.error(
863
+ new Error(
864
+ "Grants for external project is only supported by a custom indexer. Check https://github.com/show-karma/karma-gap-sdk for more information."
865
+ )
866
+ );
867
+ return [];
868
+ }
869
+ }