@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,114 @@
1
+ import axios from 'axios';
2
+ import { GelatoRelay } from '@gelatonetwork/relay-sdk';
3
+ import { watchGelatoTxn } from './watch-gelato-txn';
4
+ import { GAP } from '../../class/GAP';
5
+ import { Hex } from '../../types';
6
+
7
+ async function sendByUrl(...params: Parameters<GelatoRelay['sponsoredCall']>) {
8
+ const { data } = await axios.post<string>(GAP.gelatoOpts.sponsorUrl, params);
9
+ return data;
10
+ }
11
+
12
+ /**
13
+ * Send gelato using an explicit api key.
14
+ *
15
+ * > __This is not safe in the frontend.__
16
+ *
17
+ * @param params
18
+ * @returns Gelato's task id and a wait function.
19
+ */
20
+ async function sendByApiKey(
21
+ ...params: Parameters<GelatoRelay['sponsoredCall']>
22
+ ) {
23
+ const { apiKey } = GAP?.gelatoOpts || {};
24
+
25
+ if (!apiKey && params[1] === '{apiKey}')
26
+ throw new Error('No api key provided.');
27
+
28
+ if (apiKey && params[1] === '{apiKey}') params[1] = apiKey;
29
+
30
+ const client = new GelatoRelay();
31
+ const relayResponse = await client.sponsoredCall(...params);
32
+
33
+ return {
34
+ taskId: relayResponse.taskId,
35
+ /**
36
+ * Waits for the transaction to be confirmed by Gelato.
37
+ * @param ttl interval between checks in ms
38
+ * @returns Txn id
39
+ */
40
+ wait: (ttl?: number) => watchGelatoTxn(relayResponse.taskId, ttl),
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Sends a sponsored call using GelatoRelay
46
+ * @param payload
47
+ * @returns txn hash
48
+ */
49
+ async function sendGelatoTxn(
50
+ ...params: Parameters<GelatoRelay['sponsoredCall']>
51
+ ) {
52
+ if (!GAP.gelatoOpts) throw new Error('Gelato opts not set.');
53
+ const { env_gelatoApiKey, sponsorUrl, useGasless, contained } =
54
+ GAP.gelatoOpts;
55
+
56
+ if (!useGasless) throw new Error('Gasless is not enabled.');
57
+
58
+ if (
59
+ (sponsorUrl && contained && env_gelatoApiKey) ||
60
+ (sponsorUrl && !contained)
61
+ ) {
62
+ return sendByUrl(...params);
63
+ }
64
+
65
+ const { wait } = await sendByApiKey(...params);
66
+ return wait();
67
+ }
68
+
69
+ /**
70
+ * Builds the arguments for a sponsored call using GelatoRelay
71
+ * @param data Populated contract call.
72
+ * @param chainId
73
+ * @param target target contract address (Hex)
74
+ *
75
+ * @example
76
+ *
77
+ * ```ts
78
+ * const { data } = await contract.populateTransaction.transfer(
79
+ * recipient,
80
+ * amount
81
+ * );
82
+ * const args = buildArgs(data, chainId, target);
83
+ * const txn = sendGelatoTxn(...args);
84
+ * console.log(txn) // 0xabc..
85
+ * ```
86
+ */
87
+ function buildArgs(
88
+ /**
89
+ * Populated transaction data.
90
+ */
91
+ data: string,
92
+ chainId: bigint,
93
+ target: Hex
94
+ ): Parameters<GelatoRelay['sponsoredCall']> {
95
+ return [
96
+ {
97
+ data,
98
+ chainId,
99
+ target,
100
+ },
101
+ '{apiKey}', // filled in the api
102
+ {
103
+ retries: 3,
104
+ },
105
+ ];
106
+ }
107
+
108
+ const Gelato = {
109
+ sendByApiKey,
110
+ sendByUrl,
111
+ buildArgs,
112
+ };
113
+
114
+ export { sendGelatoTxn, Gelato };
@@ -0,0 +1,77 @@
1
+ import { GelatoRelay } from "@gelatonetwork/relay-sdk";
2
+ import { GAP } from "../../class/GAP";
3
+ import { Gelato, sendGelatoTxn } from "./send-gelato-txn";
4
+
5
+ export interface ApiRequest {
6
+ method: string;
7
+ body: unknown;
8
+ }
9
+
10
+ export interface ApiResponse {
11
+ statusCode: number;
12
+ send: (body: unknown) => void;
13
+ }
14
+
15
+ const assertionObj = [
16
+ {
17
+ data: /0x[a-fA-F0-9]+/gim,
18
+ chainId: /\d+/,
19
+ target: /0x[a-fA-F0-9]{40}/gim,
20
+ },
21
+ /\{apiKey\}/,
22
+ {
23
+ retries: /\d+/,
24
+ },
25
+ ];
26
+
27
+ function assert(body: any): body is Parameters<GelatoRelay["sponsoredCall"]> {
28
+ if (!Array.isArray(body) || body.length !== assertionObj.length)
29
+ throw new Error("Invalid request body");
30
+
31
+ assertionObj.forEach((item, index) => {
32
+ // check if objects from assertion Object are present in body
33
+ // and test them using the regexp from the assertion Object
34
+ if (typeof item === "object") {
35
+ Object.entries(item).forEach(([key, value]) => {
36
+ if (!body[index][key]?.toString().match(value))
37
+ throw new Error("Invalid request body");
38
+ });
39
+ }
40
+ // test other items as strings
41
+ else if (!body[index]?.toString().match(item))
42
+ throw new Error("Invalid request body");
43
+ });
44
+
45
+ return true;
46
+ }
47
+
48
+ export async function handler(
49
+ req: ApiRequest,
50
+ res: ApiResponse,
51
+ env_gelatoApiKey: string
52
+ ) {
53
+ if (req.method !== "POST") {
54
+ res.statusCode = 405;
55
+ res.send("Method not allowed");
56
+ return;
57
+ }
58
+ try {
59
+ const body = req.body as unknown;
60
+
61
+ if (!assert(body)) return;
62
+
63
+ const { [env_gelatoApiKey]: apiKey } = process.env;
64
+ if (!apiKey) throw new Error("Api key not provided.");
65
+ body[1] = apiKey;
66
+
67
+ const result = await Gelato.sendByApiKey(...body);
68
+ const txId = await result.wait();
69
+ res.send(txId);
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
+ } catch (error: any) {
72
+ // eslint-disable-next-line no-console
73
+ console.log(error);
74
+ res.statusCode = 400;
75
+ res.send(error.message);
76
+ }
77
+ }
@@ -0,0 +1,67 @@
1
+ import { GelatoRelay } from '@gelatonetwork/relay-sdk';
2
+
3
+ enum TaskState {
4
+ CheckPending = 'CheckPending',
5
+ ExecPending = 'ExecPending',
6
+ ExecSuccess = 'ExecSuccess',
7
+ ExecReverted = 'ExecReverted',
8
+ WaitingForConfirmation = 'WaitingForConfirmation',
9
+ Blacklisted = 'Blacklisted',
10
+ Cancelled = 'Cancelled',
11
+ NotFound = 'NotFound',
12
+ }
13
+
14
+ /**
15
+ * Waits for a transaction to be mined at Gelato Network
16
+ * @param taskId
17
+ * @returns
18
+ */
19
+ async function watchGelatoTxn(taskId: string, ttl = 500): Promise<string> {
20
+ const client = new GelatoRelay();
21
+ return new Promise((resolve, reject) => {
22
+ const loop = async () => {
23
+ const oneSecond = 1;
24
+ try {
25
+ while (oneSecond) {
26
+ const status = await client.getTaskStatus(taskId);
27
+ // print status :D so we can debug this for now
28
+ // eslint-disable-next-line no-console
29
+ console.log(status);
30
+ if (!status) {
31
+ reject(new Error('Transaction goes wrong.'));
32
+ break;
33
+ }
34
+ if (status && status.taskState === TaskState.ExecSuccess) {
35
+ resolve(status.transactionHash || '');
36
+ break;
37
+ } else if (
38
+ [
39
+ TaskState.Cancelled,
40
+ TaskState.ExecReverted,
41
+ TaskState.Blacklisted,
42
+ ].includes(status?.taskState)
43
+ ) {
44
+ reject(
45
+ new Error(
46
+ status.lastCheckMessage
47
+ ?.split(/(RegisterDelegate)|(Execution error): /)
48
+ .at(-1) || ''
49
+ )
50
+ );
51
+ break;
52
+ }
53
+
54
+ await new Promise((r) => setTimeout(r, ttl));
55
+ }
56
+ } catch {
57
+ // gelato may throw 429 error, so we need to retry
58
+ // Increase ttl to avoid too deadlocking
59
+ // Max ttl is 30s
60
+ ttl += Math.max(30000, ttl + 1000);
61
+ }
62
+ };
63
+ loop();
64
+ });
65
+ }
66
+
67
+ export { watchGelatoTxn };
@@ -0,0 +1,3 @@
1
+ export function getDate(date: Date | number) {
2
+ return typeof date === "number" ? new Date(date * 1000) : date;
3
+ }
@@ -0,0 +1,13 @@
1
+ import axios from 'axios';
2
+
3
+ export async function getIPFSData<T>(cid: string): Promise<T> {
4
+ try {
5
+ const { data } = await axios.get(`https://ipfs.io/ipfs/${cid}`, {
6
+ timeout: 5000,
7
+ });
8
+ return data as T;
9
+ } catch (err) {
10
+ console.error(err);
11
+ throw new Error(`Error to retrive data for CID: ${cid}`);
12
+ }
13
+ }
@@ -0,0 +1,18 @@
1
+ import { Networks } from "../consts";
2
+ import { ethers } from "ethers";
3
+
4
+ const providers: Record<number, ethers.JsonRpcProvider> = {};
5
+
6
+ export const getWeb3Provider = (chainId: number): ethers.JsonRpcProvider => {
7
+ const rpcUrl = Object.values(Networks).find((n) => n.chainId === chainId)
8
+ ?.rpcUrl;
9
+
10
+ if (!rpcUrl) {
11
+ throw new Error(`No rpcUrl found for chainId ${chainId}`);
12
+ }
13
+
14
+ if (!providers[chainId]) {
15
+ providers[chainId] = new ethers.JsonRpcProvider(rpcUrl);
16
+ }
17
+ return providers[chainId];
18
+ };
@@ -0,0 +1,133 @@
1
+ import { nullRef } from "../consts";
2
+ import { Hex } from "../types";
3
+
4
+ const inStatement = (values: (string | number)[]) =>
5
+ `[${values.map((v) => `"${v}"`).join(",")}]`;
6
+
7
+ const attestationFields = `
8
+ uid: id
9
+ attester
10
+ data
11
+ decodedDataJson
12
+ recipient
13
+ revoked
14
+ createdAt: timeCreated
15
+ refUID
16
+ isOffchain
17
+ revocable
18
+ revocationTime
19
+ schemaId
20
+ `;
21
+
22
+ const schemaQuery = (schemaId: Hex, content: string) =>
23
+ `{schema(where: {id: "${schemaId}"}) {${content}}}`;
24
+
25
+ export const gqlQueries = {
26
+ attestation: (uid: Hex) => `
27
+ {
28
+ attestation(where: {
29
+ id: "${uid}"
30
+ }) {${attestationFields}}
31
+ }`,
32
+ attestations: (schemaId: Hex, uid: Hex) =>
33
+ schemaQuery(
34
+ schemaId,
35
+ `attestations(where: {
36
+ revoked: {equals: false}
37
+ decodedDataJson: {contains: "${uid}"}
38
+ }) {${attestationFields}}`
39
+ ),
40
+ attestationsIn: (uids: Hex[], search?: string) => `
41
+ {
42
+ attestations(where: {
43
+ id:{in: ${inStatement(uids)}}
44
+ revoked:{equals:false}
45
+ ${
46
+ search
47
+ ? `decodedDataJson:{contains:"${search}",mode:insensitive}`
48
+ : ""
49
+ }
50
+ }) {${attestationFields}}
51
+ }`,
52
+ attestationsFrom: (schemaId: Hex, attester: Hex) =>
53
+ schemaQuery(
54
+ schemaId,
55
+ `attestations(orderBy:{timeCreated: desc},
56
+ where:{attester:{equals:"${attester}"}
57
+ revoked:{equals:false}
58
+ }){${attestationFields}}`
59
+ ),
60
+ attestationsTo: (schemaId: Hex, recipient: Hex) =>
61
+ schemaQuery(
62
+ schemaId,
63
+ `attestations(orderBy:{timeCreated: desc},
64
+ where:{
65
+ recipient:{equals:"${recipient}"}
66
+ revoked:{equals:false}
67
+ }){${attestationFields}}`
68
+ ),
69
+ attestationPairs: (schemaId: Hex, attester: Hex, recipient: Hex) =>
70
+ schemaQuery(
71
+ schemaId,
72
+ `attestations(where: {
73
+ attester: {equals: "${attester}"}
74
+ recipient: {equals: "${recipient}"}
75
+ revoked: {equals: false}
76
+ }) {${attestationFields}}`
77
+ ),
78
+ attestationsOf: (
79
+ schemaId: Hex,
80
+ search?: string[] | string,
81
+ refUids?: Hex[]
82
+ ) =>
83
+ schemaQuery(
84
+ schemaId,
85
+ `attestations(orderBy:{timeCreated: desc},
86
+ where: {
87
+ revoked:{equals:false}
88
+ ${
89
+ refUids && refUids.length
90
+ ? `refUID:{in: ${inStatement(refUids)}}`
91
+ : ""
92
+ }
93
+ ${
94
+ search
95
+ ? `OR: [
96
+ ${[search]
97
+ .flat()
98
+ .map(
99
+ (s) =>
100
+ `{decodedDataJson:{contains:"${s}",mode:insensitive}}`
101
+ )
102
+ .join(",")}
103
+ ]`
104
+ : ""
105
+ }
106
+ })
107
+ {${attestationFields}}`
108
+ ),
109
+
110
+ dependentsOf: (
111
+ refs: Hex | Hex[],
112
+ schemaIds: Hex[],
113
+ attesters: Hex[] = []
114
+ ) => `
115
+ {
116
+ attestations(
117
+ orderBy:{timeCreated: desc},
118
+ where: {
119
+ refUID:{in: ${inStatement([refs].flat())}}
120
+ revoked:{equals: false}
121
+ schemaId:{in: ${inStatement(schemaIds)}}
122
+ ${attesters.length ? `attester:{in:${inStatement(attesters)}}` : ""}
123
+ }){${attestationFields}}
124
+ }
125
+ `,
126
+ schemata: (creator: Hex) => `
127
+ {
128
+ schemata(where: {creator: {equals: "${creator}"}}) {
129
+ uid: id
130
+ schema
131
+ }
132
+ }`,
133
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Filters an array by its condition then maps it to the desired format.
3
+ * @param arr
4
+ * @param condition
5
+ * @param mapTo
6
+ * @returns
7
+ */
8
+ export function mapFilter<T = unknown, U = unknown>(
9
+ arr: U[],
10
+ condition: (item: U) => boolean,
11
+ mapTo: (item: U) => T
12
+ ): T[] {
13
+ const newArray: T[] = [];
14
+
15
+ for (const item of arr) {
16
+ if (condition(item)) {
17
+ newArray.push(mapTo(item));
18
+ }
19
+ }
20
+ return newArray;
21
+ }
@@ -0,0 +1,7 @@
1
+ export function serializeWithBigint(value: unknown) {
2
+ return JSON.stringify(
3
+ value,
4
+ (this,
5
+ (key, value) => (typeof value === "bigint" ? value.toString() : value))
6
+ );
7
+ }
@@ -0,0 +1,18 @@
1
+ export function toUnix(value: number | Date | string) {
2
+ switch (typeof value) {
3
+ case "number":
4
+ value = Math.round(value);
5
+ if (value.toString().length > 13)
6
+ throw new Error("Invalid timestamp length");
7
+ if (value.toString().length === 10) return value;
8
+ return Math.floor(value / 1000);
9
+ case "string":
10
+ if (/\D/.test(value)) return null;
11
+ return toUnix(+value);
12
+ case "object":
13
+ if (value instanceof Date) return toUnix(value.getTime());
14
+ return null;
15
+ default:
16
+ return null;
17
+ }
18
+ }
@@ -0,0 +1,119 @@
1
+ import { ethers } from "ethers";
2
+ import { GAP } from "./core/class/GAP";
3
+ import { GapIndexerClient } from "./core/class/karma-indexer/GapIndexerClient";
4
+ import { Community } from "./core/class/entities/Community";
5
+ import { ICommunityDetails } from "./core/class/types/attestations";
6
+ import { Hex } from "./core/types";
7
+ import * as dotenv from "dotenv";
8
+
9
+ // Load environment variables
10
+ dotenv.config();
11
+
12
+ // Configuration
13
+ const API_URL = process.env.API_URL || "https://gapstagapi.karmahq.xyz";
14
+ const RPC_URL = process.env.RPC_URL || "https://sepolia.optimism.io"; // Replace with your RPC URL
15
+ const PRIVATE_KEY = process.env.PRIVATE_KEY; // Your private key from .env file
16
+
17
+ if (!PRIVATE_KEY) {
18
+ throw new Error("PRIVATE_KEY not found in .env file");
19
+ }
20
+
21
+ // Community data
22
+ const COMMUNITY_DATA = {
23
+ name: "Polygon",
24
+ description: "A polygon community",
25
+ imageURL:
26
+ "https://polygontechnology.notion.site/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F51562dc1-1dc5-4484-bf96-2aeac848ae2f%2Ff0ed090e-4957-4520-8b0c-56d5ec80a17f%2FPolygon_Icon_White_Purple_Rn-3.png?id=fdedfbfe-e6e1-430e-ac7c-b2fb988b47b9&table=block&spaceId=51562dc1-1dc5-4484-bf96-2aeac848ae2f&width=2000&userId=&cache=v2",
27
+ slug: "polygon", // Optional - will be auto-generated if not provided
28
+ links: [],
29
+ type: "community-details",
30
+ };
31
+ async function main() {
32
+ try {
33
+ console.log("=== Creating Community ===\n");
34
+
35
+ // Initialize provider and signer
36
+ const provider = new ethers.JsonRpcProvider(RPC_URL);
37
+ const signer = new ethers.Wallet(PRIVATE_KEY, provider);
38
+ const signerAddress = (await signer.getAddress()) as `0x${string}`;
39
+
40
+ console.log(`Using signer address: ${signerAddress}`);
41
+
42
+ // Initialize GAP SDK
43
+ const gap = new GAP({
44
+ globalSchemas: false,
45
+ network: "optimism-sepolia", // Change to your target network (e.g., "optimism", "arbitrum")
46
+ apiClient: new GapIndexerClient(API_URL),
47
+ });
48
+
49
+ console.log("GAP SDK initialized\n");
50
+
51
+ // Generate a unique slug if not provided
52
+ let communityDetails: ICommunityDetails = { ...COMMUNITY_DATA };
53
+ if (!communityDetails.slug) {
54
+ console.log("Generating slug...");
55
+ communityDetails.slug = await gap.generateSlug(communityDetails.name);
56
+ console.log(`Generated slug: ${communityDetails.slug}\n`);
57
+ }
58
+
59
+ // Create the community attestation
60
+ console.log("Creating community attestation...");
61
+ const community = new Community({
62
+ schema: gap.findSchema("Community"),
63
+ recipient: signerAddress,
64
+ data: {
65
+ community: true,
66
+ },
67
+ });
68
+
69
+ // Attest the community with its details
70
+ console.log("Attesting community to blockchain...\n");
71
+ const result = await community.attest(
72
+ signer,
73
+ communityDetails,
74
+ (status: string) => {
75
+ console.log(`Status: ${status}`);
76
+ }
77
+ );
78
+
79
+ console.log("\n✅ Community created successfully!");
80
+ console.log(`Community UID: ${result.uids[0]}`);
81
+ if (result.uids[1]) {
82
+ console.log(`Community Details UID: ${result.uids[1]}`);
83
+ }
84
+ console.log(`Transaction Hash: ${result.tx[0].hash}`);
85
+ console.log(
86
+ `\nYou can view your community at: https://gap.karmahq.xyz/community/${communityDetails.slug}`
87
+ );
88
+
89
+ // Fetch the created community to verify
90
+ console.log("\nVerifying community creation...");
91
+ const fetchedCommunity = await gap.fetch.communityBySlug(
92
+ communityDetails.slug
93
+ );
94
+
95
+ if (fetchedCommunity) {
96
+ console.log("✅ Community verified!");
97
+ console.log(`Community Name: ${fetchedCommunity.details?.name}`);
98
+ console.log(
99
+ `Community Description: ${fetchedCommunity.details?.description}`
100
+ );
101
+ } else {
102
+ console.log("⚠️ Community not yet indexed (this may take a few moments)");
103
+ }
104
+ } catch (error) {
105
+ console.error("\n❌ Error creating community:", error);
106
+ throw error;
107
+ }
108
+ }
109
+
110
+ // Run the script
111
+ main()
112
+ .then(() => {
113
+ console.log("\n✅ Script completed successfully");
114
+ process.exit(0);
115
+ })
116
+ .catch((error) => {
117
+ console.error("\n❌ Script failed:", error);
118
+ process.exit(1);
119
+ });
@@ -0,0 +1,74 @@
1
+ # Configuration
2
+
3
+ This directory contains configuration files for the application.
4
+
5
+ ## Files
6
+
7
+ ### `config.ts`
8
+
9
+ Contains non-sensitive configuration and constants:
10
+
11
+ - `CHAIN_IDS`: Chain IDs for different networks
12
+ - `DEFAULT_CONFIG`: Default configuration values
13
+ - `API_ENDPOINTS`: API endpoint paths
14
+ - `LINK_TYPES`: Types of links supported
15
+ - `GRANT_UPDATE_TYPES`: Types of grant updates
16
+
17
+ ### `keys.json`
18
+
19
+ Contains sensitive configuration (not committed to version control). Copy `keys.example.json` to `keys.json` and fill in your values.
20
+
21
+ Required structure:
22
+ ```json
23
+ {
24
+ "optimism": {
25
+ "privateKey": "your_private_key_here",
26
+ "rpcURL": "your_rpc_url_here",
27
+ "gapAPI": "your_gap_api_url_here",
28
+ "ipfsKey": "your_ipfs_key_here"
29
+ },
30
+ // ... other networks with same structure
31
+ "gapAccessToken": "your_gap_access_token_here"
32
+ }
33
+ ```
34
+
35
+ ## Networks Supported
36
+
37
+ - optimism
38
+ - optimism-sepolia
39
+ - sepolia
40
+ - arbitrum
41
+ - base-sepolia
42
+ - celo
43
+ - sei
44
+ - sei-testnet
45
+ - lisk
46
+ - scroll
47
+
48
+ ## Setup
49
+
50
+ 1. Copy `keys.example.json` to `keys.json`
51
+ 2. Fill in your values in `keys.json`
52
+ 3. Never commit `keys.json` to version control
53
+
54
+ ## Environment Variables
55
+
56
+ The following environment variables can override configuration values if present:
57
+
58
+ - `GAP_NETWORK`: Override the default network
59
+ - `GAP_API_URL`: Override the GAP API URL
60
+ - `MAINNET_RPC_URL`: Override the mainnet RPC URL
61
+
62
+ ## Usage
63
+
64
+ Import configuration values from `config.ts`:
65
+
66
+ ```typescript
67
+ import {
68
+ CHAIN_IDS,
69
+ DEFAULT_CONFIG,
70
+ API_ENDPOINTS,
71
+ LINK_TYPES,
72
+ GRANT_UPDATE_TYPES
73
+ } from "./config/config";
74
+ ```