@yallet/rwa-sdk 0.2.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.
- package/README.md +365 -0
- package/dist/credential-nft-server.d.ts +75 -0
- package/dist/credential-nft-server.d.ts.map +1 -0
- package/dist/credential-nft-server.js +122 -0
- package/dist/credential-nft-server.js.map +1 -0
- package/dist/credential-nft.d.ts +31 -0
- package/dist/credential-nft.d.ts.map +1 -0
- package/dist/credential-nft.js +38 -0
- package/dist/credential-nft.js.map +1 -0
- package/dist/encryption.d.ts +107 -0
- package/dist/encryption.d.ts.map +1 -0
- package/dist/encryption.js +176 -0
- package/dist/encryption.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata.d.ts +161 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +180 -0
- package/dist/metadata.js.map +1 -0
- package/dist/minting.d.ts +63 -0
- package/dist/minting.d.ts.map +1 -0
- package/dist/minting.js +194 -0
- package/dist/minting.js.map +1 -0
- package/dist/payloads.d.ts +120 -0
- package/dist/payloads.d.ts.map +1 -0
- package/dist/payloads.js +118 -0
- package/dist/payloads.js.map +1 -0
- package/dist/registry.d.ts +77 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +173 -0
- package/dist/registry.js.map +1 -0
- package/dist/sdk.d.ts +158 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +346 -0
- package/dist/sdk.js.map +1 -0
- package/dist/storage.d.ts +80 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +284 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +278 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
- package/src/credential-nft-server.ts +201 -0
- package/src/credential-nft.ts +64 -0
- package/src/encryption.ts +308 -0
- package/src/index.ts +151 -0
- package/src/metadata.ts +336 -0
- package/src/minting.ts +266 -0
- package/src/payloads.ts +238 -0
- package/src/registry.ts +236 -0
- package/src/sdk.ts +507 -0
- package/src/storage.ts +364 -0
- package/src/types.ts +318 -0
- package/wasm/README.md +33 -0
package/src/minting.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yallet RWA SDK - Minting Module
|
|
3
|
+
*
|
|
4
|
+
* Handles cNFT minting using Metaplex Bubblegum
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createUmi,
|
|
9
|
+
publicKey,
|
|
10
|
+
keypairIdentity,
|
|
11
|
+
type Umi,
|
|
12
|
+
type PublicKey,
|
|
13
|
+
type Keypair,
|
|
14
|
+
} from '@metaplex-foundation/umi';
|
|
15
|
+
import { createUmi as createUmiDefaults } from '@metaplex-foundation/umi-bundle-defaults';
|
|
16
|
+
import {
|
|
17
|
+
mplBubblegum,
|
|
18
|
+
mintV1,
|
|
19
|
+
findLeafAssetIdPda,
|
|
20
|
+
type MetadataArgsArgs,
|
|
21
|
+
} from '@metaplex-foundation/mpl-bubblegum';
|
|
22
|
+
import { Connection, PublicKey as SolanaPublicKey } from '@solana/web3.js';
|
|
23
|
+
|
|
24
|
+
import type {
|
|
25
|
+
TreeConfig,
|
|
26
|
+
MintResult,
|
|
27
|
+
Signer,
|
|
28
|
+
SDKConfig,
|
|
29
|
+
} from './types.js';
|
|
30
|
+
import { AssetType } from './types.js';
|
|
31
|
+
|
|
32
|
+
// ======================================
|
|
33
|
+
// Minter Class
|
|
34
|
+
// ======================================
|
|
35
|
+
|
|
36
|
+
export class CNFTMinter {
|
|
37
|
+
private umi: Umi;
|
|
38
|
+
private connection: Connection;
|
|
39
|
+
private config: SDKConfig;
|
|
40
|
+
private treeConfig: TreeConfig | null = null;
|
|
41
|
+
|
|
42
|
+
constructor(config: SDKConfig) {
|
|
43
|
+
this.config = config;
|
|
44
|
+
this.connection = new Connection(config.rpcEndpoint);
|
|
45
|
+
|
|
46
|
+
// Initialize Umi
|
|
47
|
+
this.umi = createUmiDefaults(config.rpcEndpoint).use(mplBubblegum());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set the signer for minting transactions
|
|
52
|
+
*
|
|
53
|
+
* @param signer - Signer with publicKey and signTransaction
|
|
54
|
+
*/
|
|
55
|
+
setSigner(keypair: Keypair): void {
|
|
56
|
+
this.umi = this.umi.use(keypairIdentity(keypair));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Set the Merkle tree configuration
|
|
61
|
+
*
|
|
62
|
+
* @param treeConfig - Tree configuration
|
|
63
|
+
*/
|
|
64
|
+
setTreeConfig(treeConfig: TreeConfig): void {
|
|
65
|
+
this.treeConfig = treeConfig;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get or fetch tree configuration
|
|
70
|
+
*/
|
|
71
|
+
async getTreeConfig(): Promise<TreeConfig | null> {
|
|
72
|
+
if (this.treeConfig) {
|
|
73
|
+
return this.treeConfig;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Try to fetch from configured address
|
|
77
|
+
if (this.config.merkleTreeAddress) {
|
|
78
|
+
// For now, return a basic config
|
|
79
|
+
// In production, you'd fetch the actual tree data
|
|
80
|
+
return {
|
|
81
|
+
treeAddress: this.config.merkleTreeAddress,
|
|
82
|
+
treeAuthority: this.config.merkleTreeAuthority || this.config.merkleTreeAddress,
|
|
83
|
+
maxDepth: 14,
|
|
84
|
+
capacity: 16384,
|
|
85
|
+
used: 0,
|
|
86
|
+
remaining: 16384,
|
|
87
|
+
usagePercent: 0,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Try to fetch from backend API
|
|
92
|
+
if (this.config.backendApiUrl) {
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch(
|
|
95
|
+
`${this.config.backendApiUrl}/api/cnft/active-tree?network=${this.config.network}`
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (response.ok) {
|
|
99
|
+
const data = (await response.json()) as {
|
|
100
|
+
available?: boolean;
|
|
101
|
+
tree?: {
|
|
102
|
+
address: string;
|
|
103
|
+
authority: string;
|
|
104
|
+
maxDepth: number;
|
|
105
|
+
capacity: number;
|
|
106
|
+
used: number;
|
|
107
|
+
remaining: number;
|
|
108
|
+
usagePercent: string;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
if (data.available && data.tree) {
|
|
112
|
+
this.treeConfig = {
|
|
113
|
+
treeAddress: data.tree.address,
|
|
114
|
+
treeAuthority: data.tree.authority,
|
|
115
|
+
maxDepth: data.tree.maxDepth,
|
|
116
|
+
capacity: data.tree.capacity,
|
|
117
|
+
used: data.tree.used,
|
|
118
|
+
remaining: data.tree.remaining,
|
|
119
|
+
usagePercent: parseFloat(data.tree.usagePercent),
|
|
120
|
+
};
|
|
121
|
+
return this.treeConfig;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.warn('Failed to fetch tree from backend:', err);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Mint a compressed NFT
|
|
134
|
+
*
|
|
135
|
+
* @param params - Minting parameters
|
|
136
|
+
* @returns Mint result
|
|
137
|
+
*/
|
|
138
|
+
async mint(params: {
|
|
139
|
+
metadataUri: string;
|
|
140
|
+
name: string;
|
|
141
|
+
symbol?: string;
|
|
142
|
+
recipient: string;
|
|
143
|
+
sellerFeeBasisPoints?: number;
|
|
144
|
+
creators?: Array<{ address: string; share: number }>;
|
|
145
|
+
}): Promise<MintResult> {
|
|
146
|
+
const {
|
|
147
|
+
metadataUri,
|
|
148
|
+
name,
|
|
149
|
+
symbol = 'YRWA',
|
|
150
|
+
recipient,
|
|
151
|
+
sellerFeeBasisPoints = 0,
|
|
152
|
+
creators = [],
|
|
153
|
+
} = params;
|
|
154
|
+
|
|
155
|
+
// Get tree configuration
|
|
156
|
+
const treeConfig = await this.getTreeConfig();
|
|
157
|
+
|
|
158
|
+
if (!treeConfig) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
error: 'No Merkle tree configured. Please set a tree address or backend API.',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const merkleTree = publicKey(treeConfig.treeAddress);
|
|
167
|
+
const leafOwner = publicKey(recipient);
|
|
168
|
+
|
|
169
|
+
// Prepare metadata args
|
|
170
|
+
const metadataArgs: MetadataArgsArgs = {
|
|
171
|
+
name,
|
|
172
|
+
symbol,
|
|
173
|
+
uri: metadataUri,
|
|
174
|
+
sellerFeeBasisPoints,
|
|
175
|
+
primarySaleHappened: false,
|
|
176
|
+
isMutable: true,
|
|
177
|
+
editionNonce: null,
|
|
178
|
+
tokenStandard: null,
|
|
179
|
+
collection: null,
|
|
180
|
+
uses: null,
|
|
181
|
+
tokenProgramVersion: 0, // Original
|
|
182
|
+
creators: creators.map((c) => ({
|
|
183
|
+
address: publicKey(c.address),
|
|
184
|
+
verified: false,
|
|
185
|
+
share: c.share,
|
|
186
|
+
})),
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Build and send mint instruction
|
|
190
|
+
const { signature } = await mintV1(this.umi, {
|
|
191
|
+
merkleTree,
|
|
192
|
+
leafOwner,
|
|
193
|
+
metadata: metadataArgs,
|
|
194
|
+
}).sendAndConfirm(this.umi);
|
|
195
|
+
|
|
196
|
+
// Get the leaf index to compute asset ID
|
|
197
|
+
// Note: In production, you'd parse the transaction logs
|
|
198
|
+
const signatureStr = Buffer.from(signature).toString('base64');
|
|
199
|
+
|
|
200
|
+
// Notify backend of successful mint (if configured)
|
|
201
|
+
if (this.config.backendApiUrl) {
|
|
202
|
+
try {
|
|
203
|
+
await fetch(`${this.config.backendApiUrl}/api/cnft/mint-complete`, {
|
|
204
|
+
method: 'POST',
|
|
205
|
+
headers: { 'Content-Type': 'application/json' },
|
|
206
|
+
body: JSON.stringify({
|
|
207
|
+
network: this.config.network,
|
|
208
|
+
treeAddress: treeConfig.treeAddress,
|
|
209
|
+
}),
|
|
210
|
+
});
|
|
211
|
+
} catch (err) {
|
|
212
|
+
console.warn('Failed to notify backend of mint:', err);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
signature: signatureStr,
|
|
219
|
+
// Asset ID would be computed from the leaf index
|
|
220
|
+
// For now, we return the signature
|
|
221
|
+
};
|
|
222
|
+
} catch (err) {
|
|
223
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
error: `Minting failed: ${errorMessage}`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ======================================
|
|
233
|
+
// Helper Functions
|
|
234
|
+
// ======================================
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Create a new CNFTMinter instance
|
|
238
|
+
*
|
|
239
|
+
* @param config - SDK configuration
|
|
240
|
+
* @returns CNFTMinter instance
|
|
241
|
+
*/
|
|
242
|
+
export function createMinter(config: SDKConfig): CNFTMinter {
|
|
243
|
+
return new CNFTMinter(config);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Estimate minting cost
|
|
248
|
+
*
|
|
249
|
+
* @param connection - Solana connection
|
|
250
|
+
* @returns Estimated cost in lamports
|
|
251
|
+
*/
|
|
252
|
+
export async function estimateMintCost(connection: Connection): Promise<number> {
|
|
253
|
+
// cNFT minting is very cheap - typically around 0.00001 SOL
|
|
254
|
+
// The main cost is the priority fee
|
|
255
|
+
const recentPrioritizationFees = await connection.getRecentPrioritizationFees();
|
|
256
|
+
const avgFee = recentPrioritizationFees.reduce(
|
|
257
|
+
(sum, fee) => sum + fee.prioritizationFee,
|
|
258
|
+
0
|
|
259
|
+
) / (recentPrioritizationFees.length || 1);
|
|
260
|
+
|
|
261
|
+
// Base transaction fee + priority fee
|
|
262
|
+
const baseFee = 5000; // 0.000005 SOL
|
|
263
|
+
const priorityFee = Math.ceil(avgFee);
|
|
264
|
+
|
|
265
|
+
return baseFee + priorityFee;
|
|
266
|
+
}
|
package/src/payloads.ts
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yallet RWA SDK - Payload builders
|
|
3
|
+
*
|
|
4
|
+
* Build JSON payloads that match chrome-extension decryption expectations
|
|
5
|
+
* for invoice / statements / receipts / files / messages / pictures.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
InvoiceData,
|
|
10
|
+
InvoiceItem,
|
|
11
|
+
NoteData,
|
|
12
|
+
FileData,
|
|
13
|
+
PhotoData,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
|
|
16
|
+
// ======================================
|
|
17
|
+
// Extension-compatible types (output)
|
|
18
|
+
// ======================================
|
|
19
|
+
|
|
20
|
+
/** Line item shape expected by extension */
|
|
21
|
+
export interface ExtensionLineItem {
|
|
22
|
+
name: string;
|
|
23
|
+
price: number;
|
|
24
|
+
qty: number;
|
|
25
|
+
unit: string;
|
|
26
|
+
total: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Invoice payload bundle (extension decrypts and uses invoice + optional pdf) */
|
|
30
|
+
export interface InvoicePayloadBundle {
|
|
31
|
+
invoice: ExtensionInvoicePayload;
|
|
32
|
+
pdf?: string;
|
|
33
|
+
senderProfile?: Record<string, unknown>;
|
|
34
|
+
recipientProfile?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ExtensionInvoicePayload {
|
|
38
|
+
id?: string;
|
|
39
|
+
uuid?: string;
|
|
40
|
+
invoiceNo: string;
|
|
41
|
+
type?: 'sent' | 'received';
|
|
42
|
+
status?: string;
|
|
43
|
+
billTo?: string;
|
|
44
|
+
poNo?: string;
|
|
45
|
+
dueDate?: string;
|
|
46
|
+
lineItems: ExtensionLineItem[];
|
|
47
|
+
discountRate?: number;
|
|
48
|
+
discountAmount?: number;
|
|
49
|
+
taxRate?: number;
|
|
50
|
+
subtotal: number;
|
|
51
|
+
taxAmount?: number;
|
|
52
|
+
total: number;
|
|
53
|
+
currency: string;
|
|
54
|
+
paymentMethod?: string;
|
|
55
|
+
paymentTerms?: string;
|
|
56
|
+
notePayee?: string;
|
|
57
|
+
createdAt: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** File payload (extension expects name, fileData base64, type, etc.) */
|
|
61
|
+
export interface ExtensionFilePayload {
|
|
62
|
+
id?: string;
|
|
63
|
+
name: string;
|
|
64
|
+
originalName?: string;
|
|
65
|
+
description?: string;
|
|
66
|
+
tag?: string | null;
|
|
67
|
+
size: number;
|
|
68
|
+
type: string;
|
|
69
|
+
fileData: string;
|
|
70
|
+
createdAt: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Note/Message payload (extension: note with title, content, type, replyTo) */
|
|
74
|
+
export interface ExtensionNotePayload {
|
|
75
|
+
id?: string;
|
|
76
|
+
uuid?: string;
|
|
77
|
+
title: string;
|
|
78
|
+
content: string;
|
|
79
|
+
tag?: string | null;
|
|
80
|
+
type?: 'private' | 'sent' | 'received';
|
|
81
|
+
recipientId?: string | null;
|
|
82
|
+
recipientName?: string | null;
|
|
83
|
+
replyTo?: string | null;
|
|
84
|
+
createdAt: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Photo payload (extension: imageData base64, mimeType, name) */
|
|
88
|
+
export interface ExtensionPhotoPayload {
|
|
89
|
+
id?: string;
|
|
90
|
+
name: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
tag?: string | null;
|
|
93
|
+
imageData: string;
|
|
94
|
+
mimeType: string;
|
|
95
|
+
dataUrl?: string;
|
|
96
|
+
thumbnail?: string;
|
|
97
|
+
createdAt: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ======================================
|
|
101
|
+
// Helpers
|
|
102
|
+
// ======================================
|
|
103
|
+
|
|
104
|
+
function toExtensionLineItems(items: InvoiceItem[] | undefined): ExtensionLineItem[] {
|
|
105
|
+
if (!items?.length) return [];
|
|
106
|
+
return items.map((li) => ({
|
|
107
|
+
name: li.name ?? li.description,
|
|
108
|
+
price: li.unitPrice ?? 0,
|
|
109
|
+
qty: li.qty ?? li.quantity,
|
|
110
|
+
unit: li.unit ?? 'units',
|
|
111
|
+
total: li.total ?? li.amount ?? (li.quantity * (li.unitPrice ?? 0)),
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ======================================
|
|
116
|
+
// Payload builders
|
|
117
|
+
// ======================================
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Build invoice payload bundle for minting.
|
|
121
|
+
* Matches chrome-extension format so the extension can decrypt and display.
|
|
122
|
+
*/
|
|
123
|
+
export function buildInvoicePayload(
|
|
124
|
+
invoice: InvoiceData,
|
|
125
|
+
options?: {
|
|
126
|
+
id?: string;
|
|
127
|
+
uuid?: string;
|
|
128
|
+
pdfBase64?: string;
|
|
129
|
+
senderProfile?: Record<string, unknown>;
|
|
130
|
+
recipientProfile?: Record<string, unknown>;
|
|
131
|
+
}
|
|
132
|
+
): InvoicePayloadBundle {
|
|
133
|
+
const lineItems = toExtensionLineItems(invoice.items);
|
|
134
|
+
const subtotal = invoice.subtotal ?? invoice.amount ?? 0;
|
|
135
|
+
const total = invoice.total ?? invoice.amount ?? subtotal;
|
|
136
|
+
|
|
137
|
+
const extensionInvoice: ExtensionInvoicePayload = {
|
|
138
|
+
id: options?.id,
|
|
139
|
+
uuid: options?.uuid,
|
|
140
|
+
invoiceNo: invoice.invoiceId,
|
|
141
|
+
type: 'sent',
|
|
142
|
+
status: 'submitted',
|
|
143
|
+
billTo: invoice.billTo,
|
|
144
|
+
poNo: invoice.poNo,
|
|
145
|
+
dueDate: invoice.dueDate,
|
|
146
|
+
lineItems,
|
|
147
|
+
discountRate: invoice.discountRate ?? 0,
|
|
148
|
+
discountAmount: invoice.discountAmount,
|
|
149
|
+
taxRate: invoice.taxRate ?? 0,
|
|
150
|
+
subtotal,
|
|
151
|
+
taxAmount: invoice.taxAmount,
|
|
152
|
+
total,
|
|
153
|
+
currency: invoice.currency,
|
|
154
|
+
paymentMethod: invoice.paymentMethod,
|
|
155
|
+
paymentTerms: invoice.paymentTerms,
|
|
156
|
+
notePayee: invoice.notePayee ?? invoice.notes,
|
|
157
|
+
createdAt: invoice.date ?? new Date().toISOString(),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const bundle: InvoicePayloadBundle = {
|
|
161
|
+
invoice: extensionInvoice,
|
|
162
|
+
};
|
|
163
|
+
if (options?.pdfBase64) bundle.pdf = options.pdfBase64;
|
|
164
|
+
if (options?.senderProfile) bundle.senderProfile = options.senderProfile;
|
|
165
|
+
if (options?.recipientProfile) bundle.recipientProfile = options.recipientProfile;
|
|
166
|
+
return bundle;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Build file payload for minting.
|
|
171
|
+
* Matches chrome-extension format (name, fileData base64, type, size).
|
|
172
|
+
*/
|
|
173
|
+
export function buildFilePayload(
|
|
174
|
+
file: FileData,
|
|
175
|
+
options?: { id?: string }
|
|
176
|
+
): ExtensionFilePayload {
|
|
177
|
+
return {
|
|
178
|
+
id: options?.id,
|
|
179
|
+
name: file.filename,
|
|
180
|
+
originalName: file.filename,
|
|
181
|
+
description: (file.metadata as { description?: string })?.description,
|
|
182
|
+
tag: null,
|
|
183
|
+
size: file.size,
|
|
184
|
+
type: file.mimeType,
|
|
185
|
+
fileData: file.content,
|
|
186
|
+
createdAt: (file.metadata as { createdAt?: string })?.createdAt ?? new Date().toISOString(),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Build message/note payload for minting.
|
|
192
|
+
* Matches chrome-extension format (title, content, replyTo). Type is always 'sent' (institution sends to users).
|
|
193
|
+
*/
|
|
194
|
+
export function buildMessagePayload(
|
|
195
|
+
note: NoteData,
|
|
196
|
+
options?: {
|
|
197
|
+
id?: string;
|
|
198
|
+
uuid?: string;
|
|
199
|
+
recipientId?: string | null;
|
|
200
|
+
recipientName?: string | null;
|
|
201
|
+
replyTo?: string | null;
|
|
202
|
+
}
|
|
203
|
+
): ExtensionNotePayload {
|
|
204
|
+
return {
|
|
205
|
+
id: options?.id,
|
|
206
|
+
uuid: options?.uuid,
|
|
207
|
+
title: note.title,
|
|
208
|
+
content: note.content,
|
|
209
|
+
tag: note.tags?.length ? note.tags.join(', ') : null,
|
|
210
|
+
type: 'sent',
|
|
211
|
+
recipientId: options?.recipientId ?? null,
|
|
212
|
+
recipientName: options?.recipientName ?? null,
|
|
213
|
+
replyTo: options?.replyTo ?? null,
|
|
214
|
+
createdAt: (note.metadata as { createdAt?: string })?.createdAt ?? new Date().toISOString(),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Build picture/photo payload for minting.
|
|
220
|
+
* Matches chrome-extension format (imageData base64, mimeType, name).
|
|
221
|
+
*/
|
|
222
|
+
export function buildPhotoPayload(
|
|
223
|
+
photo: PhotoData,
|
|
224
|
+
options?: { id?: string; thumbnail?: string; dataUrl?: string }
|
|
225
|
+
): ExtensionPhotoPayload {
|
|
226
|
+
const dataUrl = options?.dataUrl ?? (photo.content ? `data:${photo.mimeType};base64,${photo.content}` : undefined);
|
|
227
|
+
return {
|
|
228
|
+
id: options?.id,
|
|
229
|
+
name: photo.filename,
|
|
230
|
+
description: (photo.metadata as { description?: string })?.description,
|
|
231
|
+
tag: null,
|
|
232
|
+
imageData: photo.content,
|
|
233
|
+
mimeType: photo.mimeType,
|
|
234
|
+
dataUrl,
|
|
235
|
+
thumbnail: options?.thumbnail,
|
|
236
|
+
createdAt: (photo.metadata as { createdAt?: string })?.createdAt ?? new Date().toISOString(),
|
|
237
|
+
};
|
|
238
|
+
}
|