@show-karma/karma-gap-sdk 0.1.41 → 0.2.2

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.
@@ -134,10 +134,10 @@ export declare class Attestation<T = unknown, S extends Schema = GapSchema> impl
134
134
  * @param refIdx
135
135
  * @returns [Encoded payload, Raw payload]
136
136
  */
137
- payloadFor(refIdx: number): {
137
+ payloadFor(refIdx: number): Promise<{
138
138
  payload: MultiAttestData;
139
139
  raw: MultiAttestData;
140
- };
140
+ }>;
141
141
  /**
142
142
  * Returns an Attestation instance from a JSON decoded schema.
143
143
  * @param data
@@ -4,6 +4,7 @@ exports.Attestation = void 0;
4
4
  const Schema_1 = require("./Schema");
5
5
  const SchemaError_1 = require("./SchemaError");
6
6
  const get_date_1 = require("../utils/get-date");
7
+ const GAP_1 = require("./GAP");
7
8
  const consts_1 = require("../consts");
8
9
  const GapContract_1 = require("./contract/GapContract");
9
10
  /**
@@ -184,8 +185,16 @@ class Attestation {
184
185
  * @param refIdx
185
186
  * @returns [Encoded payload, Raw payload]
186
187
  */
187
- payloadFor(refIdx) {
188
+ async payloadFor(refIdx) {
188
189
  this.assertPayload();
190
+ if (this.schema.isJsonSchema()) {
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
+ }
197
+ }
189
198
  const payload = (encode = true) => ({
190
199
  uid: consts_1.nullRef,
191
200
  refIdx,
@@ -0,0 +1,7 @@
1
+ import { IPFS } from './IPFS/IPFS';
2
+ export declare class AttestationIPFS extends IPFS {
3
+ encode(data: string, storageType: number): {
4
+ ipfsHash: string;
5
+ type: number;
6
+ };
7
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AttestationIPFS = void 0;
4
+ const IPFS_1 = require("./IPFS/IPFS");
5
+ class AttestationIPFS extends IPFS_1.IPFS {
6
+ encode(data, storageType) {
7
+ return { ipfsHash: data, type: storageType };
8
+ }
9
+ }
10
+ exports.AttestationIPFS = AttestationIPFS;
@@ -1,7 +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";
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';
5
6
  interface GAPArgs {
6
7
  network: TNetwork;
7
8
  globalSchemas?: boolean;
@@ -88,6 +89,12 @@ interface GAPArgs {
88
89
  */
89
90
  useGasless?: boolean;
90
91
  };
92
+ /**
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.
96
+ */
97
+ remoteStorage?: RemoteStorage;
91
98
  }
92
99
  /**
93
100
  * GAP SDK Facade.
@@ -146,12 +153,13 @@ interface GAPArgs {
146
153
  */
147
154
  export declare class GAP extends Facade {
148
155
  private static client;
156
+ private static remoteStorage?;
149
157
  readonly fetch: Fetcher;
150
158
  readonly network: TNetwork;
151
159
  private _schemas;
152
160
  private static _gelatoOpts;
153
161
  constructor(args: GAPArgs);
154
- private assert;
162
+ private assertGelatoOpts;
155
163
  /**
156
164
  * Creates the attestation payload using a specific schema.
157
165
  * @param from
@@ -206,7 +214,8 @@ export declare class GAP extends Facade {
206
214
  * In case of true, the transactions will be sent through [Gelato](https://gelato.network)
207
215
  * and an API key is needed.
208
216
  */
209
- static get gelatoOpts(): GAPArgs["gelatoOpts"];
217
+ static get gelatoOpts(): GAPArgs['gelatoOpts'];
210
218
  static set useGasLess(useGasLess: boolean);
219
+ static get remoteClient(): RemoteStorage<unknown>;
211
220
  }
212
221
  export {};
package/core/class/GAP.js CHANGED
@@ -79,13 +79,13 @@ class GAP extends types_1.Facade {
79
79
  this.generateSlug = async (text) => {
80
80
  let slug = text
81
81
  .toLowerCase()
82
- .replace(/ /g, "-")
83
- .replace(/[^\w-]+/g, "");
82
+ .replace(/ /g, '-')
83
+ .replace(/[^\w-]+/g, '');
84
84
  const slugExists = await this.fetch.slugExists(slug);
85
85
  if (slugExists) {
86
- const parts = slug.split("-");
86
+ const parts = slug.split('-');
87
87
  const counter = parts.pop();
88
- slug = /\d+/g.test(counter) ? parts.join("-") : slug;
88
+ slug = /\d+/g.test(counter) ? parts.join('-') : slug;
89
89
  // eslint-disable-next-line no-param-reassign
90
90
  const nextSlug = `${slug}-${counter && /\d+/g.test(counter) ? +counter + 1 : 1}`;
91
91
  console.log({ nextSlug, counter, slug });
@@ -97,27 +97,28 @@ class GAP extends types_1.Facade {
97
97
  this.network = args.network;
98
98
  GAP._eas = new eas_sdk_1.EAS(consts_1.Networks[args.network].contracts.eas);
99
99
  this.fetch = args.apiClient || new GapEasClient_1.GapEasClient({ network: args.network });
100
- this.assert(args);
100
+ this.assertGelatoOpts(args);
101
101
  GAP._gelatoOpts = args.gelatoOpts;
102
+ GAP.remoteStorage = args.remoteStorage;
102
103
  this._schemas = schemas.map((schema) => new GapSchema_1.GapSchema(schema, false, args.globalSchemas ? !args.globalSchemas : false));
103
104
  Schema_1.Schema.validate();
104
105
  console.info(`Loaded GAP SDK v${package_json_1.version}`);
105
106
  }
106
- assert(args) {
107
+ assertGelatoOpts(args) {
107
108
  if (args.gelatoOpts &&
108
109
  !(args.gelatoOpts.sponsorUrl || args.gelatoOpts.apiKey)) {
109
- throw new Error("You must provide a `sponsorUrl` or an `apiKey`.");
110
+ throw new Error('You must provide a `sponsorUrl` or an `apiKey`.');
110
111
  }
111
112
  if (args.gelatoOpts?.sponsorUrl &&
112
113
  args.gelatoOpts?.contained &&
113
114
  !args.gelatoOpts.env_gelatoApiKey) {
114
- 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.');
115
116
  }
116
117
  if ((args.gelatoOpts?.env_gelatoApiKey ||
117
118
  args.gelatoOpts?.apiKey ||
118
119
  args.gelatoOpts?.sponsorUrl) &&
119
120
  !args.gelatoOpts?.useGasless) {
120
- console.warn("GAP::You are using gelatoOpts but not setting useGasless to true. This will send transactions through the normal provider.");
121
+ console.warn('GAP::You are using gelatoOpts but not setting useGasless to true. This will send transactions through the normal provider.');
121
122
  }
122
123
  }
123
124
  /**
@@ -176,11 +177,11 @@ class GAP extends types_1.Facade {
176
177
  * and an API key is needed.
177
178
  */
178
179
  static set gelatoOpts(gelatoOpts) {
179
- if (typeof this._gelatoOpts === "undefined") {
180
+ if (typeof this._gelatoOpts === 'undefined') {
180
181
  this._gelatoOpts = gelatoOpts;
181
182
  }
182
183
  else {
183
- throw new Error("Cannot change a readonly value gelatoOpts.");
184
+ throw new Error('Cannot change a readonly value gelatoOpts.');
184
185
  }
185
186
  }
186
187
  /**
@@ -197,10 +198,13 @@ class GAP extends types_1.Facade {
197
198
  !this._gelatoOpts?.apiKey &&
198
199
  !this._gelatoOpts?.sponsorUrl &&
199
200
  !this._gelatoOpts?.env_gelatoApiKey) {
200
- 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.');
201
202
  }
202
203
  this._gelatoOpts.useGasless = useGasLess;
203
204
  }
205
+ static get remoteClient() {
206
+ return this.remoteStorage;
207
+ }
204
208
  }
205
209
  exports.GAP = GAP;
206
210
  GAP._gelatoOpts = null;
@@ -0,0 +1,13 @@
1
+ import { NFTStorage } from "nft.storage";
2
+ export declare abstract class IPFS {
3
+ protected client: NFTStorage;
4
+ constructor(ipfsKey: string);
5
+ /**
6
+ * Insert the data in the IPFS and return the cid.
7
+ */
8
+ save<T = unknown>(data: T): Promise<string>;
9
+ /**
10
+ * Encode Attestation data using IPFS cid
11
+ */
12
+ protected abstract encode(data: string, storageType: number): any;
13
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IPFS = void 0;
4
+ const nft_storage_1 = require("nft.storage");
5
+ const SchemaError_1 = require("../SchemaError");
6
+ class IPFS {
7
+ constructor(ipfsKey) {
8
+ this.client = new nft_storage_1.NFTStorage({ token: ipfsKey });
9
+ }
10
+ /**
11
+ * Insert the data in the IPFS and return the cid.
12
+ */
13
+ async save(data) {
14
+ try {
15
+ const blob = new nft_storage_1.Blob([JSON.stringify(data)], { type: 'application/json' });
16
+ const cid = await this.client.storeBlob(blob);
17
+ return cid;
18
+ }
19
+ catch (error) {
20
+ throw new SchemaError_1.AttestationError('IPFS_UPLOAD', `Error adding data to IPFS`);
21
+ }
22
+ }
23
+ }
24
+ exports.IPFS = IPFS;
@@ -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.
@@ -125,9 +125,18 @@ export declare abstract class Schema<T extends string = string> implements Schem
125
125
  */
126
126
  multiRevokeOffchain(uids: Hex[], signer: SignerOrProvider): Promise<import("@ethereum-attestation-service/eas-sdk/dist/transaction").Transaction<bigint[]>>;
127
127
  /**
128
- * Attest for a schema.
129
- * @param param0
130
- * @returns
128
+ * Validates and attests a given schema.
129
+ *
130
+ * This function checks a schema against predefined standards or rules. If the 'ipfsKey' parameter is enabled,
131
+ * it uploads the data to the IPFS (InterPlanetary File System). Upon successful upload, the function
132
+ * returns the CID (Content Identifier) within the Attestation Body, providing a reference to the data on IPFS.
133
+ *
134
+ * Usage:
135
+ * - Ensure that the schema to be attested conforms to the required format.
136
+ * - Enable 'ipfsKey' if you wish to store the data on IPFS and retrieve its CID.
137
+ *
138
+ * @param {Object} param0 - An object containing the schema and other optional settings.
139
+ * @returns {Object} An object containing the attestation results, including the CID if 'ipfsKey' is enabled.
131
140
  */
132
141
  attest<T>({ data, to, signer, refUID }: AttestArgs<T>): Promise<Hex>;
133
142
  /**
@@ -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
  /**
@@ -205,16 +205,31 @@ class Schema {
205
205
  return eas.multiRevokeOffchain(uids);
206
206
  }
207
207
  /**
208
- * Attest for a schema.
209
- * @param param0
210
- * @returns
208
+ * Validates and attests a given schema.
209
+ *
210
+ * This function checks a schema against predefined standards or rules. If the 'ipfsKey' parameter is enabled,
211
+ * it uploads the data to the IPFS (InterPlanetary File System). Upon successful upload, the function
212
+ * returns the CID (Content Identifier) within the Attestation Body, providing a reference to the data on IPFS.
213
+ *
214
+ * Usage:
215
+ * - Ensure that the schema to be attested conforms to the required format.
216
+ * - Enable 'ipfsKey' if you wish to store the data on IPFS and retrieve its CID.
217
+ *
218
+ * @param {Object} param0 - An object containing the schema and other optional settings.
219
+ * @returns {Object} An object containing the attestation results, including the CID if 'ipfsKey' is enabled.
211
220
  */
212
221
  async attest({ data, to, signer, refUID }) {
213
222
  const eas = GAP_1.GAP.eas.connect(signer);
214
223
  if (this.references && !refUID)
215
- 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.');
216
225
  if (this.isJsonSchema()) {
217
- this.setValue("json", JSON.stringify(data));
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
+ data = encodedData;
231
+ }
232
+ this.setValue('json', JSON.stringify(data));
218
233
  }
219
234
  else {
220
235
  Object.entries(data).forEach(([key, value]) => {
@@ -261,7 +276,7 @@ class Schema {
261
276
  async multiAttest(signer, entities = []) {
262
277
  entities.forEach((entity) => {
263
278
  if (this.references && !entity.refUID)
264
- 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.`);
265
280
  });
266
281
  const eas = GAP_1.GAP.eas.connect(signer);
267
282
  const entityBySchema = entities.reduce((acc, entity) => {
@@ -323,7 +338,7 @@ class Schema {
323
338
  if (!this.exists(schema.name))
324
339
  this.schemas.push(schema);
325
340
  else
326
- 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.`);
327
342
  });
328
343
  }
329
344
  static getAll() {
@@ -332,7 +347,7 @@ class Schema {
332
347
  static get(name) {
333
348
  const schema = this.schemas.find((schema) => schema.name === name || schema.uid === name);
334
349
  if (!schema)
335
- 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()}`);
336
351
  return schema;
337
352
  }
338
353
  /**
@@ -357,7 +372,7 @@ class Schema {
357
372
  if (!schema.references || Schema.exists(schema.references))
358
373
  return;
359
374
  else
360
- 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.`));
361
376
  });
362
377
  if (errors.length)
363
378
  throw errors;
@@ -377,7 +392,7 @@ class Schema {
377
392
  static replaceOne(schema) {
378
393
  const idx = this.schemas.findIndex((item) => schema.name === item.name);
379
394
  if (!~idx)
380
- 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.`);
381
396
  this.schemas[idx] = schema;
382
397
  }
383
398
  /**
@@ -392,9 +407,9 @@ class Schema {
392
407
  * @returns
393
408
  */
394
409
  static rawToObject(abi) {
395
- const items = abi.trim().replace(/,\s+/gim, ",").split(",");
410
+ const items = abi.trim().replace(/,\s+/gim, ',').split(',');
396
411
  const schema = items.map((item) => {
397
- const [type, name] = item.split(" ");
412
+ const [type, name] = item.split(' ');
398
413
  return { type, name, value: null };
399
414
  });
400
415
  return schema;
@@ -408,7 +423,7 @@ class Schema {
408
423
  * ```
409
424
  */
410
425
  get raw() {
411
- return this.schema.map((item) => `${item.type} ${item.name}`).join(",");
426
+ return this.schema.map((item) => `${item.type} ${item.name}`).join(',');
412
427
  }
413
428
  get schema() {
414
429
  return this._schema;
@@ -14,6 +14,7 @@ declare const SchemaErrorCodes: {
14
14
  INVALID_REF_UID: number;
15
15
  REVOKATION_ERROR: number;
16
16
  NOT_REVOCABLE: number;
17
+ REMOTE_STORAGE_UPLOAD: number;
17
18
  };
18
19
  export declare class SchemaError extends Error {
19
20
  readonly code: number;
@@ -23,4 +24,6 @@ export declare class SchemaError extends Error {
23
24
  }
24
25
  export declare class AttestationError extends SchemaError {
25
26
  }
27
+ export declare class RemoteStorageError extends SchemaError {
28
+ }
26
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,11 +17,12 @@ const SchemaErrorCodes = {
17
17
  INVALID_REF_UID: 50013,
18
18
  REVOKATION_ERROR: 50014,
19
19
  NOT_REVOCABLE: 50015,
20
+ REMOTE_STORAGE_UPLOAD: 50016,
20
21
  };
21
22
  class SchemaError extends Error {
22
23
  constructor(code, append) {
23
- super(`${code}${append ? `: ${append}` : ""}`);
24
- this._message = append || code.replace(/_/g, " ");
24
+ super(`${code}${append ? `: ${append}` : ''}`);
25
+ this._message = append || code.replace(/_/g, ' ');
25
26
  this.code = SchemaErrorCodes[code];
26
27
  }
27
28
  get message() {
@@ -32,3 +33,6 @@ exports.SchemaError = SchemaError;
32
33
  class AttestationError extends SchemaError {
33
34
  }
34
35
  exports.AttestationError = AttestationError;
36
+ class RemoteStorageError extends SchemaError {
37
+ }
38
+ exports.RemoteStorageError = RemoteStorageError;
@@ -22,7 +22,7 @@ export declare class Community extends Attestation<ICommunity> {
22
22
  * @param payload
23
23
  * @param refIdx
24
24
  */
25
- multiAttestPayload(): MultiAttestPayload;
25
+ multiAttestPayload(): Promise<MultiAttestPayload>;
26
26
  /**
27
27
  * Attest a community with its details.
28
28
  *
@@ -23,15 +23,13 @@ class Community extends Attestation_1.Attestation {
23
23
  * @param payload
24
24
  * @param refIdx
25
25
  */
26
- multiAttestPayload() {
27
- const payload = [[this, this.payloadFor(0)]];
26
+ async multiAttestPayload() {
27
+ const payload = [[this, await this.payloadFor(0)]];
28
28
  if (this.details) {
29
- payload.push([this.details, this.details.payloadFor(0)]);
29
+ payload.push([this.details, await this.details.payloadFor(0)]);
30
30
  }
31
31
  if (this.projects?.length) {
32
- this.projects.forEach((p) => {
33
- payload.push(...p.multiAttestPayload(payload, 0));
34
- });
32
+ await Promise.all(this.projects.map(async (p) => payload.push(...(await p.multiAttestPayload(payload, 0)))));
35
33
  }
36
34
  return payload;
37
35
  }
@@ -42,7 +42,7 @@ export declare class Grant extends Attestation<IGrant> {
42
42
  * @param payload
43
43
  * @param projectIdx
44
44
  */
45
- multiAttestPayload(currentPayload?: MultiAttestPayload, projectIdx?: number): [Attestation<unknown, GapSchema>, import("core/types").RawMultiAttestPayload][];
45
+ multiAttestPayload(currentPayload?: MultiAttestPayload, projectIdx?: number): Promise<[Attestation<unknown, GapSchema>, import("core/types").RawMultiAttestPayload][]>;
46
46
  /**
47
47
  * @inheritdoc
48
48
  */
@@ -70,22 +70,18 @@ class Grant extends Attestation_1.Attestation {
70
70
  * @param payload
71
71
  * @param projectIdx
72
72
  */
73
- multiAttestPayload(currentPayload = [], projectIdx = 0) {
73
+ async multiAttestPayload(currentPayload = [], projectIdx = 0) {
74
74
  this.assertPayload();
75
75
  const payload = [...currentPayload];
76
- const grantIdx = payload.push([this, this.payloadFor(projectIdx)]) - 1;
76
+ const grantIdx = payload.push([this, await this.payloadFor(projectIdx)]) - 1;
77
77
  if (this.details) {
78
- payload.push([this.details, this.details.payloadFor(grantIdx)]);
78
+ payload.push([this.details, await this.details.payloadFor(grantIdx)]);
79
79
  }
80
80
  if (this.milestones.length) {
81
- this.milestones.forEach((m) => {
82
- payload.push([m, m.payloadFor(grantIdx)]);
83
- });
81
+ await Promise.all(this.milestones.map(async (m) => payload.push(...(await m.multiAttestPayload(currentPayload, grantIdx)))));
84
82
  }
85
83
  if (this.updates.length) {
86
- this.updates.forEach((u) => {
87
- payload.push([u, u.payloadFor(grantIdx)]);
88
- });
84
+ await Promise.all(this.updates.map(async (u) => payload.push([u, await u.payloadFor(grantIdx)])));
89
85
  }
90
86
  return payload.slice(currentPayload.length, payload.length);
91
87
  }
@@ -94,7 +90,7 @@ class Grant extends Attestation_1.Attestation {
94
90
  */
95
91
  async attest(signer) {
96
92
  this.assertPayload();
97
- const payload = this.multiAttestPayload();
93
+ const payload = await this.multiAttestPayload();
98
94
  const uids = await GapContract_1.GapContract.multiAttest(signer, payload.map((p) => p[1]));
99
95
  uids.forEach((uid, index) => {
100
96
  payload[index][0].uid = uid;
@@ -6,6 +6,6 @@ export interface IMemberOf {
6
6
  }
7
7
  export declare class MemberOf extends Attestation<IMemberOf> {
8
8
  details?: MemberDetails;
9
- multiAttestPayload(currentPayload?: MultiAttestPayload, projectIdx?: number): [Attestation<unknown, import("..").GapSchema>, import("core/types").RawMultiAttestPayload][];
9
+ multiAttestPayload(currentPayload?: MultiAttestPayload, projectIdx?: number): Promise<[Attestation<unknown, import("..").GapSchema>, import("core/types").RawMultiAttestPayload][]>;
10
10
  attest(signer: SignerOrProvider): Promise<void>;
11
11
  }
@@ -5,16 +5,16 @@ const Attestation_1 = require("../Attestation");
5
5
  const SchemaError_1 = require("../SchemaError");
6
6
  const GapContract_1 = require("../contract/GapContract");
7
7
  class MemberOf extends Attestation_1.Attestation {
8
- multiAttestPayload(currentPayload = [], projectIdx = 0) {
8
+ async multiAttestPayload(currentPayload = [], projectIdx = 0) {
9
9
  const payload = [...currentPayload];
10
- const memberIdx = payload.push([this, this.payloadFor(projectIdx)]) - 1;
10
+ const memberIdx = payload.push([this, await this.payloadFor(projectIdx)]) - 1;
11
11
  if (this.details) {
12
- payload.push([this.details, this.details.payloadFor(memberIdx)]);
12
+ payload.push([this.details, await this.details.payloadFor(memberIdx)]);
13
13
  }
14
14
  return payload.slice(currentPayload.length, payload.length);
15
15
  }
16
16
  async attest(signer) {
17
- const payload = this.multiAttestPayload();
17
+ const payload = await this.multiAttestPayload();
18
18
  try {
19
19
  const [memberUID, detailsUID] = await GapContract_1.GapContract.multiAttest(signer, payload.map((p) => p[1]));
20
20
  this.uid = memberUID;
@@ -1,5 +1,6 @@
1
- import { SignerOrProvider } from '../../types';
1
+ import { MultiAttestPayload, SignerOrProvider } from '../../types';
2
2
  import { Attestation } from '../Attestation';
3
+ import { GapSchema } from '../GapSchema';
3
4
  import { MilestoneCompleted } from '../types/attestations';
4
5
  interface _Milestone extends Milestone {
5
6
  }
@@ -54,6 +55,21 @@ export declare class Milestone extends Attestation<IMilestone> implements IMiles
54
55
  * @param signer
55
56
  */
56
57
  revokeCompletion(signer: SignerOrProvider): Promise<void>;
58
+ /**
59
+ * Creates the payload for a multi-attestation.
60
+ *
61
+ * > if Current payload is set, it'll be used as the base payload
62
+ * and the project should refer to an index of the current payload,
63
+ * usually the community position.
64
+ *
65
+ * @param payload
66
+ * @param grantIdx
67
+ */
68
+ multiAttestPayload(currentPayload?: MultiAttestPayload, grantIdx?: number): Promise<[Attestation<unknown, GapSchema>, import("../../types").RawMultiAttestPayload][]>;
69
+ /**
70
+ * @inheritdoc
71
+ */
72
+ attest(signer: SignerOrProvider): Promise<void>;
57
73
  /**
58
74
  * Attest the status of the milestone as approved, rejected or completed.
59
75
  */
@@ -5,6 +5,7 @@ const Attestation_1 = require("../Attestation");
5
5
  const GAP_1 = require("../GAP");
6
6
  const GapSchema_1 = require("../GapSchema");
7
7
  const SchemaError_1 = require("../SchemaError");
8
+ const GapContract_1 = require("../contract/GapContract");
8
9
  const attestations_1 = require("../types/attestations");
9
10
  class Milestone extends Attestation_1.Attestation {
10
11
  /**
@@ -119,6 +120,40 @@ class Milestone extends Attestation_1.Attestation {
119
120
  },
120
121
  ]);
121
122
  }
123
+ /**
124
+ * Creates the payload for a multi-attestation.
125
+ *
126
+ * > if Current payload is set, it'll be used as the base payload
127
+ * and the project should refer to an index of the current payload,
128
+ * usually the community position.
129
+ *
130
+ * @param payload
131
+ * @param grantIdx
132
+ */
133
+ async multiAttestPayload(currentPayload = [], grantIdx = 0) {
134
+ this.assertPayload();
135
+ const payload = [...currentPayload];
136
+ const milestoneIdx = payload.push([this, await this.payloadFor(grantIdx)]) - 1;
137
+ if (this.completed) {
138
+ payload.push([
139
+ this.completed,
140
+ await this.completed.payloadFor(milestoneIdx),
141
+ ]);
142
+ }
143
+ return payload.slice(currentPayload.length, payload.length);
144
+ }
145
+ /**
146
+ * @inheritdoc
147
+ */
148
+ async attest(signer) {
149
+ this.assertPayload();
150
+ const payload = await this.multiAttestPayload();
151
+ const uids = await GapContract_1.GapContract.multiAttest(signer, payload.map((p) => p[1]));
152
+ uids.forEach((uid, index) => {
153
+ payload[index][0].uid = uid;
154
+ });
155
+ console.log(uids);
156
+ }
122
157
  /**
123
158
  * Attest the status of the milestone as approved, rejected or completed.
124
159
  */
@@ -23,7 +23,7 @@ export declare class Project extends Attestation<IProject> {
23
23
  * @param payload
24
24
  * @param communityIdx
25
25
  */
26
- multiAttestPayload(currentPayload?: MultiAttestPayload, communityIdx?: number): MultiAttestPayload;
26
+ multiAttestPayload(currentPayload?: MultiAttestPayload, communityIdx?: number): Promise<MultiAttestPayload>;
27
27
  attest(signer: SignerOrProvider): Promise<void>;
28
28
  /**
29
29
  * Add new members to the project.
@@ -26,26 +26,22 @@ class Project extends Attestation_1.Attestation {
26
26
  * @param payload
27
27
  * @param communityIdx
28
28
  */
29
- multiAttestPayload(currentPayload = [], communityIdx = 0) {
29
+ async multiAttestPayload(currentPayload = [], communityIdx = 0) {
30
30
  const payload = [...currentPayload];
31
- const projectIdx = payload.push([this, this.payloadFor(communityIdx)]) - 1;
31
+ const projectIdx = payload.push([this, await this.payloadFor(communityIdx)]) - 1;
32
32
  if (this.details) {
33
- payload.push([this.details, this.details.payloadFor(projectIdx)]);
33
+ payload.push([this.details, await this.details.payloadFor(projectIdx)]);
34
34
  }
35
35
  if (this.members?.length) {
36
- this.members.forEach((m) => {
37
- payload.push(...m.multiAttestPayload(payload, projectIdx));
38
- });
36
+ await Promise.all(this.members.map(async (m) => payload.push(...(await m.multiAttestPayload(payload, projectIdx)))));
39
37
  }
40
38
  if (this.grants?.length) {
41
- this.grants.forEach((g) => {
42
- payload.push(...g.multiAttestPayload(payload, projectIdx));
43
- });
39
+ await Promise.all(this.grants.map(async (g) => payload.push(...(await g.multiAttestPayload(payload, projectIdx)))));
44
40
  }
45
41
  return payload.slice(currentPayload.length, payload.length);
46
42
  }
47
43
  async attest(signer) {
48
- const payload = this.multiAttestPayload();
44
+ const payload = await this.multiAttestPayload();
49
45
  const uids = await GapContract_1.GapContract.multiAttest(signer, payload.map((p) => p[1]));
50
46
  uids.forEach((uid, index) => {
51
47
  payload[index][0].uid = uid;
@@ -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
+ };
@@ -0,0 +1 @@
1
+ export declare function getIPFSData<T>(cid: string): Promise<T>;
@@ -0,0 +1,20 @@
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.getIPFSData = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ async function getIPFSData(cid) {
9
+ try {
10
+ const { data } = await axios_1.default.get(`https://ipfs.io/ipfs/${cid}`, {
11
+ timeout: 5000,
12
+ });
13
+ return data;
14
+ }
15
+ catch (err) {
16
+ console.error(err);
17
+ throw new Error(`Error to retrive data for CID: ${cid}`);
18
+ }
19
+ }
20
+ exports.getIPFSData = getIPFSData;
@@ -4,3 +4,4 @@ export * from './gql-queries';
4
4
  export * from './map-filter';
5
5
  export * from './serialize-bigint';
6
6
  export * from './to-unix';
7
+ export * from './get-ipfs-data';
@@ -20,3 +20,4 @@ __exportStar(require("./gql-queries"), exports);
20
20
  __exportStar(require("./map-filter"), exports);
21
21
  __exportStar(require("./serialize-bigint"), exports);
22
22
  __exportStar(require("./to-unix"), exports);
23
+ __exportStar(require("./get-ipfs-data"), exports);
package/package.json CHANGED
@@ -3,9 +3,9 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.41",
6
+ "version": "0.2.2",
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,
@@ -23,6 +23,7 @@
23
23
  "@types/sha256": "^0.2.0",
24
24
  "axios": "^1.4.0",
25
25
  "ethers": "5.7",
26
+ "nft.storage": "^7.1.1",
26
27
  "sha256": "^0.2.0",
27
28
  "simple-ts-job-runner": "^1.0.12"
28
29
  },