@quantovainc/qcore 0.1.24 → 0.1.26

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.
Files changed (3) hide show
  1. package/index.d.ts +19 -0
  2. package/package.json +1 -1
  3. package/shared.js +87 -8
package/index.d.ts CHANGED
@@ -197,6 +197,25 @@ export class Client {
197
197
  meterLimit: number | bigint,
198
198
  maxFeeQuon: string | bigint,
199
199
  ): Promise<TransferResult>;
200
+ assetCall(
201
+ seedHex: string,
202
+ index: number,
203
+ target: string,
204
+ argsHex: string,
205
+ assetIssuer: string,
206
+ amount: string | bigint,
207
+ meterLimit: number | bigint,
208
+ maxFeeQuon: string | bigint,
209
+ ): Promise<TransferResult>;
210
+ payableCall(
211
+ seedHex: string,
212
+ index: number,
213
+ target: string,
214
+ argsHex: string,
215
+ value: string | bigint,
216
+ meterLimit: number | bigint,
217
+ maxFeeQuon: string | bigint,
218
+ ): Promise<TransferResult>;
200
219
  contractNonce(contract: string, signerHex: string): Promise<bigint>;
201
220
  contractScalar(contract: string, slot: number | bigint): Promise<bigint>;
202
221
  callSignedOrder(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantovainc/qcore",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Quantova's post quantum client core for JavaScript. Signs with ML-DSA-65 and builds Q1 addresses.",
5
5
  "scripts": {
6
6
  "build": "bash scripts/build.sh",
package/shared.js CHANGED
@@ -35,6 +35,39 @@ function accountIndex(index) {
35
35
  return i;
36
36
  }
37
37
 
38
+ // The nonce is the one signing input that comes from the gateway. A number above 2^53 has already
39
+ // lost precision when read, and a negative or oversized value would wrap into a different unsigned
40
+ // 64 bit nonce at the signer, so each is refused before it is bound into a signature.
41
+ function accountNonce(nonce) {
42
+ if (typeof nonce === 'number' && !Number.isSafeInteger(nonce)) {
43
+ throw new Error('the gateway reported a nonce outside the safe integer range, a number that large silently rounds and would sign a different nonce');
44
+ }
45
+ let n;
46
+ try {
47
+ n = BigInt(nonce);
48
+ } catch {
49
+ throw new Error('the gateway reported a nonce that is not a whole number');
50
+ }
51
+ if (n < 0n || n > 0xffffffffffffffffn) {
52
+ throw new Error('the gateway reported a nonce outside the unsigned 64 bit range');
53
+ }
54
+ return n;
55
+ }
56
+
57
+ function gatewayFee(fee) {
58
+ if (typeof fee === 'number' && !Number.isSafeInteger(fee)) {
59
+ throw new Error('the gateway reported a fee outside the safe integer range, a number that large silently rounds and would sign a different fee');
60
+ }
61
+ let f;
62
+ try {
63
+ f = BigInt(fee);
64
+ } catch {
65
+ throw new Error('the gateway reported a fee that is not a whole number');
66
+ }
67
+ if (f < 0n) throw new Error('the gateway reported a negative fee');
68
+ return f;
69
+ }
70
+
38
71
  async function readBounded(res) {
39
72
  if (!res.body || typeof res.body.getReader !== 'function') {
40
73
  const header = res.headers.get('content-length');
@@ -188,6 +221,7 @@ function makeClient(core) {
188
221
  _signingChainId(info) {
189
222
  const name = info && info.chain_id;
190
223
  if (!name) throw new Error('the gateway did not report a chain id to bind the signature to');
224
+ if (typeof name !== 'string') throw new Error('the gateway reported a chain id that is not a string, refusing to bind a signature to it');
191
225
  const id = BigInt(core.chainIdFromName(name));
192
226
  const configured = this.network && this.network.chainId;
193
227
  if (configured && name !== configured) {
@@ -252,14 +286,14 @@ function makeClient(core) {
252
286
  const chainId = this._signingChainId(info);
253
287
  const fee = info && info.fee && info.fee.transfer_quon;
254
288
  if (fee == null) throw new Error('the gateway did not report a transfer fee');
255
- if (BigInt(fee) > ceiling) {
289
+ if (gatewayFee(fee) > ceiling) {
256
290
  throw new Error(`the gateway fee ${fee} is above the maximum you allowed ${maxFeeQuon}, refusing to sign`);
257
291
  }
258
292
  const from = core.address(seedHex, accountIndex(index));
259
293
  const acct = await this.account(from);
260
294
  if (!acct || acct.nonce == null) throw new Error('the gateway did not report a nonce');
261
295
  const signed = JSON.parse(
262
- core.sign_transfer(seedHex, accountIndex(index), to, String(amount), BigInt(acct.nonce), String(fee), chainId)
296
+ core.sign_transfer(seedHex, accountIndex(index), to, String(amount), accountNonce(acct.nonce), String(fee), chainId)
263
297
  );
264
298
  const outcome = await this.submit(signed.tx_hex);
265
299
  return { signed, outcome };
@@ -272,13 +306,13 @@ function makeClient(core) {
272
306
  const chainId = this._signingChainId(info);
273
307
  const fee = info && info.fee && info.fee.transfer_quon;
274
308
  if (fee == null) throw new Error('the gateway did not report a transfer fee');
275
- if (BigInt(fee) > ceiling) {
309
+ if (gatewayFee(fee) > ceiling) {
276
310
  throw new Error(`the gateway fee ${fee} is above the maximum you allowed ${maxFeeQuon}, refusing to sign`);
277
311
  }
278
312
  const from = core.address(seedHex, accountIndex(index));
279
313
  const acct = await this.account(from);
280
314
  if (!acct || acct.nonce == null) throw new Error('the gateway did not report a nonce');
281
- const signed = JSON.parse(core.signRegister(seedHex, accountIndex(index), BigInt(acct.nonce), String(fee), chainId));
315
+ const signed = JSON.parse(core.signRegister(seedHex, accountIndex(index), accountNonce(acct.nonce), String(fee), chainId));
282
316
  const outcome = await this.submit(signed.tx_hex);
283
317
  return { signed, outcome };
284
318
  }
@@ -291,14 +325,59 @@ function makeClient(core) {
291
325
  const chainId = this._signingChainId(info);
292
326
  const fee = info && info.fee && info.fee.transfer_quon;
293
327
  if (fee == null) throw new Error('the gateway did not report a transfer fee');
294
- if (BigInt(fee) > ceiling) {
328
+ if (gatewayFee(fee) > ceiling) {
329
+ throw new Error(`the gateway fee ${fee} is above the maximum you allowed ${maxFeeQuon}, refusing to sign`);
330
+ }
331
+ const from = core.address(seedHex, accountIndex(index));
332
+ const acct = await this.account(from);
333
+ if (!acct || acct.nonce == null) throw new Error('the gateway did not report a nonce');
334
+ const signed = JSON.parse(
335
+ core.sign_call(seedHex, accountIndex(index), target, argsHex, accountNonce(acct.nonce), BigInt(meterLimit), String(fee), chainId)
336
+ );
337
+ const outcome = await this.submit(signed.tx_hex);
338
+ return { signed, outcome };
339
+ }
340
+
341
+ async assetCall(seedHex, index, target, argsHex, assetIssuer, amount, meterLimit, maxFeeQuon) {
342
+ if (!core.valid_address(target)) throw new Error('the target is not a q1 address');
343
+ if (!core.valid_address(assetIssuer)) throw new Error('the asset issuer is not a q1 address');
344
+ checkAmount(amount);
345
+ const ceiling = feeCeiling(maxFeeQuon);
346
+ const info = await this.nodeInfo();
347
+ this._guardMainnet();
348
+ const chainId = this._signingChainId(info);
349
+ const fee = info && info.fee && info.fee.transfer_quon;
350
+ if (fee == null) throw new Error('the gateway did not report a transfer fee');
351
+ if (gatewayFee(fee) > ceiling) {
352
+ throw new Error(`the gateway fee ${fee} is above the maximum you allowed ${maxFeeQuon}, refusing to sign`);
353
+ }
354
+ const from = core.address(seedHex, accountIndex(index));
355
+ const acct = await this.account(from);
356
+ if (!acct || acct.nonce == null) throw new Error('the gateway did not report a nonce');
357
+ const signed = JSON.parse(
358
+ core.signAssetCall(seedHex, accountIndex(index), target, argsHex, assetIssuer, String(amount), accountNonce(acct.nonce), BigInt(meterLimit), String(fee), chainId)
359
+ );
360
+ const outcome = await this.submit(signed.tx_hex);
361
+ return { signed, outcome };
362
+ }
363
+
364
+ async payableCall(seedHex, index, target, argsHex, value, meterLimit, maxFeeQuon) {
365
+ if (!core.valid_address(target)) throw new Error('the target is not a q1 address');
366
+ checkAmount(value);
367
+ const ceiling = feeCeiling(maxFeeQuon);
368
+ const info = await this.nodeInfo();
369
+ this._guardMainnet();
370
+ const chainId = this._signingChainId(info);
371
+ const fee = info && info.fee && info.fee.transfer_quon;
372
+ if (fee == null) throw new Error('the gateway did not report a transfer fee');
373
+ if (gatewayFee(fee) > ceiling) {
295
374
  throw new Error(`the gateway fee ${fee} is above the maximum you allowed ${maxFeeQuon}, refusing to sign`);
296
375
  }
297
376
  const from = core.address(seedHex, accountIndex(index));
298
377
  const acct = await this.account(from);
299
378
  if (!acct || acct.nonce == null) throw new Error('the gateway did not report a nonce');
300
379
  const signed = JSON.parse(
301
- core.sign_call(seedHex, accountIndex(index), target, argsHex, BigInt(acct.nonce), BigInt(meterLimit), String(fee), chainId)
380
+ core.signPayableCall(seedHex, accountIndex(index), target, argsHex, accountNonce(acct.nonce), BigInt(meterLimit), String(fee), String(value), chainId)
302
381
  );
303
382
  const outcome = await this.submit(signed.tx_hex);
304
383
  return { signed, outcome };
@@ -322,7 +401,7 @@ function makeClient(core) {
322
401
  const chainId = this._signingChainId(info);
323
402
  const fee = info && info.fee && info.fee.transfer_quon;
324
403
  if (fee == null) throw new Error('the gateway did not report a transfer fee');
325
- if (BigInt(fee) > ceiling) {
404
+ if (gatewayFee(fee) > ceiling) {
326
405
  throw new Error(`the gateway fee ${fee} is above the maximum you allowed ${maxFeeQuon}, refusing to sign`);
327
406
  }
328
407
  const signer = core.orderSigner(ownerSeedHex, accountIndex(ownerIndex));
@@ -343,7 +422,7 @@ function makeClient(core) {
343
422
  const acct = await this.account(from);
344
423
  if (!acct || acct.nonce == null) throw new Error('the gateway did not report a nonce');
345
424
  const signed = JSON.parse(
346
- core.sign_call(callerSeedHex, accountIndex(callerIndex), contract, order.call_args, BigInt(acct.nonce), BigInt(meterLimit), String(fee), chainId)
425
+ core.sign_call(callerSeedHex, accountIndex(callerIndex), contract, order.call_args, accountNonce(acct.nonce), BigInt(meterLimit), String(fee), chainId)
347
426
  );
348
427
  const outcome = await this.submit(signed.tx_hex);
349
428
  return { order, signed, outcome };