@wishknish/knishio-client-ts 0.8.3 → 0.9.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.cjs CHANGED
@@ -945,9 +945,19 @@ var init_StackableUnitAmountException = __esm({
945
945
  });
946
946
 
947
947
  // src/exception/StackableUnitDecimalsException.ts
948
+ var StackableUnitDecimalsException, StackableUnitDecimalsException_default;
948
949
  var init_StackableUnitDecimalsException = __esm({
949
950
  "src/exception/StackableUnitDecimalsException.ts"() {
950
951
  init_BaseException();
952
+ StackableUnitDecimalsException = class extends exports.BaseException {
953
+ /**
954
+ * Constructor using proper error type
955
+ */
956
+ constructor(message = "Stackable tokens with unit IDs cannot have decimal places!") {
957
+ super("STACKABLE_UNIT_DECIMALS_ERROR", message);
958
+ }
959
+ };
960
+ StackableUnitDecimalsException_default = StackableUnitDecimalsException;
951
961
  }
952
962
  });
953
963
 
@@ -1929,9 +1939,6 @@ function generateSecret(seed = null, length = CRYPTO_CONSTANTS.SECRET_LENGTH) {
1929
1939
  }
1930
1940
  }
1931
1941
  function generateBundleHash(secret, _source = null) {
1932
- if (!secret || secret.length === 0) {
1933
- throw new Error("Secret is required for bundle hash generation");
1934
- }
1935
1942
  const hash = shake256(secret, CRYPTO_CONSTANTS.BUNDLE_HASH_LENGTH * 4);
1936
1943
  return hash;
1937
1944
  }
@@ -3342,8 +3349,7 @@ var QUERY_TYPES = [
3342
3349
  "Batch",
3343
3350
  "ActiveSession",
3344
3351
  "Policy",
3345
- "Token",
3346
- "UserActivity"
3352
+ "Token"
3347
3353
  ];
3348
3354
  function createLookup(values) {
3349
3355
  return values.reduce((acc, value) => {
@@ -3602,6 +3608,29 @@ var Wallet = class _Wallet {
3602
3608
  }
3603
3609
  remainderWallet.tokenUnits = remainderTokenUnits;
3604
3610
  }
3611
+ /**
3612
+ * Split token units across MULTIPLE recipients (N-way sibling of splitUnits).
3613
+ *
3614
+ * The source retains the SENT union (all units leaving), each recipient gets its own
3615
+ * subset, and the remainder keeps the KEPT units (those not assigned to any recipient).
3616
+ * recipientUnitLists is parallel to recipientWallets. No-op when no units are sent.
3617
+ *
3618
+ * @param recipientUnitLists - per-recipient arrays of token unit IDs (parallel to recipientWallets)
3619
+ * @param recipientWallets - destination wallets
3620
+ * @param remainderWallet - wallet to receive the KEPT units
3621
+ */
3622
+ splitUnitsMulti(recipientUnitLists, recipientWallets, remainderWallet) {
3623
+ const sentIds = new Set(recipientUnitLists.flat());
3624
+ if (sentIds.size === 0) {
3625
+ return;
3626
+ }
3627
+ recipientWallets.forEach((recipientWallet, i) => {
3628
+ const ids = recipientUnitLists[i] ?? [];
3629
+ recipientWallet.tokenUnits = this.tokenUnits.filter((tokenUnit) => ids.includes(tokenUnit.id));
3630
+ });
3631
+ remainderWallet.tokenUnits = this.tokenUnits.filter((tokenUnit) => !sentIds.has(tokenUnit.id));
3632
+ this.tokenUnits = this.tokenUnits.filter((tokenUnit) => sentIds.has(tokenUnit.id));
3633
+ }
3605
3634
  /**
3606
3635
  * Get token units data
3607
3636
  * Stub for compatibility
@@ -3648,6 +3677,15 @@ var Wallet = class _Wallet {
3648
3677
  };
3649
3678
  }
3650
3679
  async decryptMessage(encryptedData) {
3680
+ const decryptedString = await this._mlkemDecryptToString(encryptedData);
3681
+ return decryptedString === null ? null : JSON.parse(decryptedString);
3682
+ }
3683
+ /**
3684
+ * ML-KEM768 decapsulate + AES-256-GCM decrypt → the RAW decrypted UTF-8 string (no JSON.parse).
3685
+ * Shared by {@link decryptMessage} (which JSON.parses the result) and the PQ CipherHash transport
3686
+ * ({@link decryptMyMessageML768}, which needs the raw response JSON text). PQ-transport Phase E.
3687
+ */
3688
+ async _mlkemDecryptToString(encryptedData) {
3651
3689
  const { cipherText, encryptedMessage } = encryptedData;
3652
3690
  let sharedSecret;
3653
3691
  try {
@@ -3676,9 +3714,8 @@ var Wallet = class _Wallet {
3676
3714
  console.info("Wallet::decryptMessage() - deserialized encrypted message", deserializedEncryptedMessage);
3677
3715
  return null;
3678
3716
  }
3679
- let decryptedString;
3680
3717
  try {
3681
- decryptedString = new TextDecoder().decode(decryptedUint8);
3718
+ return new TextDecoder().decode(decryptedUint8);
3682
3719
  } catch (e) {
3683
3720
  console.warn("Wallet::decryptMessage() - Decoding failed", e);
3684
3721
  console.info("Wallet::decryptMessage() - my public key", this.pubkey);
@@ -3687,7 +3724,40 @@ var Wallet = class _Wallet {
3687
3724
  console.info("Wallet::decryptMessage() - decrypted Uint8Array", decryptedUint8);
3688
3725
  return null;
3689
3726
  }
3690
- return JSON.parse(decryptedString);
3727
+ }
3728
+ /**
3729
+ * Canonical cross-SDK hashShare for a public key: standard base64 of SHAKE256(pubkey_utf8, 8 bytes)
3730
+ * — byte-matches the validator's hash_share and the JS/Kotlin/PHP hashShare. `shake256(pubkey, 64)`
3731
+ * = 64 bits = 8 bytes (hex) → hex-decode → standard base64 via serializeKey. PQ-transport Phase E.
3732
+ */
3733
+ hashShare(pubkey) {
3734
+ const hex = shake256(pubkey, 64);
3735
+ const bytes = new Uint8Array(hex.length / 2);
3736
+ for (let i = 0; i < bytes.length; i++) {
3737
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
3738
+ }
3739
+ return this.serializeKey(bytes);
3740
+ }
3741
+ /**
3742
+ * Post-quantum (ML-KEM768) CipherHash request envelope: a stringified single-recipient map
3743
+ * `{ "<hashShare(recipientPubkey)>": {cipherText, encryptedMessage} }` (object-valued, via
3744
+ * {@link encryptMessage}). Matches the Rust validator's CipherHash handler. PQ-transport Phase E.
3745
+ */
3746
+ async encryptStringML768(message, recipientPubkey) {
3747
+ const envelope = await this.encryptMessage(message, recipientPubkey);
3748
+ return JSON.stringify({ [this.hashShare(recipientPubkey)]: envelope });
3749
+ }
3750
+ /**
3751
+ * Decrypt a CipherHash response map addressed to THIS wallet's ML-KEM pubkey
3752
+ * (`hashShare(this.pubkey)`) → the RAW decrypted GraphQL response JSON text (NOT JSON.parsed;
3753
+ * it replaces the HTTP response body for the normal parser). `null` if no entry / decrypt fails.
3754
+ */
3755
+ async decryptMyMessageML768(map) {
3756
+ const envelope = map[this.hashShare(this.pubkey)];
3757
+ if (!envelope) {
3758
+ return null;
3759
+ }
3760
+ return this._mlkemDecryptToString(envelope);
3691
3761
  }
3692
3762
  // =============================================================================
3693
3763
  // SYMMETRIC ENCRYPTION HELPERS (AES-GCM with shared secret)
@@ -4986,6 +5056,46 @@ var Molecule = class _Molecule {
4986
5056
  }));
4987
5057
  return this;
4988
5058
  }
5059
+ /**
5060
+ * Initialize a MULTI-recipient V-type molecule: one source debits its FULL balance to fund
5061
+ * N recipients (each its own amount + stackable units) plus a remainder back to the sender.
5062
+ * Multi-recipient sibling of initValue (WP line 544: fund multiple recipients with one
5063
+ * transaction). recipientWallets is parallel to amounts.
5064
+ */
5065
+ initValues({
5066
+ recipientWallets,
5067
+ amounts
5068
+ }) {
5069
+ if (!this.sourceWallet) {
5070
+ throw new Error("Source wallet required for value transfer");
5071
+ }
5072
+ const total = amounts.reduce((sum, amount) => sum + amount, 0);
5073
+ if (Number(this.sourceWallet.balance) - total < 0) {
5074
+ throw new BalanceInsufficientException();
5075
+ }
5076
+ this.addAtom(Atom.create({
5077
+ isotope: "V",
5078
+ wallet: this.sourceWallet,
5079
+ value: -Number(this.sourceWallet.balance)
5080
+ }));
5081
+ recipientWallets.forEach((recipientWallet, i) => {
5082
+ this.addAtom(Atom.create({
5083
+ isotope: "V",
5084
+ wallet: recipientWallet,
5085
+ value: amounts[i],
5086
+ metaType: "walletBundle",
5087
+ metaId: recipientWallet.bundle
5088
+ }));
5089
+ });
5090
+ this.addAtom(Atom.create({
5091
+ isotope: "V",
5092
+ wallet: this.remainderWallet,
5093
+ value: Number(this.sourceWallet.balance) - total,
5094
+ metaType: "walletBundle",
5095
+ metaId: this.remainderWallet.bundle
5096
+ }));
5097
+ return this;
5098
+ }
4989
5099
  /**
4990
5100
  * Sign the molecule with one-time signature
4991
5101
  * Matches JavaScript SDK sign method
@@ -5743,7 +5853,7 @@ var Decimal = class _Decimal {
5743
5853
  * @param debug Enable debug logging
5744
5854
  * @returns 0 if equal, 1 if value1 > value2, -1 if value1 < value2
5745
5855
  */
5746
- static cmp(value1, value2, debug = false) {
5856
+ static cmp(value1, value2, _debug = false) {
5747
5857
  const val1 = _Decimal.val(value1) * MULTIPLIER;
5748
5858
  const val2 = _Decimal.val(value2) * MULTIPLIER;
5749
5859
  if (Math.abs(val1 - val2) < 1) {
@@ -5758,6 +5868,14 @@ var Decimal = class _Decimal {
5758
5868
  return _Decimal.cmp(value1, value2) === 0;
5759
5869
  }
5760
5870
  };
5871
+ var CIPHER_HASH_QUERY = "query ( $Hash: String! ) { CipherHash ( Hash: $Hash ) { hash } }";
5872
+ function parseOperation(query) {
5873
+ const typeMatch = (query || "").match(/\b(query|mutation|subscription)\b/i);
5874
+ const type = typeMatch?.[1]?.toLowerCase() ?? "query";
5875
+ const braceIdx = (query || "").indexOf("{");
5876
+ const nameMatch = braceIdx >= 0 ? query.slice(braceIdx + 1).match(/[A-Za-z_][A-Za-z0-9_]*/) : null;
5877
+ return { type, name: nameMatch?.[0] ?? "" };
5878
+ }
5761
5879
  var GraphQLClient = class {
5762
5880
  $__client;
5763
5881
  $__authToken = "";
@@ -5803,6 +5921,10 @@ var GraphQLClient = class {
5803
5921
  return core.createClient({
5804
5922
  url: serverUri,
5805
5923
  exchanges,
5924
+ // PQ-transport Phase E: when encryption is on, route fetch through the CipherHash wrapper
5925
+ // (encrypt the request body to the validator's ML-KEM pubkey, decrypt the response).
5926
+ // Omitted → urql uses the global fetch (plaintext).
5927
+ ...this.cipherLink ? { fetch: ((input, init) => this.cipherFetch(input, init)) } : {},
5806
5928
  fetchOptions: () => ({
5807
5929
  headers: {
5808
5930
  "X-Auth-Token": this.$__authToken
@@ -5812,6 +5934,62 @@ var GraphQLClient = class {
5812
5934
  })
5813
5935
  });
5814
5936
  }
5937
+ /**
5938
+ * Whether an outgoing GraphQL request body should be wrapped in CipherHash. Bypass (plaintext):
5939
+ * introspection `__schema`, `ContinuId`, the `AccessToken` mutation, and the U-isotope
5940
+ * `ProposeMolecule` (auth bootstrap — the key exchange itself can't be encrypted). Mirrors the
5941
+ * Kotlin/PHP/validator bypass set.
5942
+ */
5943
+ shouldEncrypt(body) {
5944
+ let parsed;
5945
+ try {
5946
+ parsed = JSON.parse(body);
5947
+ } catch (e) {
5948
+ return false;
5949
+ }
5950
+ const { type, name } = parseOperation(parsed.query);
5951
+ if (type === "query" && (name === "__schema" || name === "ContinuId")) return false;
5952
+ if (type === "mutation" && name === "AccessToken") return false;
5953
+ if (type === "mutation" && name === "ProposeMolecule") {
5954
+ const isotope = parsed.variables?.molecule?.atoms?.[0]?.isotope;
5955
+ if (isotope === "U") return false;
5956
+ }
5957
+ return true;
5958
+ }
5959
+ /**
5960
+ * Custom `fetch` that wraps a GraphQL request in the ML-KEM CipherHash envelope and decrypts the
5961
+ * response (PQ-transport Phase E). Operates on the raw POST body (mirrors the JS/Kotlin transform).
5962
+ * Reads the CURRENT client wallet + validator pubkey from auth.
5963
+ */
5964
+ async cipherFetch(input, init) {
5965
+ const wallet = this.$__wallet;
5966
+ const serverPubkey = this.$__pubkey;
5967
+ let encryptedRequest = false;
5968
+ let requestInit = init;
5969
+ if (wallet && serverPubkey && init && typeof init.body === "string" && this.shouldEncrypt(init.body)) {
5970
+ const hashVar = await wallet.encryptStringML768(init.body, serverPubkey);
5971
+ requestInit = { ...init, body: JSON.stringify({ query: CIPHER_HASH_QUERY, variables: { Hash: hashVar } }) };
5972
+ encryptedRequest = true;
5973
+ }
5974
+ const response = await fetch(input, requestInit);
5975
+ if (!encryptedRequest) {
5976
+ return response;
5977
+ }
5978
+ const text = await response.text();
5979
+ const init2 = { status: response.status, statusText: response.statusText, headers: response.headers };
5980
+ let parsed;
5981
+ try {
5982
+ parsed = JSON.parse(text);
5983
+ } catch (e) {
5984
+ return new Response(text, init2);
5985
+ }
5986
+ const hash = parsed?.data?.CipherHash?.hash;
5987
+ if (typeof hash !== "string") {
5988
+ return new Response(text, init2);
5989
+ }
5990
+ const decrypted = await wallet.decryptMyMessageML768(JSON.parse(hash));
5991
+ return new Response(decrypted != null ? decrypted : text, init2);
5992
+ }
5815
5993
  setAuthData({
5816
5994
  token,
5817
5995
  pubkey,
@@ -6106,8 +6284,8 @@ var Query = class {
6106
6284
  * Returns a Response object
6107
6285
  */
6108
6286
  createResponse(json) {
6109
- const Response2 = (init_Response(), __toCommonJS(Response_exports)).default;
6110
- return new Response2({
6287
+ const Response3 = (init_Response(), __toCommonJS(Response_exports)).default;
6288
+ return new Response3({
6111
6289
  query: this,
6112
6290
  json
6113
6291
  });
@@ -6177,8 +6355,8 @@ var Query = class {
6177
6355
  } catch (error) {
6178
6356
  if (error.name === "AbortError") {
6179
6357
  this.knishIOClient.log("warn", "Query was cancelled");
6180
- const Response2 = (init_Response(), __toCommonJS(Response_exports)).default;
6181
- return new Response2({
6358
+ const Response3 = (init_Response(), __toCommonJS(Response_exports)).default;
6359
+ return new Response3({
6182
6360
  query: this,
6183
6361
  json: { data: null, errors: [{ message: "Query was cancelled" }] }
6184
6362
  });
@@ -6222,8 +6400,8 @@ var Mutation = class extends Query {
6222
6400
  } catch (error) {
6223
6401
  if (error.name === "AbortError") {
6224
6402
  this.knishIOClient.log("warn", "Mutation was cancelled");
6225
- const Response2 = (init_Response(), __toCommonJS(Response_exports)).default;
6226
- return new Response2({
6403
+ const Response3 = (init_Response(), __toCommonJS(Response_exports)).default;
6404
+ return new Response3({
6227
6405
  query: this,
6228
6406
  json: { data: null, errors: [{ message: "Mutation was cancelled" }] }
6229
6407
  });
@@ -7159,6 +7337,89 @@ var ConfigValidator = class _ConfigValidator {
7159
7337
 
7160
7338
  // src/response/ResponseBalance.ts
7161
7339
  init_Response();
7340
+
7341
+ // src/core/TokenUnit.ts
7342
+ var TokenUnit = class _TokenUnit {
7343
+ id;
7344
+ name;
7345
+ metas;
7346
+ /**
7347
+ * Create new TokenUnit instance
7348
+ * Matches JavaScript SDK constructor signature exactly
7349
+ */
7350
+ constructor(id, name, metas) {
7351
+ this.id = id;
7352
+ this.name = name;
7353
+ this.metas = metas || {};
7354
+ }
7355
+ /**
7356
+ * Create TokenUnit from GraphQL response data
7357
+ * Matches JavaScript SDK createFromGraphQL method exactly
7358
+ */
7359
+ static createFromGraphQL(data) {
7360
+ let metas = data.metas || {};
7361
+ if (Array.isArray(metas) && metas.length) {
7362
+ try {
7363
+ metas = JSON.parse(metas);
7364
+ if (!metas) {
7365
+ metas = {};
7366
+ }
7367
+ } catch (error) {
7368
+ metas = {};
7369
+ }
7370
+ }
7371
+ return new _TokenUnit(
7372
+ data.id,
7373
+ data.name,
7374
+ metas
7375
+ );
7376
+ }
7377
+ /**
7378
+ * Create TokenUnit from database array data
7379
+ * Matches JavaScript SDK createFromDB method exactly
7380
+ */
7381
+ static createFromDB(data) {
7382
+ return new _TokenUnit(
7383
+ data[0],
7384
+ data[1],
7385
+ data.length > 2 ? data[2] : {}
7386
+ );
7387
+ }
7388
+ /**
7389
+ * Get fragment zone from metadata
7390
+ * Matches JavaScript SDK getFragmentZone method exactly
7391
+ */
7392
+ getFragmentZone() {
7393
+ return this.metas.fragmentZone || null;
7394
+ }
7395
+ /**
7396
+ * Get fused token units from metadata
7397
+ * Matches JavaScript SDK getFusedTokenUnits method exactly
7398
+ */
7399
+ getFusedTokenUnits() {
7400
+ return this.metas.fusedTokenUnits || null;
7401
+ }
7402
+ /**
7403
+ * Convert to data array format
7404
+ * Matches JavaScript SDK toData method exactly
7405
+ */
7406
+ toData() {
7407
+ return [this.id, this.name, this.metas];
7408
+ }
7409
+ /**
7410
+ * Convert to GraphQL response format
7411
+ * Matches JavaScript SDK toGraphQLResponse method exactly
7412
+ */
7413
+ toGraphQLResponse() {
7414
+ return {
7415
+ id: this.id,
7416
+ name: this.name,
7417
+ metas: JSON.stringify(this.metas)
7418
+ };
7419
+ }
7420
+ };
7421
+
7422
+ // src/response/ResponseBalance.ts
7162
7423
  var ResponseBalance = class _ResponseBalance extends exports.Response {
7163
7424
  /**
7164
7425
  * Class constructor
@@ -7221,6 +7482,11 @@ var ResponseBalance = class _ResponseBalance extends exports.Response {
7221
7482
  wallet.tokenSupply = data.token.supply;
7222
7483
  wallet.tokenFungibility = data.token.fungibility;
7223
7484
  }
7485
+ if (data.tokenUnits && data.tokenUnits.length) {
7486
+ for (const tokenUnitData of data.tokenUnits) {
7487
+ wallet.tokenUnits.push(TokenUnit.createFromGraphQL(tokenUnitData));
7488
+ }
7489
+ }
7224
7490
  if (data.tradeRates && data.tradeRates.length) {
7225
7491
  for (const tradeRate of data.tradeRates) {
7226
7492
  wallet.tradeRates[tradeRate.tokenSlug] = tradeRate.amount;
@@ -7278,89 +7544,6 @@ var QueryBalance = class extends Query {
7278
7544
 
7279
7545
  // src/response/ResponseWalletList.ts
7280
7546
  init_Response();
7281
-
7282
- // src/core/TokenUnit.ts
7283
- var TokenUnit = class _TokenUnit {
7284
- id;
7285
- name;
7286
- metas;
7287
- /**
7288
- * Create new TokenUnit instance
7289
- * Matches JavaScript SDK constructor signature exactly
7290
- */
7291
- constructor(id, name, metas) {
7292
- this.id = id;
7293
- this.name = name;
7294
- this.metas = metas || {};
7295
- }
7296
- /**
7297
- * Create TokenUnit from GraphQL response data
7298
- * Matches JavaScript SDK createFromGraphQL method exactly
7299
- */
7300
- static createFromGraphQL(data) {
7301
- let metas = data.metas || {};
7302
- if (Array.isArray(metas) && metas.length) {
7303
- try {
7304
- metas = JSON.parse(metas);
7305
- if (!metas) {
7306
- metas = {};
7307
- }
7308
- } catch (error) {
7309
- metas = {};
7310
- }
7311
- }
7312
- return new _TokenUnit(
7313
- data.id,
7314
- data.name,
7315
- metas
7316
- );
7317
- }
7318
- /**
7319
- * Create TokenUnit from database array data
7320
- * Matches JavaScript SDK createFromDB method exactly
7321
- */
7322
- static createFromDB(data) {
7323
- return new _TokenUnit(
7324
- data[0],
7325
- data[1],
7326
- data.length > 2 ? data[2] : {}
7327
- );
7328
- }
7329
- /**
7330
- * Get fragment zone from metadata
7331
- * Matches JavaScript SDK getFragmentZone method exactly
7332
- */
7333
- getFragmentZone() {
7334
- return this.metas.fragmentZone || null;
7335
- }
7336
- /**
7337
- * Get fused token units from metadata
7338
- * Matches JavaScript SDK getFusedTokenUnits method exactly
7339
- */
7340
- getFusedTokenUnits() {
7341
- return this.metas.fusedTokenUnits || null;
7342
- }
7343
- /**
7344
- * Convert to data array format
7345
- * Matches JavaScript SDK toData method exactly
7346
- */
7347
- toData() {
7348
- return [this.id, this.name, this.metas];
7349
- }
7350
- /**
7351
- * Convert to GraphQL response format
7352
- * Matches JavaScript SDK toGraphQLResponse method exactly
7353
- */
7354
- toGraphQLResponse() {
7355
- return {
7356
- id: this.id,
7357
- name: this.name,
7358
- metas: JSON.stringify(this.metas)
7359
- };
7360
- }
7361
- };
7362
-
7363
- // src/response/ResponseWalletList.ts
7364
7547
  var ResponseWalletList = class _ResponseWalletList extends exports.Response {
7365
7548
  /**
7366
7549
  * Class constructor
@@ -8284,131 +8467,6 @@ var QueryActiveSession = class extends Query {
8284
8467
  }
8285
8468
  };
8286
8469
 
8287
- // src/response/ResponseQueryUserActivity.ts
8288
- init_Response();
8289
- var ResponseQueryUserActivity = class extends exports.Response {
8290
- /**
8291
- * Constructor
8292
- */
8293
- constructor({
8294
- query,
8295
- json
8296
- }) {
8297
- super({
8298
- query,
8299
- json,
8300
- dataKey: "data.UserActivity"
8301
- });
8302
- }
8303
- /**
8304
- * Returns processed user activity data
8305
- */
8306
- payload() {
8307
- const data = JSON.parse(JSON.stringify(this.data()));
8308
- if (data.instances) {
8309
- for (const datum of data.instances) {
8310
- if (datum.jsonData) {
8311
- datum.jsonData = JSON.parse(datum.jsonData);
8312
- }
8313
- }
8314
- }
8315
- return data;
8316
- }
8317
- };
8318
- var QueryUserActivity = class extends Query {
8319
- /**
8320
- * Constructor
8321
- */
8322
- constructor(graphQLClient, knishIOClient) {
8323
- super(graphQLClient, knishIOClient);
8324
- this.$__query = core.gql`
8325
- query UserActivity(
8326
- $bundleHash: String
8327
- $metaType: String
8328
- $metaId: String
8329
- $ipAddress: String
8330
- $browser: String
8331
- $osCpu: String
8332
- $resolution: String
8333
- $timeZone: String
8334
- $countBy: [CountByUserActivity]
8335
- $interval: span
8336
- ) {
8337
- UserActivity(
8338
- bundleHash: $bundleHash
8339
- metaType: $metaType
8340
- metaId: $metaId
8341
- ipAddress: $ipAddress
8342
- browser: $browser
8343
- osCpu: $osCpu
8344
- resolution: $resolution
8345
- timeZone: $timeZone
8346
- countBy: $countBy
8347
- interval: $interval
8348
- ) {
8349
- createdAt
8350
- bundleHash
8351
- metaType
8352
- metaId
8353
- instances {
8354
- bundleHash
8355
- metaType
8356
- metaId
8357
- jsonData
8358
- createdAt
8359
- updatedAt
8360
- }
8361
- instanceCount {
8362
- ...SubFields
8363
- ...Recursive
8364
- }
8365
- }
8366
- }
8367
-
8368
- fragment SubFields on InstanceCountType {
8369
- id
8370
- count
8371
- }
8372
-
8373
- fragment Recursive on InstanceCountType {
8374
- instances {
8375
- ...SubFields
8376
- instances {
8377
- ...SubFields
8378
- instances {
8379
- ...SubFields
8380
- instances {
8381
- ...SubFields
8382
- instances {
8383
- ...SubFields
8384
- instances {
8385
- ...SubFields
8386
- instances {
8387
- ...SubFields
8388
- instances {
8389
- ...SubFields
8390
- }
8391
- }
8392
- }
8393
- }
8394
- }
8395
- }
8396
- }
8397
- }
8398
- }
8399
- `;
8400
- }
8401
- /**
8402
- * Returns a Response object
8403
- */
8404
- createResponse(json) {
8405
- return new ResponseQueryUserActivity({
8406
- query: this,
8407
- json
8408
- });
8409
- }
8410
- };
8411
-
8412
8470
  // src/query/QueryToken.ts
8413
8471
  init_Response();
8414
8472
  var QueryToken = class extends Query {
@@ -9242,6 +9300,20 @@ var MutationTransferTokens = class extends MutationProposeMolecule {
9242
9300
  this.$__molecule.sign({});
9243
9301
  this.$__molecule.check(this.$__molecule.sourceWallet);
9244
9302
  }
9303
+ /**
9304
+ * Fills the Molecule for a MULTI-recipient transfer (one source funds N recipients)
9305
+ */
9306
+ fillMoleculeMulti({
9307
+ recipientWallets,
9308
+ amounts
9309
+ }) {
9310
+ this.$__molecule.initValues({
9311
+ recipientWallets,
9312
+ amounts
9313
+ });
9314
+ this.$__molecule.sign({});
9315
+ this.$__molecule.check(this.$__molecule.sourceWallet);
9316
+ }
9245
9317
  /**
9246
9318
  * Builds a Response object out of a JSON object
9247
9319
  * Matches JavaScript SDK createResponse method signature exactly
@@ -9302,7 +9374,8 @@ var MutationClaimShadowWallet = class extends MutationProposeMolecule {
9302
9374
  bundle: this.$__molecule.bundle,
9303
9375
  token,
9304
9376
  batchId
9305
- })(this.$__molecule).initShadowWalletClaim(wallet);
9377
+ });
9378
+ this.$__molecule.initShadowWalletClaim(wallet);
9306
9379
  this.$__molecule.sign({});
9307
9380
  this.$__molecule.check();
9308
9381
  }
@@ -10548,6 +10621,75 @@ var KnishIOClient = class {
10548
10621
  });
10549
10622
  return await this.executeQuery(query);
10550
10623
  }
10624
+ /**
10625
+ * Fund N recipients from a single source in ONE molecule (multi-recipient sibling of
10626
+ * transferToken). Each recipient gets its own subset of stackable units (or a fungible
10627
+ * amount); a remainder returns the rest to the sender. Conserves:
10628
+ * -balance + Σamounts + (balance - Σ) === 0.
10629
+ *
10630
+ * @param token - token slug
10631
+ * @param recipients - destinations (bundleHash + units OR amount + optional batchId)
10632
+ * @param sourceWallet - optional pre-resolved source (defaults to the client's own wallet)
10633
+ */
10634
+ async transferTokens({
10635
+ token,
10636
+ recipients,
10637
+ sourceWallet = null
10638
+ }) {
10639
+ const amounts = recipients.map((recipient) => {
10640
+ const unitCount = recipient.units ? recipient.units.length : 0;
10641
+ if (unitCount > 0) {
10642
+ if (recipient.amount !== null && recipient.amount !== void 0 && recipient.amount > 0) {
10643
+ throw new StackableUnitAmountException_default();
10644
+ }
10645
+ return unitCount;
10646
+ }
10647
+ return recipient.amount ?? 0;
10648
+ });
10649
+ const total = amounts.reduce((sum, amount) => sum + amount, 0);
10650
+ if (sourceWallet === null) {
10651
+ sourceWallet = await this.querySourceWallet({
10652
+ token,
10653
+ amount: total
10654
+ });
10655
+ }
10656
+ if (sourceWallet === null || Decimal.cmp(Number(sourceWallet.balance), total) < 0) {
10657
+ throw new exports.TransferBalanceException();
10658
+ }
10659
+ const recipientWallets = recipients.map((recipient) => {
10660
+ const recipientWallet = Wallet.create({
10661
+ bundle: recipient.bundleHash,
10662
+ token
10663
+ });
10664
+ if (recipient.batchId !== null && recipient.batchId !== void 0) {
10665
+ recipientWallet.batchId = recipient.batchId;
10666
+ } else {
10667
+ recipientWallet.initBatchId({ sourceWallet });
10668
+ }
10669
+ return recipientWallet;
10670
+ });
10671
+ const remainderWallet = sourceWallet.createRemainder(this.getSecret());
10672
+ if (recipients.some((recipient) => recipient.units && recipient.units.length > 0)) {
10673
+ sourceWallet.splitUnitsMulti(
10674
+ recipients.map((recipient) => recipient.units ?? []),
10675
+ recipientWallets,
10676
+ remainderWallet
10677
+ );
10678
+ }
10679
+ const molecule = await this.createMolecule({
10680
+ sourceWallet,
10681
+ remainderWallet
10682
+ });
10683
+ const query = await this.createMoleculeMutation({
10684
+ mutationClass: MutationTransferTokens,
10685
+ molecule
10686
+ });
10687
+ query.fillMoleculeMulti({
10688
+ recipientWallets,
10689
+ amounts
10690
+ });
10691
+ return await this.executeQuery(query);
10692
+ }
10551
10693
  /**
10552
10694
  * Request authorization token (guest or profile)
10553
10695
  *
@@ -11004,36 +11146,6 @@ var KnishIOClient = class {
11004
11146
  metaId
11005
11147
  });
11006
11148
  }
11007
- /**
11008
- * Query user activity
11009
- */
11010
- async queryUserActivity({
11011
- bundleHash,
11012
- metaType,
11013
- metaId,
11014
- ipAddress = null,
11015
- browser = null,
11016
- osCpu = null,
11017
- resolution = null,
11018
- timeZone = null,
11019
- countBy = null,
11020
- interval = null
11021
- }) {
11022
- const query = this.createQuery(QueryUserActivity);
11023
- const variables = {
11024
- bundleHash,
11025
- metaType,
11026
- metaId
11027
- };
11028
- if (ipAddress !== null) variables.ipAddress = ipAddress;
11029
- if (browser !== null) variables.browser = browser;
11030
- if (osCpu !== null) variables.osCpu = osCpu;
11031
- if (resolution !== null) variables.resolution = resolution;
11032
- if (timeZone !== null) variables.timeZone = timeZone;
11033
- if (countBy !== null) variables.countBy = countBy;
11034
- if (interval !== null) variables.interval = interval;
11035
- return this.executeQuery(query, variables);
11036
- }
11037
11149
  /**
11038
11150
  * Query token information
11039
11151
  */
@@ -11142,9 +11254,27 @@ var KnishIOClient = class {
11142
11254
  amount = null,
11143
11255
  meta = null,
11144
11256
  batchId = null,
11145
- units: _units = null
11257
+ units = null
11146
11258
  }) {
11147
11259
  this.log("info", `KnishIOClient::createToken() - Creating token ${token}...`);
11260
+ const tokenMeta = meta || {};
11261
+ const fungibility = tokenMeta.fungibility;
11262
+ let resolvedAmount = amount ?? 0;
11263
+ if (fungibility === "stackable") {
11264
+ tokenMeta.batchId = batchId || generateBatchId({});
11265
+ }
11266
+ if ((fungibility === "stackable" || fungibility === "nonfungible" || fungibility === "non-fungible") && units && units.length > 0) {
11267
+ if (Number(tokenMeta.decimals ?? 0) > 0) {
11268
+ throw new StackableUnitDecimalsException_default();
11269
+ }
11270
+ if (Number(amount ?? 0) > 0) {
11271
+ throw new StackableUnitAmountException_default();
11272
+ }
11273
+ resolvedAmount = units.length;
11274
+ tokenMeta.splittable = "1";
11275
+ tokenMeta.decimals = "0";
11276
+ tokenMeta.tokenUnits = JSON.stringify(units);
11277
+ }
11148
11278
  const mutation = await this.createMoleculeMutation({ mutationClass: MutationCreateToken });
11149
11279
  const recipientWallet = new Wallet({
11150
11280
  secret: this.getSecret(),
@@ -11154,8 +11284,8 @@ var KnishIOClient = class {
11154
11284
  });
11155
11285
  await mutation.fillMolecule({
11156
11286
  recipientWallet,
11157
- amount: amount ?? 0,
11158
- meta
11287
+ amount: resolvedAmount,
11288
+ meta: tokenMeta
11159
11289
  });
11160
11290
  const response = await this.executeQuery(mutation);
11161
11291
  if (!response) {
@@ -11596,8 +11726,12 @@ var KnishIOClient = class {
11596
11726
  mutationClass: MutationRequestAuthorization,
11597
11727
  molecule
11598
11728
  });
11729
+ const authMeta = { encrypt: encrypt ? "true" : "false" };
11730
+ if (wallet.pubkey) {
11731
+ authMeta.walletPubkey = wallet.pubkey;
11732
+ }
11599
11733
  mutation.fillMolecule({
11600
- meta: { encrypt: encrypt ? "true" : "false" }
11734
+ meta: authMeta
11601
11735
  });
11602
11736
  const response = await this.executeQuery(mutation);
11603
11737
  if (!response) {