@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,224 @@
1
+ import { Hex, IAttestation, TSchemaName } from "core/types";
2
+ import { Attestation } from "./Attestation";
3
+ import { Community, Grant, MemberOf, Milestone, Project } from "./entities";
4
+ import { Grantee } from "./types/attestations";
5
+ import { AxiosGQL } from "./GraphQL/AxiosGQL";
6
+ import { GAP } from "./GAP";
7
+
8
+ export abstract class Fetcher extends AxiosGQL {
9
+ protected gap: GAP;
10
+
11
+ constructor(url: string) {
12
+ super(url);
13
+ }
14
+
15
+ set gapInstance(gap: GAP) {
16
+ this.gap = gap;
17
+ }
18
+
19
+ /**
20
+ * Fetch a single attestation by its UID.
21
+ * @param uid
22
+ */
23
+ abstract attestation<T = unknown>(uid: Hex): Promise<Attestation<T>>;
24
+
25
+ /**
26
+ * Fetch attestations of a schema.
27
+ * @param schemaName
28
+ * @param search if set, will search decodedDataJson by the value.
29
+ * @returns
30
+ */
31
+ abstract attestations(
32
+ schemaName: TSchemaName,
33
+ search?: string
34
+ ): Promise<IAttestation[]>;
35
+
36
+ /**
37
+ * Fetch attestations of a schema.
38
+ * @param schemaName
39
+ * @param recipient
40
+ * @returns
41
+ */
42
+ abstract attestationsOf(
43
+ schemaName: TSchemaName,
44
+ recipient: Hex
45
+ ): Promise<IAttestation[]>;
46
+
47
+ /**
48
+ * Fetch attestations of a schema for a specific recipient.
49
+ * @param schemaName
50
+ * @param recipient
51
+ * @returns
52
+ */
53
+ abstract attestationsTo(
54
+ schemaName: TSchemaName,
55
+ recipient: Hex
56
+ ): Promise<IAttestation[]>;
57
+
58
+ /**
59
+ * Fetch all available communities with details and grantees uids.
60
+ *
61
+ * If search is defined, will try to find communities by the search string.
62
+ * @param search
63
+ * @returns
64
+ */
65
+ abstract communities(search?: string): Promise<Community[]>;
66
+
67
+ /**
68
+ * Fetch all available communities with details for a grantee;
69
+ *
70
+ * If search is defined, will try to find communities by the search string.
71
+ * @param address grantee address
72
+ * @param withGrants if true, will get community grants.
73
+ * @returns
74
+ */
75
+ abstract communitiesOf(
76
+ address: Hex,
77
+ withGrants?: boolean
78
+ ): Promise<Community[]>;
79
+
80
+ /**
81
+ * Fetch all available communities (admin) with details for a grantee;
82
+ *
83
+ * If search is defined, will try to find communities by the search string.
84
+ * @param address grantee address
85
+ * @param withGrants if true, will get community grants.
86
+ * @returns
87
+ */
88
+ abstract communitiesAdminOf(
89
+ address: Hex,
90
+ withGrants?: boolean
91
+ ): Promise<Community[]>;
92
+
93
+ /**
94
+ * Fetch a set of communities by their ids.
95
+ * @param uids
96
+ * @returns
97
+ */
98
+ abstract communitiesByIds(uids: Hex[]): Promise<Community[]>;
99
+
100
+ /**
101
+ * Fetch a community by its name with details, grants and milestones.
102
+ *
103
+ * It is possible that the resulted community is not the one you are looking for.
104
+ * @param name
105
+ * @returns
106
+ */
107
+ abstract communityBySlug(slug: string): Promise<Community>;
108
+
109
+ /**
110
+ * Fetch a community by its id. This method will also return the
111
+ * community details and projects.
112
+ */
113
+ abstract communityById(uid: Hex): Promise<Community>;
114
+
115
+ /**
116
+ * Fetch a project by its id.
117
+ * @param uid
118
+ * @returns
119
+ */
120
+ abstract projectById(uid: Hex): Promise<Project>;
121
+
122
+ /**
123
+ * Fetch a project by its slug.
124
+ * @param slug
125
+ * @returns
126
+ */
127
+ abstract projectBySlug(slug: string): Promise<Project>;
128
+
129
+ /**
130
+ * Search projects and communities by name. This method will return a list of projects and a list of communities
131
+ * __Must be implemented by the indexer__
132
+ * @param query
133
+ */
134
+ abstract search(
135
+ query: string
136
+ ): Promise<{ projects: Project[]; communities: Community[] }>;
137
+
138
+ /**
139
+ * Search projects by name. This method will return a list of projects
140
+ * __Must be implemented by the indexer__
141
+ * @param query
142
+ */
143
+ abstract searchProjects(query: string): Promise<Project[]>;
144
+
145
+ /**
146
+ * Fetch projects with details and members.
147
+ * @param name if set, will search by the name.
148
+ * @returns
149
+ */
150
+ abstract projects(name?: string): Promise<Project[]>;
151
+
152
+ /**
153
+ * Fetch projects with details and members.
154
+ * @param grantee the public address of the grantee
155
+ * @returns
156
+ */
157
+ abstract projectsOf(grantee: Hex): Promise<Project[]>;
158
+
159
+ /**
160
+ * Fetch Grantee with details and projects.
161
+ * @param address
162
+ * @param withProjects if true, will get grantee project details.
163
+ * @returns
164
+ */
165
+ abstract grantee(address: Hex): Promise<Grantee>;
166
+
167
+ /**
168
+ * Fetch all Grantees with details.
169
+ * @returns
170
+ */
171
+ abstract grantees(): Promise<Grantee[]>;
172
+
173
+ /**
174
+ * Fetches the grantes related to a grantee address (recipient).
175
+ * @param grantee grantee address
176
+ * @returns
177
+ */
178
+ abstract grantsOf(grantee: Hex, withCommunity?: boolean): Promise<Grant[]>;
179
+
180
+ /**
181
+ * Fetch grants for an array of projects with milestones.
182
+ * @param projects
183
+ * @returns
184
+ */
185
+ abstract grantsFor(
186
+ projects: Project[],
187
+ withCommunity?: boolean
188
+ ): Promise<Grant[]>;
189
+
190
+ /**
191
+ * Fetch a grants that belongs to a community.
192
+ * @param uid community uid
193
+ * @returns
194
+ */
195
+ abstract grantsByCommunity(uid: Hex, page?: number, pageLimit?: number);
196
+
197
+ /**
198
+ * Fetch all milestones related to an array of Grants.
199
+ * @param grants
200
+ * @returns
201
+ */
202
+ abstract milestonesOf(grants: Grant[]): Promise<Milestone[]>;
203
+
204
+ /**
205
+ * Bulk fetch members with details of an array of Projects.
206
+ * @param projects
207
+ * @returns
208
+ */
209
+ abstract membersOf(projects: Project[]): Promise<MemberOf[]>;
210
+
211
+ /**
212
+ * Check if a name is already in use.
213
+ * @param slug
214
+ * @returns
215
+ */
216
+ abstract slugExists(slug: string): Promise<boolean>;
217
+
218
+ /**
219
+ * Get grants for a project by an external uid
220
+ * > Works only for the indexed projects
221
+ * @param projectExtId
222
+ */
223
+ abstract grantsForExtProject(projectExtId: string): Promise<Grant[]>;
224
+ }
@@ -0,0 +1,481 @@
1
+ import CommunityResolverABI from "../abi/CommunityResolverABI.json";
2
+ import MulticallABI from "../abi/MultiAttester.json";
3
+ import ProjectResolverABI from "../abi/ProjectResolver.json";
4
+
5
+ import { EAS } from "@ethereum-attestation-service/eas-sdk";
6
+ import { ethers } from "ethers";
7
+ import { version } from "../../package.json";
8
+ import { MountEntities, Networks } from "../consts";
9
+ import {
10
+ AttestArgs,
11
+ Facade,
12
+ SchemaInterface,
13
+ SignerOrProvider,
14
+ TNetwork,
15
+ TSchemaName,
16
+ } from "../types";
17
+ import { getWeb3Provider } from "../utils/get-web3-provider";
18
+ import { Fetcher } from "./Fetcher";
19
+ import { GapSchema } from "./GapSchema";
20
+ import { GapEasClient } from "./GraphQL";
21
+ import { RemoteStorage } from "./remote-storage/RemoteStorage";
22
+ import { Schema } from "./Schema";
23
+
24
+ interface GAPArgs {
25
+ network: TNetwork;
26
+ globalSchemas?: boolean;
27
+ /**
28
+ * Custom API Client to be used to fetch attestation data.
29
+ * If not defined, will use the default EAS Client and rely on EAS's GraphQL API.
30
+ */
31
+ apiClient?: Fetcher;
32
+ schemas?: SchemaInterface<TSchemaName>[];
33
+ /**
34
+ * Defined if the transactions will be gasless or not.
35
+ *
36
+ * In case of true, the transactions will be sent through [Gelato](https://gelato.network)
37
+ * and an API key is needed.
38
+ *
39
+ * > __Note that to safely transact through Gelato, the user must
40
+ * have set a handlerUrl and not expose gelato api in the frontend.__
41
+ */
42
+ gelatoOpts?: {
43
+ /**
44
+ * Endpoint in which the transaction will be sent.
45
+ * A custom endpoint will ensure that the transaction will be sent through Gelato
46
+ * and api keys won't be exposed in the frontend.
47
+ *
48
+ * __If coding a backend, you can use `apiKey` prop instead.__
49
+ *
50
+ * `core/utils/gelato/sponsor-handler.ts` is a base handler that can be used
51
+ * together with NextJS API routes.
52
+ *
53
+ * @example
54
+ *
55
+ * ```ts
56
+ * // pages/api/gelato.ts
57
+ * import { handler as sponsorHandler } from "core/utils/gelato/sponsor-handler";
58
+ *
59
+ * export default const handler(req, res) => sponsorHandler(req, res, "GELATO_API_KEY_ENV_VARIABLE");
60
+ *
61
+ * ```
62
+ */
63
+ sponsorUrl?: string;
64
+ /**
65
+ * If true, env_gelatoApiKey will be marked as required.
66
+ * This means that the endpoint at sponsorUrl is contained in this application.
67
+ *
68
+ * E.g. Next.JS api route.
69
+ */
70
+ contained?: boolean;
71
+ /**
72
+ * The env key of gelato api key that will be used in the handler.
73
+ *
74
+ * @example
75
+ *
76
+ * ```
77
+ * // .env
78
+ * GELATO_API_KEY=1234567890
79
+ *
80
+ * // sponsor-handler.ts
81
+ *
82
+ * export async function handler(req, res) {
83
+ * // ...code
84
+ *
85
+ * const { env_gelatoApiKey } = GAP.gelatoOpts;
86
+ *
87
+ * // Will be used to get the key from environment.
88
+ * const { [env_gelatoApiKey]: apiKey } = process.env;
89
+ *
90
+ * // send txn
91
+ * // res.send(result);
92
+ * }
93
+ * ```
94
+ */
95
+ env_gelatoApiKey?: string;
96
+ /**
97
+ * API key to be used in the handler.
98
+ *
99
+ * @deprecated Use this only if you have no option of setting a backend, next/nuxt api route
100
+ * or if this application is a backend.
101
+ *
102
+ * > __This will expose the api key if used in the frontend.__
103
+ */
104
+ apiKey?: string;
105
+ /**
106
+ * If true, will use gelato to send transactions.
107
+ */
108
+ useGasless?: boolean;
109
+ };
110
+ /**
111
+ * Defines a remote storage client to be used to store data.
112
+ * If defined, all the details data from an attestation will
113
+ * be stored in the remote storage, e.g. IPFS.
114
+ */
115
+ remoteStorage?: RemoteStorage;
116
+ }
117
+
118
+ /**
119
+ * GAP SDK Facade.
120
+ *
121
+ * This is the main class that is used to interact with the GAP SDK.
122
+ *
123
+ * This class implements the singleton pattern to ensure only one instance exists
124
+ * throughout the application lifecycle.
125
+ *
126
+ * Using this class, the user will be able to:
127
+ *
128
+ * - Create and manage attestations
129
+ * - Create and manage schemas
130
+ * - Fetch data from the EAS
131
+ *
132
+ * #### Features
133
+ * - EAS Client: used to interact with EAS contracts
134
+ * - EAS Fetcher: used to fetch data from the EAS GraphQL API, providing methods for:
135
+ * - Get projects
136
+ * - Get grants with its details
137
+ * - Get grantees
138
+ * - Get members
139
+ * - Get tags
140
+ * - Get external links
141
+ * - Get schemas
142
+ * - Get attestations by pair, attester, recipient, schema, or UID
143
+ * - Get dependent attestations
144
+ * - Schema: used to create and manage schemas
145
+ * - Attestation: used to create and manage attestations
146
+ * - Replace schemas: used to replace the schema list with a new list
147
+ * - Replace single schema: used to replace a single schema from the schema list
148
+ *
149
+ * ---
150
+ * @example
151
+ * ```ts
152
+ * import { GAP } from "./core/class/GAP";
153
+ * import { GapSchema } from "./core/class/GapSchema";
154
+ * import { Schema } from "./core/class/Schema";
155
+ * import { MountEntities, Networks } from "./core/consts";
156
+ *
157
+ * const schemas = MountEntities(Networks.sepolia);
158
+ *
159
+ * // Initialize the singleton instance
160
+ * const gap = GAP.getInstance({
161
+ * network: "sepolia",
162
+ * owner: "0xd7d1DB401EA825b0325141Cd5e6cd7C2d01825f2",
163
+ * schemas: Object.values(schemas),
164
+ * });
165
+ *
166
+ * // Later in the code, get the same instance
167
+ * const sameGap = GAP.getInstance();
168
+ *
169
+ * gap.fetcher
170
+ * .fetchProjects()
171
+ * .then((res) => {
172
+ * console.log(JSON.stringify(res, null, 2));
173
+ * })
174
+ *
175
+ * ```
176
+ */
177
+ export class GAP extends Facade {
178
+ private static remoteStorage?: RemoteStorage;
179
+ private static instances: Map<TNetwork, GAP> = new Map();
180
+
181
+ readonly fetch: Fetcher;
182
+ readonly network: TNetwork;
183
+
184
+ private _schemas: GapSchema[];
185
+ private static _gelatoOpts = null;
186
+
187
+ /**
188
+ * Get the singleton instance of GAP for a specific network.
189
+ * If no instance exists for the network, creates one with the provided args.
190
+ * @param args Optional initialization arguments
191
+ * @returns The singleton instance of GAP for the specified network
192
+ */
193
+ static getInstance(args?: GAPArgs): GAP {
194
+ if (!args) {
195
+ throw new Error("Network must be specified when getting GAP instance");
196
+ }
197
+
198
+ const existingInstance = GAP.instances.get(args.network);
199
+ if (existingInstance) {
200
+ return existingInstance;
201
+ }
202
+
203
+ if (!args) {
204
+ throw new Error("Initialization arguments required for first instance");
205
+ }
206
+
207
+ const newInstance = new GAP(args);
208
+ GAP.instances.set(args.network, newInstance);
209
+ return newInstance;
210
+ }
211
+
212
+ /**
213
+ * Creates a new instance of GAP.
214
+ * You can either use this constructor directly or use the singleton pattern via getInstance().
215
+ * @param args Initialization arguments
216
+ */
217
+ constructor(args: GAPArgs) {
218
+ super();
219
+
220
+ const schemas =
221
+ args.schemas || Object.values(MountEntities(Networks[args.network]));
222
+
223
+ this.network = args.network;
224
+
225
+ this._eas = new EAS(Networks[args.network].contracts.eas);
226
+
227
+ this.fetch =
228
+ args.apiClient ||
229
+ new GapEasClient({
230
+ network: args.network,
231
+ });
232
+
233
+ this.fetch.gapInstance = this;
234
+
235
+ this.assertGelatoOpts(args);
236
+ GAP._gelatoOpts = args.gelatoOpts;
237
+
238
+ GAP.remoteStorage = args.remoteStorage;
239
+
240
+ this._schemas = schemas.map(
241
+ (schema) =>
242
+ new GapSchema(
243
+ schema,
244
+ this,
245
+ false,
246
+ args.globalSchemas ? !args.globalSchemas : false
247
+ )
248
+ );
249
+
250
+ Schema.validate(this.network);
251
+
252
+ console.info(`Loaded GAP SDK v${version} for network ${this.network}`);
253
+
254
+ GAP.instances.set(this.network, this);
255
+ }
256
+
257
+ private assertGelatoOpts(args: GAPArgs) {
258
+ if (
259
+ args.gelatoOpts &&
260
+ !(args.gelatoOpts.sponsorUrl || args.gelatoOpts.apiKey)
261
+ ) {
262
+ throw new Error("You must provide a `sponsorUrl` or an `apiKey`.");
263
+ }
264
+
265
+ if (
266
+ args.gelatoOpts?.sponsorUrl &&
267
+ args.gelatoOpts?.contained &&
268
+ !args.gelatoOpts.env_gelatoApiKey
269
+ ) {
270
+ throw new Error(
271
+ "You must provide `env_gelatoApiKey` to be able to use it in a backend handler."
272
+ );
273
+ }
274
+
275
+ if (
276
+ (args.gelatoOpts?.env_gelatoApiKey ||
277
+ args.gelatoOpts?.apiKey ||
278
+ args.gelatoOpts?.sponsorUrl) &&
279
+ !args.gelatoOpts?.useGasless
280
+ ) {
281
+ console.warn(
282
+ "GAP::You are using gelatoOpts but not setting useGasless to true. This will send transactions through the normal provider."
283
+ );
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Creates the attestation payload using a specific schema.
289
+ * @param from
290
+ * @param to
291
+ * @param data
292
+ * @param schema
293
+ */
294
+ async attest<T>(attestation: AttestArgs<T> & { schemaName: TSchemaName }) {
295
+ const schema = GapSchema.find(attestation.schemaName, this.network);
296
+ return schema.attest(attestation);
297
+ }
298
+
299
+ /**
300
+ * Replaces the schema list with a new list.
301
+ * @param schemas
302
+ */
303
+ replaceSchemas(schemas: GapSchema[]) {
304
+ Schema.replaceAll(schemas, this.network);
305
+ }
306
+
307
+ /**
308
+ * Replaces a schema from the schema list.
309
+ * @throws {SchemaError} if desired schema name does not exist.
310
+ */
311
+ replaceSingleSchema(schema: GapSchema) {
312
+ Schema.replaceOne(schema, this.network);
313
+ }
314
+
315
+ /**
316
+ * Generates a slug from a text.
317
+ * @param text
318
+ * @returns
319
+ */
320
+ generateSlug = async (text: string): Promise<string> => {
321
+ let slug = text
322
+ .toLowerCase()
323
+ // Remove emojis
324
+ .replace(
325
+ /([\uE000-\uF8FF]|\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDDFF])/g,
326
+ ""
327
+ )
328
+ // Remove basic text emoticons
329
+ .replace(/[:;=][()DP]/g, "")
330
+ .replace(/ /g, "-")
331
+ .replace(/[^\w-]+/g, "")
332
+ .trim()
333
+ .replace(/^-+|-+$/g, ""); // Remove leading and trailing hyphens
334
+
335
+ const checkSlug = async (
336
+ currentSlug: string,
337
+ counter: number = 0
338
+ ): Promise<string> => {
339
+ const slugToCheck =
340
+ counter === 0 ? currentSlug : `${currentSlug}-${counter}`;
341
+ const slugExists = await this.fetch.slugExists(slugToCheck);
342
+
343
+ if (slugExists) {
344
+ return checkSlug(currentSlug, counter + 1);
345
+ }
346
+
347
+ return slugToCheck.toLowerCase();
348
+ };
349
+
350
+ return checkSlug(slug);
351
+ };
352
+
353
+ /**
354
+ * Returns a copy of the original schema with no pointers.
355
+ * @param name
356
+ * @returns
357
+ */
358
+ findSchema(name: TSchemaName): GapSchema {
359
+ const found = Schema.get<TSchemaName, GapSchema>(name, this.network);
360
+ return GapSchema.clone(found);
361
+ }
362
+
363
+ /**
364
+ * Find many schemas by name and return their copies as an array in the same order.
365
+ * @param names
366
+ * @returns
367
+ */
368
+ findManySchemas(names: TSchemaName[]): GapSchema[] {
369
+ const schemas = Schema.getMany<TSchemaName, GapSchema>(names, this.network);
370
+ return schemas.map((s) => GapSchema.clone(s));
371
+ }
372
+
373
+ /**
374
+ * Get the multicall contract
375
+ * @param signer
376
+ */
377
+ static async getMulticall(signer: SignerOrProvider) {
378
+ const chain =
379
+ (await signer.provider.getNetwork()) || (signer.provider as any).network;
380
+ const network = Object.values(Networks).find(
381
+ (n) => +n.chainId === Number(chain.chainId)
382
+ );
383
+ if (!network)
384
+ throw new Error(`Network ${chain.name || chain.chainId} not supported.`);
385
+
386
+ const address = network.contracts.multicall;
387
+ return new ethers.Contract(address, MulticallABI, signer as any);
388
+ }
389
+
390
+ /**
391
+ * Get the multicall contract
392
+ * @param signer
393
+ */
394
+ static async getProjectResolver(
395
+ signer: SignerOrProvider & { getChainId?: () => Promise<number> },
396
+ chainId?: number
397
+ ) {
398
+ const currentChainId =
399
+ chainId ||
400
+ Number(
401
+ (await signer.provider.getNetwork())?.chainId ||
402
+ (await signer.getChainId())
403
+ );
404
+
405
+ const provider = chainId ? getWeb3Provider(chainId) : signer;
406
+ const network = Object.values(Networks).find(
407
+ (n) => +n.chainId === Number(currentChainId)
408
+ );
409
+ const address = network.contracts.projectResolver;
410
+ return new ethers.Contract(address, ProjectResolverABI, provider as any);
411
+ }
412
+
413
+ /**
414
+ * Get the multicall contract
415
+ * @param signer
416
+ */
417
+ static async getCommunityResolver(
418
+ signer: SignerOrProvider & { getChainId?: () => Promise<number> },
419
+ chainId?: number
420
+ ) {
421
+ const currentChainId =
422
+ chainId ||
423
+ Number(
424
+ (await signer.provider.getNetwork())?.chainId ||
425
+ (await signer.getChainId())
426
+ );
427
+
428
+ const provider = chainId ? getWeb3Provider(chainId) : signer;
429
+ const network = Object.values(Networks).find(
430
+ (n) => +n.chainId === Number(currentChainId)
431
+ );
432
+ const address = network.contracts.communityResolver;
433
+ return new ethers.Contract(address, CommunityResolverABI, provider as any);
434
+ }
435
+
436
+ get schemas() {
437
+ return this._schemas;
438
+ }
439
+
440
+ /**
441
+ * Defined if the transactions will be gasless or not.
442
+ *
443
+ * In case of true, the transactions will be sent through [Gelato](https://gelato.network)
444
+ * and an API key is needed.
445
+ */
446
+ private static set gelatoOpts(gelatoOpts: GAPArgs["gelatoOpts"]) {
447
+ if (typeof this._gelatoOpts === "undefined") {
448
+ this._gelatoOpts = gelatoOpts;
449
+ } else {
450
+ throw new Error("Cannot change a readonly value gelatoOpts.");
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Defined if the transactions will be gasless or not.
456
+ *
457
+ * In case of true, the transactions will be sent through [Gelato](https://gelato.network)
458
+ * and an API key is needed.
459
+ */
460
+ static get gelatoOpts(): GAPArgs["gelatoOpts"] {
461
+ return this._gelatoOpts;
462
+ }
463
+
464
+ static set useGasLess(useGasLess: boolean) {
465
+ if (
466
+ useGasLess &&
467
+ !this._gelatoOpts?.apiKey &&
468
+ !this._gelatoOpts?.sponsorUrl &&
469
+ !this._gelatoOpts?.env_gelatoApiKey
470
+ ) {
471
+ throw new Error(
472
+ "You must provide a `sponsorUrl` or an `apiKey` before using gasless transactions."
473
+ );
474
+ }
475
+ this._gelatoOpts.useGasless = useGasLess;
476
+ }
477
+
478
+ static get remoteClient() {
479
+ return this.remoteStorage;
480
+ }
481
+ }