@defdone/annon 0.0.0-stage → 0.1.0-alpha.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.
Files changed (214) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/CLI.md +378 -0
  3. package/LICENSE +201 -0
  4. package/NOTICE +7 -0
  5. package/README.md +581 -2
  6. package/dist/api/annon-types.d.ts +108 -0
  7. package/dist/api/annon-types.js +2 -0
  8. package/dist/api/annon.d.ts +117 -0
  9. package/dist/api/annon.js +1152 -0
  10. package/dist/api/hosted-record.d.ts +13 -0
  11. package/dist/api/hosted-record.js +149 -0
  12. package/dist/api/sharing-types.d.ts +109 -0
  13. package/dist/api/sharing-types.js +1 -0
  14. package/dist/api/sharing.d.ts +36 -0
  15. package/dist/api/sharing.js +879 -0
  16. package/dist/browser/download.d.ts +19 -0
  17. package/dist/browser/download.js +52 -0
  18. package/dist/browser/index.d.ts +23 -0
  19. package/dist/browser/index.js +22 -0
  20. package/dist/browser/login-bundle.js +116 -0
  21. package/dist/browser/login.d.ts +1 -0
  22. package/dist/browser/login.js +177 -0
  23. package/dist/browser/purchase-bundle.js +84 -0
  24. package/dist/browser/purchases.d.ts +98 -0
  25. package/dist/browser/purchases.js +735 -0
  26. package/dist/browser/record.d.ts +55 -0
  27. package/dist/browser/record.js +16 -0
  28. package/dist/browser/selling.d.ts +146 -0
  29. package/dist/browser/selling.js +418 -0
  30. package/dist/browser/wallets.d.ts +53 -0
  31. package/dist/browser/wallets.js +286 -0
  32. package/dist/bytes.d.ts +4 -0
  33. package/dist/bytes.js +16 -0
  34. package/dist/download/download.d.ts +59 -0
  35. package/dist/download/download.js +384 -0
  36. package/dist/download/host.d.ts +56 -0
  37. package/dist/download/host.js +161 -0
  38. package/dist/download/http.d.ts +23 -0
  39. package/dist/download/http.js +87 -0
  40. package/dist/download/proof.d.ts +20 -0
  41. package/dist/download/proof.js +79 -0
  42. package/dist/download/upload.d.ts +130 -0
  43. package/dist/download/upload.js +408 -0
  44. package/dist/envelope/evm.d.ts +4 -0
  45. package/dist/envelope/evm.js +32 -0
  46. package/dist/envelope/pay2.d.ts +176 -0
  47. package/dist/envelope/pay2.js +541 -0
  48. package/dist/envelope/replay.d.ts +45 -0
  49. package/dist/envelope/replay.js +186 -0
  50. package/dist/envelope/stream.d.ts +78 -0
  51. package/dist/envelope/stream.js +372 -0
  52. package/dist/errors.d.ts +21 -0
  53. package/dist/errors.js +300 -0
  54. package/dist/index.d.ts +25 -0
  55. package/dist/index.js +23 -0
  56. package/dist/keys/near-ckd.d.ts +47 -0
  57. package/dist/keys/near-ckd.js +96 -0
  58. package/dist/limits.d.ts +4 -0
  59. package/dist/limits.js +4 -0
  60. package/dist/near/nep413.d.ts +22 -0
  61. package/dist/near/nep413.js +39 -0
  62. package/dist/near/rpc.d.ts +170 -0
  63. package/dist/near/rpc.js +423 -0
  64. package/dist/near/texts.d.ts +39 -0
  65. package/dist/near/texts.js +60 -0
  66. package/dist/net/ip.d.ts +7 -0
  67. package/dist/net/ip.js +62 -0
  68. package/dist/net/retry.d.ts +3 -0
  69. package/dist/net/retry.js +31 -0
  70. package/dist/node/adapter-checks.d.ts +6 -0
  71. package/dist/node/adapter-checks.js +13 -0
  72. package/dist/node/adapter.d.ts +8 -0
  73. package/dist/node/adapter.js +99 -0
  74. package/dist/node/agent.d.ts +49 -0
  75. package/dist/node/agent.js +266 -0
  76. package/dist/node/amounts.d.ts +2 -0
  77. package/dist/node/amounts.js +20 -0
  78. package/dist/node/browser-login.d.ts +18 -0
  79. package/dist/node/browser-login.js +212 -0
  80. package/dist/node/budget.d.ts +24 -0
  81. package/dist/node/budget.js +59 -0
  82. package/dist/node/buy-commands.d.ts +24 -0
  83. package/dist/node/buy-commands.js +301 -0
  84. package/dist/node/cli.d.ts +60 -0
  85. package/dist/node/cli.js +201 -0
  86. package/dist/node/command-context.d.ts +82 -0
  87. package/dist/node/command-context.js +77 -0
  88. package/dist/node/config.d.ts +37 -0
  89. package/dist/node/config.js +189 -0
  90. package/dist/node/core-adapter.d.ts +43 -0
  91. package/dist/node/core-adapter.js +122 -0
  92. package/dist/node/errors.d.ts +8 -0
  93. package/dist/node/errors.js +12 -0
  94. package/dist/node/index.d.ts +7 -0
  95. package/dist/node/index.js +5 -0
  96. package/dist/node/io.d.ts +13 -0
  97. package/dist/node/io.js +112 -0
  98. package/dist/node/keychain.d.ts +10 -0
  99. package/dist/node/keychain.js +67 -0
  100. package/dist/node/main.d.ts +2 -0
  101. package/dist/node/main.js +21 -0
  102. package/dist/node/offers.d.ts +16 -0
  103. package/dist/node/offers.js +91 -0
  104. package/dist/node/origin.d.ts +11 -0
  105. package/dist/node/origin.js +80 -0
  106. package/dist/node/paths.d.ts +3 -0
  107. package/dist/node/paths.js +71 -0
  108. package/dist/node/payment-terms.d.ts +26 -0
  109. package/dist/node/payment-terms.js +127 -0
  110. package/dist/node/purchase-commands.d.ts +16 -0
  111. package/dist/node/purchase-commands.js +237 -0
  112. package/dist/node/purchase-guard.d.ts +19 -0
  113. package/dist/node/purchase-guard.js +53 -0
  114. package/dist/node/relay-transport.d.ts +6 -0
  115. package/dist/node/relay-transport.js +173 -0
  116. package/dist/node/resources.d.ts +2 -0
  117. package/dist/node/resources.js +26 -0
  118. package/dist/node/retry-gate.d.ts +15 -0
  119. package/dist/node/retry-gate.js +53 -0
  120. package/dist/node/seller-commands.d.ts +12 -0
  121. package/dist/node/seller-commands.js +207 -0
  122. package/dist/node/sharing.d.ts +3 -0
  123. package/dist/node/sharing.js +28 -0
  124. package/dist/node/shops.d.ts +17 -0
  125. package/dist/node/shops.js +74 -0
  126. package/dist/node/signer-mock.d.ts +11 -0
  127. package/dist/node/signer-mock.js +79 -0
  128. package/dist/node/signer-wallet.d.ts +8 -0
  129. package/dist/node/signer-wallet.js +147 -0
  130. package/dist/node/signing.d.ts +29 -0
  131. package/dist/node/signing.js +200 -0
  132. package/dist/node/storage-commands.d.ts +11 -0
  133. package/dist/node/storage-commands.js +50 -0
  134. package/dist/node/sync-dir.d.ts +1 -0
  135. package/dist/node/sync-dir.js +11 -0
  136. package/dist/node/wallets.d.ts +13 -0
  137. package/dist/node/wallets.js +146 -0
  138. package/dist/oneclick/oneclick.d.ts +132 -0
  139. package/dist/oneclick/oneclick.js +342 -0
  140. package/dist/pay/backup.d.ts +70 -0
  141. package/dist/pay/backup.js +402 -0
  142. package/dist/pay/base.d.ts +135 -0
  143. package/dist/pay/base.js +393 -0
  144. package/dist/pay/eip3009.d.ts +59 -0
  145. package/dist/pay/eip3009.js +72 -0
  146. package/dist/pay/pay.d.ts +86 -0
  147. package/dist/pay/pay.js +297 -0
  148. package/dist/pay/purchase.d.ts +103 -0
  149. package/dist/pay/purchase.js +20 -0
  150. package/dist/pay/recover.d.ts +31 -0
  151. package/dist/pay/recover.js +34 -0
  152. package/dist/pay/seller.d.ts +130 -0
  153. package/dist/pay/seller.js +444 -0
  154. package/dist/pay/settle.d.ts +96 -0
  155. package/dist/pay/settle.js +308 -0
  156. package/dist/pay/split.d.ts +115 -0
  157. package/dist/pay/split.js +256 -0
  158. package/dist/pay/store-node.d.ts +8 -0
  159. package/dist/pay/store-node.js +224 -0
  160. package/dist/pay/store.d.ts +54 -0
  161. package/dist/pay/store.js +135 -0
  162. package/dist/pay/x402.d.ts +95 -0
  163. package/dist/pay/x402.js +136 -0
  164. package/dist/relay/admission.d.ts +8 -0
  165. package/dist/relay/admission.js +58 -0
  166. package/dist/relay/capabilities.d.ts +36 -0
  167. package/dist/relay/capabilities.js +232 -0
  168. package/dist/relay/fees.d.ts +17 -0
  169. package/dist/relay/fees.js +105 -0
  170. package/dist/relay/index.d.ts +10 -0
  171. package/dist/relay/index.js +8 -0
  172. package/dist/relay/keeper-balance.d.ts +17 -0
  173. package/dist/relay/keeper-balance.js +46 -0
  174. package/dist/relay/keeper-runner.d.ts +114 -0
  175. package/dist/relay/keeper-runner.js +703 -0
  176. package/dist/relay/keeper.d.ts +38 -0
  177. package/dist/relay/keeper.js +73 -0
  178. package/dist/relay/ledger.d.ts +155 -0
  179. package/dist/relay/ledger.js +392 -0
  180. package/dist/relay/relay-checks.d.ts +3 -0
  181. package/dist/relay/relay-checks.js +244 -0
  182. package/dist/relay/relay-reconciliation.d.ts +15 -0
  183. package/dist/relay/relay-reconciliation.js +293 -0
  184. package/dist/relay/relay-request.d.ts +6 -0
  185. package/dist/relay/relay-request.js +139 -0
  186. package/dist/relay/relay-transactions.d.ts +23 -0
  187. package/dist/relay/relay-transactions.js +182 -0
  188. package/dist/relay/relay-types.d.ts +128 -0
  189. package/dist/relay/relay-types.js +17 -0
  190. package/dist/relay/relay.d.ts +102 -0
  191. package/dist/relay/relay.js +426 -0
  192. package/dist/relay/reservation.d.ts +29 -0
  193. package/dist/relay/reservation.js +132 -0
  194. package/dist/relay/sponsorship.d.ts +42 -0
  195. package/dist/relay/sponsorship.js +184 -0
  196. package/dist/routes.d.ts +2 -0
  197. package/dist/routes.js +1 -0
  198. package/dist/settle-near/codec.d.ts +17 -0
  199. package/dist/settle-near/codec.js +86 -0
  200. package/dist/settle-near/identity.d.ts +27 -0
  201. package/dist/settle-near/identity.js +80 -0
  202. package/dist/settle-near/purchase.d.ts +40 -0
  203. package/dist/settle-near/purchase.js +153 -0
  204. package/dist/settle-near/terms.d.ts +37 -0
  205. package/dist/settle-near/terms.js +89 -0
  206. package/dist/shops/discovery.d.ts +95 -0
  207. package/dist/shops/discovery.js +384 -0
  208. package/dist/wallet/account-only.d.ts +11 -0
  209. package/dist/wallet/account-only.js +6 -0
  210. package/dist/wallet/uncertain.d.ts +8 -0
  211. package/dist/wallet/uncertain.js +13 -0
  212. package/dist/wallet/wallet.d.ts +100 -0
  213. package/dist/wallet/wallet.js +204 -0
  214. package/package.json +135 -3
@@ -0,0 +1,139 @@
1
+ /** Relay request canonicalization, contract arguments and operation identity. */
2
+ import { bytes, exactKeys, uint } from '../settle-near/codec.js';
3
+ import { buyerKey, parsePurchaseMessage } from '../settle-near/purchase.js';
4
+ import { termsHash } from '../settle-near/terms.js';
5
+ import { fail } from './relay-types.js';
6
+ import { parseReservation } from './reservation.js';
7
+ export function canonicalRequest(input, assets) {
8
+ try {
9
+ const r = structuredClone(input);
10
+ if (!r || typeof r !== 'object')
11
+ throw new Error('expected request');
12
+ exactKeys(r, [
13
+ 'method',
14
+ 'args',
15
+ ...('auth' in r ? ['auth'] : []),
16
+ ...('baseSplit' in r ? ['baseSplit'] : []),
17
+ ...('hostingReservation' in r ? ['hostingReservation'] : []),
18
+ ]);
19
+ if ('hostingReservation' in r) {
20
+ if (r.method !== 'register')
21
+ throw new Error('invalid hosting reservation');
22
+ r.hostingReservation = parseReservation(r.hostingReservation);
23
+ }
24
+ if ('baseSplit' in r) {
25
+ if (r.method !== 'register' || !r.baseSplit)
26
+ throw new Error('invalid split provenance');
27
+ exactKeys(r.baseSplit, ['kind', 'salt']);
28
+ if (!['push', 'pull'].includes(r.baseSplit.kind))
29
+ throw new Error('invalid split kind');
30
+ bytes(r.baseSplit.salt, 32);
31
+ }
32
+ if ('auth' in r) {
33
+ if (!['register', 'reopen', 'reopen_seller', 'stop_sales', 'fund_file_storage'].includes(r.method))
34
+ throw new Error('unexpected authorization');
35
+ exactKeys(r.auth, ['kind', 'sig']);
36
+ if (!['evm', 'solana'].includes(r.auth.kind))
37
+ throw new Error('invalid auth kind');
38
+ bytes(r.auth.sig, r.auth.kind === 'evm' ? 65 : 64);
39
+ }
40
+ const a = r.args;
41
+ switch (r.method) {
42
+ case 'fund_file_storage':
43
+ exactKeys(a, ['file_id', 'amount', 'nonce', 'deadline']);
44
+ bytes(r.args.nonce, 32);
45
+ bytes(r.args.file_id, 32);
46
+ if (uint(r.args.amount, 128) <= 0n)
47
+ throw new Error('positive topup required');
48
+ break;
49
+ case 'register':
50
+ exactKeys(a, ['terms', 'seller_key', 'deadline']);
51
+ termsHash(r.args.terms, assets);
52
+ buyerKey(r.args.seller_key);
53
+ break;
54
+ case 'reopen':
55
+ exactKeys(a, ['purchase_id', 'fresh_key', 'nonce', 'deadline']);
56
+ bytes(r.args.purchase_id, 32);
57
+ buyerKey(r.args.fresh_key);
58
+ bytes(r.args.nonce, 32);
59
+ break;
60
+ case 'reopen_seller':
61
+ case 'stop_sales':
62
+ exactKeys(a, ['file_id', 'key', 'deadline']);
63
+ bytes(r.args.file_id, 32);
64
+ if (r.method === 'reopen_seller')
65
+ buyerKey(r.args.key ?? '');
66
+ else if (r.args.key !== null)
67
+ throw new Error('stop key must be null');
68
+ break;
69
+ case 'claim_key':
70
+ exactKeys(a, ['purchase_id', ...('grant' in a ? ['grant'] : [])]);
71
+ bytes(r.args.purchase_id, 32);
72
+ if (r.args.grant !== undefined &&
73
+ (!Number.isInteger(r.args.grant) || r.args.grant < 0 || r.args.grant > 0xffffffff))
74
+ throw new Error('invalid grant');
75
+ r.args.grant = r.args.grant ?? 0;
76
+ break;
77
+ case 'unlock':
78
+ exactKeys(a, ['purchase', 'logs', 'header']);
79
+ if (parsePurchaseMessage(r.args.purchase, assets).route !== 'base-usdc')
80
+ throw new Error('not a Base purchase');
81
+ if (!Array.isArray(r.args.logs) ||
82
+ r.args.logs.length !== 2 ||
83
+ (r.args.header !== null && !/^0x([0-9a-f]{2})+$/.test(r.args.header)))
84
+ throw new Error('invalid logs/header');
85
+ r.args.logs = r.args.logs.map((input) => {
86
+ const fields = [
87
+ 'address',
88
+ 'block_hash',
89
+ 'block_number',
90
+ 'data',
91
+ 'log_index',
92
+ 'removed',
93
+ 'topics',
94
+ 'transaction_hash',
95
+ 'transaction_index',
96
+ ];
97
+ exactKeys(input, [...fields]);
98
+ for (const key of ['address', 'block_hash', 'transaction_hash'])
99
+ bytes(`0x${input[key]}`, key === 'address' ? 20 : 32);
100
+ if (!Array.isArray(input.topics) || input.topics.length !== 3)
101
+ throw new Error('invalid log topics');
102
+ for (const topic of input.topics)
103
+ bytes(`0x${topic}`, 32);
104
+ for (const key of ['block_number', 'log_index', 'transaction_index'])
105
+ if (!Number.isSafeInteger(input[key]) || input[key] < 0)
106
+ throw new Error('invalid log index');
107
+ if (input.removed !== false ||
108
+ typeof input.data !== 'string' ||
109
+ input.data.length > 256 ||
110
+ !/^0x(?:[0-9a-f]{2})*$/.test(input.data))
111
+ throw new Error('invalid log data');
112
+ return Object.fromEntries(fields.map((k) => [k, input[k]]));
113
+ });
114
+ break;
115
+ default:
116
+ throw new Error('unknown method');
117
+ }
118
+ if ('deadline' in a) {
119
+ if (!Number.isSafeInteger(a.deadline))
120
+ throw new Error('invalid deadline');
121
+ uint(String(a.deadline), 64);
122
+ }
123
+ if (['register', 'reopen', 'stop_sales', 'reopen_seller', 'fund_file_storage'].includes(r.method) &&
124
+ !('auth' in r))
125
+ throw new Error('auth required; NEAR identities call directly');
126
+ return r;
127
+ }
128
+ catch (e) {
129
+ return fail('bad_request', e.message);
130
+ }
131
+ }
132
+ /** Contract wire shape: unlock and identity calls wrap args; claim_key is flat. */
133
+ export const contractArgs = (r) => r.method === 'fund_file_storage'
134
+ ? { file_id: r.args.file_id, amount: r.args.amount }
135
+ : 'auth' in r
136
+ ? { args: r.args, auth: r.auth }
137
+ : r.method === 'unlock'
138
+ ? { args: r.args }
139
+ : r.args;
@@ -0,0 +1,23 @@
1
+ /** NEAR transaction signing, submission retries and RPC evidence helpers. */
2
+ import { type RelayResult, type Sender } from './relay-types.js';
3
+ /** Signs one function call, no deposit, with the sender's key (one account per key, so near-api-js
4
+ * numbers its nonces locally across calls). */
5
+ declare function sign(o: {
6
+ nearRpc: string;
7
+ contractId: string;
8
+ sender: Sender;
9
+ }, key: string, method: string, args: object, gas?: bigint): Promise<{
10
+ signed: string;
11
+ referenceHash?: string;
12
+ txHash: string;
13
+ }>;
14
+ /**
15
+ * Sends signed bytes; after a lost or unclear answer, looks the transaction up and sends the same
16
+ * bytes again (same hash). Only definite validation refusals are reported as not sent.
17
+ */
18
+ declare function send(o: {
19
+ nearRpc: string;
20
+ sender: Sender;
21
+ }, key: string, txHash: string, signed: string, keeper?: boolean, unknownFinal?: boolean): Promise<RelayResult>;
22
+ export { lookUp, senderKey } from './relay.js';
23
+ export { send, sign };
@@ -0,0 +1,182 @@
1
+ /** NEAR transaction signing, submission retries and RPC evidence helpers. */
2
+ import { sha256 } from '@noble/hashes/sha2.js';
3
+ import { base58, base64 } from '@scure/base';
4
+ import { Account, actions, encodeTransaction, JsonRpcProvider } from 'near-api-js';
5
+ import { RPC_TIMEOUT_MS, rpcReply } from '../near/rpc.js';
6
+ import { sleep } from '../net/retry.js';
7
+ import { accounts, GAS, inBlock, isObject, lookUp, RESENDS, RETRY_WAIT } from './relay.js';
8
+ import { fail } from './relay-types.js';
9
+ /**
10
+ * The provider near-api-js signs with: it reads the key's nonce and a recent block. Every reply
11
+ * that sends a transaction or looks one up goes through `rpcReply` instead (its id checked, one
12
+ * fetch, aborted at the deadline).
13
+ */
14
+ // Note: the timer ends the wait, not the request (near-api-js 7.4 takes no AbortSignal).
15
+ function signingProvider(url) {
16
+ const p = new JsonRpcProvider({ url });
17
+ const send = p.sendJsonRpc.bind(p);
18
+ p.sendJsonRpc = ((method, params) => {
19
+ let timer;
20
+ const late = new Promise((_, no) => {
21
+ timer = setTimeout(() => no(new Error(`NEAR RPC ${method} timed out`)), RPC_TIMEOUT_MS);
22
+ });
23
+ return Promise.race([send(method, params), late]).finally(() => clearTimeout(timer));
24
+ });
25
+ return p;
26
+ }
27
+ /**
28
+ * What NEAR's error guide (docs.near.org/api/rpc/transactions#error-handling) says to do after an
29
+ * error: send the same bytes again now or `later`, only look it up (`pending`), ask a node that
30
+ * tracks its shard (`elsewhere`), or `stop`: the same node would give the same answer.
31
+ */
32
+ function advice(e) {
33
+ const refused = e.data
34
+ ?.TxExecutionError?.InvalidTxError;
35
+ // Nodes before nearcore 2.14 send no `cause.info`: the same as NOT_OBSERVED.
36
+ const timeout = e.cause?.name === 'TIMEOUT_ERROR'
37
+ ? (e.cause.info?.cause ?? 'NOT_OBSERVED')
38
+ : undefined;
39
+ if (e.cause?.name === 'DOES_NOT_TRACK_SHARD' || timeout === 'DOES_NOT_TRACK_SHARD')
40
+ return 'elsewhere';
41
+ if (timeout === 'PENDING')
42
+ return 'pending';
43
+ if (timeout === 'NOT_OBSERVED')
44
+ return 'again';
45
+ const busy = isObject(refused) && ('ShardCongested' in refused || 'ShardStuck' in refused);
46
+ if (busy || e.name === 'INTERNAL_ERROR' || timeout === 'ERROR')
47
+ return 'later';
48
+ return 'stop';
49
+ }
50
+ /** NEAR's own words for an error: the transaction's refusal, or the whole error. */
51
+ function describe(e) {
52
+ const { name, cause, data, message } = e;
53
+ const refused = data
54
+ ?.TxExecutionError?.InvalidTxError;
55
+ return JSON.stringify(refused ?? { name, cause, data, message });
56
+ }
57
+ /** Signs one function call, no deposit, with the sender's key (one account per key, so near-api-js
58
+ * numbers its nonces locally across calls). */
59
+ async function sign(o, key, method, args, gas = GAS) {
60
+ let account = accounts.get(key);
61
+ if (!account) {
62
+ account = new Account(o.sender.accountId, signingProvider(o.nearRpc), o.sender.key);
63
+ accounts.set(key, account);
64
+ }
65
+ const tx = await account
66
+ .createSignedTransaction({
67
+ receiverId: o.contractId,
68
+ actions: [actions.functionCall(method, args, gas, 0n)],
69
+ })
70
+ .catch(() => fail('transport', 'NEAR signing is unavailable.'));
71
+ return {
72
+ txHash: base58.encode(sha256(tx.transaction.encode())),
73
+ ...(tx.transaction.blockHash?.length === 32
74
+ ? { referenceHash: base58.encode(Uint8Array.from(tx.transaction.blockHash)) }
75
+ : {}),
76
+ signed: base64.encode(encodeTransaction(tx)),
77
+ };
78
+ }
79
+ /**
80
+ * Sends signed bytes; after a lost or unclear answer, looks the transaction up and sends the same
81
+ * bytes again (same hash). Only definite validation refusals are reported as not sent.
82
+ */
83
+ async function send(o, key, txHash, signed, keeper = false, unknownFinal = false) {
84
+ const unclear = (reason) => ({ txHash, uncertain: true, reason });
85
+ let reason = 'no clear answer from NEAR';
86
+ // After NEAR said it has the transaction (a PENDING timeout), the relayer only looks it up.
87
+ let look = false;
88
+ let ambiguous = false;
89
+ try {
90
+ for (let attempt = 0; attempt <= RESENDS; attempt++) {
91
+ const reply = look
92
+ ? undefined
93
+ : await rpcReply(o.nearRpc, 'send_tx', {
94
+ signed_tx_base64: signed,
95
+ wait_until: 'INCLUDED',
96
+ }).catch(() => null);
97
+ if (reply && !reply.error && inBlock(reply.result))
98
+ return { txHash };
99
+ const legacy = reply?.error?.data;
100
+ const info = reply?.error?.cause?.info;
101
+ const refused = legacy?.TxExecutionError?.InvalidTxError ?? info?.InvalidTxError;
102
+ const variant = typeof refused === 'string'
103
+ ? refused
104
+ : isObject(refused)
105
+ ? Object.keys(refused)[0]
106
+ : undefined;
107
+ // InvalidNonce can mean these very bytes already landed. Unknown variants are ambiguous too.
108
+ const definite = new Set([
109
+ 'NotEnoughBalance',
110
+ 'NotEnoughAllowance',
111
+ 'InvalidAccessKeyError',
112
+ 'InvalidSignerId',
113
+ 'SignerDoesNotExist',
114
+ 'InvalidReceiverId',
115
+ 'InvalidSignature',
116
+ 'CostOverflow',
117
+ 'InvalidChain',
118
+ 'Expired',
119
+ 'ActionsValidation',
120
+ 'TransactionSizeExceeded',
121
+ ]);
122
+ if (unknownFinal)
123
+ definite.add('InvalidNonce');
124
+ if (variant !== undefined && definite.has(variant)) {
125
+ accounts.delete(key);
126
+ if (ambiguous) {
127
+ const status = await lookUp(o.nearRpc, txHash, o.sender.accountId);
128
+ if (status && !status.error && inBlock(status.result))
129
+ return { txHash };
130
+ return unclear('an earlier submission may have landed before this validation refusal');
131
+ }
132
+ // Only the variant name is public; RPC data may echo arbitrary transaction fields.
133
+ return {
134
+ txHash,
135
+ rejected: true,
136
+ reason: variant && /^[A-Za-z]+$/.test(variant) ? variant : 'InvalidTxError',
137
+ };
138
+ }
139
+ ambiguous = true;
140
+ // The chain is past the nonces given out here: later operations read it afresh, whether or
141
+ // not this one landed.
142
+ if (variant === 'InvalidNonce')
143
+ accounts.delete(key);
144
+ const status = await lookUp(o.nearRpc, txHash, o.sender.accountId);
145
+ if (status && !status.error && inBlock(status.result))
146
+ return { txHash };
147
+ // A refusal doesn't prove nothing landed (a node may accept a transaction and refuse it on
148
+ // a later check in the same call), so it is unclear too. Errors of the send and of the
149
+ // lookup are taken as NEAR's guide says; an unknown transaction is only "not seen yet".
150
+ let later = false;
151
+ for (const [what, e] of [
152
+ ['send', reply?.error],
153
+ [
154
+ 'lookup',
155
+ status?.error?.cause?.name === 'UNKNOWN_TRANSACTION' ? undefined : status?.error,
156
+ ],
157
+ ]) {
158
+ if (!e)
159
+ continue;
160
+ const next = advice(e);
161
+ reason = `NEAR answered the ${what}: ${describe(e)}`;
162
+ if (next === 'elsewhere')
163
+ return unclear(`${reason}; ask a NEAR RPC that tracks its shard`);
164
+ if (what === 'send' && (next === 'stop' || (keeper && next !== 'pending')))
165
+ return unclear(reason);
166
+ if (next === 'pending')
167
+ look = true;
168
+ if (next === 'later')
169
+ later = true;
170
+ }
171
+ // Note: sends wait in line meanwhile, at most 3 s per transaction.
172
+ if (attempt < RESENDS && (later || look))
173
+ await sleep(RETRY_WAIT * (attempt + 1));
174
+ }
175
+ return unclear(reason);
176
+ }
177
+ catch {
178
+ return unclear('NEAR submission is unresolved.');
179
+ }
180
+ }
181
+ export { lookUp, senderKey } from './relay.js';
182
+ export { send, sign };
@@ -0,0 +1,128 @@
1
+ /** Relay request, policy, sender and result types with shared errors. */
2
+ import { AnnonError } from '../errors.js';
3
+ import type { FileInfo } from '../near/rpc.js';
4
+ import type { FileFundingArgs, RegisterArgs, ReopenArgs, SellerArgs } from '../near/texts.js';
5
+ import type { EvmLog } from '../pay/base.js';
6
+ import type { Authorization } from '../settle-near/identity.js';
7
+ import type { AdmissionOptions } from './admission.js';
8
+ import type { ProtocolFee } from './fees.js';
9
+ import type { RelayLedger, RelayLimits } from './ledger.js';
10
+ import type { HostingReservation } from './reservation.js';
11
+ export type RelayRequest = {
12
+ method: 'fund_file_storage';
13
+ args: FileFundingArgs;
14
+ auth: Authorization;
15
+ } | {
16
+ method: 'register';
17
+ args: RegisterArgs;
18
+ auth: Authorization;
19
+ baseSplit?: {
20
+ kind: 'push' | 'pull';
21
+ salt: string;
22
+ };
23
+ hostingReservation?: HostingReservation;
24
+ } | {
25
+ method: 'reopen';
26
+ args: ReopenArgs;
27
+ auth: Authorization;
28
+ } | {
29
+ method: 'stop_sales' | 'reopen_seller';
30
+ args: SellerArgs;
31
+ auth: Authorization;
32
+ } | {
33
+ method: 'claim_key';
34
+ args: {
35
+ purchase_id: string;
36
+ grant?: number;
37
+ };
38
+ } | {
39
+ method: 'unlock';
40
+ args: {
41
+ purchase: string;
42
+ logs: EvmLog[];
43
+ header: string | null;
44
+ };
45
+ };
46
+ export type RelayErrorCode = 'budget' | 'bad_request' | 'bad_signature' | 'bad_deadline' | 'not_sponsored' | 'limited' | 'duplicate' | 'in_flight' | 'unavailable' | 'unknown_file' | 'already_registered' | 'sales_stopped' | 'taken_down' | 'not_buyer' | 'payment_not_found' | 'settlement_pending' | 'relayer_unavailable' | 'payment_used' | 'bad_header' | 'transport';
47
+ export interface RelayPolicy {
48
+ minPrice: bigint;
49
+ baseSponsorship?: boolean;
50
+ openPoolEnabled?: boolean;
51
+ listedRegistrationsPerSeller?: number;
52
+ unlocksPerFile?: number;
53
+ unlocksPerSeller?: number;
54
+ listedUnlocksPerFile?: number;
55
+ listedUnlocksPerSeller?: number;
56
+ listedTopupPerFile?: bigint;
57
+ listedTopupPerFileDay?: bigint;
58
+ topupListedReserved?: bigint;
59
+ prepaidUnlockFloor?: bigint;
60
+ storagePerWrite?: bigint;
61
+ topupMaxDeadlineSeconds?: number;
62
+ /** Listed sellers get a larger allocation; unlisted sellers remain eligible. */
63
+ fileBudget?: bigint;
64
+ listedFileBudget?: bigint;
65
+ fee?: ProtocolFee;
66
+ sponsoredFilesPerDay?: number;
67
+ topupPerFile?: bigint;
68
+ topupPerFileDay?: bigint;
69
+ topupPerDay?: bigint;
70
+ maxDeadlineSeconds: number;
71
+ /** Listed-seller predicate for allocation tiers; does not deny unlisted sellers. */
72
+ sellerAllowed?: (seller: string) => boolean | Promise<boolean>;
73
+ registrationsPerSeller?: number;
74
+ reopensPerWalletFile?: number;
75
+ /** Daily limit per (purchase, grant). */
76
+ claimsPerGrant?: number;
77
+ holdMs?: number;
78
+ maxWaiting?: number;
79
+ maxChecking?: number;
80
+ }
81
+ export interface CheckOptions extends AdmissionOptions {
82
+ nearRpc: string;
83
+ baseRpc: string;
84
+ contractId: string;
85
+ usdc: string;
86
+ nearUsdc?: string;
87
+ policy: RelayPolicy;
88
+ now?: number;
89
+ network?: string;
90
+ ledger?: RelayLedger;
91
+ admissionLimits?: Partial<RelayLimits>;
92
+ ip?: string;
93
+ acceptingNewBasePayments?: boolean;
94
+ historicalFile?: (fileId: string, blockHash: string) => Promise<FileInfo | null>;
95
+ reservation?: {
96
+ hostOrigin: string;
97
+ publicKey: string;
98
+ fetch?: typeof fetch;
99
+ maxFileBytes?: number;
100
+ };
101
+ alert?: (type: string) => void;
102
+ }
103
+ export interface Sender {
104
+ /** The NEAR account that sends and pays, e.g. `relayer.annon.defdone.near`. */
105
+ accountId: string;
106
+ /** Its function-call key for the relay methods, including file funding, `ed25519:…`. Only for this contract. */
107
+ key: string;
108
+ }
109
+ export interface RelayResult {
110
+ txHash: string;
111
+ /**
112
+ * NEAR's answer was lost or unclear (a timeout, a gateway error, a refusal): the transaction may
113
+ * or may not be in a block. Read its outcome by hash before asking again. Sending the same
114
+ * operation again is safe: the contract refuses it if the first one landed.
115
+ */
116
+ uncertain?: true;
117
+ /** RPC validation refused this submission before execution. */
118
+ rejected?: true;
119
+ /** With `uncertain` or `rejected`: what NEAR answered, for the operator. */
120
+ reason?: string;
121
+ }
122
+ export declare class RelayError extends AnnonError {
123
+ readonly code: RelayErrorCode;
124
+ retryAt?: number;
125
+ constructor(code: RelayErrorCode, message?: string);
126
+ }
127
+ declare const fail: (code: RelayErrorCode, message?: string) => never;
128
+ export { fail };
@@ -0,0 +1,17 @@
1
+ /** Relay request, policy, sender and result types with shared errors. */
2
+ import { AnnonError } from '../errors.js';
3
+ export class RelayError extends AnnonError {
4
+ // A plain field, not a parameter property: the relayer runs this file with Node's type
5
+ // stripping, which has no parameter properties.
6
+ code;
7
+ retryAt;
8
+ constructor(code, message = code) {
9
+ super(code, message);
10
+ this.code = code;
11
+ this.name = 'RelayError';
12
+ }
13
+ }
14
+ const fail = (code, message) => {
15
+ throw new RelayError(code, message);
16
+ };
17
+ export { fail };
@@ -0,0 +1,102 @@
1
+ /** Relay entry points, admission serialization and module-load state. */
2
+ import type { Account } from 'near-api-js';
3
+ import '../net/retry.js';
4
+ import '../pay/base.js';
5
+ import '../pay/split.js';
6
+ import { type SettlementAssets } from '../settle-near/terms.js';
7
+ import './fees.js';
8
+ import { type KeepOptions, type KeepResult } from './keeper-runner.js';
9
+ import type { RelayLedger } from './ledger.js';
10
+ import './reservation.js';
11
+ import './sponsorship.js';
12
+ import { type CheckOptions, fail, type RelayPolicy, type RelayRequest, type RelayResult, type Sender } from './relay-types.js';
13
+ export type { KeeperRecord, KeepOptions, KeepResult, RefundSource } from './keeper-runner.js';
14
+ export { checkRelay } from './relay-checks.js';
15
+ export { reconcileRelayStorage } from './relay-reconciliation.js';
16
+ export { canonicalRequest, contractArgs } from './relay-request.js';
17
+ export type { CheckOptions, RelayErrorCode, RelayPolicy, RelayRequest, RelayResult, Sender, } from './relay-types.js';
18
+ export { RelayError } from './relay-types.js';
19
+ declare const asRelayError: (e: unknown) => never;
20
+ /** Gas for every call: `unlock` needs 150 Tgas, MPC's callbacks use the rest; unused gas returns. */
21
+ declare const GAS = 300000000000000n;
22
+ /** An operation is held this long after it is sent, so a repeat doesn't burn gas on a refusal. */
23
+ declare const HOLD: number;
24
+ /** How many more tries after the first send: the same bytes again, or after PENDING a lookup. */
25
+ declare const RESENDS = 2;
26
+ /** The first wait before trying again when NEAR says "later" or "pending"; it grows each try. */
27
+ declare const RETRY_WAIT = 1000;
28
+ declare const accounts: Map<string, Account>;
29
+ declare const verifiedBaseTerms: Set<string>;
30
+ declare const refusedBaseTerms: Map<string, number>;
31
+ declare const statusReads: WeakMap<RelayLedger, Map<string, Promise<"pending" | RelayResult | null>>>;
32
+ declare const reconciliationCursor: WeakMap<RelayLedger, number>;
33
+ declare const isObject: (v: unknown) => v is object;
34
+ /** The statuses of a transaction in a block (`NONE`: known, not yet in one). */
35
+ declare const IN_BLOCK: Set<string>;
36
+ declare const inBlock: (result: unknown) => boolean;
37
+ /** Ask for FINAL evidence by hash; a lost reply never proves rejection. */
38
+ declare const lookUp: (url: string, tx: string, accountId: string) => Promise<import("../near/rpc.js").RpcReply | null>;
39
+ declare const senderKey: (o: {
40
+ nearRpc: string;
41
+ sender: Sender;
42
+ }) => string;
43
+ /**
44
+ * What makes two requests the same operation. Only a party that could make the request succeed
45
+ * can hold it: wallet signatures are checked against their authoritative identities before
46
+ * anything is held, and an unlock
47
+ * includes the buyer's key, which only the buyer knows before it is sent.
48
+ */
49
+ declare const identity: (r: RelayRequest) => string;
50
+ declare const signedByItsWallet: (r: RelayRequest, contract: string, assets?: SettlementAssets) => void;
51
+ declare const quota: (r: RelayRequest, p: RelayPolicy) => [string, number] | null;
52
+ declare const requestBinding: (r: RelayRequest) => {
53
+ method: "fund_file_storage";
54
+ args: import("../near/texts.js").FileFundingArgs;
55
+ auth: import("../settle-near/identity.js").Authorization;
56
+ } | {
57
+ method: "reopen";
58
+ args: import("../near/texts.js").ReopenArgs;
59
+ auth: import("../settle-near/identity.js").Authorization;
60
+ } | {
61
+ method: "stop_sales" | "reopen_seller";
62
+ args: import("../near/texts.js").SellerArgs;
63
+ auth: import("../settle-near/identity.js").Authorization;
64
+ } | {
65
+ method: "claim_key";
66
+ args: {
67
+ purchase_id: string;
68
+ grant?: number;
69
+ };
70
+ } | {
71
+ method: "unlock";
72
+ args: {
73
+ purchase: string;
74
+ logs: import("../pay/base.js").EvmLog[];
75
+ header: string | null;
76
+ };
77
+ } | {
78
+ method: "register";
79
+ args: import("../near/texts.js").RegisterArgs;
80
+ auth: import("../settle-near/identity.js").Authorization;
81
+ baseSplit?: {
82
+ kind: "push" | "pull";
83
+ salt: string;
84
+ };
85
+ };
86
+ /** Runs jobs one at a time, in order: checks and nonces never interleave. */
87
+ declare function enqueue<T>(job: () => Promise<T>, scope?: string): Promise<T>;
88
+ /** Read an already admitted operation without charging admission limits or sending anything. */
89
+ export declare function relayStatus(input: unknown, o: CheckOptions): Promise<RelayResult | 'pending' | null>;
90
+ /** Every sponsored send reserves durable admission before signing. */
91
+ export declare function relay(input: unknown, o: CheckOptions & {
92
+ sender: Sender;
93
+ }): Promise<RelayResult>;
94
+ /** Retain bounded paid work while admission or the service is temporarily paused. */
95
+ export declare function deferPaidUnlock(input: unknown, o: CheckOptions, nextAt: number): Promise<void>;
96
+ /** Resume due paid work without creating a second purchase or losing its frozen policy. */
97
+ export declare function retryPaidUnlocks(o: CheckOptions & {
98
+ sender: Sender;
99
+ }): Promise<void>;
100
+ /** A dedicated account/key has its own nonce queue, independent from relay traffic. */
101
+ export declare function keep(o: KeepOptions): Promise<KeepResult>;
102
+ export { accounts, asRelayError, enqueue, fail, GAS, HOLD, IN_BLOCK, identity, inBlock, isObject, lookUp, quota, RESENDS, RETRY_WAIT, reconciliationCursor, refusedBaseTerms, requestBinding, senderKey, signedByItsWallet, statusReads, verifiedBaseTerms, };