@wopr-network/platform-core 1.49.5 → 1.50.0

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.
@@ -67,6 +67,7 @@ function mockDeps() {
67
67
  displayName: "Bitcoin",
68
68
  contractAddress: null,
69
69
  confirmations: 6,
70
+ iconUrl: null,
70
71
  },
71
72
  {
72
73
  id: "base-usdc",
@@ -76,6 +77,7 @@ function mockDeps() {
76
77
  displayName: "USDC on Base",
77
78
  contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
78
79
  confirmations: 12,
80
+ iconUrl: null,
79
81
  },
80
82
  ]),
81
83
  listAll: vi.fn(),
@@ -91,12 +93,14 @@ function mockDeps() {
91
93
  oracleAddress: "0x64c911996D3c6aC71f9b455B1E8E7266BcbD848F",
92
94
  xpub: null,
93
95
  displayOrder: 0,
96
+ iconUrl: null,
94
97
  enabled: true,
95
98
  rpcUrl: null,
96
99
  }),
97
100
  listByType: vi.fn(),
98
101
  upsert: vi.fn().mockResolvedValue(undefined),
99
102
  setEnabled: vi.fn().mockResolvedValue(undefined),
103
+ patchMetadata: vi.fn().mockResolvedValue(true),
100
104
  };
101
105
  return {
102
106
  db: createMockDb(),
@@ -46,6 +46,7 @@ export interface ChainInfo {
46
46
  displayName: string;
47
47
  contractAddress: string | null;
48
48
  confirmations: number;
49
+ iconUrl: string | null;
49
50
  }
50
51
  /**
51
52
  * Client for the shared crypto key server.
@@ -218,6 +218,7 @@ export function createKeyServerApp(deps) {
218
218
  displayName: m.displayName,
219
219
  contractAddress: m.contractAddress,
220
220
  confirmations: m.confirmations,
221
+ iconUrl: m.iconUrl,
221
222
  })));
222
223
  });
223
224
  // --- Admin API ---
@@ -282,7 +283,8 @@ export function createKeyServerApp(deps) {
282
283
  decimals: body.decimals,
283
284
  displayName: body.display_name ?? `${body.token} on ${body.network}`,
284
285
  enabled: true,
285
- displayOrder: 0,
286
+ displayOrder: body.display_order ?? 0,
287
+ iconUrl: body.icon_url ?? null,
286
288
  rpcUrl: body.rpc_url,
287
289
  oracleAddress: body.oracle_address ?? null,
288
290
  xpub: body.xpub,
@@ -291,6 +293,19 @@ export function createKeyServerApp(deps) {
291
293
  });
292
294
  return c.json({ id: body.id, path: `m/44'/${body.coin_type}'/${body.account_index}'` }, 201);
293
295
  });
296
+ /** PATCH /admin/chains/:id — update metadata (icon_url, display_order, display_name) */
297
+ app.patch("/admin/chains/:id", async (c) => {
298
+ const id = c.req.param("id");
299
+ const body = await c.req.json();
300
+ const updated = await deps.methodStore.patchMetadata(id, {
301
+ iconUrl: body.icon_url,
302
+ displayOrder: body.display_order,
303
+ displayName: body.display_name,
304
+ });
305
+ if (!updated)
306
+ return c.json({ id, updated: false }, 200);
307
+ return c.json({ id, updated: true });
308
+ });
294
309
  /** DELETE /admin/chains/:id — soft disable */
295
310
  app.delete("/admin/chains/:id", async (c) => {
296
311
  await deps.methodStore.setEnabled(c.req.param("id"), false);
@@ -9,6 +9,7 @@ export interface PaymentMethodRecord {
9
9
  displayName: string;
10
10
  enabled: boolean;
11
11
  displayOrder: number;
12
+ iconUrl: string | null;
12
13
  rpcUrl: string | null;
13
14
  oracleAddress: string | null;
14
15
  xpub: string | null;
@@ -28,6 +29,12 @@ export interface IPaymentMethodStore {
28
29
  upsert(method: PaymentMethodRecord): Promise<void>;
29
30
  /** Enable or disable a payment method (admin). */
30
31
  setEnabled(id: string, enabled: boolean): Promise<void>;
32
+ /** Partial update of metadata fields (no read-modify-write needed). */
33
+ patchMetadata(id: string, patch: {
34
+ iconUrl?: string | null;
35
+ displayOrder?: number;
36
+ displayName?: string;
37
+ }): Promise<boolean>;
31
38
  }
32
39
  export declare class DrizzlePaymentMethodStore implements IPaymentMethodStore {
33
40
  private readonly db;
@@ -38,4 +45,9 @@ export declare class DrizzlePaymentMethodStore implements IPaymentMethodStore {
38
45
  listByType(type: string): Promise<PaymentMethodRecord[]>;
39
46
  upsert(method: PaymentMethodRecord): Promise<void>;
40
47
  setEnabled(id: string, enabled: boolean): Promise<void>;
48
+ patchMetadata(id: string, patch: {
49
+ iconUrl?: string | null;
50
+ displayOrder?: number;
51
+ displayName?: string;
52
+ }): Promise<boolean>;
41
53
  }
@@ -42,6 +42,7 @@ export class DrizzlePaymentMethodStore {
42
42
  displayName: method.displayName,
43
43
  enabled: method.enabled,
44
44
  displayOrder: method.displayOrder,
45
+ iconUrl: method.iconUrl,
45
46
  rpcUrl: method.rpcUrl,
46
47
  oracleAddress: method.oracleAddress,
47
48
  xpub: method.xpub,
@@ -59,6 +60,7 @@ export class DrizzlePaymentMethodStore {
59
60
  displayName: method.displayName,
60
61
  enabled: method.enabled,
61
62
  displayOrder: method.displayOrder,
63
+ iconUrl: method.iconUrl,
62
64
  rpcUrl: method.rpcUrl,
63
65
  oracleAddress: method.oracleAddress,
64
66
  xpub: method.xpub,
@@ -70,6 +72,19 @@ export class DrizzlePaymentMethodStore {
70
72
  async setEnabled(id, enabled) {
71
73
  await this.db.update(paymentMethods).set({ enabled }).where(eq(paymentMethods.id, id));
72
74
  }
75
+ async patchMetadata(id, patch) {
76
+ const set = {};
77
+ if (patch.iconUrl !== undefined)
78
+ set.iconUrl = patch.iconUrl;
79
+ if (patch.displayOrder !== undefined)
80
+ set.displayOrder = patch.displayOrder;
81
+ if (patch.displayName !== undefined)
82
+ set.displayName = patch.displayName;
83
+ if (Object.keys(set).length === 0)
84
+ return false;
85
+ const result = (await this.db.update(paymentMethods).set(set).where(eq(paymentMethods.id, id)));
86
+ return result.rowCount > 0;
87
+ }
73
88
  }
74
89
  function toRecord(row) {
75
90
  return {
@@ -82,6 +97,7 @@ function toRecord(row) {
82
97
  displayName: row.displayName,
83
98
  enabled: row.enabled,
84
99
  displayOrder: row.displayOrder,
100
+ iconUrl: row.iconUrl,
85
101
  rpcUrl: row.rpcUrl,
86
102
  oracleAddress: row.oracleAddress,
87
103
  xpub: row.xpub,
@@ -597,6 +597,23 @@ export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithCo
597
597
  identity: undefined;
598
598
  generated: undefined;
599
599
  }, {}, {}>;
600
+ iconUrl: import("drizzle-orm/pg-core").PgColumn<{
601
+ name: "icon_url";
602
+ tableName: "payment_methods";
603
+ dataType: "string";
604
+ columnType: "PgText";
605
+ data: string;
606
+ driverParam: string;
607
+ notNull: false;
608
+ hasDefault: false;
609
+ isPrimaryKey: false;
610
+ isAutoincrement: false;
611
+ hasRuntimeDefault: false;
612
+ enumValues: [string, ...string[]];
613
+ baseColumn: never;
614
+ identity: undefined;
615
+ generated: undefined;
616
+ }, {}, {}>;
600
617
  rpcUrl: import("drizzle-orm/pg-core").PgColumn<{
601
618
  name: "rpc_url";
602
619
  tableName: "payment_methods";
@@ -71,6 +71,7 @@ export const paymentMethods = pgTable("payment_methods", {
71
71
  displayName: text("display_name").notNull(),
72
72
  enabled: boolean("enabled").notNull().default(true),
73
73
  displayOrder: integer("display_order").notNull().default(0),
74
+ iconUrl: text("icon_url"),
74
75
  rpcUrl: text("rpc_url"), // chain node RPC endpoint
75
76
  oracleAddress: text("oracle_address"), // Chainlink feed address for price (null = 1:1 stablecoin)
76
77
  xpub: text("xpub"), // HD wallet extended public key for deposit address derivation
@@ -0,0 +1,4 @@
1
+ -- Add icon_url column to payment_methods.
2
+ -- Stores URL for chain/token icon displayed in checkout UI.
3
+
4
+ ALTER TABLE "payment_methods" ADD COLUMN IF NOT EXISTS "icon_url" text;
@@ -134,6 +134,13 @@
134
134
  "when": 1743177600000,
135
135
  "tag": "0018_address_type_column",
136
136
  "breakpoints": true
137
+ },
138
+ {
139
+ "idx": 19,
140
+ "version": "7",
141
+ "when": 1743264000000,
142
+ "tag": "0019_icon_url_column",
143
+ "breakpoints": true
137
144
  }
138
145
  ]
139
146
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wopr-network/platform-core",
3
- "version": "1.49.5",
3
+ "version": "1.50.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -76,6 +76,7 @@ function mockDeps(): KeyServerDeps & {
76
76
  displayName: "Bitcoin",
77
77
  contractAddress: null,
78
78
  confirmations: 6,
79
+ iconUrl: null,
79
80
  },
80
81
  {
81
82
  id: "base-usdc",
@@ -85,6 +86,7 @@ function mockDeps(): KeyServerDeps & {
85
86
  displayName: "USDC on Base",
86
87
  contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
87
88
  confirmations: 12,
89
+ iconUrl: null,
88
90
  },
89
91
  ]),
90
92
  listAll: vi.fn(),
@@ -100,12 +102,14 @@ function mockDeps(): KeyServerDeps & {
100
102
  oracleAddress: "0x64c911996D3c6aC71f9b455B1E8E7266BcbD848F",
101
103
  xpub: null,
102
104
  displayOrder: 0,
105
+ iconUrl: null,
103
106
  enabled: true,
104
107
  rpcUrl: null,
105
108
  }),
106
109
  listByType: vi.fn(),
107
110
  upsert: vi.fn().mockResolvedValue(undefined),
108
111
  setEnabled: vi.fn().mockResolvedValue(undefined),
112
+ patchMetadata: vi.fn().mockResolvedValue(true),
109
113
  };
110
114
  return {
111
115
  db: createMockDb() as never,
@@ -51,6 +51,7 @@ export interface ChainInfo {
51
51
  displayName: string;
52
52
  contractAddress: string | null;
53
53
  confirmations: number;
54
+ iconUrl: string | null;
54
55
  }
55
56
 
56
57
  /**
@@ -266,6 +266,7 @@ export function createKeyServerApp(deps: KeyServerDeps): Hono {
266
266
  displayName: m.displayName,
267
267
  contractAddress: m.contractAddress,
268
268
  confirmations: m.confirmations,
269
+ iconUrl: m.iconUrl,
269
270
  })),
270
271
  );
271
272
  });
@@ -328,6 +329,8 @@ export function createKeyServerApp(deps: KeyServerDeps): Hono {
328
329
  display_name?: string;
329
330
  oracle_address?: string;
330
331
  address_type?: string;
332
+ icon_url?: string;
333
+ display_order?: number;
331
334
  }>();
332
335
 
333
336
  if (!body.id || !body.xpub || !body.token) {
@@ -362,7 +365,8 @@ export function createKeyServerApp(deps: KeyServerDeps): Hono {
362
365
  decimals: body.decimals,
363
366
  displayName: body.display_name ?? `${body.token} on ${body.network}`,
364
367
  enabled: true,
365
- displayOrder: 0,
368
+ displayOrder: body.display_order ?? 0,
369
+ iconUrl: body.icon_url ?? null,
366
370
  rpcUrl: body.rpc_url,
367
371
  oracleAddress: body.oracle_address ?? null,
368
372
  xpub: body.xpub,
@@ -373,6 +377,25 @@ export function createKeyServerApp(deps: KeyServerDeps): Hono {
373
377
  return c.json({ id: body.id, path: `m/44'/${body.coin_type}'/${body.account_index}'` }, 201);
374
378
  });
375
379
 
380
+ /** PATCH /admin/chains/:id — update metadata (icon_url, display_order, display_name) */
381
+ app.patch("/admin/chains/:id", async (c) => {
382
+ const id = c.req.param("id");
383
+ const body = await c.req.json<{
384
+ icon_url?: string | null;
385
+ display_order?: number;
386
+ display_name?: string;
387
+ }>();
388
+
389
+ const updated = await deps.methodStore.patchMetadata(id, {
390
+ iconUrl: body.icon_url,
391
+ displayOrder: body.display_order,
392
+ displayName: body.display_name,
393
+ });
394
+
395
+ if (!updated) return c.json({ id, updated: false }, 200);
396
+ return c.json({ id, updated: true });
397
+ });
398
+
376
399
  /** DELETE /admin/chains/:id — soft disable */
377
400
  app.delete("/admin/chains/:id", async (c) => {
378
401
  await deps.methodStore.setEnabled(c.req.param("id"), false);
@@ -12,6 +12,7 @@ export interface PaymentMethodRecord {
12
12
  displayName: string;
13
13
  enabled: boolean;
14
14
  displayOrder: number;
15
+ iconUrl: string | null;
15
16
  rpcUrl: string | null;
16
17
  oracleAddress: string | null;
17
18
  xpub: string | null;
@@ -32,6 +33,11 @@ export interface IPaymentMethodStore {
32
33
  upsert(method: PaymentMethodRecord): Promise<void>;
33
34
  /** Enable or disable a payment method (admin). */
34
35
  setEnabled(id: string, enabled: boolean): Promise<void>;
36
+ /** Partial update of metadata fields (no read-modify-write needed). */
37
+ patchMetadata(
38
+ id: string,
39
+ patch: { iconUrl?: string | null; displayOrder?: number; displayName?: string },
40
+ ): Promise<boolean>;
35
41
  }
36
42
 
37
43
  export class DrizzlePaymentMethodStore implements IPaymentMethodStore {
@@ -78,6 +84,7 @@ export class DrizzlePaymentMethodStore implements IPaymentMethodStore {
78
84
  displayName: method.displayName,
79
85
  enabled: method.enabled,
80
86
  displayOrder: method.displayOrder,
87
+ iconUrl: method.iconUrl,
81
88
  rpcUrl: method.rpcUrl,
82
89
  oracleAddress: method.oracleAddress,
83
90
  xpub: method.xpub,
@@ -95,6 +102,7 @@ export class DrizzlePaymentMethodStore implements IPaymentMethodStore {
95
102
  displayName: method.displayName,
96
103
  enabled: method.enabled,
97
104
  displayOrder: method.displayOrder,
105
+ iconUrl: method.iconUrl,
98
106
  rpcUrl: method.rpcUrl,
99
107
  oracleAddress: method.oracleAddress,
100
108
  xpub: method.xpub,
@@ -107,6 +115,21 @@ export class DrizzlePaymentMethodStore implements IPaymentMethodStore {
107
115
  async setEnabled(id: string, enabled: boolean): Promise<void> {
108
116
  await this.db.update(paymentMethods).set({ enabled }).where(eq(paymentMethods.id, id));
109
117
  }
118
+
119
+ async patchMetadata(
120
+ id: string,
121
+ patch: { iconUrl?: string | null; displayOrder?: number; displayName?: string },
122
+ ): Promise<boolean> {
123
+ const set: Record<string, unknown> = {};
124
+ if (patch.iconUrl !== undefined) set.iconUrl = patch.iconUrl;
125
+ if (patch.displayOrder !== undefined) set.displayOrder = patch.displayOrder;
126
+ if (patch.displayName !== undefined) set.displayName = patch.displayName;
127
+ if (Object.keys(set).length === 0) return false;
128
+ const result = (await this.db.update(paymentMethods).set(set).where(eq(paymentMethods.id, id))) as {
129
+ rowCount: number;
130
+ };
131
+ return result.rowCount > 0;
132
+ }
110
133
  }
111
134
 
112
135
  function toRecord(row: typeof paymentMethods.$inferSelect): PaymentMethodRecord {
@@ -120,6 +143,7 @@ function toRecord(row: typeof paymentMethods.$inferSelect): PaymentMethodRecord
120
143
  displayName: row.displayName,
121
144
  enabled: row.enabled,
122
145
  displayOrder: row.displayOrder,
146
+ iconUrl: row.iconUrl,
123
147
  rpcUrl: row.rpcUrl,
124
148
  oracleAddress: row.oracleAddress,
125
149
  xpub: row.xpub,
@@ -78,6 +78,7 @@ export const paymentMethods = pgTable("payment_methods", {
78
78
  displayName: text("display_name").notNull(),
79
79
  enabled: boolean("enabled").notNull().default(true),
80
80
  displayOrder: integer("display_order").notNull().default(0),
81
+ iconUrl: text("icon_url"),
81
82
  rpcUrl: text("rpc_url"), // chain node RPC endpoint
82
83
  oracleAddress: text("oracle_address"), // Chainlink feed address for price (null = 1:1 stablecoin)
83
84
  xpub: text("xpub"), // HD wallet extended public key for deposit address derivation