@nina-protocol/nina-db 0.0.106 → 0.0.108

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/dist/knexfile.js CHANGED
@@ -2,6 +2,10 @@
2
2
  * @type { Object.<string, import("knex").Knex.Config> }
3
3
  */
4
4
  import "dotenv/config.js";
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
5
9
  export default {
6
10
  development: {
7
11
  client: 'postgresql',
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ export async function up(knex) {
6
+ await knex.schema.alterTable('releases', (table) => {
7
+ table.string('mint').nullable().alter();
8
+ });
9
+ }
10
+ /**
11
+ * @param { import("knex").Knex } knex
12
+ * @returns { Promise<void> }
13
+ */
14
+ export async function down(knex) {
15
+ await knex.schema.alterTable('releases', (table) => {
16
+ table.string('mint').notNullable().alter();
17
+ });
18
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ export const up = function (knex) {
6
+ return knex.schema.table('releases', table => {
7
+ table.string('solanaAddress').nullable();
8
+ }).then(() => {
9
+ return knex('releases').whereNotNull('mint').update('solanaAddress', knex.ref('publicKey'));
10
+ });
11
+ };
12
+ /**
13
+ * @param { import("knex").Knex } knex
14
+ * @returns { Promise<void> }
15
+ */
16
+ export const down = function (knex) {
17
+ return knex.schema.table('releases', table => {
18
+ table.dropColumn('solanaAddress');
19
+ });
20
+ };
@@ -13,39 +13,39 @@ import promiseRetry from 'promise-retry';
13
13
  import { customAlphabet } from 'nanoid';
14
14
  import { getTokenMetadata } from '@solana/spl-token';
15
15
  const ensureHttps = (uri) => {
16
- if (!uri.startsWith('http://') && !uri.startsWith('https://')) {
16
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
17
17
  return `https://${uri}`;
18
18
  }
19
19
  return uri;
20
20
  };
21
- const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
21
+ const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
22
22
  const randomStringGenerator = customAlphabet(alphabet, 12);
23
23
  export default class Release extends Model {
24
- static tableName = 'releases';
25
- static idColumn = 'id';
24
+ static tableName = "releases";
25
+ static idColumn = "id";
26
26
  static jsonSchema = {
27
- type: 'object',
28
- required: ['publicKey', 'mint', 'metadata', 'datetime', 'slug', 'price'],
27
+ type: "object",
28
+ required: ["metadata", "datetime", "slug", "price"],
29
29
  properties: {
30
- publicKey: { type: 'string' },
31
- mint: { type: 'string' },
32
- slug: { type: 'string' },
30
+ publicKey: { type: ["string"] },
31
+ mint: { type: "string" },
32
+ slug: { type: "string" },
33
33
  metadata: {
34
- type: 'object',
35
- required: ['name', 'symbol', 'description', 'image', 'properties',],
34
+ type: "object",
35
+ required: ["name", "symbol", "description", "image", "properties"],
36
36
  properties: {
37
- name: { type: 'string' },
38
- symbol: { type: 'string' },
39
- description: { type: 'string' },
37
+ name: { type: "string" },
38
+ symbol: { type: "string" },
39
+ description: { type: "string" },
40
40
  properties: {
41
- type: 'object',
41
+ type: "object",
42
42
  properties: {
43
- artist: { type: 'string' },
44
- title: { type: 'string' },
45
- date: { type: 'string' },
46
- files: { type: 'array' },
47
- category: { type: 'string' },
48
- creators: { type: 'array' },
43
+ artist: { type: "string" },
44
+ title: { type: "string" },
45
+ date: { type: "string" },
46
+ files: { type: "array" },
47
+ category: { type: "string" },
48
+ creators: { type: "array" }
49
49
  }
50
50
  }
51
51
  }
@@ -54,7 +54,7 @@ export default class Release extends Model {
54
54
  archived: { type: 'boolean' },
55
55
  },
56
56
  };
57
- static findOrCreate = async (publicKey, hubPublicKey = null, programId = process.env.NINA_PROGRAM_V2_ID) => {
57
+ static findOrCreate = async (publicKey, hubPublicKey = null, programId = process.env.NINA_PROGRAM_V2_ID, isLazy = false) => {
58
58
  try {
59
59
  console.log('Release.findOrCreate', publicKey, programId);
60
60
  let release = await Release.query().findOne({ publicKey });
@@ -76,13 +76,13 @@ export default class Release extends Model {
76
76
  return result;
77
77
  }
78
78
  catch (error) {
79
- console.log('error fetching release account', error);
79
+ console.log("error fetching release account", error);
80
80
  retry(error);
81
81
  }
82
82
  }, {
83
83
  retries: 50,
84
84
  minTimeout: 500,
85
- maxTimeout: 1500,
85
+ maxTimeout: 1500
86
86
  });
87
87
  let metadataAccount;
88
88
  if (programId === process.env.NINA_PROGRAM_V2_ID) {
@@ -92,14 +92,16 @@ export default class Release extends Model {
92
92
  metadataAccount = (await metaplex.nfts().findAllByMintList({ mints: [releaseAccount.releaseMint] }, { commitment: 'confirmed' }))[0];
93
93
  }
94
94
  if (!metadataAccount) {
95
- throw new Error('No metadata account found for release - is not a complete release');
95
+ throw new Error("No metadata account found for release - is not a complete release");
96
96
  }
97
97
  let json;
98
98
  try {
99
- json = (await axios.get(ensureHttps(metadataAccount.uri.replace('www.', '').replace('arweave.net', 'gateway.irys.xyz')))).data;
99
+ json = (await axios.get(ensureHttps(metadataAccount.uri
100
+ .replace("www.", "")
101
+ .replace("arweave.net", "gateway.irys.xyz")))).data;
100
102
  }
101
103
  catch (error) {
102
- json = (await axios.get(ensureHttps(metadataAccount.uri.replace('gateway.irys.xyz', 'arweave.net')))).data;
104
+ json = (await axios.get(ensureHttps(metadataAccount.uri.replace("gateway.irys.xyz", "arweave.net")))).data;
103
105
  }
104
106
  const slug = await this.generateSlug(json);
105
107
  let publisher = await Account.findOrCreate(releaseAccount.authority.toBase58());
@@ -112,25 +114,32 @@ export default class Release extends Model {
112
114
  publisherId: publisher.id,
113
115
  releaseAccount,
114
116
  programId,
117
+ solanaAddress: isLazy ? null : publicKey,
115
118
  });
116
119
  if (hubPublicKey) {
117
120
  const hub = await Hub.query().findOne({ publicKey: hubPublicKey });
118
121
  await release.$query().patch({ hubId: hub.id });
119
- await Hub.relatedQuery('releases').for(hub.id).patch({
120
- visible: true,
121
- }).where({ id: release.id });
122
+ await Hub.relatedQuery("releases")
123
+ .for(hub.id)
124
+ .patch({
125
+ visible: true
126
+ })
127
+ .where({ id: release.id });
122
128
  }
123
129
  return release;
124
130
  }
125
131
  catch (error) {
126
- console.log('error finding or creating release: ', error);
132
+ console.log("error finding or creating release: ", error);
127
133
  return null;
128
134
  }
129
135
  };
130
136
  static createRelease = async ({ publicKey, mint, metadata, datetime, publisherId, releaseAccount, programId }) => {
131
137
  const slug = await this.generateSlug(metadata);
132
- const price = releaseAccount.account?.price?.toNumber() || releaseAccount?.price?.toNumber() || 0;
133
- const paymentMint = releaseAccount.account?.paymentMint.toBase58() || releaseAccount?.paymentMint.toBase58();
138
+ const price = releaseAccount.account?.price?.toNumber() ||
139
+ releaseAccount?.price?.toNumber() ||
140
+ 0;
141
+ const paymentMint = releaseAccount.account?.paymentMint.toBase58() ||
142
+ releaseAccount?.paymentMint.toBase58();
134
143
  const release = await Release.query().insertGraph({
135
144
  publicKey,
136
145
  mint,
@@ -146,7 +155,11 @@ export default class Release extends Model {
146
155
  if (metadata.properties.tags) {
147
156
  for await (let tag of metadata.properties.tags) {
148
157
  const tagRecord = await Tag.findOrCreate(tag);
149
- await Release.relatedQuery('tags').for(release.id).relate(tagRecord.id).onConflict(['tagId', 'releaseId']).ignore();
158
+ await Release.relatedQuery("tags")
159
+ .for(release.id)
160
+ .relate(tagRecord.id)
161
+ .onConflict(["tagId", "releaseId"])
162
+ .ignore();
150
163
  }
151
164
  }
152
165
  if (programId === process.env.NINA_PROGRAM_ID) {
@@ -159,19 +172,27 @@ export default class Release extends Model {
159
172
  const royaltyRecipients = releaseData.account?.royaltyRecipients || releaseData.royaltyRecipients;
160
173
  for await (let recipient of royaltyRecipients) {
161
174
  try {
162
- if (recipient.recipientAuthority.toBase58() !== "11111111111111111111111111111111") {
175
+ if (recipient.recipientAuthority.toBase58() !==
176
+ "11111111111111111111111111111111") {
163
177
  const recipientAccount = await Account.findOrCreate(recipient.recipientAuthority.toBase58());
164
- const revenueShares = (await recipientAccount.$relatedQuery('revenueShares')).map(revenueShare => revenueShare.id);
165
- if (!revenueShares.includes(releaseRecord.id) && recipient.percentShare.toNumber() > 0) {
166
- await Account.relatedQuery('revenueShares').for(recipientAccount.id).relate(releaseRecord.id);
178
+ const revenueShares = (await recipientAccount.$relatedQuery("revenueShares")).map((revenueShare) => revenueShare.id);
179
+ if (!revenueShares.includes(releaseRecord.id) &&
180
+ recipient.percentShare.toNumber() > 0) {
181
+ await Account.relatedQuery("revenueShares")
182
+ .for(recipientAccount.id)
183
+ .relate(releaseRecord.id);
167
184
  }
168
- else if (revenueShares.includes(releaseRecord.id) && recipient.percentShare.toNumber() === 0) {
169
- await Account.relatedQuery('revenueShares').for(recipientAccount.id).unrelate().where('id', releaseRecord.id);
185
+ else if (revenueShares.includes(releaseRecord.id) &&
186
+ recipient.percentShare.toNumber() === 0) {
187
+ await Account.relatedQuery("revenueShares")
188
+ .for(recipientAccount.id)
189
+ .unrelate()
190
+ .where("id", releaseRecord.id);
170
191
  }
171
192
  }
172
193
  }
173
194
  catch (error) {
174
- console.log('error processing royaltyRecipients: ', error);
195
+ console.log("error processing royaltyRecipients: ", error);
175
196
  }
176
197
  }
177
198
  };
@@ -181,14 +202,15 @@ export default class Release extends Model {
181
202
  string = string.substring(0, 200);
182
203
  }
183
204
  const slug = string
184
- .normalize('NFKD').replace(/[\u0300-\u036F]/g, '') // remove accents and convert to closest ascii equivalent
205
+ .normalize("NFKD")
206
+ .replace(/[\u0300-\u036F]/g, "") // remove accents and convert to closest ascii equivalent
185
207
  .toLowerCase() // convert to lowercase
186
- .replace('-', '') // remove hyphens
187
- .replace(/ +/g, ' ') // remove spaces
188
- .replace(/ /g, '-') // replace spaces with hyphens
189
- .replace(/[^a-zA-Z0-9-]/g, '-') // replace non-alphanumeric characters with hyphens
190
- .replace(/-+/g, '-') // replace multiple hyphens with single hyphen
191
- .replace(/-$/, ''); // remove trailing hyphens
208
+ .replace("-", "") // remove hyphens
209
+ .replace(/ +/g, " ") // remove spaces
210
+ .replace(/ /g, "-") // replace spaces with hyphens
211
+ .replace(/[^a-zA-Z0-9-]/g, "-") // replace non-alphanumeric characters with hyphens
212
+ .replace(/-+/g, "-") // replace multiple hyphens with single hyphen
213
+ .replace(/-$/, ""); // remove trailing hyphens
192
214
  const existingRelease = await Release.query().findOne({ slug });
193
215
  if (existingRelease) {
194
216
  return `${slug}-${randomStringGenerator()}`;
@@ -196,13 +218,15 @@ export default class Release extends Model {
196
218
  return slug;
197
219
  };
198
220
  format = async () => {
199
- const publisher = await this.$relatedQuery('publisher');
200
- const publishedThroughHub = await this.$relatedQuery('publishedThroughHub');
221
+ const publisher = await this.$relatedQuery("publisher");
222
+ const publishedThroughHub = await this.$relatedQuery("publishedThroughHub");
201
223
  if (publishedThroughHub) {
202
224
  this.publishedThroughHub = publishedThroughHub.publicKey;
203
225
  this.hub = publishedThroughHub;
204
226
  delete this.hub.id;
205
- const authority = await this.hub.$relatedQuery('authority').select('publicKey');
227
+ const authority = await this.hub
228
+ .$relatedQuery("authority")
229
+ .select("publicKey");
206
230
  this.hub.authority = authority.publicKey;
207
231
  delete this.hub.authorityId;
208
232
  }
@@ -212,93 +236,93 @@ export default class Release extends Model {
212
236
  delete this.publisherId;
213
237
  delete this.hubId;
214
238
  delete this.id;
215
- stripHtmlIfNeeded(this.metadata, 'description');
239
+ stripHtmlIfNeeded(this.metadata, "description");
216
240
  };
217
241
  static relationMappings = () => ({
218
242
  publishedThroughHub: {
219
243
  relation: Model.BelongsToOneRelation,
220
244
  modelClass: Hub,
221
245
  join: {
222
- from: 'releases.hubId',
223
- to: 'hubs.id',
224
- },
246
+ from: "releases.hubId",
247
+ to: "hubs.id"
248
+ }
225
249
  },
226
250
  publisher: {
227
251
  relation: Model.HasOneRelation,
228
252
  modelClass: Account,
229
253
  join: {
230
- from: 'releases.publisherId',
231
- to: 'accounts.id',
232
- },
254
+ from: "releases.publisherId",
255
+ to: "accounts.id"
256
+ }
233
257
  },
234
258
  collectors: {
235
259
  relation: Model.ManyToManyRelation,
236
260
  modelClass: Account,
237
261
  join: {
238
- from: 'releases.id',
262
+ from: "releases.id",
239
263
  through: {
240
- from: 'releases_collected.releaseId',
241
- to: 'releases_collected.accountId',
264
+ from: "releases_collected.releaseId",
265
+ to: "releases_collected.accountId"
242
266
  },
243
- to: 'accounts.id',
244
- },
267
+ to: "accounts.id"
268
+ }
245
269
  },
246
270
  exchanges: {
247
271
  relation: Model.HasManyRelation,
248
272
  modelClass: Exchange,
249
273
  join: {
250
- from: 'releases.id',
251
- to: 'exchanges.releaseId',
252
- },
274
+ from: "releases.id",
275
+ to: "exchanges.releaseId"
276
+ }
253
277
  },
254
278
  hubs: {
255
279
  relation: Model.ManyToManyRelation,
256
280
  modelClass: Hub,
257
281
  join: {
258
- from: 'releases.id',
282
+ from: "releases.id",
259
283
  through: {
260
- from: 'hubs_releases.releaseId',
261
- to: 'hubs_releases.hubId',
262
- extra: ['hubReleasePublicKey'],
284
+ from: "hubs_releases.releaseId",
285
+ to: "hubs_releases.hubId",
286
+ extra: ["hubReleasePublicKey"]
263
287
  },
264
- to: 'hubs.id',
265
- },
288
+ to: "hubs.id"
289
+ }
266
290
  },
267
291
  posts: {
268
292
  relation: Model.ManyToManyRelation,
269
293
  modelClass: Post,
270
294
  join: {
271
- from: 'releases.id',
295
+ from: "releases.id",
272
296
  through: {
273
- from: 'posts_releases.releaseId',
274
- to: 'posts_releases.postId',
297
+ from: "posts_releases.releaseId",
298
+ to: "posts_releases.postId"
275
299
  },
276
- to: 'posts.id',
277
- },
300
+ to: "posts.id"
301
+ }
278
302
  },
279
303
  revenueShareRecipients: {
280
304
  relation: Model.ManyToManyRelation,
281
305
  modelClass: Account,
282
306
  join: {
283
- from: 'releases.id',
307
+ from: "releases.id",
284
308
  through: {
285
- from: 'releases_revenue_share.releaseId',
286
- to: 'releases_revenue_share.accountId',
309
+ from: "releases_revenue_share.releaseId",
310
+ to: "releases_revenue_share.accountId"
287
311
  },
288
- to: 'accounts.id',
289
- },
312
+ to: "accounts.id"
313
+ }
290
314
  },
291
315
  tags: {
292
316
  relation: Model.ManyToManyRelation,
293
317
  modelClass: Tag,
294
318
  join: {
295
- from: 'releases.id',
319
+ from: "releases.id",
296
320
  through: {
297
- from: 'tags_releases.releaseId',
298
- to: 'tags_releases.tagId',
321
+ from: "tags_releases.releaseId",
322
+ to: "tags_releases.tagId"
299
323
  },
300
- to: 'tags.id',
301
- },
302
- },
324
+ to: "tags.id"
325
+ }
326
+ }
303
327
  });
304
328
  }
@@ -20,6 +20,7 @@ class Transaction extends Model {
20
20
  type: {
21
21
  type: 'string',
22
22
  enum: [
23
+ 'ReleaseInitAndPurchase',
23
24
  'ReleaseInitV2',
24
25
  'ReleaseUpdate',
25
26
  'ExchangeAccept',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nina-protocol/nina-db",
3
- "version": "0.0.106",
3
+ "version": "0.0.108",
4
4
  "description": "",
5
5
  "source": "src/index.js",
6
6
  "main": "dist/index.js",
package/src/knexfile.js CHANGED
@@ -2,6 +2,11 @@
2
2
  * @type { Object.<string, import("knex").Knex.Config> }
3
3
  */
4
4
  import "dotenv/config.js";
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
5
10
 
6
11
  export default {
7
12
  development: {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ export async function up(knex) {
6
+ await knex.schema.alterTable('releases', (table) => {
7
+ table.string('mint').nullable().alter();
8
+ });
9
+ }
10
+
11
+ /**
12
+ * @param { import("knex").Knex } knex
13
+ * @returns { Promise<void> }
14
+ */
15
+ export async function down(knex) {
16
+ await knex.schema.alterTable('releases', (table) => {
17
+ table.string('mint').notNullable().alter();
18
+ });
19
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ export const up = function(knex) {
6
+ return knex.schema.table('releases', table => {
7
+ table.string('solanaAddress').nullable();
8
+ }).then(() => {
9
+ return knex('releases').whereNotNull('mint').update('solanaAddress', knex.ref('publicKey'));
10
+ });
11
+ };
12
+
13
+ /**
14
+ * @param { import("knex").Knex } knex
15
+ * @returns { Promise<void> }
16
+ */
17
+ export const down = function(knex) {
18
+ return knex.schema.table('releases', table => {
19
+ table.dropColumn('solanaAddress');
20
+ });
21
+ };
@@ -14,41 +14,41 @@ import { customAlphabet } from 'nanoid';
14
14
  import { getTokenMetadata } from '@solana/spl-token';
15
15
 
16
16
  const ensureHttps = (uri) => {
17
- if (!uri.startsWith('http://') && !uri.startsWith('https://')) {
17
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
18
18
  return `https://${uri}`;
19
19
  }
20
20
  return uri;
21
21
  };
22
22
 
23
- const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
23
+ const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
24
24
  const randomStringGenerator = customAlphabet(alphabet, 12);
25
25
  export default class Release extends Model {
26
- static tableName = 'releases';
27
-
28
- static idColumn = 'id';
26
+ static tableName = "releases";
27
+
28
+ static idColumn = "id";
29
29
  static jsonSchema = {
30
- type: 'object',
31
- required: ['publicKey', 'mint', 'metadata', 'datetime', 'slug', 'price'],
30
+ type: "object",
31
+ required: ["metadata", "datetime", "slug", "price"],
32
32
  properties: {
33
- publicKey: { type: 'string' },
34
- mint: { type: 'string' },
35
- slug: { type: 'string' },
33
+ publicKey: { type: ["string"] },
34
+ mint: { type: "string" },
35
+ slug: { type: "string" },
36
36
  metadata: {
37
- type: 'object',
38
- required: ['name', 'symbol', 'description', 'image', 'properties',],
37
+ type: "object",
38
+ required: ["name", "symbol", "description", "image", "properties"],
39
39
  properties: {
40
- name: { type: 'string' },
41
- symbol: { type: 'string' },
42
- description: { type: 'string' },
40
+ name: { type: "string" },
41
+ symbol: { type: "string" },
42
+ description: { type: "string" },
43
43
  properties: {
44
- type: 'object',
44
+ type: "object",
45
45
  properties: {
46
- artist: { type: 'string' },
47
- title: { type: 'string' },
48
- date: { type: 'string' },
49
- files: { type: 'array' },
50
- category: { type: 'string' },
51
- creators: { type: 'array' },
46
+ artist: { type: "string" },
47
+ title: { type: "string" },
48
+ date: { type: "string" },
49
+ files: { type: "array" },
50
+ category: { type: "string" },
51
+ creators: { type: "array" }
52
52
  }
53
53
  }
54
54
  }
@@ -58,7 +58,7 @@ export default class Release extends Model {
58
58
  },
59
59
  }
60
60
 
61
- static findOrCreate = async (publicKey, hubPublicKey=null, programId=process.env.NINA_PROGRAM_V2_ID) => {
61
+ static findOrCreate = async (publicKey, hubPublicKey=null, programId=process.env.NINA_PROGRAM_V2_ID, isLazy=false) => {
62
62
  try {
63
63
  console.log('Release.findOrCreate', publicKey, programId)
64
64
  let release = await Release.query().findOne({ publicKey });
@@ -84,13 +84,14 @@ export default class Release extends Model {
84
84
  const result = await program.account[programModelName].fetch(new anchor.web3.PublicKey(publicKey), 'confirmed')
85
85
  return result
86
86
  } catch (error) {
87
- console.log('error fetching release account', error)
88
- retry(error)
87
+ console.log("error fetching release account", error);
88
+ retry(error);
89
89
  }
90
- }, {
90
+ },
91
+ {
91
92
  retries: 50,
92
93
  minTimeout: 500,
93
- maxTimeout: 1500,
94
+ maxTimeout: 1500
94
95
  }
95
96
  )
96
97
 
@@ -101,17 +102,35 @@ export default class Release extends Model {
101
102
  metadataAccount = (await metaplex.nfts().findAllByMintList({mints: [releaseAccount.releaseMint]}, { commitment: 'confirmed' }))[0];
102
103
  }
103
104
  if (!metadataAccount) {
104
- throw new Error('No metadata account found for release - is not a complete release')
105
+ throw new Error(
106
+ "No metadata account found for release - is not a complete release"
107
+ );
105
108
  }
106
- let json
109
+ let json;
107
110
  try {
108
- json = (await axios.get(ensureHttps(metadataAccount.uri.replace('www.','').replace('arweave.net', 'gateway.irys.xyz')))).data
111
+ json = (
112
+ await axios.get(
113
+ ensureHttps(
114
+ metadataAccount.uri
115
+ .replace("www.", "")
116
+ .replace("arweave.net", "gateway.irys.xyz")
117
+ )
118
+ )
119
+ ).data;
109
120
  } catch (error) {
110
- json = (await axios.get(ensureHttps(metadataAccount.uri.replace('gateway.irys.xyz', 'arweave.net')))).data
121
+ json = (
122
+ await axios.get(
123
+ ensureHttps(
124
+ metadataAccount.uri.replace("gateway.irys.xyz", "arweave.net")
125
+ )
126
+ )
127
+ ).data;
111
128
  }
112
-
129
+
113
130
  const slug = await this.generateSlug(json);
114
- let publisher = await Account.findOrCreate(releaseAccount.authority.toBase58());
131
+ let publisher = await Account.findOrCreate(
132
+ releaseAccount.authority.toBase58()
133
+ );
115
134
  release = await this.createRelease({
116
135
  publicKey,
117
136
  mint: programId === process.env.NINA_PROGRAM_V2_ID ? releaseAccount.mint.toBase58() : releaseAccount.releaseMint.toBase58(),
@@ -121,28 +140,36 @@ export default class Release extends Model {
121
140
  publisherId: publisher.id,
122
141
  releaseAccount,
123
142
  programId,
143
+ solanaAddress: isLazy ? null : publicKey,
124
144
  });
125
-
145
+
126
146
  if (hubPublicKey) {
127
-
128
- const hub = await Hub.query().findOne({ publicKey: hubPublicKey })
129
- await release.$query().patch({ hubId: hub.id })
130
- await Hub.relatedQuery('releases').for(hub.id).patch({
131
- visible: true,
132
- }).where( {id: release.id });
147
+ const hub = await Hub.query().findOne({ publicKey: hubPublicKey });
148
+ await release.$query().patch({ hubId: hub.id });
149
+ await Hub.relatedQuery("releases")
150
+ .for(hub.id)
151
+ .patch({
152
+ visible: true
153
+ })
154
+ .where({ id: release.id });
133
155
  }
134
-
135
- return release;
156
+
157
+ return release;
136
158
  } catch (error) {
137
- console.log('error finding or creating release: ', error)
159
+ console.log("error finding or creating release: ", error);
138
160
  return null;
139
161
  }
140
- }
162
+ };
141
163
 
142
164
  static createRelease = async ({publicKey, mint, metadata, datetime, publisherId, releaseAccount, programId}) => {
143
165
  const slug = await this.generateSlug(metadata);
144
- const price = releaseAccount.account?.price?.toNumber() || releaseAccount?.price?.toNumber() || 0;
145
- const paymentMint = releaseAccount.account?.paymentMint.toBase58() || releaseAccount?.paymentMint.toBase58();
166
+ const price =
167
+ releaseAccount.account?.price?.toNumber() ||
168
+ releaseAccount?.price?.toNumber() ||
169
+ 0;
170
+ const paymentMint =
171
+ releaseAccount.account?.paymentMint.toBase58() ||
172
+ releaseAccount?.paymentMint.toBase58();
146
173
  const release = await Release.query().insertGraph({
147
174
  publicKey,
148
175
  mint,
@@ -158,7 +185,11 @@ export default class Release extends Model {
158
185
  if (metadata.properties.tags) {
159
186
  for await (let tag of metadata.properties.tags) {
160
187
  const tagRecord = await Tag.findOrCreate(tag);
161
- await Release.relatedQuery('tags').for(release.id).relate(tagRecord.id).onConflict(['tagId', 'releaseId']).ignore();
188
+ await Release.relatedQuery("tags")
189
+ .for(release.id)
190
+ .relate(tagRecord.id)
191
+ .onConflict(["tagId", "releaseId"])
192
+ .ignore();
162
193
  }
163
194
  }
164
195
  if (programId === process.env.NINA_PROGRAM_ID) {
@@ -166,156 +197,177 @@ export default class Release extends Model {
166
197
  }
167
198
  tweetNewRelease(metadata, publisherId, slug);
168
199
  return release;
169
- }
200
+ };
170
201
 
171
202
  static processRevenueShares = async (releaseData, releaseRecord) => {
172
- const royaltyRecipients = releaseData.account?.royaltyRecipients || releaseData.royaltyRecipients
203
+ const royaltyRecipients =
204
+ releaseData.account?.royaltyRecipients || releaseData.royaltyRecipients;
173
205
  for await (let recipient of royaltyRecipients) {
174
206
  try {
175
- if (recipient.recipientAuthority.toBase58() !== "11111111111111111111111111111111") {
176
- const recipientAccount = await Account.findOrCreate(recipient.recipientAuthority.toBase58());
177
- const revenueShares = (await recipientAccount.$relatedQuery('revenueShares')).map(revenueShare => revenueShare.id);
178
- if (!revenueShares.includes(releaseRecord.id) && recipient.percentShare.toNumber() > 0) {
179
- await Account.relatedQuery('revenueShares').for(recipientAccount.id).relate(releaseRecord.id);
180
- } else if (revenueShares.includes(releaseRecord.id) && recipient.percentShare.toNumber() === 0) {
181
- await Account.relatedQuery('revenueShares').for(recipientAccount.id).unrelate().where('id', releaseRecord.id);
207
+ if (
208
+ recipient.recipientAuthority.toBase58() !==
209
+ "11111111111111111111111111111111"
210
+ ) {
211
+ const recipientAccount = await Account.findOrCreate(
212
+ recipient.recipientAuthority.toBase58()
213
+ );
214
+ const revenueShares = (
215
+ await recipientAccount.$relatedQuery("revenueShares")
216
+ ).map((revenueShare) => revenueShare.id);
217
+ if (
218
+ !revenueShares.includes(releaseRecord.id) &&
219
+ recipient.percentShare.toNumber() > 0
220
+ ) {
221
+ await Account.relatedQuery("revenueShares")
222
+ .for(recipientAccount.id)
223
+ .relate(releaseRecord.id);
224
+ } else if (
225
+ revenueShares.includes(releaseRecord.id) &&
226
+ recipient.percentShare.toNumber() === 0
227
+ ) {
228
+ await Account.relatedQuery("revenueShares")
229
+ .for(recipientAccount.id)
230
+ .unrelate()
231
+ .where("id", releaseRecord.id);
182
232
  }
183
233
  }
184
234
  } catch (error) {
185
- console.log('error processing royaltyRecipients: ', error)
235
+ console.log("error processing royaltyRecipients: ", error);
186
236
  }
187
237
  }
188
- }
238
+ };
189
239
 
190
240
  static generateSlug = async (metadata) => {
191
- let string = metadata.name
241
+ let string = metadata.name;
192
242
  if (string.length > 200) {
193
243
  string = string.substring(0, 200);
194
244
  }
195
245
  const slug = string
196
- .normalize('NFKD').replace(/[\u0300-\u036F]/g, '') // remove accents and convert to closest ascii equivalent
246
+ .normalize("NFKD")
247
+ .replace(/[\u0300-\u036F]/g, "") // remove accents and convert to closest ascii equivalent
197
248
  .toLowerCase() // convert to lowercase
198
- .replace('-', '') // remove hyphens
199
- .replace(/ +/g, ' ') // remove spaces
200
- .replace(/ /g, '-') // replace spaces with hyphens
201
- .replace(/[^a-zA-Z0-9-]/g, '-') // replace non-alphanumeric characters with hyphens
202
- .replace(/-+/g,'-') // replace multiple hyphens with single hyphen
203
- .replace(/-$/, '') // remove trailing hyphens
249
+ .replace("-", "") // remove hyphens
250
+ .replace(/ +/g, " ") // remove spaces
251
+ .replace(/ /g, "-") // replace spaces with hyphens
252
+ .replace(/[^a-zA-Z0-9-]/g, "-") // replace non-alphanumeric characters with hyphens
253
+ .replace(/-+/g, "-") // replace multiple hyphens with single hyphen
254
+ .replace(/-$/, ""); // remove trailing hyphens
204
255
 
205
256
  const existingRelease = await Release.query().findOne({ slug });
206
257
  if (existingRelease) {
207
258
  return `${slug}-${randomStringGenerator()}`;
208
259
  }
209
260
  return slug;
210
-
211
- }
261
+ };
212
262
 
213
263
  format = async () => {
214
- const publisher = await this.$relatedQuery('publisher');
215
- const publishedThroughHub = await this.$relatedQuery('publishedThroughHub');
264
+ const publisher = await this.$relatedQuery("publisher");
265
+ const publishedThroughHub = await this.$relatedQuery("publishedThroughHub");
216
266
  if (publishedThroughHub) {
217
267
  this.publishedThroughHub = publishedThroughHub.publicKey;
218
268
  this.hub = publishedThroughHub;
219
269
  delete this.hub.id;
220
- const authority = await this.hub.$relatedQuery('authority').select('publicKey');
270
+ const authority = await this.hub
271
+ .$relatedQuery("authority")
272
+ .select("publicKey");
221
273
  this.hub.authority = authority.publicKey;
222
274
  delete this.hub.authorityId;
223
275
  }
224
276
  await publisher.format();
225
277
  this.publisher = publisher.publicKey;
226
278
  this.publisherAccount = publisher;
227
- delete this.publisherId
228
- delete this.hubId
229
- delete this.id
279
+ delete this.publisherId;
280
+ delete this.hubId;
281
+ delete this.id;
230
282
 
231
- stripHtmlIfNeeded(this.metadata, 'description');
232
- }
283
+ stripHtmlIfNeeded(this.metadata, "description");
284
+ };
233
285
 
234
286
  static relationMappings = () => ({
235
287
  publishedThroughHub: {
236
288
  relation: Model.BelongsToOneRelation,
237
289
  modelClass: Hub,
238
290
  join: {
239
- from: 'releases.hubId',
240
- to: 'hubs.id',
241
- },
291
+ from: "releases.hubId",
292
+ to: "hubs.id"
293
+ }
242
294
  },
243
295
  publisher: {
244
296
  relation: Model.HasOneRelation,
245
297
  modelClass: Account,
246
298
  join: {
247
- from: 'releases.publisherId',
248
- to: 'accounts.id',
249
- },
299
+ from: "releases.publisherId",
300
+ to: "accounts.id"
301
+ }
250
302
  },
251
303
  collectors: {
252
304
  relation: Model.ManyToManyRelation,
253
305
  modelClass: Account,
254
306
  join: {
255
- from: 'releases.id',
307
+ from: "releases.id",
256
308
  through: {
257
- from: 'releases_collected.releaseId',
258
- to: 'releases_collected.accountId',
309
+ from: "releases_collected.releaseId",
310
+ to: "releases_collected.accountId"
259
311
  },
260
- to: 'accounts.id',
261
- },
312
+ to: "accounts.id"
313
+ }
262
314
  },
263
315
  exchanges: {
264
316
  relation: Model.HasManyRelation,
265
317
  modelClass: Exchange,
266
318
  join: {
267
- from: 'releases.id',
268
- to: 'exchanges.releaseId',
269
- },
319
+ from: "releases.id",
320
+ to: "exchanges.releaseId"
321
+ }
270
322
  },
271
323
  hubs: {
272
324
  relation: Model.ManyToManyRelation,
273
325
  modelClass: Hub,
274
326
  join: {
275
- from: 'releases.id',
276
- through : {
277
- from: 'hubs_releases.releaseId',
278
- to: 'hubs_releases.hubId',
279
- extra: ['hubReleasePublicKey'],
327
+ from: "releases.id",
328
+ through: {
329
+ from: "hubs_releases.releaseId",
330
+ to: "hubs_releases.hubId",
331
+ extra: ["hubReleasePublicKey"]
280
332
  },
281
- to: 'hubs.id',
282
- },
333
+ to: "hubs.id"
334
+ }
283
335
  },
284
336
  posts: {
285
337
  relation: Model.ManyToManyRelation,
286
338
  modelClass: Post,
287
339
  join: {
288
- from: 'releases.id',
289
- through : {
290
- from: 'posts_releases.releaseId',
291
- to: 'posts_releases.postId',
340
+ from: "releases.id",
341
+ through: {
342
+ from: "posts_releases.releaseId",
343
+ to: "posts_releases.postId"
292
344
  },
293
- to: 'posts.id',
294
- },
345
+ to: "posts.id"
346
+ }
295
347
  },
296
348
  revenueShareRecipients: {
297
349
  relation: Model.ManyToManyRelation,
298
350
  modelClass: Account,
299
351
  join: {
300
- from: 'releases.id',
352
+ from: "releases.id",
301
353
  through: {
302
- from: 'releases_revenue_share.releaseId',
303
- to: 'releases_revenue_share.accountId',
354
+ from: "releases_revenue_share.releaseId",
355
+ to: "releases_revenue_share.accountId"
304
356
  },
305
- to: 'accounts.id',
306
- },
357
+ to: "accounts.id"
358
+ }
307
359
  },
308
360
  tags: {
309
361
  relation: Model.ManyToManyRelation,
310
362
  modelClass: Tag,
311
363
  join: {
312
- from: 'releases.id',
364
+ from: "releases.id",
313
365
  through: {
314
- from: 'tags_releases.releaseId',
315
- to: 'tags_releases.tagId',
366
+ from: "tags_releases.releaseId",
367
+ to: "tags_releases.tagId"
316
368
  },
317
- to: 'tags.id',
318
- },
319
- },
320
- })
369
+ to: "tags.id"
370
+ }
371
+ }
372
+ });
321
373
  }
@@ -21,6 +21,7 @@ class Transaction extends Model {
21
21
  type: {
22
22
  type: 'string',
23
23
  enum: [
24
+ 'ReleaseInitAndPurchase',
24
25
  'ReleaseInitV2',
25
26
  'ReleaseUpdate',
26
27
  'ExchangeAccept',