@defdone/annon 0.1.0-alpha.0 → 0.1.0-alpha.2

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 (59) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/CLI.md +116 -10
  3. package/README.md +57 -15
  4. package/dist/api/annon-types.d.ts +2 -1
  5. package/dist/api/annon.js +9 -2
  6. package/dist/browser/index.d.ts +1 -1
  7. package/dist/browser/login-bundle.js +113 -77
  8. package/dist/browser/login-dom.d.ts +13 -0
  9. package/dist/browser/login-dom.js +67 -0
  10. package/dist/browser/login-dropdown.d.ts +32 -0
  11. package/dist/browser/login-dropdown.js +129 -0
  12. package/dist/browser/login-format.d.ts +16 -0
  13. package/dist/browser/login-format.js +52 -0
  14. package/dist/browser/login-status.d.ts +70 -0
  15. package/dist/browser/login-status.js +85 -0
  16. package/dist/browser/login-terms.d.ts +10 -0
  17. package/dist/browser/login-terms.js +69 -0
  18. package/dist/browser/login-view.d.ts +34 -0
  19. package/dist/browser/login-view.js +227 -0
  20. package/dist/browser/login-wallet.d.ts +28 -0
  21. package/dist/browser/login-wallet.js +179 -0
  22. package/dist/browser/login.js +115 -101
  23. package/dist/browser/purchase-bundle.js +45 -45
  24. package/dist/browser/purchases.d.ts +4 -0
  25. package/dist/browser/purchases.js +14 -0
  26. package/dist/browser/reconcile.d.ts +41 -0
  27. package/dist/browser/reconcile.js +77 -0
  28. package/dist/node/adapter.js +5 -3
  29. package/dist/node/approval-status.d.ts +34 -0
  30. package/dist/node/approval-status.js +71 -0
  31. package/dist/node/backup-secret-init.d.ts +9 -0
  32. package/dist/node/backup-secret-init.js +103 -0
  33. package/dist/node/backup-secret.d.ts +35 -0
  34. package/dist/node/backup-secret.js +104 -0
  35. package/dist/node/browser-login.d.ts +9 -0
  36. package/dist/node/browser-login.js +75 -14
  37. package/dist/node/buy-commands.js +9 -2
  38. package/dist/node/cli.js +23 -7
  39. package/dist/node/command-context.js +1 -1
  40. package/dist/node/config.d.ts +2 -0
  41. package/dist/node/config.js +3 -1
  42. package/dist/node/login-page.d.ts +7 -0
  43. package/dist/node/login-page.js +71 -0
  44. package/dist/node/purchase-commands.d.ts +2 -2
  45. package/dist/node/purchase-commands.js +10 -6
  46. package/dist/node/saved-state.d.ts +4 -0
  47. package/dist/node/saved-state.js +50 -0
  48. package/dist/node/signer-wallet.js +3 -1
  49. package/dist/node/version.d.ts +1 -0
  50. package/dist/node/version.js +3 -0
  51. package/dist/node/wallets.d.ts +1 -1
  52. package/dist/node/wallets.js +2 -10
  53. package/dist/pay/base.js +36 -43
  54. package/dist/pay/purchase.d.ts +2 -1
  55. package/dist/wallet/account-only.d.ts +1 -1
  56. package/dist/wallet/account-only.js +1 -1
  57. package/dist/wallet/uncertain.d.ts +6 -3
  58. package/dist/wallet/uncertain.js +4 -1
  59. package/package.json +3 -2
package/dist/pay/base.js CHANGED
@@ -298,32 +298,44 @@ async function lastBlock(url, from, validBefore, now) {
298
298
  return at;
299
299
  }
300
300
  }
301
- /** The block of an `AuthorizationCanceled` for this nonce in `[from, to]`, searched in chunks. */
302
- async function canceledAfter(url, e, from, to) {
303
- // Note: 10,000 blocks per query, the common RPC limit; a long-expired purchase takes several.
304
- for (let at = from; at <= to; at += 10_000) {
305
- const end = Math.min(to, at + 9_999);
306
- const topics = [
307
- TOPICS.authorizationCanceled,
308
- toHex(addressWord(e.from)),
309
- e.nonce.toLowerCase(),
310
- ];
301
+ /** Note: blocks per eth_getLogs, the common RPC limit (QuickNode refuses a wider range). */
302
+ const LOG_RANGE = 10_000;
303
+ /**
304
+ * USDC's logs for `topics` (a list in a position matches any of it) in `[from, to]`, read in
305
+ * chunks of `LOG_RANGE` blocks, earliest first. Each chunk's logs must answer that chunk's query.
306
+ * Stops after the first chunk with a log `enough` accepts; an empty range reads nothing.
307
+ */
308
+ async function usdcLogs(url, usdc, topics, from, to, enough) {
309
+ const all = [];
310
+ for (let at = from; at <= to; at += LOG_RANGE) {
311
+ const end = Math.min(to, at + LOG_RANGE - 1);
311
312
  const found = await ask(url, 'eth_getLogs', [
312
- { address: e.usdc, fromBlock: qty(at), toBlock: qty(end), topics },
313
+ { address: usdc, fromBlock: qty(at), toBlock: qty(end), topics },
313
314
  ]);
314
315
  if (!Array.isArray(found))
315
316
  return malformed('eth_getLogs result');
316
- for (const l of found.map(evmLog).filter((l) => !l.removed)) {
317
- if (l.address !== strip(e.usdc) ||
318
- l.topics.join() !== topics.map(strip).join() ||
317
+ const logs = found.map(evmLog).filter((l) => !l.removed);
318
+ // Every log must answer the query; anything else is an RPC that can't be believed.
319
+ for (const l of logs) {
320
+ if (l.address !== strip(usdc) ||
321
+ l.topics.length !== topics.length ||
322
+ !topics.every((t, i) => [t].flat().some((x) => strip(x) === l.topics[i])) ||
319
323
  l.block_number < at ||
320
324
  l.block_number > end) {
321
325
  return malformed('eth_getLogs returned a log the query did not ask for');
322
326
  }
323
- return l.block_number;
324
327
  }
328
+ all.push(...logs);
329
+ if (logs.some(enough))
330
+ break;
325
331
  }
326
- return null;
332
+ return all;
333
+ }
334
+ /** The block of an `AuthorizationCanceled` for this nonce in `[from, to]`. */
335
+ async function canceledAfter(url, e, from, to) {
336
+ const topics = [TOPICS.authorizationCanceled, toHex(addressWord(e.from)), e.nonce.toLowerCase()];
337
+ const [first] = await usdcLogs(url, e.usdc, topics, from, to, () => true);
338
+ return first ? first.block_number : null;
327
339
  }
328
340
  /**
329
341
  * Where a signed purchase stands, found by its nonce alone, so a lost transaction hash loses
@@ -338,33 +350,14 @@ export async function paymentStatus(url, e) {
338
350
  const final = await block(url, 'finalized');
339
351
  const now = await head(url);
340
352
  const last = await lastBlock(url, e.block, e.validBefore, now);
341
- const query = {
342
- address: e.usdc,
343
- fromBlock: qty(e.block),
344
- toBlock: qty(last),
345
- topics: [
346
- [TOPICS.authorizationUsed, TOPICS.authorizationCanceled],
347
- toHex(addressWord(e.from)),
348
- e.nonce.toLowerCase(),
349
- ],
350
- };
351
- const found = await ask(url, 'eth_getLogs', [query]);
352
- if (!Array.isArray(found))
353
- return malformed('eth_getLogs result');
354
- const logs = found.map(evmLog).filter((l) => !l.removed);
355
- // Every log must answer the query; anything else is an RPC that can't be believed.
356
- for (const l of logs) {
357
- if (l.address !== strip(e.usdc) ||
358
- l.topics.length !== 3 ||
359
- !query.topics[0].some((t) => strip(t) === l.topics[0]) ||
360
- l.topics[1] !== topicOf(e.from) ||
361
- l.topics[2] !== strip(e.nonce) ||
362
- l.block_number < e.block ||
363
- l.block_number > last) {
364
- return malformed('eth_getLogs returned a log the query did not ask for');
365
- }
366
- }
367
- const used = logs.find((l) => l.topics[0] === strip(TOPICS.authorizationUsed));
353
+ // A used authorization in any chunk wins over a cancellation, so only that ends the search.
354
+ const usedTopic = strip(TOPICS.authorizationUsed);
355
+ const logs = await usdcLogs(url, e.usdc, [
356
+ [TOPICS.authorizationUsed, TOPICS.authorizationCanceled],
357
+ toHex(addressWord(e.from)),
358
+ e.nonce.toLowerCase(),
359
+ ], e.block, last, (l) => l.topics[0] === usedTopic);
360
+ const used = logs.find((l) => l.topics[0] === usedTopic);
368
361
  if (used) {
369
362
  const receipt = await ask(url, 'eth_getTransactionReceipt', [`0x${used.transaction_hash}`]);
370
363
  const paid = paymentLogs(receipt, e);
@@ -76,9 +76,10 @@ export interface PurchaseRecord extends Versioned {
76
76
  recoveryProved: boolean;
77
77
  /** Source details are the reviewed 1Click quote. */
78
78
  payment: PaymentSummary;
79
+ /** A NEAR send that may be on chain; `txHash` only when the wallet returned one. */
79
80
  nearSubmission?: {
80
81
  state: 'uncertain';
81
- txHash: string;
82
+ txHash?: string;
82
83
  };
83
84
  paymentState?: 'expired' | 'canceled';
84
85
  purchase: Purchase;
@@ -1,4 +1,4 @@
1
- /** Never supply contractId/methodNames: MyNearWallet otherwise generates a local secret. */
1
+ /** Never supply contractId/methodNames: a wallet would otherwise add a function-call key for them. */
2
2
  export declare function signInAccountOnly(wallet: {
3
3
  signOut?: () => Promise<void>;
4
4
  signIn(params: {
@@ -1,4 +1,4 @@
1
- /** Never supply contractId/methodNames: MyNearWallet otherwise generates a local secret. */
1
+ /** Never supply contractId/methodNames: a wallet would otherwise add a function-call key for them. */
2
2
  export async function signInAccountOnly(wallet) {
3
3
  // A reused loopback origin may contain older connector authority. Never let it sign silently.
4
4
  await wallet.signOut?.();
@@ -1,8 +1,11 @@
1
1
  import { AnnonError } from '../errors.js';
2
- /** Public evidence to retain whenever a submitted NEAR transaction cannot be confirmed. */
2
+ /**
3
+ * Public evidence to retain whenever a submitted NEAR transaction cannot be confirmed. `txHash` is
4
+ * absent when a wallet may have broadcast but never returned a hash: unknown, never invented.
5
+ */
3
6
  export declare class TransactionUncertain extends AnnonError {
4
- readonly txHash: string;
7
+ readonly txHash: string | undefined;
5
8
  readonly purchaseId?: string | undefined;
6
9
  readonly code = "transaction_uncertain";
7
- constructor(txHash: string, purchaseId?: string | undefined);
10
+ constructor(txHash: string | undefined, purchaseId?: string | undefined);
8
11
  }
@@ -1,5 +1,8 @@
1
1
  import { AnnonError } from '../errors.js';
2
- /** Public evidence to retain whenever a submitted NEAR transaction cannot be confirmed. */
2
+ /**
3
+ * Public evidence to retain whenever a submitted NEAR transaction cannot be confirmed. `txHash` is
4
+ * absent when a wallet may have broadcast but never returned a hash: unknown, never invented.
5
+ */
3
6
  export class TransactionUncertain extends AnnonError {
4
7
  txHash;
5
8
  purchaseId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defdone/annon",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "Files only the buyer can open: encrypted on the client, the key released by NEAR once the payment is verified.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://annon.dev",
@@ -115,7 +115,8 @@
115
115
  ],
116
116
  "devDependencies": {
117
117
  "@near-wallet-selector/core": "10.1.4",
118
- "@near-wallet-selector/my-near-wallet": "10.1.4",
118
+ "@near-wallet-selector/hot-wallet": "10.1.4",
119
+ "@near-wallet-selector/meteor-wallet": "10.1.4",
119
120
  "@types/node": "26.6.5",
120
121
  "buffer": "6.0.3",
121
122
  "esbuild": "0.28.2",