@empereur-rouge/pms-sdk 0.3.8 → 0.5.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/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { wordlist } from "@scure/bip39/wordlists/english";
9
9
  // src/utils.ts
10
10
  import { sha256 } from "@noble/hashes/sha2";
11
11
  import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
12
+ import { base64, bech32m } from "@scure/base";
12
13
  function sha256Hash(data) {
13
14
  return sha256(data);
14
15
  }
@@ -21,12 +22,103 @@ function fromHex(hex) {
21
22
  function encodeUtf8(str) {
22
23
  return new TextEncoder().encode(str);
23
24
  }
24
- function computeBlockId(parents, payloadJson, nonce) {
25
- const parentsStr = parents.sort().join(",");
26
- const payloadStr = payloadJson ?? "";
27
- const content = `${parentsStr}|${payloadStr}|${nonce}`;
28
- const hash = sha256Hash(encodeUtf8(content));
29
- return toHex(hash);
25
+ function toBase64(bytes) {
26
+ return base64.encode(bytes);
27
+ }
28
+ function txSigningMessage(networkId, inputs, outputs, fee) {
29
+ const canonInputs = inputs.map((i) => ({
30
+ out: { txid: i.out.txid, index: i.out.index }
31
+ }));
32
+ const canonOutputs = outputs.map((o) => {
33
+ const out = {
34
+ address: o.address,
35
+ amount: o.amount
36
+ };
37
+ if (o.asset_id !== void 0 && o.asset_id !== null) {
38
+ out.asset_id = o.asset_id;
39
+ }
40
+ if (o.locked_until !== void 0 && o.locked_until !== null) {
41
+ out.locked_until = o.locked_until;
42
+ }
43
+ if (o.spend_condition !== void 0 && o.spend_condition !== null) {
44
+ out.spend_condition = o.spend_condition;
45
+ }
46
+ if (o.created_at !== void 0 && o.created_at !== null) {
47
+ out.created_at = o.created_at;
48
+ }
49
+ return out;
50
+ });
51
+ const canon = {
52
+ network_id: networkId,
53
+ inputs: canonInputs,
54
+ outputs: canonOutputs,
55
+ fee
56
+ };
57
+ return toHex(sha256Hash(encodeUtf8(JSON.stringify(canon))));
58
+ }
59
+ var PLAIN_VARIANT_TO_PAYLOAD_TYPE = {
60
+ Genesis: "Genesis",
61
+ Mint: "Mint",
62
+ TxUtxo: "Transaction",
63
+ Milestone: "Milestone",
64
+ Nft: "Nft",
65
+ ConfigUpdate: "ConfigUpdate",
66
+ Reward: "Reward",
67
+ EncryptedReward: "EncryptedReward",
68
+ TokenCreate: "TokenCreate",
69
+ BridgeLock: "BridgeLock",
70
+ BridgeMint: "BridgeMint",
71
+ Freeze: "Freeze",
72
+ Unfreeze: "Unfreeze",
73
+ Seize: "Seize",
74
+ Reverse: "Reverse",
75
+ ContractRegister: "ContractRegister",
76
+ ContractUpdate: "ContractUpdate",
77
+ LedgerOwnershipTransfer: "LedgerOwnershipTransfer",
78
+ CoordinatorKeyRotate: "CoordinatorKeyRotate"
79
+ };
80
+ var EMPTY_COMMITMENT = "0000000000000000000000000000000000000000000000000000000000000000";
81
+ function computeBlockId(parents, payload, nonce) {
82
+ let payload_type;
83
+ let commitment;
84
+ let len_hint;
85
+ let key_version;
86
+ if (payload === void 0 || payload === null) {
87
+ payload_type = "None";
88
+ commitment = EMPTY_COMMITMENT;
89
+ len_hint = 0;
90
+ key_version = 0;
91
+ } else if ("Plain" in payload) {
92
+ const plain = payload.Plain;
93
+ const variant = typeof plain === "string" ? plain : Object.keys(plain)[0];
94
+ payload_type = PLAIN_VARIANT_TO_PAYLOAD_TYPE[variant] ?? variant;
95
+ const plainBytes = encodeUtf8(JSON.stringify(plain));
96
+ commitment = toHex(sha256Hash(plainBytes));
97
+ len_hint = plainBytes.length;
98
+ key_version = 0;
99
+ } else {
100
+ const enc = payload.Encrypted;
101
+ payload_type = "Encrypted";
102
+ commitment = enc.commitment;
103
+ len_hint = enc.ciphertext_b64.length;
104
+ key_version = enc.key_version;
105
+ }
106
+ const head = {
107
+ parents,
108
+ nonce,
109
+ envelope: { payload_type, commitment, len_hint, key_version }
110
+ };
111
+ return toHex(sha256Hash(encodeUtf8(JSON.stringify(head))));
112
+ }
113
+ function x25519FromAddress(address) {
114
+ try {
115
+ const decoded = bech32m.decode(address, false);
116
+ const bytes = bech32m.fromWords(decoded.words);
117
+ if (bytes.length !== 52) return void 0;
118
+ return toHex(bytes.slice(20));
119
+ } catch {
120
+ return void 0;
121
+ }
30
122
  }
31
123
  function parseAmount(amount) {
32
124
  const [whole, frac = ""] = amount.split(".");
@@ -39,6 +131,47 @@ function formatAmount(sats) {
39
131
  const fracStr = frac.toString().padStart(8, "0");
40
132
  return `${whole}.${fracStr}`;
41
133
  }
134
+ var DAY_MS = 864e5;
135
+ function multisigAddress(m, pubkeys) {
136
+ const normalized = pubkeys.map((pk) => {
137
+ const t = pk.trim();
138
+ return (t.startsWith("0x") ? t.slice(2) : t).toLowerCase();
139
+ }).sort();
140
+ const parts = [
141
+ encodeUtf8("pms-multisig-v1"),
142
+ new Uint8Array([m, normalized.length]),
143
+ ...normalized.map((pk) => encodeUtf8(pk))
144
+ ];
145
+ const total = parts.reduce((n, p) => n + p.length, 0);
146
+ const buf = new Uint8Array(total);
147
+ let off = 0;
148
+ for (const p of parts) {
149
+ buf.set(p, off);
150
+ off += p.length;
151
+ }
152
+ return `msig1${toHex(sha256Hash(buf).slice(0, 20))}`;
153
+ }
154
+ function hashlockHash(preimage) {
155
+ const bytes = typeof preimage === "string" ? encodeUtf8(preimage) : preimage;
156
+ return toHex(sha256Hash(bytes));
157
+ }
158
+ function effectiveValue(amount, createdAtMs, nowMs, bpsPerDay) {
159
+ if (createdAtMs === void 0 || createdAtMs === null || !bpsPerDay) {
160
+ return amount;
161
+ }
162
+ const elapsed = nowMs - createdAtMs;
163
+ const days = elapsed > 0 ? BigInt(Math.floor(elapsed / DAY_MS)) : 0n;
164
+ if (days === 0n) return amount;
165
+ const sats = parseAmount(amount);
166
+ const scaled = sats * 10000n - sats * BigInt(bpsPerDay) * days;
167
+ if (scaled <= 0n) return "0.00000000";
168
+ return formatAmount(scaled / 10000n);
169
+ }
170
+ function spendableUtxos(utxos, nowMs = Date.now()) {
171
+ return utxos.filter(
172
+ (u) => u.locked_until === void 0 || u.locked_until === null || u.locked_until <= nowMs
173
+ );
174
+ }
42
175
 
43
176
  // src/wallet.ts
44
177
  var PmsWallet = class _PmsWallet {
@@ -213,11 +346,33 @@ var PmsWallet = class _PmsWallet {
213
346
  function isValidMnemonic(mnemonic) {
214
347
  return validateMnemonic(mnemonic.trim().toLowerCase(), wordlist);
215
348
  }
349
+ function makeUnlock(signers, txHashHex, opts) {
350
+ if (signers.length === 0) {
351
+ throw new Error("makeUnlock: at least one signer is required");
352
+ }
353
+ const msg = encodeUtf8(txHashHex);
354
+ const sign = (w) => toBase64(fromHex(w.sign(msg)));
355
+ const [primary, ...rest] = signers;
356
+ const unlock = {
357
+ pubkey_hex: primary.publicKeyHex,
358
+ signature_b64: sign(primary)
359
+ };
360
+ if (rest.length > 0) {
361
+ unlock.cosigners = rest.map((w) => ({
362
+ pubkey_hex: w.publicKeyHex,
363
+ signature_b64: sign(w)
364
+ }));
365
+ }
366
+ if (opts?.preimageHex !== void 0) {
367
+ unlock.preimage_hex = opts.preimageHex;
368
+ }
369
+ return unlock;
370
+ }
216
371
 
217
372
  // src/types.ts
218
373
  var DEFAULT_CONFIG = {
219
374
  networkId: "pms-mainnet",
220
- protocolVersion: 1,
375
+ protocolVersion: 3,
221
376
  timeout: 3e4,
222
377
  seedNodes: [],
223
378
  enableRacing: true,
@@ -232,13 +387,13 @@ import { gcm } from "@noble/ciphers/aes.js";
232
387
  import { hkdf as hkdf2 } from "@noble/hashes/hkdf";
233
388
  import { sha256 as sha2563 } from "@noble/hashes/sha2";
234
389
  import { randomBytes, bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
235
- import { base64 } from "@scure/base";
390
+ import { base64 as base642 } from "@scure/base";
236
391
  var SCHEME = "x25519+aes256gcm";
237
392
  var HKDF_SALT = new TextEncoder().encode("pms-dek-wrap");
238
393
  var HKDF_INFO_KEK = new TextEncoder().encode("kek-v1");
239
394
  var HKDF_INFO_KID = new TextEncoder().encode("kid-v1");
240
395
  function fromBase64(str) {
241
- return base64.decode(str);
396
+ return base642.decode(str);
242
397
  }
243
398
  function sha256Hex(data) {
244
399
  return bytesToHex2(sha2563(data));
@@ -403,11 +558,24 @@ var PmsClient = class {
403
558
  }
404
559
  /**
405
560
  * Récupère les UTXOs d'une adresse.
561
+ *
562
+ * Depuis dag-pms v0.10.0, chaque UTXO expose aussi ses contraintes de
563
+ * dépense : `locked_until` (time-lock), `spend_condition`
564
+ * (MultiSig/HashLock) et `created_at` (base du demurrage). Utiliser
565
+ * [`spendableUtxos`] pour exclure les UTXOs encore verrouillés avant
566
+ * toute sélection de coins côté client.
406
567
  */
407
568
  async getUtxos(address) {
408
569
  const res = await this.fetch(`/v1/wallet/${address}/utxos`, void 0, { idempotent: true });
409
570
  return res.utxos ?? [];
410
571
  }
572
+ /**
573
+ * Dernier snapshot de preuve de réserves ancré (protocole 2.6,
574
+ * `GET /v1/reserves/latest`). 404 si aucun snapshot n'a encore été ancré.
575
+ */
576
+ async getLatestReserves() {
577
+ return this.fetch("/v1/reserves/latest", void 0, { idempotent: true });
578
+ }
411
579
  // ═══════════════════════════════════════════════════════════════════════
412
580
  // Wallet API (Custodial — Server-Side)
413
581
  // ═══════════════════════════════════════════════════════════════════════
@@ -864,101 +1032,83 @@ var PmsClient = class {
864
1032
  }
865
1033
  /**
866
1034
  * Envoie des tokens à une adresse.
867
- * Construit automatiquement la transaction, la signe et la soumet.
1035
+ *
1036
+ * Flux aligné sur dag-pms v0.9.0 (single-writer + vérification des
1037
+ * signatures de transaction par l'engine) :
1038
+ * 1. `POST /v1/tx/prepare` — le serveur sélectionne les UTXOs et calcule
1039
+ * les frais.
1040
+ * 2. Vérification défensive côté client : l'output destinataire demandé
1041
+ * existe bien, et le `tx_hash` retourné correspond au message canonique
1042
+ * recalculé localement (`txSigningMessage`). On ne signe JAMAIS un hash
1043
+ * opaque non vérifié.
1044
+ * 3. Le wallet signe la TRANSACTION (unlocks) — pas le bloc. C'est le
1045
+ * serveur qui forge et signe le bloc (single-writer enforcement).
1046
+ * 4. `POST /v1/wallet/tx/send` avec la TX signée + les clés X25519 des
1047
+ * destinataires pour le chiffrement du payload.
1048
+ *
1049
+ * @param params.to - Adresse destinataire (Bech32m de préférence)
1050
+ * @param params.amount - Montant décimal (ex: "10.0")
1051
+ * @param params.wallet - Wallet signataire (propriétaire des UTXOs)
1052
+ * @param params.assetId - Asset ID optionnel (undefined = PMS natif)
1053
+ * @returns `{id, status, block_id}` — id du bloc forgé par le serveur
868
1054
  */
869
1055
  async send(params) {
870
- const { to, amount, wallet, memo: _memo } = params;
871
- const utxos = await this.getUtxos(wallet.address);
872
- if (utxos.length === 0) {
873
- throw new Error("No UTXOs available");
874
- }
875
- const netConfig = await this.getNetworkConfig();
876
- const baseFeeSats = parseAmount(netConfig.base_fee || "0");
877
- const feeRateBps = BigInt(netConfig.fee_rate_bps || 0);
878
- const amountSats = parseAmount(amount);
879
- const variableFee = amountSats * feeRateBps / 10000n;
880
- const fee = baseFeeSats + variableFee;
881
- const totalNeeded = amountSats + fee;
882
- let selectedSats = 0n;
883
- const selectedUtxos = [];
884
- for (const utxo of utxos) {
885
- selectedUtxos.push(utxo);
886
- selectedSats += parseAmount(utxo.amount || "0");
887
- if (selectedSats >= totalNeeded) break;
1056
+ const { to, amount, wallet, assetId } = params;
1057
+ const prepareBody = {
1058
+ from: wallet.address,
1059
+ to,
1060
+ amount
1061
+ };
1062
+ if (assetId !== void 0) {
1063
+ prepareBody.asset_id = assetId;
888
1064
  }
889
- if (selectedSats < totalNeeded) {
1065
+ const prepared = await this.prepareTx(prepareBody);
1066
+ const { unsigned_tx, tx_hash } = prepared;
1067
+ const requestedSats = parseAmount(amount);
1068
+ const recipientOutput = unsigned_tx.outputs.find(
1069
+ (o) => o.address === to && (o.asset_id ?? void 0) === (assetId ?? void 0) && parseAmount(o.amount) >= requestedSats
1070
+ );
1071
+ if (!recipientOutput) {
890
1072
  throw new Error(
891
- `Insufficient balance: have ${formatAmount(selectedSats)}, need ${formatAmount(totalNeeded)} (incl. fee ${formatAmount(fee)})`
1073
+ `Prepared transaction does not contain the requested output (to=${to}, amount=${amount}, asset=${assetId ?? "PMS"}). Refusing to sign a transaction that does not pay the intended recipient.`
892
1074
  );
893
1075
  }
894
- const outputs = [
895
- { address: to, amount: formatAmount(amountSats) }
896
- ];
897
- if (fee > 0n) {
898
- outputs.push({
899
- address: netConfig.fee_recipient,
900
- amount: formatAmount(fee)
901
- });
902
- }
903
- const change = selectedSats - amountSats - fee;
904
- if (change > 0n) {
905
- outputs.push({ address: wallet.address, amount: formatAmount(change) });
1076
+ const localHash = txSigningMessage(
1077
+ this.config.networkId,
1078
+ unsigned_tx.inputs,
1079
+ unsigned_tx.outputs,
1080
+ unsigned_tx.fee
1081
+ );
1082
+ if (localHash !== tx_hash) {
1083
+ throw new Error(
1084
+ `tx_hash mismatch: server returned ${tx_hash}, locally computed ${localHash}. Either the client networkId ("${this.config.networkId}") does not match the node's network, or the server response was tampered with. Refusing to sign.`
1085
+ );
906
1086
  }
907
- const inputs = selectedUtxos.map((utxo) => ({
908
- out: {
909
- txid: utxo.outpoint.txid,
910
- index: utxo.outpoint.index
911
- }
912
- }));
913
- const txCanonical = {
914
- inputs,
915
- outputs,
916
- fee: formatAmount(fee)
917
- };
918
- const txMessage = JSON.stringify(txCanonical);
919
- const txSignatureHex = wallet.sign(encodeUtf8(txMessage));
920
- const txSigBytes = fromHex(txSignatureHex);
921
- const txSigB64 = btoa(String.fromCharCode(...txSigBytes));
922
- const unlocks = inputs.map(() => ({
1087
+ const sigHex = wallet.sign(encodeUtf8(tx_hash));
1088
+ const sigB64 = toBase64(fromHex(sigHex));
1089
+ const unlocks = unsigned_tx.inputs.map(() => ({
923
1090
  pubkey_hex: wallet.publicKeyHex,
924
- signature_b64: txSigB64
1091
+ signature_b64: sigB64
925
1092
  }));
926
1093
  const tx = {
927
- inputs,
928
- outputs,
929
- fee: formatAmount(fee),
1094
+ inputs: unsigned_tx.inputs,
1095
+ outputs: unsigned_tx.outputs,
1096
+ fee: unsigned_tx.fee,
930
1097
  unlocks
931
1098
  };
932
- const tips = await this.getTips();
933
- const parents = tips.slice(0, 2);
934
- const payload = { Plain: { TxUtxo: tx } };
935
- const payloadJson = JSON.stringify(payload);
936
- let nonce = 0;
937
- let blockId = computeBlockId(parents, payloadJson, nonce);
938
- const canonicalView = {
939
- id: blockId,
940
- parents,
941
- payload_json: payloadJson,
942
- nonce,
943
- network_id: this.config.networkId,
944
- protocol_version: this.config.protocolVersion,
945
- signer_pk_hex: wallet.publicKeyHex
946
- };
947
- const messageToSign = JSON.stringify(canonicalView);
948
- const signatureHex = wallet.sign(encodeUtf8(messageToSign));
949
- const signatureBytes = fromHex(signatureHex);
950
- const signatureB64 = btoa(String.fromCharCode(...signatureBytes));
951
- const wireBlock = {
952
- id: blockId,
953
- parents,
954
- payload_json: payloadJson,
955
- nonce,
956
- network_id: this.config.networkId,
957
- protocol_version: this.config.protocolVersion,
958
- signer_pk_hex: wallet.publicKeyHex,
959
- signature_hex: signatureB64
960
- };
961
- return this.submitBlock(wireBlock);
1099
+ const recipientsXpk = [wallet.x25519PublicKeyHex];
1100
+ const toXpk = x25519FromAddress(to);
1101
+ if (toXpk && !recipientsXpk.includes(toXpk)) {
1102
+ recipientsXpk.push(toXpk);
1103
+ }
1104
+ const resp = await this.fetch(
1105
+ "/v1/wallet/tx/send",
1106
+ {
1107
+ method: "POST",
1108
+ body: JSON.stringify({ tx, recipients_xpk: recipientsXpk })
1109
+ }
1110
+ );
1111
+ return { ...resp, block_id: resp.id };
962
1112
  }
963
1113
  // ═══════════════════════════════════════════════════════════════════════
964
1114
  // Méthodes NFT Cube (Burn et Mint spécialisé)
@@ -966,7 +1116,15 @@ var PmsClient = class {
966
1116
  /**
967
1117
  * Transférer un NFT à un autre propriétaire.
968
1118
  * Prend en charge le re-chiffrement des métadonnées via le coordinateur.
969
- *
1119
+ *
1120
+ * @deprecated Cette méthode soumet un WireBlock signé par la clé
1121
+ * utilisateur via `/submit/block`. Depuis dag-pms v0.9.0, l'engine tourne
1122
+ * en mode **single-writer** : tout bloc signé par une clé non-coordinateur
1123
+ * est rejeté. Cette méthode ne fonctionne que sur un réseau SANS
1124
+ * single-writer enforcement. Utilisez les flux server-side
1125
+ * (`/v1/nft/transfer/prepare` + endpoints serveur) qui forgent et signent
1126
+ * le bloc côté coordinateur.
1127
+ *
970
1128
  * @param params - Paramètres du transfert
971
1129
  * @param params.tokenId - Identifiant du NFT
972
1130
  * @param params.to - Adresse du nouveau propriétaire
@@ -1002,7 +1160,7 @@ var PmsClient = class {
1002
1160
  const payload = { Plain: { Nft: action } };
1003
1161
  const payloadJson = JSON.stringify(payload);
1004
1162
  const nonce = 0;
1005
- const blockId = computeBlockId(parents, payloadJson, nonce);
1163
+ const blockId = computeBlockId(parents, payload, nonce);
1006
1164
  const canonicalView = {
1007
1165
  id: blockId,
1008
1166
  parents,
@@ -1034,10 +1192,17 @@ var PmsClient = class {
1034
1192
  * Seul le propriétaire du NFT peut le brûler.
1035
1193
  * Une fois brûlé, le NFT est supprimé définitivement.
1036
1194
  *
1037
- * Pour les Cubes authentiques (avec signature Authority valide),
1195
+ * Pour les Cubes authentiques (avec signature Authority valide),
1038
1196
  * un remboursement est calculé selon la formule:
1039
1197
  * `(weight * size * density) / 10000` PMS
1040
- *
1198
+ *
1199
+ * @deprecated Cette méthode poste un WireBlock signé par la clé
1200
+ * utilisateur. Depuis dag-pms v0.9.0, l'engine tourne en mode
1201
+ * **single-writer** : tout bloc signé par une clé non-coordinateur est
1202
+ * rejeté. Cette méthode ne fonctionne que sur un réseau SANS single-writer
1203
+ * enforcement. Le flux supporté est le burn server-side
1204
+ * (`/v1/nft/burn-simple`), où le serveur forge et signe le bloc.
1205
+ *
1041
1206
  * @param params - Paramètres du burn
1042
1207
  * @param params.tokenId - Identifiant du NFT à brûler
1043
1208
  * @param params.wallet - Wallet PMS du propriétaire (doit être l'owner actuel)
@@ -1071,7 +1236,7 @@ var PmsClient = class {
1071
1236
  };
1072
1237
  const payloadJson = JSON.stringify(payload);
1073
1238
  const nonce = 0;
1074
- const blockId = computeBlockId(parents, payloadJson, nonce);
1239
+ const blockId = computeBlockId(parents, payload, nonce);
1075
1240
  const canonicalView = {
1076
1241
  id: blockId,
1077
1242
  parents,
@@ -1103,7 +1268,14 @@ var PmsClient = class {
1103
1268
  }
1104
1269
  /**
1105
1270
  * Brûle (détruit) plusieurs NFTs en une seule transaction.
1106
- *
1271
+ *
1272
+ * @deprecated Cette méthode poste un WireBlock signé par la clé
1273
+ * utilisateur. Depuis dag-pms v0.9.0, l'engine tourne en mode
1274
+ * **single-writer** : tout bloc signé par une clé non-coordinateur est
1275
+ * rejeté. Cette méthode ne fonctionne que sur un réseau SANS single-writer
1276
+ * enforcement. Le flux supporté est le burn server-side
1277
+ * (`/v1/nft/burn-simple`), où le serveur forge et signe le bloc.
1278
+ *
1107
1279
  * @param params - Paramètres du batch burn
1108
1280
  * @param params.tokenIds - Liste des Identifiants des NFTs à brûler
1109
1281
  * @param params.wallet - Wallet PMS du propriétaire
@@ -1128,7 +1300,7 @@ var PmsClient = class {
1128
1300
  };
1129
1301
  const payloadJson = JSON.stringify(payload);
1130
1302
  const nonce = 0;
1131
- const blockId = computeBlockId(parents, payloadJson, nonce);
1303
+ const blockId = computeBlockId(parents, payload, nonce);
1132
1304
  const canonicalView = {
1133
1305
  id: blockId,
1134
1306
  parents,
@@ -1396,12 +1568,19 @@ function mergeAbortSignals(a, b) {
1396
1568
  return controller.signal;
1397
1569
  }
1398
1570
  export {
1571
+ DAY_MS,
1399
1572
  PmsClient,
1400
1573
  PmsWallet,
1401
1574
  decryptPayload,
1575
+ effectiveValue,
1402
1576
  formatAmount,
1403
1577
  fromHex,
1578
+ hashlockHash,
1404
1579
  isValidMnemonic,
1580
+ makeUnlock,
1581
+ multisigAddress,
1405
1582
  parseAmount,
1406
- toHex
1583
+ spendableUtxos,
1584
+ toHex,
1585
+ txSigningMessage
1407
1586
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empereur-rouge/pms-sdk",
3
- "version": "0.3.8",
3
+ "version": "0.5.0",
4
4
  "description": "TypeScript SDK for PMS (Planetary Monetary System) — wallet management, transactions, NFTs, and DAG interactions",
5
5
  "author": "empereur-rouge",
6
6
  "license": "MIT",