@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.
- package/core/class/Attestation.d.ts +2 -2
- package/core/class/Attestation.js +10 -1
- package/core/class/AttestationIPFS.d.ts +7 -0
- package/core/class/AttestationIPFS.js +10 -0
- package/core/class/GAP.d.ts +15 -6
- package/core/class/GAP.js +16 -12
- package/core/class/IPFS/IPFS.d.ts +13 -0
- package/core/class/IPFS/IPFS.js +24 -0
- package/core/class/Schema.d.ts +15 -6
- package/core/class/Schema.js +47 -32
- package/core/class/SchemaError.d.ts +3 -0
- package/core/class/SchemaError.js +7 -3
- package/core/class/entities/Community.d.ts +1 -1
- package/core/class/entities/Community.js +4 -6
- package/core/class/entities/Grant.d.ts +1 -1
- package/core/class/entities/Grant.js +6 -10
- package/core/class/entities/MemberOf.d.ts +1 -1
- package/core/class/entities/MemberOf.js +4 -4
- package/core/class/entities/Milestone.d.ts +17 -1
- package/core/class/entities/Milestone.js +35 -0
- package/core/class/entities/Project.d.ts +1 -1
- package/core/class/entities/Project.js +6 -10
- 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.d.ts +1 -0
- package/core/utils/get-ipfs-data.js +20 -0
- package/core/utils/index.d.ts +1 -0
- package/core/utils/index.js +1 -0
- package/package.json +3 -2
|
@@ -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,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;
|
package/core/class/GAP.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { AttestArgs, Facade, SchemaInterface, TNetwork, TSchemaName, SignerOrProvider } from
|
|
2
|
-
import { GapSchema } from
|
|
3
|
-
import { ethers } from
|
|
4
|
-
import { Fetcher } from
|
|
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
|
|
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[
|
|
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(
|
|
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.
|
|
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
|
-
|
|
107
|
+
assertGelatoOpts(args) {
|
|
107
108
|
if (args.gelatoOpts &&
|
|
108
109
|
!(args.gelatoOpts.sponsorUrl || args.gelatoOpts.apiKey)) {
|
|
109
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
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 ===
|
|
180
|
+
if (typeof this._gelatoOpts === 'undefined') {
|
|
180
181
|
this._gelatoOpts = gelatoOpts;
|
|
181
182
|
}
|
|
182
183
|
else {
|
|
183
|
-
throw new Error(
|
|
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(
|
|
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;
|
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.
|
|
@@ -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
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
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
|
/**
|
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
|
/**
|
|
@@ -205,16 +205,31 @@ class Schema {
|
|
|
205
205
|
return eas.multiRevokeOffchain(uids);
|
|
206
206
|
}
|
|
207
207
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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;
|
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
|
+
};
|
|
@@ -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;
|
package/core/utils/index.d.ts
CHANGED
package/core/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
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,
|
|
@@ -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
|
},
|