@show-karma/karma-gap-sdk 0.3.2 → 0.3.4
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/config/keys.example.json +6 -0
- package/core/abi/EAS.json +1 -0
- package/core/abi/SchemaRegistry.json +1 -0
- package/core/class/Attestation.ts +402 -0
- package/core/class/Fetcher.ts +202 -0
- package/core/class/GAP.d.ts +3 -1
- package/core/class/GAP.js +5 -2
- package/core/class/GAP.ts +398 -0
- package/core/class/GapSchema.ts +90 -0
- package/core/class/Gelato/Gelato.ts +286 -0
- package/core/class/GraphQL/AxiosGQL.ts +29 -0
- package/core/class/GraphQL/EASClient.ts +34 -0
- package/core/class/GraphQL/GapEasClient.ts +845 -0
- package/core/class/GraphQL/index.ts +3 -0
- package/core/class/Schema.ts +609 -0
- package/core/class/SchemaError.ts +36 -0
- package/core/class/contract/GapContract.js +2 -2
- package/core/class/contract/GapContract.ts +353 -0
- package/core/class/entities/Community.ts +115 -0
- package/core/class/entities/Grant.ts +309 -0
- package/core/class/entities/MemberOf.ts +42 -0
- package/core/class/entities/Milestone.ts +269 -0
- package/core/class/entities/Project.ts +370 -0
- package/core/class/entities/index.ts +5 -0
- package/core/class/index.ts +10 -0
- package/core/class/karma-indexer/GapIndexerClient.ts +245 -0
- package/core/class/remote-storage/IpfsStorage.ts +51 -0
- package/core/class/remote-storage/RemoteStorage.ts +65 -0
- package/core/class/types/attestations.ts +158 -0
- package/core/consts.js +1 -1
- package/core/consts.ts +282 -0
- package/core/index.ts +7 -0
- package/core/scripts/deploy.ts +67 -0
- package/core/scripts/index.ts +1 -0
- package/core/types.ts +186 -0
- package/core/utils/gelato/index.ts +3 -0
- package/core/utils/gelato/send-gelato-txn.ts +114 -0
- package/core/utils/gelato/sponsor-handler.ts +77 -0
- package/core/utils/gelato/watch-gelato-txn.ts +67 -0
- package/core/utils/get-date.ts +3 -0
- package/core/utils/get-ipfs-data.ts +13 -0
- package/core/utils/get-web3-provider.ts +20 -0
- package/core/utils/gql-queries.ts +133 -0
- package/core/utils/index.ts +7 -0
- package/core/utils/map-filter.ts +21 -0
- package/core/utils/serialize-bigint.ts +7 -0
- package/core/utils/to-unix.ts +18 -0
- package/csv-upload/.gitkeep +0 -0
- package/csv-upload/example.csv +2 -0
- package/csv-upload/scripts/run.ts +193 -0
- package/docs/.gitkeep +0 -0
- package/docs/images/attestation-architecture.png +0 -0
- package/docs/images/dfd-get-projects.png +0 -0
- package/index.ts +1 -0
- package/package.json +1 -1
- package/readme.md +39 -34
- package/schemas/.gitkeep +0 -0
- package/schemas/GAP-schemas-1692135812877.json +33 -0
- package/test-file.ts +92 -0
- package/tsconfig.json +26 -0
- package/core/class/AttestationIPFS.d.ts +0 -7
- package/core/class/AttestationIPFS.js +0 -10
- package/core/class/GraphQL/Fetcher.d.ts +0 -132
- package/core/class/GraphQL/Fetcher.js +0 -7
- package/core/class/GraphQL/GAPFetcher.d.ts +0 -160
- package/core/class/GraphQL/GAPFetcher.js +0 -516
- package/core/class/IPFS/IPFS.d.ts +0 -13
- package/core/class/IPFS/IPFS.js +0 -24
- package/core/class/contract/MultiAttest.d.ts +0 -10
- package/core/class/contract/MultiAttest.js +0 -19
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { Attestation } from '../Attestation';
|
|
2
|
+
import {
|
|
3
|
+
Grantee,
|
|
4
|
+
MemberDetails,
|
|
5
|
+
ProjectDetails,
|
|
6
|
+
Tag,
|
|
7
|
+
} from '../types/attestations';
|
|
8
|
+
import {
|
|
9
|
+
Hex,
|
|
10
|
+
MultiAttestPayload,
|
|
11
|
+
SignerOrProvider,
|
|
12
|
+
TNetwork,
|
|
13
|
+
} from 'core/types';
|
|
14
|
+
import { GapSchema } from '../GapSchema';
|
|
15
|
+
import { AttestationError } from '../SchemaError';
|
|
16
|
+
import { mapFilter } from '../../utils';
|
|
17
|
+
import { Grant } from './Grant';
|
|
18
|
+
import { nullRef } from '../../consts';
|
|
19
|
+
import { MemberOf } from './MemberOf';
|
|
20
|
+
import { GapContract } from '../contract/GapContract';
|
|
21
|
+
|
|
22
|
+
interface _Project extends Project {}
|
|
23
|
+
|
|
24
|
+
export interface IProject {
|
|
25
|
+
project: true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class Project extends Attestation<IProject> {
|
|
29
|
+
details?: ProjectDetails;
|
|
30
|
+
members: MemberOf[] = [];
|
|
31
|
+
grants: Grant[] = [];
|
|
32
|
+
grantee: Grantee;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates the payload for a multi-attestation.
|
|
36
|
+
*
|
|
37
|
+
* > if Current payload is set, it'll be used as the base payload
|
|
38
|
+
* and the project should refer to an index of the current payload,
|
|
39
|
+
* usually the community position.
|
|
40
|
+
*
|
|
41
|
+
* @param payload
|
|
42
|
+
* @param communityIdx
|
|
43
|
+
*/
|
|
44
|
+
async multiAttestPayload(
|
|
45
|
+
currentPayload: MultiAttestPayload = [],
|
|
46
|
+
communityIdx = 0
|
|
47
|
+
): Promise<MultiAttestPayload> {
|
|
48
|
+
const payload = [...currentPayload];
|
|
49
|
+
const projectIdx =
|
|
50
|
+
payload.push([this, await this.payloadFor(communityIdx)]) - 1;
|
|
51
|
+
|
|
52
|
+
if (this.details) {
|
|
53
|
+
payload.push([this.details, await this.details.payloadFor(projectIdx)]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (this.members?.length) {
|
|
57
|
+
await Promise.all(
|
|
58
|
+
this.members.map(async (m) =>
|
|
59
|
+
payload.push(...(await m.multiAttestPayload(payload, projectIdx)))
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (this.grants?.length) {
|
|
65
|
+
await Promise.all(
|
|
66
|
+
this.grants.map(async (g) =>
|
|
67
|
+
payload.push(...(await g.multiAttestPayload(payload, projectIdx)))
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return payload.slice(currentPayload.length, payload.length);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async attest(signer: SignerOrProvider): Promise<void> {
|
|
76
|
+
const payload = await this.multiAttestPayload();
|
|
77
|
+
const uids = await GapContract.multiAttest(
|
|
78
|
+
signer,
|
|
79
|
+
payload.map((p) => p[1])
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
uids.forEach((uid, index) => {
|
|
83
|
+
payload[index][0].uid = uid;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async transferOwnership(signer: SignerOrProvider, newOwner: Hex) {
|
|
88
|
+
await GapContract.transferProjectOwnership(signer, this.uid, newOwner);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isOwner(signer: SignerOrProvider): Promise<boolean> {
|
|
92
|
+
return GapContract.isProjectOwner(signer, this.uid, this.chainID);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Add new members to the project.
|
|
97
|
+
* If any member in the array already exists in the project
|
|
98
|
+
* it'll be ignored.
|
|
99
|
+
* @param members
|
|
100
|
+
*/
|
|
101
|
+
pushMembers(...members: Hex[]) {
|
|
102
|
+
this.members.push(
|
|
103
|
+
...mapFilter(
|
|
104
|
+
members,
|
|
105
|
+
(member) => !!this.members.find((m) => m.recipient === member),
|
|
106
|
+
(member) =>
|
|
107
|
+
new MemberOf({
|
|
108
|
+
data: { memberOf: true },
|
|
109
|
+
refUID: this.uid,
|
|
110
|
+
schema: this.schema.gap.findSchema('MemberOf'),
|
|
111
|
+
recipient: member,
|
|
112
|
+
uid: nullRef,
|
|
113
|
+
})
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Add new members to the project.
|
|
120
|
+
* If any member in the array already exists in the project
|
|
121
|
+
* it'll be ignored.
|
|
122
|
+
*
|
|
123
|
+
* __To modify member details, use `addMemberDetails(signer, MemberDetails[])` instead.__
|
|
124
|
+
* @param signer
|
|
125
|
+
* @param members
|
|
126
|
+
*/
|
|
127
|
+
async attestMembers(signer: SignerOrProvider, members: MemberDetails[]) {
|
|
128
|
+
const newMembers = mapFilter(
|
|
129
|
+
members,
|
|
130
|
+
(member) => !this.members.find((m) => m.recipient === member.recipient),
|
|
131
|
+
// (member) => !!member,
|
|
132
|
+
(details) => {
|
|
133
|
+
const member = new MemberOf({
|
|
134
|
+
data: { memberOf: true },
|
|
135
|
+
refUID: this.uid,
|
|
136
|
+
schema: this.schema.gap.findSchema('MemberOf'),
|
|
137
|
+
createdAt: Date.now(),
|
|
138
|
+
recipient: details.recipient,
|
|
139
|
+
uid: nullRef,
|
|
140
|
+
});
|
|
141
|
+
return { member, details };
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
if (!newMembers.length) {
|
|
146
|
+
throw new AttestationError('ATTEST_ERROR', 'No new members to add.');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log(`Creating ${newMembers.length} new members`);
|
|
150
|
+
|
|
151
|
+
const attestedMembers = <Hex[]>await this.schema.multiAttest(
|
|
152
|
+
signer,
|
|
153
|
+
newMembers.map((m) => m.member)
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
console.log('attested-members', attestedMembers);
|
|
157
|
+
|
|
158
|
+
newMembers.forEach(({ member, details }, idx) => {
|
|
159
|
+
Object.assign(member, { uid: attestedMembers[idx] });
|
|
160
|
+
|
|
161
|
+
if (!details) return;
|
|
162
|
+
Object.assign(details, { refUID: attestedMembers[idx] });
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
this.members.push(...newMembers.map((m) => m.member));
|
|
166
|
+
|
|
167
|
+
await this.addMemberDetails(
|
|
168
|
+
signer,
|
|
169
|
+
newMembers.map((m) => m.details)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Add new details to the members of a project. Note that it will overwrite
|
|
175
|
+
* any existing details.
|
|
176
|
+
*
|
|
177
|
+
* @param signer
|
|
178
|
+
* @param entities
|
|
179
|
+
*/
|
|
180
|
+
private async addMemberDetails(
|
|
181
|
+
signer: SignerOrProvider,
|
|
182
|
+
entities: MemberDetails[]
|
|
183
|
+
) {
|
|
184
|
+
// Check if any of members should be revoked (details modified)
|
|
185
|
+
const toRevoke = mapFilter(
|
|
186
|
+
this.members,
|
|
187
|
+
(member) =>
|
|
188
|
+
!!entities.find(
|
|
189
|
+
(entity) =>
|
|
190
|
+
member.uid === entity.refUID &&
|
|
191
|
+
member.details &&
|
|
192
|
+
member.details?.refUID !== entity.refUID
|
|
193
|
+
),
|
|
194
|
+
(member) => member.uid
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (toRevoke.length) {
|
|
198
|
+
console.log('Revoking details');
|
|
199
|
+
await this.cleanDetails(signer, toRevoke);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.log(`Creating ${entities.length} new member details`);
|
|
203
|
+
|
|
204
|
+
const attestedEntities = <Hex[]>(
|
|
205
|
+
await this.schema.multiAttest(signer, entities)
|
|
206
|
+
);
|
|
207
|
+
console.log('attested-entities', attestedEntities);
|
|
208
|
+
|
|
209
|
+
entities.forEach((entity, idx) => {
|
|
210
|
+
const member = this.members.find(
|
|
211
|
+
(member) => member.uid === entity.refUID
|
|
212
|
+
);
|
|
213
|
+
if (!member) return;
|
|
214
|
+
|
|
215
|
+
Object.assign(entity, { uid: attestedEntities[idx] });
|
|
216
|
+
member.details = entity;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Clean member details.
|
|
222
|
+
* @param signer
|
|
223
|
+
* @param uids
|
|
224
|
+
*/
|
|
225
|
+
async cleanDetails(signer: SignerOrProvider, uids: Hex[]) {
|
|
226
|
+
if (!uids.length) {
|
|
227
|
+
throw new AttestationError('ATTEST_ERROR', 'No details to clean.');
|
|
228
|
+
}
|
|
229
|
+
const memberDetails = this.schema.gap.findSchema('MemberDetails');
|
|
230
|
+
|
|
231
|
+
await this.schema.multiRevoke(
|
|
232
|
+
signer,
|
|
233
|
+
uids.map((uid) => ({ schemaId: memberDetails.uid, uid }))
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
this.members.forEach((member) => {
|
|
237
|
+
if (!member.details) return;
|
|
238
|
+
if (uids.includes(member.details.uid)) {
|
|
239
|
+
member.details = undefined;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Remove members from the project.
|
|
246
|
+
* @param signer
|
|
247
|
+
* @param uids
|
|
248
|
+
* @returns
|
|
249
|
+
*/
|
|
250
|
+
async removeMembers(signer: SignerOrProvider, uids: Hex[]) {
|
|
251
|
+
if (!uids.length) {
|
|
252
|
+
throw new AttestationError('ATTEST_ERROR', 'No members to remove.');
|
|
253
|
+
}
|
|
254
|
+
const memberOf = this.schema.gap.findSchema('MemberOf');
|
|
255
|
+
|
|
256
|
+
const details = mapFilter(
|
|
257
|
+
this.members,
|
|
258
|
+
(m) => uids.includes(m.uid) && !!m.details,
|
|
259
|
+
(m) => m.details?.uid
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
if (details.length) {
|
|
263
|
+
await this.cleanDetails(signer, details);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
await this.schema.multiRevoke(
|
|
267
|
+
signer,
|
|
268
|
+
uids.map((uid) => ({ schemaId: memberOf.uid, uid }))
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
this.members = this.members.filter((m) => !uids.includes(m.uid));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Remove all members from the project.
|
|
276
|
+
* @param signer
|
|
277
|
+
*/
|
|
278
|
+
async removeAllMembers(signer: SignerOrProvider) {
|
|
279
|
+
const members = mapFilter(
|
|
280
|
+
this.members,
|
|
281
|
+
(m) => !!m.uid,
|
|
282
|
+
(m) => m.uid
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (!members.length) {
|
|
286
|
+
throw new AttestationError('REVOKATION_ERROR', 'No members to revoke.');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const details = mapFilter(
|
|
290
|
+
this.members,
|
|
291
|
+
(m) => !!m.details,
|
|
292
|
+
(m) => m.details?.uid
|
|
293
|
+
);
|
|
294
|
+
if (details.length) {
|
|
295
|
+
await this.cleanDetails(signer, details);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
await this.removeMembers(signer, members);
|
|
299
|
+
this.members.splice(0, this.members.length);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
static from(attestations: _Project[], network: TNetwork): Project[] {
|
|
303
|
+
return attestations.map((attestation) => {
|
|
304
|
+
const project = new Project({
|
|
305
|
+
...attestation,
|
|
306
|
+
data: {
|
|
307
|
+
project: true,
|
|
308
|
+
},
|
|
309
|
+
schema: GapSchema.find('Project', network),
|
|
310
|
+
chainID: attestation.chainID,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
if (attestation.details) {
|
|
314
|
+
const { details } = attestation;
|
|
315
|
+
project.details = new ProjectDetails({
|
|
316
|
+
...details,
|
|
317
|
+
data: {
|
|
318
|
+
...details.data,
|
|
319
|
+
},
|
|
320
|
+
schema: GapSchema.find('ProjectDetails', network),
|
|
321
|
+
chainID: attestation.chainID,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
project.details.links = details.data.links || [];
|
|
325
|
+
project.details.tags = details.data.tags || [];
|
|
326
|
+
|
|
327
|
+
if ((attestation.data as any).links) {
|
|
328
|
+
project.details.links = (attestation.data as any).links;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if ((attestation.data as any).tags) {
|
|
332
|
+
project.details.tags = (attestation as any).tags;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (attestation.members) {
|
|
337
|
+
project.members = attestation.members.map((m) => {
|
|
338
|
+
const member = new MemberOf({
|
|
339
|
+
...m,
|
|
340
|
+
data: {
|
|
341
|
+
memberOf: true,
|
|
342
|
+
},
|
|
343
|
+
schema: GapSchema.find('MemberOf', network),
|
|
344
|
+
chainID: attestation.chainID,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
if (m.details) {
|
|
348
|
+
const { details } = m;
|
|
349
|
+
member.details = new MemberDetails({
|
|
350
|
+
...details,
|
|
351
|
+
data: {
|
|
352
|
+
...details.data,
|
|
353
|
+
},
|
|
354
|
+
schema: GapSchema.find('MemberDetails', network),
|
|
355
|
+
chainID: attestation.chainID,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return member;
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (attestation.grants) {
|
|
364
|
+
project.grants = Grant.from(attestation.grants, network);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return project;
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './Attestation';
|
|
2
|
+
export * from './GAP';
|
|
3
|
+
export * from './GapSchema';
|
|
4
|
+
export * from './Schema';
|
|
5
|
+
export * from './SchemaError';
|
|
6
|
+
export * from './entities';
|
|
7
|
+
export * from './Fetcher';
|
|
8
|
+
export * from './karma-indexer/GapIndexerClient';
|
|
9
|
+
export * from './remote-storage/IpfsStorage';
|
|
10
|
+
export * from './remote-storage/RemoteStorage';
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { TSchemaName, IAttestation, TNetwork, Hex } from 'core/types';
|
|
2
|
+
import { Attestation } from '../Attestation';
|
|
3
|
+
import { GapSchema } from '../GapSchema';
|
|
4
|
+
import { Fetcher } from '../Fetcher';
|
|
5
|
+
import { Community, Project, Grant, Milestone, MemberOf } from '../entities';
|
|
6
|
+
import { Grantee } from '../types/attestations';
|
|
7
|
+
|
|
8
|
+
const Endpoints = {
|
|
9
|
+
attestations: {
|
|
10
|
+
all: () => '/attestations',
|
|
11
|
+
byUid: (uid: Hex) => `/attestations/${uid}`,
|
|
12
|
+
},
|
|
13
|
+
communities: {
|
|
14
|
+
all: () => '/communities',
|
|
15
|
+
byUidOrSlug: (uidOrSlug: string) => `/communities/${uidOrSlug}`,
|
|
16
|
+
grants: (uidOrSlug: string) => `/communities/${uidOrSlug}/grants`,
|
|
17
|
+
},
|
|
18
|
+
grantees: {
|
|
19
|
+
all: () => '/grantees',
|
|
20
|
+
byAddress: (address: Hex) => `/grantees/${address}`,
|
|
21
|
+
grants: (address: Hex) => `/grantees/${address}/grants`,
|
|
22
|
+
projects: (address: Hex) => `/grantees/${address}/projects`,
|
|
23
|
+
communities: (address: Hex, withGrants) =>
|
|
24
|
+
`/grantees/${address}/communities${withGrants ? '?withGrants=true' : ''}`,
|
|
25
|
+
},
|
|
26
|
+
grants: {
|
|
27
|
+
all: () => '/grants',
|
|
28
|
+
byUid: (uid: Hex) => `/grants/${uid}`,
|
|
29
|
+
byExternalId: (id: string) => `/grants/external-id/${id}`,
|
|
30
|
+
},
|
|
31
|
+
project: {
|
|
32
|
+
all: () => '/projects',
|
|
33
|
+
byUidOrSlug: (uidOrSlug: string) => `/projects/${uidOrSlug}`,
|
|
34
|
+
grants: (uidOrSlug: string) => `/projects/${uidOrSlug}/grants`,
|
|
35
|
+
milestones: (uidOrSlug: string) => `/projects/${uidOrSlug}/milestones`,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class GapIndexerClient extends Fetcher {
|
|
40
|
+
async attestation<T = unknown>(
|
|
41
|
+
uid: `0x${string}`
|
|
42
|
+
): Promise<Attestation<T, GapSchema>> {
|
|
43
|
+
const { data } = await this.client.get<IAttestation>(
|
|
44
|
+
Endpoints.attestations.byUid(uid)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (!data) throw new Error('Attestation not found');
|
|
48
|
+
return Attestation.fromInterface<Attestation<T>>(
|
|
49
|
+
[data],
|
|
50
|
+
this.gap.network
|
|
51
|
+
)[0];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async attestations(
|
|
55
|
+
schemaName: TSchemaName,
|
|
56
|
+
search?: string
|
|
57
|
+
): Promise<IAttestation[]> {
|
|
58
|
+
const { data } = await this.client.get<IAttestation[]>(
|
|
59
|
+
Endpoints.attestations.all(),
|
|
60
|
+
{
|
|
61
|
+
params: {
|
|
62
|
+
'filter[schemaUID]': this.gap.findSchema(schemaName).uid,
|
|
63
|
+
'filter[data]': search,
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return data || [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async attestationsOf(
|
|
72
|
+
schemaName: TSchemaName,
|
|
73
|
+
attester: `0x${string}`
|
|
74
|
+
): Promise<IAttestation[]> {
|
|
75
|
+
const { data } = await this.client.get<IAttestation[]>(
|
|
76
|
+
Endpoints.attestations.all(),
|
|
77
|
+
{
|
|
78
|
+
params: {
|
|
79
|
+
'filter[schemaUID]': this.gap.findSchema(schemaName).uid,
|
|
80
|
+
'filter[recipient]': attester,
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return data || [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
attestationsTo(
|
|
89
|
+
schemaName: TSchemaName,
|
|
90
|
+
recipient: `0x${string}`
|
|
91
|
+
): Promise<IAttestation[]> {
|
|
92
|
+
return this.attestationsOf(schemaName, recipient);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async communities(search?: string): Promise<Community[]> {
|
|
96
|
+
const { data } = await this.client.get<Community[]>(
|
|
97
|
+
Endpoints.communities.all(),
|
|
98
|
+
{
|
|
99
|
+
params: {
|
|
100
|
+
'filter[name]': search,
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return Community.from(data, this.gap.network);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async communitiesOf(address: Hex, withGrants: boolean): Promise<Community[]> {
|
|
109
|
+
const { data } = await this.client.get<Community[]>(
|
|
110
|
+
Endpoints.grantees.communities(address, withGrants)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
return Community.from(data, this.gap.network);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
communitiesByIds(uids: `0x${string}`[]): Promise<Community[]> {
|
|
117
|
+
throw new Error('Method not implemented.');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async communityBySlug(slug: string): Promise<Community> {
|
|
121
|
+
const { data } = await this.client.get<Community>(
|
|
122
|
+
Endpoints.communities.byUidOrSlug(slug)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return Community.from([data], this.gap.network)[0];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
communityById(uid: `0x${string}`): Promise<Community> {
|
|
129
|
+
return this.communityBySlug(uid);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async projectBySlug(slug: string): Promise<Project> {
|
|
133
|
+
const { data } = await this.client.get<Project>(
|
|
134
|
+
Endpoints.project.byUidOrSlug(slug)
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return Project.from([data], this.gap.network)[0];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
projectById(uid: `0x${string}`): Promise<Project> {
|
|
141
|
+
return this.projectBySlug(uid);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async searchProjects(query: string): Promise<Project[]> {
|
|
145
|
+
const { data } = await this.client.get<Project[]>(Endpoints.project.all(), {
|
|
146
|
+
params: {
|
|
147
|
+
q: query,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return Project.from(data, this.gap.network);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async projects(name?: string): Promise<Project[]> {
|
|
155
|
+
const { data } = await this.client.get<Project[]>(Endpoints.project.all(), {
|
|
156
|
+
params: {
|
|
157
|
+
'filter[title]': name,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return Project.from(data, this.gap.network);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async projectsOf(grantee: `0x${string}`): Promise<Project[]> {
|
|
165
|
+
const { data } = await this.client.get<Project[]>(
|
|
166
|
+
Endpoints.grantees.projects(grantee)
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
return Project.from(data, this.gap.network);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async grantee(address: `0x${string}`): Promise<Grantee> {
|
|
173
|
+
const { data } = await this.client.get<Grantee>(
|
|
174
|
+
Endpoints.grantees.byAddress(address)
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
return data;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async grantees(): Promise<Grantee[]> {
|
|
181
|
+
const { data } = await this.client.get<Grantee[]>(Endpoints.grantees.all());
|
|
182
|
+
|
|
183
|
+
return data;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async grantsOf(
|
|
187
|
+
grantee: `0x${string}`,
|
|
188
|
+
withCommunity?: boolean
|
|
189
|
+
): Promise<Grant[]> {
|
|
190
|
+
const { data } = await this.client.get<Grant[]>(
|
|
191
|
+
Endpoints.grantees.grants(grantee)
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return Grant.from(data, this.gap.network);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async grantsFor(
|
|
198
|
+
projects: Project[],
|
|
199
|
+
withCommunity?: boolean
|
|
200
|
+
): Promise<Grant[]> {
|
|
201
|
+
const { data } = await this.client.get<Grant[]>(
|
|
202
|
+
Endpoints.project.grants(projects[0].uid)
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
return Grant.from(data, this.gap.network);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async grantsForExtProject(projectExtId: string): Promise<Grant[]> {
|
|
209
|
+
const { data } = await this.client.get<Grant[]>(
|
|
210
|
+
Endpoints.grants.byExternalId(projectExtId)
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
return Grant.from(data, this.gap.network);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async grantsByCommunity(uid: `0x${string}`) {
|
|
217
|
+
const { data } = await this.client.get<Grant[]>(
|
|
218
|
+
Endpoints.communities.grants(uid)
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
return Grant.from(data, this.gap.network);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async milestonesOf(grants: Grant[]): Promise<Milestone[]> {
|
|
225
|
+
const { data } = await this.client.get<Milestone[]>(
|
|
226
|
+
Endpoints.project.milestones(grants[0].uid)
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
return Milestone.from(data, this.gap.network);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async membersOf(projects: Project[]): Promise<MemberOf[]> {
|
|
233
|
+
throw new Error('Method not implemented.');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async slugExists(slug: string): Promise<boolean> {
|
|
237
|
+
try {
|
|
238
|
+
await this.client.get<Project>(Endpoints.project.byUidOrSlug(slug));
|
|
239
|
+
|
|
240
|
+
return true;
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { NFTStorage } from 'nft.storage';
|
|
2
|
+
import { RemoteStorage } from './RemoteStorage';
|
|
3
|
+
import { RemoteStorageError } from '../SchemaError';
|
|
4
|
+
import { getIPFSData } from '../../utils';
|
|
5
|
+
import { STORAGE_TYPE, TRemoteStorageOutput } from 'core/types';
|
|
6
|
+
|
|
7
|
+
export interface IpfsStorageOptions {
|
|
8
|
+
token: string;
|
|
9
|
+
endpoint?: URL;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class IpfsStorage extends RemoteStorage<NFTStorage> {
|
|
13
|
+
constructor(
|
|
14
|
+
opts: IpfsStorageOptions,
|
|
15
|
+
/**
|
|
16
|
+
* If set, will send request to another server instead of
|
|
17
|
+
* using the local instance
|
|
18
|
+
*/
|
|
19
|
+
sponsor?: RemoteStorage['sponsor']
|
|
20
|
+
) {
|
|
21
|
+
super(STORAGE_TYPE.IPFS, sponsor);
|
|
22
|
+
|
|
23
|
+
this.assert(opts);
|
|
24
|
+
this.client = new NFTStorage({ ...opts });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private assert(opts: IpfsStorageOptions) {}
|
|
28
|
+
|
|
29
|
+
async save<T = unknown>(data: T): Promise<string> {
|
|
30
|
+
try {
|
|
31
|
+
const blob = new Blob([JSON.stringify(data)], {
|
|
32
|
+
type: 'application/json',
|
|
33
|
+
});
|
|
34
|
+
const cid = await this.client.storeBlob(blob);
|
|
35
|
+
return cid;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
throw new RemoteStorageError(
|
|
38
|
+
'REMOTE_STORAGE_UPLOAD',
|
|
39
|
+
`Error adding data to IPFS`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
encode(data: string): TRemoteStorageOutput<string> {
|
|
45
|
+
return { hash: data, storageType: this.storageType };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async get<T = unknown>(args: { cid: string }): Promise<T> {
|
|
49
|
+
return getIPFSData<T>(args.cid);
|
|
50
|
+
}
|
|
51
|
+
}
|