@show-karma/karma-gap-sdk 0.2.1 → 0.2.3

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.
@@ -188,11 +188,11 @@ class Attestation {
188
188
  async payloadFor(refIdx) {
189
189
  this.assertPayload();
190
190
  if (this.schema.isJsonSchema()) {
191
- const ipfsManager = GAP_1.GAP.ipfs;
192
- if (ipfsManager) {
193
- const ipfsHash = await ipfsManager.save(this._data);
194
- const encodedData = ipfsManager.encode(ipfsHash, 0);
195
- this.schema.setValue("json", JSON.stringify(encodedData));
191
+ const { remoteClient } = GAP_1.GAP;
192
+ if (remoteClient) {
193
+ const cid = await remoteClient.save(this._data, this.schema.name);
194
+ const encodedData = remoteClient.encode(cid);
195
+ this.schema.setValue('json', JSON.stringify(encodedData));
196
196
  }
197
197
  }
198
198
  const payload = (encode = true) => ({
@@ -1,8 +1,8 @@
1
- import { AttestArgs, Facade, SchemaInterface, TNetwork, TSchemaName, SignerOrProvider } from "../types";
2
- import { GapSchema } from "./GapSchema";
3
- import { ethers } from "ethers";
4
- import { Fetcher } from "./Fetcher";
5
- import { AttestationIPFS } from "./AttestationIPFS";
1
+ import { AttestArgs, Facade, SchemaInterface, TNetwork, TSchemaName, SignerOrProvider } from '../types';
2
+ import { GapSchema } from './GapSchema';
3
+ import { ethers } from 'ethers';
4
+ import { Fetcher } from './Fetcher';
5
+ import { RemoteStorage } from './remote-storage/RemoteStorage';
6
6
  interface GAPArgs {
7
7
  network: TNetwork;
8
8
  globalSchemas?: boolean;
@@ -90,16 +90,11 @@ interface GAPArgs {
90
90
  useGasless?: boolean;
91
91
  };
92
92
  /**
93
- * Specifies an optional IPFS key for uploading project details and other related data.
94
- *
95
- * This key is used to authenticate with the IPFS storage service, specifically designed for use with "NFT.STORAGE".
96
- * Utilizing IPFS (InterPlanetary File System) offers a decentralized solution for storing data, ensuring better
97
- * scalability and efficiency compared to sending large amounts of data directly in the attestation body.
98
- *
99
- * If an IPFS key is not provided, the default storage method will be used.
100
- *
93
+ * Defines a remote storage client to be used to store data.
94
+ * If defined, all the details data from an attestation will
95
+ * be stored in the remote storage, e.g. IPFS.
101
96
  */
102
- ipfsKey?: string;
97
+ remoteStorage?: RemoteStorage;
103
98
  }
104
99
  /**
105
100
  * GAP SDK Facade.
@@ -158,14 +153,13 @@ interface GAPArgs {
158
153
  */
159
154
  export declare class GAP extends Facade {
160
155
  private static client;
161
- private static ipfsManager;
156
+ private static remoteStorage?;
162
157
  readonly fetch: Fetcher;
163
158
  readonly network: TNetwork;
164
159
  private _schemas;
165
160
  private static _gelatoOpts;
166
161
  constructor(args: GAPArgs);
167
162
  private assertGelatoOpts;
168
- private assertIPFSOpts;
169
163
  /**
170
164
  * Creates the attestation payload using a specific schema.
171
165
  * @param from
@@ -220,8 +214,8 @@ export declare class GAP extends Facade {
220
214
  * In case of true, the transactions will be sent through [Gelato](https://gelato.network)
221
215
  * and an API key is needed.
222
216
  */
223
- static get gelatoOpts(): GAPArgs["gelatoOpts"];
217
+ static get gelatoOpts(): GAPArgs['gelatoOpts'];
224
218
  static set useGasLess(useGasLess: boolean);
225
- static get ipfs(): AttestationIPFS;
219
+ static get remoteClient(): RemoteStorage<unknown>;
226
220
  }
227
221
  export {};
package/core/class/GAP.js CHANGED
@@ -13,7 +13,6 @@ const consts_1 = require("../consts");
13
13
  const ethers_1 = require("ethers");
14
14
  const MultiAttester_json_1 = __importDefault(require("../abi/MultiAttester.json"));
15
15
  const package_json_1 = require("../../package.json");
16
- const AttestationIPFS_1 = require("./AttestationIPFS");
17
16
  /**
18
17
  * GAP SDK Facade.
19
18
  *
@@ -80,13 +79,13 @@ class GAP extends types_1.Facade {
80
79
  this.generateSlug = async (text) => {
81
80
  let slug = text
82
81
  .toLowerCase()
83
- .replace(/ /g, "-")
84
- .replace(/[^\w-]+/g, "");
82
+ .replace(/ /g, '-')
83
+ .replace(/[^\w-]+/g, '');
85
84
  const slugExists = await this.fetch.slugExists(slug);
86
85
  if (slugExists) {
87
- const parts = slug.split("-");
86
+ const parts = slug.split('-');
88
87
  const counter = parts.pop();
89
- slug = /\d+/g.test(counter) ? parts.join("-") : slug;
88
+ slug = /\d+/g.test(counter) ? parts.join('-') : slug;
90
89
  // eslint-disable-next-line no-param-reassign
91
90
  const nextSlug = `${slug}-${counter && /\d+/g.test(counter) ? +counter + 1 : 1}`;
92
91
  console.log({ nextSlug, counter, slug });
@@ -100,9 +99,7 @@ class GAP extends types_1.Facade {
100
99
  this.fetch = args.apiClient || new GapEasClient_1.GapEasClient({ network: args.network });
101
100
  this.assertGelatoOpts(args);
102
101
  GAP._gelatoOpts = args.gelatoOpts;
103
- if (this.assertIPFSOpts(args)) {
104
- GAP.ipfsManager = new AttestationIPFS_1.AttestationIPFS(args.ipfsKey);
105
- }
102
+ GAP.remoteStorage = args.remoteStorage;
106
103
  this._schemas = schemas.map((schema) => new GapSchema_1.GapSchema(schema, false, args.globalSchemas ? !args.globalSchemas : false));
107
104
  Schema_1.Schema.validate();
108
105
  console.info(`Loaded GAP SDK v${package_json_1.version}`);
@@ -110,25 +107,19 @@ class GAP extends types_1.Facade {
110
107
  assertGelatoOpts(args) {
111
108
  if (args.gelatoOpts &&
112
109
  !(args.gelatoOpts.sponsorUrl || args.gelatoOpts.apiKey)) {
113
- throw new Error("You must provide a `sponsorUrl` or an `apiKey`.");
110
+ throw new Error('You must provide a `sponsorUrl` or an `apiKey`.');
114
111
  }
115
112
  if (args.gelatoOpts?.sponsorUrl &&
116
113
  args.gelatoOpts?.contained &&
117
114
  !args.gelatoOpts.env_gelatoApiKey) {
118
- throw new Error("You must provide `env_gelatoApiKey` to be able to use it in a backend handler.");
115
+ throw new Error('You must provide `env_gelatoApiKey` to be able to use it in a backend handler.');
119
116
  }
120
117
  if ((args.gelatoOpts?.env_gelatoApiKey ||
121
118
  args.gelatoOpts?.apiKey ||
122
119
  args.gelatoOpts?.sponsorUrl) &&
123
120
  !args.gelatoOpts?.useGasless) {
124
- console.warn("GAP::You are using gelatoOpts but not setting useGasless to true. This will send transactions through the normal provider.");
125
- }
126
- }
127
- assertIPFSOpts(args) {
128
- if (!args.ipfsKey) {
129
- return false;
121
+ console.warn('GAP::You are using gelatoOpts but not setting useGasless to true. This will send transactions through the normal provider.');
130
122
  }
131
- return true;
132
123
  }
133
124
  /**
134
125
  * Creates the attestation payload using a specific schema.
@@ -186,11 +177,11 @@ class GAP extends types_1.Facade {
186
177
  * and an API key is needed.
187
178
  */
188
179
  static set gelatoOpts(gelatoOpts) {
189
- if (typeof this._gelatoOpts === "undefined") {
180
+ if (typeof this._gelatoOpts === 'undefined') {
190
181
  this._gelatoOpts = gelatoOpts;
191
182
  }
192
183
  else {
193
- throw new Error("Cannot change a readonly value gelatoOpts.");
184
+ throw new Error('Cannot change a readonly value gelatoOpts.');
194
185
  }
195
186
  }
196
187
  /**
@@ -207,12 +198,12 @@ class GAP extends types_1.Facade {
207
198
  !this._gelatoOpts?.apiKey &&
208
199
  !this._gelatoOpts?.sponsorUrl &&
209
200
  !this._gelatoOpts?.env_gelatoApiKey) {
210
- throw new Error("You must provide a `sponsorUrl` or an `apiKey` before using gasless transactions.");
201
+ throw new Error('You must provide a `sponsorUrl` or an `apiKey` before using gasless transactions.');
211
202
  }
212
203
  this._gelatoOpts.useGasless = useGasLess;
213
204
  }
214
- static get ipfs() {
215
- return this.ipfsManager;
205
+ static get remoteClient() {
206
+ return this.remoteStorage;
216
207
  }
217
208
  }
218
209
  exports.GAP = GAP;
@@ -1,6 +1,6 @@
1
- import { SchemaEncoder, SchemaItem, SchemaValue } from "@ethereum-attestation-service/eas-sdk";
2
- import { AttestArgs, Hex, MultiRevokeArgs, SchemaInterface, SignerOrProvider } from "../types";
3
- import { Attestation } from "./Attestation";
1
+ import { SchemaEncoder, SchemaItem, SchemaValue } from '@ethereum-attestation-service/eas-sdk';
2
+ import { AttestArgs, Hex, MultiRevokeArgs, SchemaInterface, SignerOrProvider } from '../types';
3
+ import { Attestation } from './Attestation';
4
4
  /**
5
5
  * Represents the EAS Schema and provides methods to encode and decode the schema,
6
6
  * and validate the schema references.
@@ -104,7 +104,7 @@ class Schema {
104
104
  setValue(key, value) {
105
105
  const idx = this._schema.findIndex((item) => item.name === key);
106
106
  if (!~idx)
107
- throw new SchemaError_1.SchemaError("INVALID_SCHEMA_FIELD", `Field ${key} not found in schema ${this.name}`);
107
+ throw new SchemaError_1.SchemaError('INVALID_SCHEMA_FIELD', `Field ${key} not found in schema ${this.name}`);
108
108
  this.assertField(this._schema[idx], value);
109
109
  this._schema[idx].value = value;
110
110
  }
@@ -114,35 +114,35 @@ class Schema {
114
114
  * @returns boolean
115
115
  */
116
116
  isJsonSchema() {
117
- return !!this.schema.find((s) => s.name === "json" && s.type === "string");
117
+ return !!this.schema.find((s) => s.name === 'json' && s.type === 'string');
118
118
  }
119
119
  assertField(item, value) {
120
120
  const { type, name } = item;
121
- if (type.includes("uint") && /\D/.test(value)) {
122
- throw new SchemaError_1.SchemaError("INVALID_SCHEMA_FIELD", `Field ${name} is of type ${type} but value is not a number.`);
121
+ if (type.includes('uint') && /\D/.test(value)) {
122
+ throw new SchemaError_1.SchemaError('INVALID_SCHEMA_FIELD', `Field ${name} is of type ${type} but value is not a number.`);
123
123
  }
124
- if (type.includes("address") &&
124
+ if (type.includes('address') &&
125
125
  !ethers_1.ethers.utils.isAddress(value) &&
126
126
  value !== consts_1.zeroAddress) {
127
- throw new SchemaError_1.SchemaError("INVALID_SCHEMA_FIELD", `Field ${name} is of type ${type} but value is not a valid address.`);
127
+ throw new SchemaError_1.SchemaError('INVALID_SCHEMA_FIELD', `Field ${name} is of type ${type} but value is not a valid address.`);
128
128
  }
129
- if (type.includes("bytes") && !value.startsWith("0x")) {
130
- throw new SchemaError_1.SchemaError("INVALID_SCHEMA_FIELD", `Field ${name} is of type ${type} but value is not a valid hex string.`);
129
+ if (type.includes('bytes') && !value.startsWith('0x')) {
130
+ throw new SchemaError_1.SchemaError('INVALID_SCHEMA_FIELD', `Field ${name} is of type ${type} but value is not a valid hex string.`);
131
131
  }
132
- if (type.includes("bool") &&
133
- (!["true", "false", true, false].includes(value) ||
134
- typeof value !== "boolean")) {
135
- throw new SchemaError_1.SchemaError("INVALID_SCHEMA_FIELD", `Field ${name} is of type ${type} but value is not a valid boolean.`);
132
+ if (type.includes('bool') &&
133
+ (!['true', 'false', true, false].includes(value) ||
134
+ typeof value !== 'boolean')) {
135
+ throw new SchemaError_1.SchemaError('INVALID_SCHEMA_FIELD', `Field ${name} is of type ${type} but value is not a valid boolean.`);
136
136
  }
137
- if (type.includes("tuple") && !Array.isArray(value)) {
138
- throw new SchemaError_1.SchemaError("INVALID_SCHEMA_FIELD", `Field ${name} is of type ${type} but value is not a valid array.`);
137
+ if (type.includes('tuple') && !Array.isArray(value)) {
138
+ throw new SchemaError_1.SchemaError('INVALID_SCHEMA_FIELD', `Field ${name} is of type ${type} but value is not a valid array.`);
139
139
  }
140
- if (type === "string" && name === "json") {
140
+ if (type === 'string' && name === 'json') {
141
141
  try {
142
142
  JSON.parse(value);
143
143
  }
144
144
  catch (error) {
145
- throw new SchemaError_1.SchemaError("INVALID_SCHEMA_FIELD", `Field ${name} is of type ${type} but value is not a valid JSON string.`);
145
+ throw new SchemaError_1.SchemaError('INVALID_SCHEMA_FIELD', `Field ${name} is of type ${type} but value is not a valid JSON string.`);
146
146
  }
147
147
  }
148
148
  }
@@ -154,16 +154,16 @@ class Schema {
154
154
  assert(args, strict = false) {
155
155
  const { name, schema, uid, references } = args;
156
156
  if (!name) {
157
- throw new SchemaError_1.SchemaError("MISSING_FIELD", "Schema name is required");
157
+ throw new SchemaError_1.SchemaError('MISSING_FIELD', 'Schema name is required');
158
158
  }
159
159
  if (!schema && !Array.isArray(schema)) {
160
- throw new SchemaError_1.SchemaError("MISSING_FIELD", "Schema must be an array.");
160
+ throw new SchemaError_1.SchemaError('MISSING_FIELD', 'Schema must be an array.');
161
161
  }
162
162
  // if (!uid) {
163
163
  // throw new SchemaError("MISSING_FIELD", "Schema uid is required");
164
164
  // }
165
165
  if (strict && references && !Schema.exists(references)) {
166
- throw new SchemaError_1.SchemaError("INVALID_REFERENCE", `Schema ${name} references ${references} but it does not exist.`);
166
+ throw new SchemaError_1.SchemaError('INVALID_REFERENCE', `Schema ${name} references ${references} but it does not exist.`);
167
167
  }
168
168
  }
169
169
  /**
@@ -221,15 +221,15 @@ class Schema {
221
221
  async attest({ data, to, signer, refUID }) {
222
222
  const eas = GAP_1.GAP.eas.connect(signer);
223
223
  if (this.references && !refUID)
224
- throw new SchemaError_1.AttestationError("INVALID_REFERENCE", "Attestation schema references another schema but no reference UID was provided.");
224
+ throw new SchemaError_1.AttestationError('INVALID_REFERENCE', 'Attestation schema references another schema but no reference UID was provided.');
225
225
  if (this.isJsonSchema()) {
226
- const ipfsManager = GAP_1.GAP.ipfs;
227
- if (ipfsManager) {
228
- const ipfsHash = await ipfsManager.save(data);
229
- const encodedData = ipfsManager.encode(ipfsHash, 0);
226
+ const { remoteClient } = GAP_1.GAP;
227
+ if (remoteClient) {
228
+ const cid = await remoteClient.save(data, this.name);
229
+ const encodedData = remoteClient.encode(cid);
230
230
  data = encodedData;
231
231
  }
232
- this.setValue("json", JSON.stringify(data));
232
+ this.setValue('json', JSON.stringify(data));
233
233
  }
234
234
  else {
235
235
  Object.entries(data).forEach(([key, value]) => {
@@ -276,7 +276,7 @@ class Schema {
276
276
  async multiAttest(signer, entities = []) {
277
277
  entities.forEach((entity) => {
278
278
  if (this.references && !entity.refUID)
279
- throw new SchemaError_1.SchemaError("INVALID_REF_UID", `Entity ${entity.schema.name} references another schema but no reference UID was provided.`);
279
+ throw new SchemaError_1.SchemaError('INVALID_REF_UID', `Entity ${entity.schema.name} references another schema but no reference UID was provided.`);
280
280
  });
281
281
  const eas = GAP_1.GAP.eas.connect(signer);
282
282
  const entityBySchema = entities.reduce((acc, entity) => {
@@ -338,7 +338,7 @@ class Schema {
338
338
  if (!this.exists(schema.name))
339
339
  this.schemas.push(schema);
340
340
  else
341
- throw new SchemaError_1.SchemaError("SCHEMA_ALREADY_EXISTS", `Schema ${schema.name} already exists.`);
341
+ throw new SchemaError_1.SchemaError('SCHEMA_ALREADY_EXISTS', `Schema ${schema.name} already exists.`);
342
342
  });
343
343
  }
344
344
  static getAll() {
@@ -347,7 +347,7 @@ class Schema {
347
347
  static get(name) {
348
348
  const schema = this.schemas.find((schema) => schema.name === name || schema.uid === name);
349
349
  if (!schema)
350
- throw new SchemaError_1.SchemaError("SCHEMA_NOT_FOUND", `Schema ${name} not found. Available schemas: ${Schema.getNames()}`);
350
+ throw new SchemaError_1.SchemaError('SCHEMA_NOT_FOUND', `Schema ${name} not found. Available schemas: ${Schema.getNames()}`);
351
351
  return schema;
352
352
  }
353
353
  /**
@@ -372,7 +372,7 @@ class Schema {
372
372
  if (!schema.references || Schema.exists(schema.references))
373
373
  return;
374
374
  else
375
- errors.push(new SchemaError_1.SchemaError("INVALID_REFERENCE", `Schema ${schema.name} references ${schema.references} but it does not exist.`));
375
+ errors.push(new SchemaError_1.SchemaError('INVALID_REFERENCE', `Schema ${schema.name} references ${schema.references} but it does not exist.`));
376
376
  });
377
377
  if (errors.length)
378
378
  throw errors;
@@ -392,7 +392,7 @@ class Schema {
392
392
  static replaceOne(schema) {
393
393
  const idx = this.schemas.findIndex((item) => schema.name === item.name);
394
394
  if (!~idx)
395
- throw new SchemaError_1.SchemaError("SCHEMA_NOT_FOUND", `Schema ${schema.name} not found.`);
395
+ throw new SchemaError_1.SchemaError('SCHEMA_NOT_FOUND', `Schema ${schema.name} not found.`);
396
396
  this.schemas[idx] = schema;
397
397
  }
398
398
  /**
@@ -407,9 +407,9 @@ class Schema {
407
407
  * @returns
408
408
  */
409
409
  static rawToObject(abi) {
410
- const items = abi.trim().replace(/,\s+/gim, ",").split(",");
410
+ const items = abi.trim().replace(/,\s+/gim, ',').split(',');
411
411
  const schema = items.map((item) => {
412
- const [type, name] = item.split(" ");
412
+ const [type, name] = item.split(' ');
413
413
  return { type, name, value: null };
414
414
  });
415
415
  return schema;
@@ -423,7 +423,7 @@ class Schema {
423
423
  * ```
424
424
  */
425
425
  get raw() {
426
- return this.schema.map((item) => `${item.type} ${item.name}`).join(",");
426
+ return this.schema.map((item) => `${item.type} ${item.name}`).join(',');
427
427
  }
428
428
  get schema() {
429
429
  return this._schema;
@@ -14,7 +14,7 @@ declare const SchemaErrorCodes: {
14
14
  INVALID_REF_UID: number;
15
15
  REVOKATION_ERROR: number;
16
16
  NOT_REVOCABLE: number;
17
- IPFS_UPLOAD: number;
17
+ REMOTE_STORAGE_UPLOAD: number;
18
18
  };
19
19
  export declare class SchemaError extends Error {
20
20
  readonly code: number;
@@ -24,4 +24,6 @@ export declare class SchemaError extends Error {
24
24
  }
25
25
  export declare class AttestationError extends SchemaError {
26
26
  }
27
+ export declare class RemoteStorageError extends SchemaError {
28
+ }
27
29
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AttestationError = exports.SchemaError = void 0;
3
+ exports.RemoteStorageError = exports.AttestationError = exports.SchemaError = void 0;
4
4
  const SchemaErrorCodes = {
5
5
  INVALID_SCHEMA: 50001,
6
6
  INVALID_SCHEMA_NAME: 50002,
@@ -17,12 +17,12 @@ const SchemaErrorCodes = {
17
17
  INVALID_REF_UID: 50013,
18
18
  REVOKATION_ERROR: 50014,
19
19
  NOT_REVOCABLE: 50015,
20
- IPFS_UPLOAD: 50016,
20
+ REMOTE_STORAGE_UPLOAD: 50016,
21
21
  };
22
22
  class SchemaError extends Error {
23
23
  constructor(code, append) {
24
- super(`${code}${append ? `: ${append}` : ""}`);
25
- this._message = append || code.replace(/_/g, " ");
24
+ super(`${code}${append ? `: ${append}` : ''}`);
25
+ this._message = append || code.replace(/_/g, ' ');
26
26
  this.code = SchemaErrorCodes[code];
27
27
  }
28
28
  get message() {
@@ -33,3 +33,6 @@ exports.SchemaError = SchemaError;
33
33
  class AttestationError extends SchemaError {
34
34
  }
35
35
  exports.AttestationError = AttestationError;
36
+ class RemoteStorageError extends SchemaError {
37
+ }
38
+ exports.RemoteStorageError = RemoteStorageError;
@@ -25,6 +25,7 @@ export declare class Grant extends Attestation<IGrant> {
25
25
  members: string[];
26
26
  completed?: GrantCompleted;
27
27
  project?: ISummaryProject;
28
+ categories?: string[];
28
29
  verify(signer: SignerOrProvider): Promise<void>;
29
30
  /**
30
31
  * Add milestones to the grant.
@@ -17,6 +17,7 @@ class Grant extends Attestation_1.Attestation {
17
17
  this.milestones = [];
18
18
  this.updates = [];
19
19
  this.members = [];
20
+ this.categories = [];
20
21
  }
21
22
  async verify(signer) {
22
23
  const eas = GAP_1.GAP.eas.connect(signer);
@@ -186,6 +187,9 @@ class Grant extends Attestation_1.Attestation {
186
187
  if (attestation.members) {
187
188
  grant.members = attestation.members;
188
189
  }
190
+ if (attestation.categories) {
191
+ grant.categories = attestation.categories;
192
+ }
189
193
  return grant;
190
194
  });
191
195
  }
@@ -1,7 +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 "./karma-indexer/GapIndexerClient";
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';
@@ -20,4 +20,7 @@ __exportStar(require("./GapSchema"), exports);
20
20
  __exportStar(require("./Schema"), exports);
21
21
  __exportStar(require("./SchemaError"), exports);
22
22
  __exportStar(require("./entities"), exports);
23
+ __exportStar(require("./Fetcher"), exports);
23
24
  __exportStar(require("./karma-indexer/GapIndexerClient"), exports);
25
+ __exportStar(require("./remote-storage/IpfsStorage"), exports);
26
+ __exportStar(require("./remote-storage/RemoteStorage"), exports);
@@ -0,0 +1,22 @@
1
+ /// <reference types="node" />
2
+ import { NFTStorage } from 'nft.storage';
3
+ import { RemoteStorage } from './RemoteStorage';
4
+ import { TRemoteStorageOutput } from 'core/types';
5
+ export interface IpfsStorageOptions {
6
+ token: string;
7
+ endpoint?: URL;
8
+ }
9
+ export declare class IpfsStorage extends RemoteStorage<NFTStorage> {
10
+ constructor(opts: IpfsStorageOptions,
11
+ /**
12
+ * If set, will send request to another server instead of
13
+ * using the local instance
14
+ */
15
+ sponsor?: RemoteStorage['sponsor']);
16
+ private assert;
17
+ save<T = unknown>(data: T): Promise<string>;
18
+ encode(data: string): TRemoteStorageOutput<string>;
19
+ get<T = unknown>(args: {
20
+ cid: string;
21
+ }): Promise<T>;
22
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IpfsStorage = void 0;
4
+ const nft_storage_1 = require("nft.storage");
5
+ const RemoteStorage_1 = require("./RemoteStorage");
6
+ const SchemaError_1 = require("../SchemaError");
7
+ const utils_1 = require("../../utils");
8
+ class IpfsStorage extends RemoteStorage_1.RemoteStorage {
9
+ constructor(opts,
10
+ /**
11
+ * If set, will send request to another server instead of
12
+ * using the local instance
13
+ */
14
+ sponsor) {
15
+ super(0 /* STORAGE_TYPE.IPFS */, sponsor);
16
+ this.assert(opts);
17
+ this.client = new nft_storage_1.NFTStorage({ ...opts });
18
+ }
19
+ assert(opts) { }
20
+ async save(data) {
21
+ try {
22
+ const blob = new Blob([JSON.stringify(data)], {
23
+ type: 'application/json',
24
+ });
25
+ const cid = await this.client.storeBlob(blob);
26
+ return cid;
27
+ }
28
+ catch (error) {
29
+ throw new SchemaError_1.RemoteStorageError('REMOTE_STORAGE_UPLOAD', `Error adding data to IPFS`);
30
+ }
31
+ }
32
+ encode(data) {
33
+ return { hash: data, storageType: this.storageType };
34
+ }
35
+ async get(args) {
36
+ return (0, utils_1.getIPFSData)(args.cid);
37
+ }
38
+ }
39
+ exports.IpfsStorage = IpfsStorage;
@@ -0,0 +1,41 @@
1
+ import { STORAGE_TYPE, TRemoteStorageOutput } from 'core/types';
2
+ interface SponsoredRemote {
3
+ url: string;
4
+ responseParser: (response: any) => string;
5
+ }
6
+ export declare abstract class RemoteStorage<C = unknown> {
7
+ protected client: C;
8
+ readonly storageType: number;
9
+ readonly sponsor?: SponsoredRemote;
10
+ constructor(storageType: STORAGE_TYPE,
11
+ /**
12
+ * If set, will try to POST request to another server instead of
13
+ * using the local instance.
14
+ *
15
+ * > If a response parser is not set, it will try to get { cid: string }.
16
+ */
17
+ sponsor: SponsoredRemote);
18
+ /**
19
+ * Try to save data to remote storage and return the CID.
20
+ * IF sponsorUrl is set, this method will be automatically
21
+ * intercepted and will send a POST request to the sponsorUrl
22
+ * with the contents: `{ data: T, type: "<AttestationType>" }`
23
+ */
24
+ abstract save<T = unknown>(data: T, schemaName: string): Promise<string>;
25
+ /**
26
+ * Encodes the data according to the remote storage type parameters
27
+ * OR returns the data as is if no encoding is required
28
+ */
29
+ abstract encode(data: unknown): TRemoteStorageOutput;
30
+ /**
31
+ * Get data from Remote Storage
32
+ */
33
+ abstract get<T = unknown>(args: unknown): Promise<T>;
34
+ /**
35
+ * If sponsorUrl is set, intercept the save method and send a POST request
36
+ * to the sponsorUrl instead of using the local instance.
37
+ * @returns
38
+ */
39
+ private interceptRemoteStorage;
40
+ }
41
+ export {};
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RemoteStorage = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ class RemoteStorage {
9
+ constructor(storageType,
10
+ /**
11
+ * If set, will try to POST request to another server instead of
12
+ * using the local instance.
13
+ *
14
+ * > If a response parser is not set, it will try to get { cid: string }.
15
+ */
16
+ sponsor) {
17
+ this.storageType = storageType;
18
+ this.sponsor = sponsor;
19
+ this.interceptRemoteStorage();
20
+ }
21
+ /**
22
+ * If sponsorUrl is set, intercept the save method and send a POST request
23
+ * to the sponsorUrl instead of using the local instance.
24
+ * @returns
25
+ */
26
+ interceptRemoteStorage() {
27
+ if (!this.sponsor?.url)
28
+ return;
29
+ this.save = async (data, schemaName) => {
30
+ const { data: response } = await axios_1.default.post(this.sponsor.url, {
31
+ data: data,
32
+ type: schemaName,
33
+ });
34
+ return this.sponsor.responseParser?.(response) || response.cid;
35
+ };
36
+ }
37
+ }
38
+ exports.RemoteStorage = RemoteStorage;
package/core/types.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { BytesLike } from "ethers";
2
- import { AttestationRequestData, EAS, MultiAttestationRequest, SchemaItem } from "@ethereum-attestation-service/eas-sdk";
3
- import { SignerOrProvider as EASSigner } from "@ethereum-attestation-service/eas-sdk/dist/transaction";
4
- import { Attestation } from "./class";
5
- import { Fetcher } from "./class/Fetcher";
1
+ import { BytesLike } from 'ethers';
2
+ import { AttestationRequestData, EAS, MultiAttestationRequest, SchemaItem } from '@ethereum-attestation-service/eas-sdk';
3
+ import { SignerOrProvider as EASSigner } from '@ethereum-attestation-service/eas-sdk/dist/transaction';
4
+ import { Attestation } from './class';
5
+ import { Fetcher } from './class/Fetcher';
6
6
  export type Hex = `0x${string}`;
7
7
  export type SignerOrProvider = EASSigner & {
8
8
  address?: Hex;
@@ -26,10 +26,10 @@ export interface AttestArgs<T = unknown> {
26
26
  refUID?: Hex;
27
27
  signer: SignerOrProvider;
28
28
  }
29
- export type TSchemaName = "Community" | "CommunityDetails" | "Grant" | "GrantDetails" | "GrantVerified" | "MemberOf" | "MemberDetails" | "Milestone" | "MilestoneCompleted" | "MilestoneApproved" | "Project" | "ProjectDetails" | "Details";
30
- export type TResolvedSchemaNames = "Community" | "Grant" | "GrantVerified" | "MemberOf" | "MilestoneCompleted" | "MilestoneApproved" | "Project" | "Details";
31
- export type TExternalLink = "twitter" | "github" | "website" | "linkedin" | "discord";
32
- export type TNetwork = "optimism" | "optimism-goerli" | "sepolia";
29
+ export type TSchemaName = 'Community' | 'CommunityDetails' | 'Grant' | 'GrantDetails' | 'GrantVerified' | 'MemberOf' | 'MemberDetails' | 'Milestone' | 'MilestoneCompleted' | 'MilestoneApproved' | 'Project' | 'ProjectDetails' | 'Details';
30
+ export type TResolvedSchemaNames = 'Community' | 'Grant' | 'GrantVerified' | 'MemberOf' | 'MilestoneCompleted' | 'MilestoneApproved' | 'Project' | 'Details';
31
+ export type TExternalLink = 'twitter' | 'github' | 'website' | 'linkedin' | 'discord';
32
+ export type TNetwork = 'optimism' | 'optimism-goerli' | 'sepolia';
33
33
  /**
34
34
  * Generic GAP Facade interface.
35
35
  * This supplies the GAP class with the necessary properties.
@@ -105,3 +105,16 @@ export interface SchemaRes {
105
105
  attestations: IAttestation[];
106
106
  };
107
107
  }
108
+ /**
109
+ * Valid remote storage types
110
+ */
111
+ export declare const enum STORAGE_TYPE {
112
+ IPFS = 0,
113
+ ARWEAVE = 1,
114
+ SWARM = 2,
115
+ UNKNOWN = 3
116
+ }
117
+ export type TRemoteStorageOutput<T = unknown> = {
118
+ hash: T;
119
+ storageType: number;
120
+ };
@@ -7,10 +7,13 @@ exports.getIPFSData = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  async function getIPFSData(cid) {
9
9
  try {
10
- const { data } = await axios_1.default.get(`https://ipfs.io/ipfs/${cid}`);
10
+ const { data } = await axios_1.default.get(`https://ipfs.io/ipfs/${cid}`, {
11
+ timeout: 5000,
12
+ });
11
13
  return data;
12
14
  }
13
15
  catch (err) {
16
+ console.error(err);
14
17
  throw new Error(`Error to retrive data for CID: ${cid}`);
15
18
  }
16
19
  }
package/package.json CHANGED
@@ -3,9 +3,9 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.1",
6
+ "version": "0.2.3",
7
7
  "description": "Simple and easy interface between EAS and Karma GAP.",
8
- "main": "dist/index.js",
8
+ "main": "./index.js",
9
9
  "author": "KarmaHQ",
10
10
  "license": "MIT",
11
11
  "private": false,