@show-karma/karma-gap-sdk 0.2.1 → 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.
- package/core/class/Attestation.js +5 -5
- package/core/class/GAP.d.ts +12 -18
- package/core/class/GAP.js +13 -22
- package/core/class/Schema.d.ts +3 -3
- package/core/class/Schema.js +33 -33
- package/core/class/SchemaError.d.ts +3 -1
- package/core/class/SchemaError.js +7 -4
- package/core/class/index.d.ts +10 -7
- package/core/class/index.js +3 -0
- package/core/class/remote-storage/IpfsStorage.d.ts +22 -0
- package/core/class/remote-storage/IpfsStorage.js +39 -0
- package/core/class/remote-storage/RemoteStorage.d.ts +41 -0
- package/core/class/remote-storage/RemoteStorage.js +38 -0
- package/core/types.d.ts +22 -9
- package/core/utils/get-ipfs-data.js +4 -1
- package/package.json +2 -2
|
@@ -188,11 +188,11 @@ class Attestation {
|
|
|
188
188
|
async payloadFor(refIdx) {
|
|
189
189
|
this.assertPayload();
|
|
190
190
|
if (this.schema.isJsonSchema()) {
|
|
191
|
-
const
|
|
192
|
-
if (
|
|
193
|
-
const
|
|
194
|
-
const encodedData =
|
|
195
|
-
this.schema.setValue(
|
|
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) => ({
|
package/core/class/GAP.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { AttestArgs, Facade, SchemaInterface, TNetwork, TSchemaName, SignerOrProvider } from
|
|
2
|
-
import { GapSchema } from
|
|
3
|
-
import { ethers } from
|
|
4
|
-
import { Fetcher } from
|
|
5
|
-
import {
|
|
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
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
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
|
-
|
|
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
|
|
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[
|
|
217
|
+
static get gelatoOpts(): GAPArgs['gelatoOpts'];
|
|
224
218
|
static set useGasLess(useGasLess: boolean);
|
|
225
|
-
static get
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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 ===
|
|
180
|
+
if (typeof this._gelatoOpts === 'undefined') {
|
|
190
181
|
this._gelatoOpts = gelatoOpts;
|
|
191
182
|
}
|
|
192
183
|
else {
|
|
193
|
-
throw new Error(
|
|
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(
|
|
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
|
|
215
|
-
return this.
|
|
205
|
+
static get remoteClient() {
|
|
206
|
+
return this.remoteStorage;
|
|
216
207
|
}
|
|
217
208
|
}
|
|
218
209
|
exports.GAP = GAP;
|
package/core/class/Schema.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { SchemaEncoder, SchemaItem, SchemaValue } from
|
|
2
|
-
import { AttestArgs, Hex, MultiRevokeArgs, SchemaInterface, SignerOrProvider } from
|
|
3
|
-
import { Attestation } from
|
|
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.
|
package/core/class/Schema.js
CHANGED
|
@@ -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(
|
|
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 ===
|
|
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(
|
|
122
|
-
throw new SchemaError_1.SchemaError(
|
|
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(
|
|
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(
|
|
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(
|
|
130
|
-
throw new SchemaError_1.SchemaError(
|
|
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(
|
|
133
|
-
(![
|
|
134
|
-
typeof value !==
|
|
135
|
-
throw new SchemaError_1.SchemaError(
|
|
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(
|
|
138
|
-
throw new SchemaError_1.SchemaError(
|
|
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 ===
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
227
|
-
if (
|
|
228
|
-
const
|
|
229
|
-
const encodedData =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
package/core/class/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
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';
|
package/core/class/index.js
CHANGED
|
@@ -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
|
|
2
|
-
import { AttestationRequestData, EAS, MultiAttestationRequest, SchemaItem } from
|
|
3
|
-
import { SignerOrProvider as EASSigner } from
|
|
4
|
-
import { Attestation } from
|
|
5
|
-
import { Fetcher } from
|
|
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 =
|
|
30
|
-
export type TResolvedSchemaNames =
|
|
31
|
-
export type TExternalLink =
|
|
32
|
-
export type TNetwork =
|
|
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.
|
|
6
|
+
"version": "0.2.2",
|
|
7
7
|
"description": "Simple and easy interface between EAS and Karma GAP.",
|
|
8
|
-
"main": "
|
|
8
|
+
"main": "./index.js",
|
|
9
9
|
"author": "KarmaHQ",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"private": false,
|