@defdone/annon 0.0.0-stage → 0.1.0-alpha.1
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/CHANGELOG.md +63 -0
- package/CLI.md +378 -0
- package/LICENSE +201 -0
- package/NOTICE +7 -0
- package/README.md +591 -2
- package/dist/api/annon-types.d.ts +108 -0
- package/dist/api/annon-types.js +2 -0
- package/dist/api/annon.d.ts +117 -0
- package/dist/api/annon.js +1152 -0
- package/dist/api/hosted-record.d.ts +13 -0
- package/dist/api/hosted-record.js +149 -0
- package/dist/api/sharing-types.d.ts +109 -0
- package/dist/api/sharing-types.js +1 -0
- package/dist/api/sharing.d.ts +36 -0
- package/dist/api/sharing.js +879 -0
- package/dist/browser/download.d.ts +19 -0
- package/dist/browser/download.js +52 -0
- package/dist/browser/index.d.ts +23 -0
- package/dist/browser/index.js +22 -0
- package/dist/browser/login-bundle.js +151 -0
- package/dist/browser/login.d.ts +1 -0
- package/dist/browser/login.js +216 -0
- package/dist/browser/purchase-bundle.js +84 -0
- package/dist/browser/purchases.d.ts +98 -0
- package/dist/browser/purchases.js +735 -0
- package/dist/browser/record.d.ts +55 -0
- package/dist/browser/record.js +16 -0
- package/dist/browser/selling.d.ts +146 -0
- package/dist/browser/selling.js +418 -0
- package/dist/browser/wallets.d.ts +53 -0
- package/dist/browser/wallets.js +286 -0
- package/dist/bytes.d.ts +4 -0
- package/dist/bytes.js +16 -0
- package/dist/download/download.d.ts +59 -0
- package/dist/download/download.js +384 -0
- package/dist/download/host.d.ts +56 -0
- package/dist/download/host.js +161 -0
- package/dist/download/http.d.ts +23 -0
- package/dist/download/http.js +87 -0
- package/dist/download/proof.d.ts +20 -0
- package/dist/download/proof.js +79 -0
- package/dist/download/upload.d.ts +130 -0
- package/dist/download/upload.js +408 -0
- package/dist/envelope/evm.d.ts +4 -0
- package/dist/envelope/evm.js +32 -0
- package/dist/envelope/pay2.d.ts +176 -0
- package/dist/envelope/pay2.js +541 -0
- package/dist/envelope/replay.d.ts +45 -0
- package/dist/envelope/replay.js +186 -0
- package/dist/envelope/stream.d.ts +78 -0
- package/dist/envelope/stream.js +372 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.js +300 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +23 -0
- package/dist/keys/near-ckd.d.ts +47 -0
- package/dist/keys/near-ckd.js +96 -0
- package/dist/limits.d.ts +4 -0
- package/dist/limits.js +4 -0
- package/dist/near/nep413.d.ts +22 -0
- package/dist/near/nep413.js +39 -0
- package/dist/near/rpc.d.ts +170 -0
- package/dist/near/rpc.js +423 -0
- package/dist/near/texts.d.ts +39 -0
- package/dist/near/texts.js +60 -0
- package/dist/net/ip.d.ts +7 -0
- package/dist/net/ip.js +62 -0
- package/dist/net/retry.d.ts +3 -0
- package/dist/net/retry.js +31 -0
- package/dist/node/adapter-checks.d.ts +6 -0
- package/dist/node/adapter-checks.js +13 -0
- package/dist/node/adapter.d.ts +8 -0
- package/dist/node/adapter.js +98 -0
- package/dist/node/agent.d.ts +49 -0
- package/dist/node/agent.js +266 -0
- package/dist/node/amounts.d.ts +2 -0
- package/dist/node/amounts.js +20 -0
- package/dist/node/browser-login.d.ts +25 -0
- package/dist/node/browser-login.js +219 -0
- package/dist/node/budget.d.ts +24 -0
- package/dist/node/budget.js +59 -0
- package/dist/node/buy-commands.d.ts +24 -0
- package/dist/node/buy-commands.js +301 -0
- package/dist/node/cli.d.ts +60 -0
- package/dist/node/cli.js +201 -0
- package/dist/node/command-context.d.ts +82 -0
- package/dist/node/command-context.js +77 -0
- package/dist/node/config.d.ts +37 -0
- package/dist/node/config.js +189 -0
- package/dist/node/core-adapter.d.ts +43 -0
- package/dist/node/core-adapter.js +122 -0
- package/dist/node/errors.d.ts +8 -0
- package/dist/node/errors.js +12 -0
- package/dist/node/index.d.ts +7 -0
- package/dist/node/index.js +5 -0
- package/dist/node/io.d.ts +13 -0
- package/dist/node/io.js +112 -0
- package/dist/node/keychain.d.ts +10 -0
- package/dist/node/keychain.js +67 -0
- package/dist/node/main.d.ts +2 -0
- package/dist/node/main.js +21 -0
- package/dist/node/offers.d.ts +16 -0
- package/dist/node/offers.js +91 -0
- package/dist/node/origin.d.ts +11 -0
- package/dist/node/origin.js +80 -0
- package/dist/node/paths.d.ts +3 -0
- package/dist/node/paths.js +71 -0
- package/dist/node/payment-terms.d.ts +26 -0
- package/dist/node/payment-terms.js +127 -0
- package/dist/node/purchase-commands.d.ts +16 -0
- package/dist/node/purchase-commands.js +237 -0
- package/dist/node/purchase-guard.d.ts +19 -0
- package/dist/node/purchase-guard.js +53 -0
- package/dist/node/relay-transport.d.ts +6 -0
- package/dist/node/relay-transport.js +173 -0
- package/dist/node/resources.d.ts +2 -0
- package/dist/node/resources.js +26 -0
- package/dist/node/retry-gate.d.ts +15 -0
- package/dist/node/retry-gate.js +53 -0
- package/dist/node/seller-commands.d.ts +12 -0
- package/dist/node/seller-commands.js +207 -0
- package/dist/node/sharing.d.ts +3 -0
- package/dist/node/sharing.js +28 -0
- package/dist/node/shops.d.ts +17 -0
- package/dist/node/shops.js +74 -0
- package/dist/node/signer-mock.d.ts +11 -0
- package/dist/node/signer-mock.js +79 -0
- package/dist/node/signer-wallet.d.ts +8 -0
- package/dist/node/signer-wallet.js +149 -0
- package/dist/node/signing.d.ts +29 -0
- package/dist/node/signing.js +200 -0
- package/dist/node/storage-commands.d.ts +11 -0
- package/dist/node/storage-commands.js +50 -0
- package/dist/node/sync-dir.d.ts +1 -0
- package/dist/node/sync-dir.js +11 -0
- package/dist/node/wallets.d.ts +13 -0
- package/dist/node/wallets.js +146 -0
- package/dist/oneclick/oneclick.d.ts +132 -0
- package/dist/oneclick/oneclick.js +342 -0
- package/dist/pay/backup.d.ts +70 -0
- package/dist/pay/backup.js +402 -0
- package/dist/pay/base.d.ts +135 -0
- package/dist/pay/base.js +386 -0
- package/dist/pay/eip3009.d.ts +59 -0
- package/dist/pay/eip3009.js +72 -0
- package/dist/pay/pay.d.ts +86 -0
- package/dist/pay/pay.js +297 -0
- package/dist/pay/purchase.d.ts +103 -0
- package/dist/pay/purchase.js +20 -0
- package/dist/pay/recover.d.ts +31 -0
- package/dist/pay/recover.js +34 -0
- package/dist/pay/seller.d.ts +130 -0
- package/dist/pay/seller.js +444 -0
- package/dist/pay/settle.d.ts +96 -0
- package/dist/pay/settle.js +308 -0
- package/dist/pay/split.d.ts +115 -0
- package/dist/pay/split.js +256 -0
- package/dist/pay/store-node.d.ts +8 -0
- package/dist/pay/store-node.js +224 -0
- package/dist/pay/store.d.ts +54 -0
- package/dist/pay/store.js +135 -0
- package/dist/pay/x402.d.ts +95 -0
- package/dist/pay/x402.js +136 -0
- package/dist/relay/admission.d.ts +8 -0
- package/dist/relay/admission.js +58 -0
- package/dist/relay/capabilities.d.ts +36 -0
- package/dist/relay/capabilities.js +232 -0
- package/dist/relay/fees.d.ts +17 -0
- package/dist/relay/fees.js +105 -0
- package/dist/relay/index.d.ts +10 -0
- package/dist/relay/index.js +8 -0
- package/dist/relay/keeper-balance.d.ts +17 -0
- package/dist/relay/keeper-balance.js +46 -0
- package/dist/relay/keeper-runner.d.ts +114 -0
- package/dist/relay/keeper-runner.js +703 -0
- package/dist/relay/keeper.d.ts +38 -0
- package/dist/relay/keeper.js +73 -0
- package/dist/relay/ledger.d.ts +155 -0
- package/dist/relay/ledger.js +392 -0
- package/dist/relay/relay-checks.d.ts +3 -0
- package/dist/relay/relay-checks.js +244 -0
- package/dist/relay/relay-reconciliation.d.ts +15 -0
- package/dist/relay/relay-reconciliation.js +293 -0
- package/dist/relay/relay-request.d.ts +6 -0
- package/dist/relay/relay-request.js +139 -0
- package/dist/relay/relay-transactions.d.ts +23 -0
- package/dist/relay/relay-transactions.js +182 -0
- package/dist/relay/relay-types.d.ts +128 -0
- package/dist/relay/relay-types.js +17 -0
- package/dist/relay/relay.d.ts +102 -0
- package/dist/relay/relay.js +426 -0
- package/dist/relay/reservation.d.ts +29 -0
- package/dist/relay/reservation.js +132 -0
- package/dist/relay/sponsorship.d.ts +42 -0
- package/dist/relay/sponsorship.js +184 -0
- package/dist/routes.d.ts +2 -0
- package/dist/routes.js +1 -0
- package/dist/settle-near/codec.d.ts +17 -0
- package/dist/settle-near/codec.js +86 -0
- package/dist/settle-near/identity.d.ts +27 -0
- package/dist/settle-near/identity.js +80 -0
- package/dist/settle-near/purchase.d.ts +40 -0
- package/dist/settle-near/purchase.js +153 -0
- package/dist/settle-near/terms.d.ts +37 -0
- package/dist/settle-near/terms.js +89 -0
- package/dist/shops/discovery.d.ts +95 -0
- package/dist/shops/discovery.js +384 -0
- package/dist/wallet/account-only.d.ts +11 -0
- package/dist/wallet/account-only.js +6 -0
- package/dist/wallet/uncertain.d.ts +8 -0
- package/dist/wallet/uncertain.js +13 -0
- package/dist/wallet/wallet.d.ts +100 -0
- package/dist/wallet/wallet.js +204 -0
- package/package.json +136 -3
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { rpcReply } from '../near/rpc.js';
|
|
2
|
+
import { RelayError } from './relay.js';
|
|
3
|
+
const paused = new WeakMap();
|
|
4
|
+
/** Final liquid account funds, excluding required account storage stake. */
|
|
5
|
+
export async function admissionBalance(nearRpc, accountId) {
|
|
6
|
+
const account = await rpcReply(nearRpc, 'query', {
|
|
7
|
+
request_type: 'view_account',
|
|
8
|
+
finality: 'final',
|
|
9
|
+
account_id: accountId,
|
|
10
|
+
});
|
|
11
|
+
const a = account.result;
|
|
12
|
+
const integer = (v) => {
|
|
13
|
+
if (!((typeof v === 'string' && /^\d+$/.test(v)) ||
|
|
14
|
+
(typeof v === 'number' && Number.isSafeInteger(v) && v >= 0)))
|
|
15
|
+
throw new RelayError('relayer_unavailable');
|
|
16
|
+
return BigInt(v);
|
|
17
|
+
};
|
|
18
|
+
if (account.error || !a?.block_hash)
|
|
19
|
+
throw new RelayError('relayer_unavailable');
|
|
20
|
+
const usage = integer(a.storage_usage), amount = integer(a.amount), locked = integer(a.locked);
|
|
21
|
+
const protocol = await rpcReply(nearRpc, 'EXPERIMENTAL_protocol_config', {
|
|
22
|
+
block_id: a.block_hash,
|
|
23
|
+
});
|
|
24
|
+
const price = protocol.result
|
|
25
|
+
?.runtime_config?.storage_amount_per_byte;
|
|
26
|
+
if (protocol.error)
|
|
27
|
+
throw new RelayError('relayer_unavailable');
|
|
28
|
+
const stake = usage <= 770n ? 0n : usage * integer(price);
|
|
29
|
+
const held = stake > locked ? stake - locked : 0n;
|
|
30
|
+
return amount > held ? amount - held : 0n;
|
|
31
|
+
}
|
|
32
|
+
export async function checkAdmissionFloor(method, o) {
|
|
33
|
+
if (['unlock', 'claim_key', 'reopen'].includes(method) || o.admissionFloor === undefined)
|
|
34
|
+
return;
|
|
35
|
+
let balance;
|
|
36
|
+
try {
|
|
37
|
+
if (!o.admissionBalance)
|
|
38
|
+
throw new Error('balance unavailable');
|
|
39
|
+
balance = await o.admissionBalance();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
const error = new RelayError('relayer_unavailable');
|
|
43
|
+
error.retryAt = Date.now() + 30000;
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
const below = balance < o.admissionFloor;
|
|
47
|
+
if (o.alert && paused.get(o.alert) !== below) {
|
|
48
|
+
const previous = paused.get(o.alert);
|
|
49
|
+
paused.set(o.alert, below);
|
|
50
|
+
if (below || previous === true)
|
|
51
|
+
o.alert(below ? 'admissions_paused' : 'admissions_resumed');
|
|
52
|
+
}
|
|
53
|
+
if (below) {
|
|
54
|
+
const error = new RelayError('budget');
|
|
55
|
+
error.retryAt = Date.now() + 30000;
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { FileInfo } from '../near/rpc.js';
|
|
2
|
+
import { type ProtocolFee } from './fees.js';
|
|
3
|
+
export declare const BASE_ELIGIBILITY_MAX_AGE_SECONDS = 30;
|
|
4
|
+
export interface BaseSponsorshipOptions {
|
|
5
|
+
deployment: {
|
|
6
|
+
network: string;
|
|
7
|
+
contractId: string;
|
|
8
|
+
nearUsdc: string;
|
|
9
|
+
baseUsdc: string;
|
|
10
|
+
};
|
|
11
|
+
file: FileInfo;
|
|
12
|
+
baseRpc: string;
|
|
13
|
+
relayer: string;
|
|
14
|
+
fetch?: typeof fetch;
|
|
15
|
+
now?: () => number;
|
|
16
|
+
}
|
|
17
|
+
export interface BaseSponsorship {
|
|
18
|
+
network: string;
|
|
19
|
+
contract: string;
|
|
20
|
+
fileId: string;
|
|
21
|
+
termsHash: string;
|
|
22
|
+
eligible: true;
|
|
23
|
+
tier: 'listed' | 'pool' | 'legacy' | 'external';
|
|
24
|
+
policyVersion: string;
|
|
25
|
+
minPrice: string;
|
|
26
|
+
fee: ProtocolFee;
|
|
27
|
+
decidedAt: number;
|
|
28
|
+
checkedAt: number;
|
|
29
|
+
splitCheckedAt: number;
|
|
30
|
+
}
|
|
31
|
+
/** Only records checked here, unchanged and still bound to the same deployment and final file, can be reused. */
|
|
32
|
+
export declare function validBaseSponsorship(record: BaseSponsorship | undefined, o: BaseSponsorshipOptions): boolean;
|
|
33
|
+
/** Verify the deployed immutable split even when the caller funds its own unlock. */
|
|
34
|
+
export declare function checkBaseSplit(file: FileInfo, baseRpc: string): Promise<void>;
|
|
35
|
+
/** Fail closed before signatures, payment submission or release of payment instructions. */
|
|
36
|
+
export declare function checkBaseSponsorship(o: BaseSponsorshipOptions): Promise<BaseSponsorship>;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { AnnonError, isTransient } from '../errors.js';
|
|
2
|
+
import { parseRetryAfterMs } from '../net/retry.js';
|
|
3
|
+
import { PayError } from '../pay/base.js';
|
|
4
|
+
import { readSplitState } from '../pay/split.js';
|
|
5
|
+
import { parseIdentity } from '../settle-near/identity.js';
|
|
6
|
+
import { termsHash } from '../settle-near/terms.js';
|
|
7
|
+
import { checkProtocolFee } from './fees.js';
|
|
8
|
+
const MAX_JSON_BYTES = 65536;
|
|
9
|
+
export const BASE_ELIGIBILITY_MAX_AGE_SECONDS = 30;
|
|
10
|
+
const issued = new WeakMap();
|
|
11
|
+
const binding = (o) => JSON.stringify([
|
|
12
|
+
o.deployment,
|
|
13
|
+
o.file.terms,
|
|
14
|
+
o.file.termsHash,
|
|
15
|
+
o.file.salesStoppedAt,
|
|
16
|
+
o.file.takenDown,
|
|
17
|
+
o.relayer,
|
|
18
|
+
o.baseRpc,
|
|
19
|
+
]);
|
|
20
|
+
const nowOf = (o) => (o.now ?? (() => Math.floor(Date.now() / 1000)))();
|
|
21
|
+
const fresh = (at, now) => Number.isSafeInteger(at) && at >= 0 && Math.abs(now - at) <= BASE_ELIGIBILITY_MAX_AGE_SECONDS;
|
|
22
|
+
const error = (code, retryAfterSeconds) => Object.assign(new AnnonError(code), {
|
|
23
|
+
code,
|
|
24
|
+
...(retryAfterSeconds === undefined ? {} : { retryAfterSeconds }),
|
|
25
|
+
});
|
|
26
|
+
const uint = (v) => typeof v === 'string' && /^(0|[1-9][0-9]*)$/.test(v) && v.length <= 39 && BigInt(v) < 2n ** 128n;
|
|
27
|
+
function origin(value) {
|
|
28
|
+
const u = new URL(value);
|
|
29
|
+
if (u.protocol !== 'https:' ||
|
|
30
|
+
u.username ||
|
|
31
|
+
u.password ||
|
|
32
|
+
u.search ||
|
|
33
|
+
u.hash ||
|
|
34
|
+
u.pathname !== '/')
|
|
35
|
+
throw error('relayer_unavailable');
|
|
36
|
+
return u.origin;
|
|
37
|
+
}
|
|
38
|
+
async function boundedJson(r) {
|
|
39
|
+
if (!r.body)
|
|
40
|
+
throw error('relayer_unavailable');
|
|
41
|
+
const reader = r.body.getReader();
|
|
42
|
+
const parts = [];
|
|
43
|
+
let size = 0;
|
|
44
|
+
try {
|
|
45
|
+
for (;;) {
|
|
46
|
+
const next = await reader.read();
|
|
47
|
+
if (next.done)
|
|
48
|
+
break;
|
|
49
|
+
size += next.value.length;
|
|
50
|
+
if (size > MAX_JSON_BYTES)
|
|
51
|
+
throw error('relayer_unavailable');
|
|
52
|
+
parts.push(next.value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await reader.cancel().catch(() => { });
|
|
57
|
+
}
|
|
58
|
+
const all = new Uint8Array(size);
|
|
59
|
+
let at = 0;
|
|
60
|
+
for (const part of parts) {
|
|
61
|
+
all.set(part, at);
|
|
62
|
+
at += part.length;
|
|
63
|
+
}
|
|
64
|
+
const value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(all));
|
|
65
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
66
|
+
throw error('relayer_unavailable');
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
/** Only records checked here, unchanged and still bound to the same deployment and final file, can be reused. */
|
|
70
|
+
export function validBaseSponsorship(record, o) {
|
|
71
|
+
if (!record)
|
|
72
|
+
return false;
|
|
73
|
+
const saved = issued.get(record);
|
|
74
|
+
const now = nowOf(o);
|
|
75
|
+
return (!!saved &&
|
|
76
|
+
saved.binding === binding(o) &&
|
|
77
|
+
saved.value === JSON.stringify(record) &&
|
|
78
|
+
fresh(record.checkedAt, now) &&
|
|
79
|
+
record.splitCheckedAt <= now &&
|
|
80
|
+
fresh(record.splitCheckedAt, now));
|
|
81
|
+
}
|
|
82
|
+
/** Verify the deployed immutable split even when the caller funds its own unlock. */
|
|
83
|
+
export async function checkBaseSplit(file, baseRpc) {
|
|
84
|
+
if (!baseRpc)
|
|
85
|
+
throw error('relayer_unavailable');
|
|
86
|
+
const terms = file.terms;
|
|
87
|
+
if (!terms.base ||
|
|
88
|
+
file.takenDown !== null ||
|
|
89
|
+
file.salesStoppedAt !== null ||
|
|
90
|
+
terms.near.recipients.some((r) => r.identity.kind !== 'evm'))
|
|
91
|
+
throw error('not_sponsored');
|
|
92
|
+
try {
|
|
93
|
+
await readSplitState(baseRpc, terms.base.pay_to, {
|
|
94
|
+
recipients: terms.near.recipients.map((r) => r.identity.id),
|
|
95
|
+
allocations: terms.near.recipients.map((r) => BigInt(r.bps)),
|
|
96
|
+
totalAllocation: 10000n,
|
|
97
|
+
distributionIncentive: 0,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
if (e instanceof PayError && e.code === 'network_mismatch')
|
|
102
|
+
throw e;
|
|
103
|
+
throw error(e instanceof PayError && !isTransient(e) ? 'not_sponsored' : 'relayer_unavailable');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Fail closed before signatures, payment submission or release of payment instructions. */
|
|
107
|
+
export async function checkBaseSponsorship(o) {
|
|
108
|
+
if (!o.relayer)
|
|
109
|
+
throw error('transport_required');
|
|
110
|
+
let retryAfterSeconds;
|
|
111
|
+
try {
|
|
112
|
+
const url = origin(o.relayer);
|
|
113
|
+
const d = o.deployment;
|
|
114
|
+
const t = o.file.terms;
|
|
115
|
+
if (t.near.recipients.some((r) => r.identity.kind !== 'evm'))
|
|
116
|
+
throw error('not_sponsored');
|
|
117
|
+
if (t.network !== d.network ||
|
|
118
|
+
t.contract !== d.contractId ||
|
|
119
|
+
t.near.token !== d.nearUsdc ||
|
|
120
|
+
t.base?.usdc !== d.baseUsdc ||
|
|
121
|
+
termsHash(t, { nearUsdc: d.nearUsdc, baseUsdc: d.baseUsdc }) !== o.file.termsHash)
|
|
122
|
+
throw error('not_sponsored');
|
|
123
|
+
const fetcher = o.fetch ?? globalThis.fetch;
|
|
124
|
+
const request = async (path, body) => {
|
|
125
|
+
const r = await fetcher(new URL(path, url), {
|
|
126
|
+
redirect: 'error',
|
|
127
|
+
credentials: 'omit',
|
|
128
|
+
cache: 'no-store',
|
|
129
|
+
signal: AbortSignal.timeout(20000),
|
|
130
|
+
...(body
|
|
131
|
+
? {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
headers: { 'content-type': 'application/json' },
|
|
134
|
+
body: JSON.stringify(body),
|
|
135
|
+
}
|
|
136
|
+
: {}),
|
|
137
|
+
});
|
|
138
|
+
const delay = parseRetryAfterMs(r.headers.get('Retry-After'), nowOf(o) * 1000);
|
|
139
|
+
if (delay !== undefined)
|
|
140
|
+
retryAfterSeconds = Math.max(1, Math.ceil(delay / 1000));
|
|
141
|
+
if (!r.ok) {
|
|
142
|
+
await r.body?.cancel();
|
|
143
|
+
throw error('relayer_unavailable', retryAfterSeconds);
|
|
144
|
+
}
|
|
145
|
+
return boundedJson(r);
|
|
146
|
+
};
|
|
147
|
+
const cap = await request('/v1/capabilities');
|
|
148
|
+
if (cap.network !== d.network ||
|
|
149
|
+
cap.contract !== d.contractId ||
|
|
150
|
+
cap.nearUsdc !== d.nearUsdc ||
|
|
151
|
+
cap.baseUsdc !== d.baseUsdc ||
|
|
152
|
+
!Array.isArray(cap.methods) ||
|
|
153
|
+
!cap.methods.includes('unlock') ||
|
|
154
|
+
typeof cap.disabled !== 'boolean' ||
|
|
155
|
+
typeof cap.baseSponsorship !== 'boolean' ||
|
|
156
|
+
typeof cap.acceptingNewBasePayments !== 'boolean')
|
|
157
|
+
throw error('relayer_unavailable');
|
|
158
|
+
if (cap.disabled || !cap.baseSponsorship || !cap.acceptingNewBasePayments)
|
|
159
|
+
throw error('relayer_unavailable', retryAfterSeconds ?? 5);
|
|
160
|
+
const v = await request('/v1/base-eligibility', {
|
|
161
|
+
fileId: t.file_id,
|
|
162
|
+
termsHash: o.file.termsHash,
|
|
163
|
+
});
|
|
164
|
+
if (v.network !== d.network ||
|
|
165
|
+
v.contract !== d.contractId ||
|
|
166
|
+
v.fileId !== t.file_id ||
|
|
167
|
+
v.termsHash !== o.file.termsHash ||
|
|
168
|
+
typeof v.eligible !== 'boolean')
|
|
169
|
+
throw error('relayer_unavailable');
|
|
170
|
+
if (!v.eligible)
|
|
171
|
+
throw error('not_sponsored');
|
|
172
|
+
if (!['listed', 'pool', 'legacy', 'external'].includes(String(v.tier)) ||
|
|
173
|
+
typeof v.policyVersion !== 'string' ||
|
|
174
|
+
!v.policyVersion ||
|
|
175
|
+
!uint(v.minPrice) ||
|
|
176
|
+
!v.fee ||
|
|
177
|
+
typeof v.fee !== 'object' ||
|
|
178
|
+
!fresh(v.checkedAt, nowOf(o)) ||
|
|
179
|
+
!Number.isSafeInteger(v.decidedAt) ||
|
|
180
|
+
v.decidedAt < 0 ||
|
|
181
|
+
v.decidedAt > v.checkedAt)
|
|
182
|
+
throw error('relayer_unavailable');
|
|
183
|
+
const fee = v.fee;
|
|
184
|
+
if (!uint(fee.fixed) ||
|
|
185
|
+
!Number.isSafeInteger(fee.bps) ||
|
|
186
|
+
fee.bps < 0 ||
|
|
187
|
+
fee.bps > 10000 ||
|
|
188
|
+
!Object.hasOwn(fee, 'treasuryNear') ||
|
|
189
|
+
!Object.hasOwn(fee, 'treasuryEvm'))
|
|
190
|
+
throw error('relayer_unavailable');
|
|
191
|
+
try {
|
|
192
|
+
if (!t.base || BigInt(t.base.price) < BigInt(v.minPrice))
|
|
193
|
+
throw new Error();
|
|
194
|
+
checkProtocolFee(t, fee);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
throw error('not_sponsored');
|
|
198
|
+
}
|
|
199
|
+
await checkBaseSplit(o.file, o.baseRpc);
|
|
200
|
+
const checked = {
|
|
201
|
+
network: d.network,
|
|
202
|
+
contract: d.contractId,
|
|
203
|
+
fileId: t.file_id,
|
|
204
|
+
termsHash: o.file.termsHash,
|
|
205
|
+
eligible: true,
|
|
206
|
+
tier: v.tier,
|
|
207
|
+
policyVersion: v.policyVersion,
|
|
208
|
+
minPrice: v.minPrice,
|
|
209
|
+
fee: {
|
|
210
|
+
fixed: fee.fixed,
|
|
211
|
+
bps: fee.bps,
|
|
212
|
+
treasuryNear: fee.treasuryNear === null ? null : parseIdentity(fee.treasuryNear),
|
|
213
|
+
treasuryEvm: fee.treasuryEvm === null ? null : parseIdentity(fee.treasuryEvm),
|
|
214
|
+
},
|
|
215
|
+
decidedAt: v.decidedAt,
|
|
216
|
+
checkedAt: v.checkedAt,
|
|
217
|
+
splitCheckedAt: nowOf(o),
|
|
218
|
+
};
|
|
219
|
+
if (!fresh(checked.checkedAt, nowOf(o)))
|
|
220
|
+
throw error('relayer_unavailable');
|
|
221
|
+
issued.set(checked, { binding: binding(o), value: JSON.stringify(checked) });
|
|
222
|
+
return checked;
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
if (e &&
|
|
226
|
+
typeof e === 'object' &&
|
|
227
|
+
'code' in e &&
|
|
228
|
+
['not_sponsored', 'relayer_unavailable', 'network_mismatch'].includes(String(e.code)))
|
|
229
|
+
throw e;
|
|
230
|
+
throw error('relayer_unavailable', retryAfterSeconds);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Identity } from '../settle-near/identity.js';
|
|
2
|
+
import type { NearTerms, Terms } from '../settle-near/terms.js';
|
|
3
|
+
export interface ProtocolFee {
|
|
4
|
+
fixed: string;
|
|
5
|
+
bps: number;
|
|
6
|
+
treasuryNear: Identity | null;
|
|
7
|
+
treasuryEvm: Identity | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function requiredFeeBps(price: string, fee: Pick<ProtocolFee, 'fixed' | 'bps'>): number;
|
|
10
|
+
/** Base terms use one EVM treasury share satisfying the larger route requirement. */
|
|
11
|
+
export declare function applySponsoredFee(near: NearTerms, fee: ProtocolFee, basePrice?: string): NearTerms;
|
|
12
|
+
export declare const DEFAULT_MAX_PROTOCOL_FEE: Readonly<{
|
|
13
|
+
fixed: "30000";
|
|
14
|
+
bps: 100;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function protocolFeeAboveMax(price: string, fee: Pick<ProtocolFee, 'fixed' | 'bps'>, max?: Pick<ProtocolFee, 'fixed' | 'bps'>): boolean;
|
|
17
|
+
export declare function checkProtocolFee(terms: Terms, fee: ProtocolFee): void;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** Public sponsorship policy. Amounts use USDC base units, arithmetic stays integer-only. */
|
|
2
|
+
import { compare } from '../settle-near/codec.js';
|
|
3
|
+
import { encodeIdentity, identityJson, parseIdentity, } from '../settle-near/identity.js';
|
|
4
|
+
export function requiredFeeBps(price, fee) {
|
|
5
|
+
if (!/^(0|[1-9][0-9]*)$/.test(fee.fixed) ||
|
|
6
|
+
!Number.isSafeInteger(fee.bps) ||
|
|
7
|
+
fee.bps < 0 ||
|
|
8
|
+
fee.bps > 10000)
|
|
9
|
+
throw new TypeError('invalid protocol fee');
|
|
10
|
+
const p = BigInt(price);
|
|
11
|
+
if (p <= 0n)
|
|
12
|
+
throw new TypeError('positive price required');
|
|
13
|
+
const n = (BigInt(fee.fixed) * 10000n + p * BigInt(fee.bps) + p - 1n) / p;
|
|
14
|
+
if (n >= 10000n)
|
|
15
|
+
throw new TypeError('protocol fee leaves no seller share');
|
|
16
|
+
return Number(n);
|
|
17
|
+
}
|
|
18
|
+
function allocate(near, requests) {
|
|
19
|
+
const required = new Map();
|
|
20
|
+
for (const request of requests) {
|
|
21
|
+
if (!request.bps)
|
|
22
|
+
continue;
|
|
23
|
+
if (!request.identity)
|
|
24
|
+
throw new TypeError('sponsorship treasury unavailable');
|
|
25
|
+
const identity = parseIdentity(request.identity);
|
|
26
|
+
const key = identityJson(identity);
|
|
27
|
+
const existing = near.recipients.find((r) => identityJson(r.identity) === key)?.bps ?? 0;
|
|
28
|
+
required.set(key, {
|
|
29
|
+
identity,
|
|
30
|
+
bps: Math.max(existing, request.bps, required.get(key)?.bps ?? 0),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (!required.size ||
|
|
34
|
+
[...required].every(([key, r]) => near.recipients.some((old) => identityJson(old.identity) === key && old.bps >= r.bps))) {
|
|
35
|
+
const unchanged = structuredClone(near);
|
|
36
|
+
const remainder = near.recipients[near.remainder];
|
|
37
|
+
if (remainder && required.has(identityJson(remainder.identity))) {
|
|
38
|
+
const seller = near.recipients.findIndex((r) => !required.has(identityJson(r.identity)));
|
|
39
|
+
if (seller >= 0)
|
|
40
|
+
unchanged.remainder = seller;
|
|
41
|
+
}
|
|
42
|
+
return unchanged;
|
|
43
|
+
}
|
|
44
|
+
const originalRemainder = near.recipients[near.remainder]?.identity;
|
|
45
|
+
const others = near.recipients.filter((r) => !required.has(identityJson(r.identity)));
|
|
46
|
+
const fixed = [...required.values()];
|
|
47
|
+
const available = 10000 - fixed.reduce((n, r) => n + r.bps, 0);
|
|
48
|
+
const total = others.reduce((n, r) => n + r.bps, 0);
|
|
49
|
+
if (!others.length || available <= 0 || others.length + fixed.length > 8)
|
|
50
|
+
throw new TypeError('fee requires seller share and one of eight recipient slots');
|
|
51
|
+
const recipients = others.map((r) => ({ ...r, bps: Math.floor((r.bps * available) / total) }));
|
|
52
|
+
recipients.push(...fixed);
|
|
53
|
+
const rounding = recipients.find((r) => originalRemainder &&
|
|
54
|
+
!required.has(identityJson(r.identity)) &&
|
|
55
|
+
identityJson(r.identity) === identityJson(originalRemainder)) ?? recipients[0];
|
|
56
|
+
if (!rounding)
|
|
57
|
+
throw new TypeError('seller recipient required');
|
|
58
|
+
rounding.bps += 10000 - recipients.reduce((n, r) => n + r.bps, 0);
|
|
59
|
+
if (recipients.some((r) => r.bps <= 0))
|
|
60
|
+
throw new TypeError('fee rounds a recipient to zero');
|
|
61
|
+
recipients.sort((a, b) => compare(encodeIdentity(a.identity), encodeIdentity(b.identity)));
|
|
62
|
+
return { ...near, recipients, remainder: recipients.indexOf(rounding) };
|
|
63
|
+
}
|
|
64
|
+
/** Base terms use one EVM treasury share satisfying the larger route requirement. */
|
|
65
|
+
export function applySponsoredFee(near, fee, basePrice) {
|
|
66
|
+
if (basePrice && fee.treasuryEvm?.kind !== 'evm')
|
|
67
|
+
throw new TypeError('EVM treasury required');
|
|
68
|
+
return allocate(near, [
|
|
69
|
+
{
|
|
70
|
+
identity: basePrice ? fee.treasuryEvm : (fee.treasuryNear ?? fee.treasuryEvm),
|
|
71
|
+
bps: Math.max(requiredFeeBps(near.price, fee), basePrice ? requiredFeeBps(basePrice, fee) : 0),
|
|
72
|
+
},
|
|
73
|
+
]);
|
|
74
|
+
}
|
|
75
|
+
export const DEFAULT_MAX_PROTOCOL_FEE = Object.freeze({ fixed: '30000', bps: 100 });
|
|
76
|
+
export function protocolFeeAboveMax(price, fee, max = DEFAULT_MAX_PROTOCOL_FEE) {
|
|
77
|
+
for (const value of [fee, max]) {
|
|
78
|
+
if (!/^(0|[1-9][0-9]*)$/.test(value.fixed) ||
|
|
79
|
+
!Number.isSafeInteger(value.bps) ||
|
|
80
|
+
value.bps < 0 ||
|
|
81
|
+
value.bps > 10000)
|
|
82
|
+
throw new TypeError('invalid protocol fee');
|
|
83
|
+
}
|
|
84
|
+
const p = BigInt(price);
|
|
85
|
+
if (p <= 0n)
|
|
86
|
+
throw new TypeError('positive price required');
|
|
87
|
+
return (BigInt(fee.fixed) * 10000n + p * BigInt(fee.bps) >
|
|
88
|
+
BigInt(max.fixed) * 10000n + p * BigInt(max.bps));
|
|
89
|
+
}
|
|
90
|
+
export function checkProtocolFee(terms, fee) {
|
|
91
|
+
const check = (price, treasury) => {
|
|
92
|
+
const required = requiredFeeBps(price, fee);
|
|
93
|
+
if (!required)
|
|
94
|
+
return;
|
|
95
|
+
if (!treasury ||
|
|
96
|
+
!terms.near.recipients.some((r) => identityJson(r.identity) === identityJson(treasury) && r.bps >= required))
|
|
97
|
+
throw new TypeError('required treasury share missing');
|
|
98
|
+
};
|
|
99
|
+
check(terms.near.price, terms.base ? fee.treasuryEvm : (fee.treasuryNear ?? fee.treasuryEvm));
|
|
100
|
+
if (terms.base) {
|
|
101
|
+
if (fee.treasuryEvm?.kind !== 'evm' && requiredFeeBps(terms.base.price, fee))
|
|
102
|
+
throw new TypeError('EVM treasury required');
|
|
103
|
+
check(terms.base.price, fee.treasuryEvm);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Service entrypoint for relayer admission, durable sponsorship and settlement. */
|
|
2
|
+
export { clientKey } from '../net/ip.js';
|
|
3
|
+
export { NEAR_USDC } from '../settle-near/terms.js';
|
|
4
|
+
export { checkProtocolFee, type ProtocolFee } from './fees.js';
|
|
5
|
+
export { keeperBalance } from './keeper-balance.js';
|
|
6
|
+
export type { KeeperRecord, KeepOptions, KeepResult, RefundSource } from './keeper-runner.js';
|
|
7
|
+
export { type Admission, type Cutover, RelayLedger, type RelayLimits, type RelayOperation, type RelayState, requestHash, type SponsorshipDecision, } from './ledger.js';
|
|
8
|
+
export { type CheckOptions, deferPaidUnlock, keep, RelayError, type RelayErrorCode, type RelayPolicy, type RelayRequest, type RelayResult, reconcileRelayStorage, relay, relayStatus, retryPaidUnlocks, type Sender, } from './relay.js';
|
|
9
|
+
export type { HostingReservation } from './reservation.js';
|
|
10
|
+
export { baseEligibility, relayLimits, ZERO_FEE } from './sponsorship.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Service entrypoint for relayer admission, durable sponsorship and settlement. */
|
|
2
|
+
export { clientKey } from '../net/ip.js';
|
|
3
|
+
export { NEAR_USDC } from '../settle-near/terms.js';
|
|
4
|
+
export { checkProtocolFee } from './fees.js';
|
|
5
|
+
export { keeperBalance } from './keeper-balance.js';
|
|
6
|
+
export { RelayLedger, requestHash, } from './ledger.js';
|
|
7
|
+
export { deferPaidUnlock, keep, RelayError, reconcileRelayStorage, relay, relayStatus, retryPaidUnlocks, } from './relay.js';
|
|
8
|
+
export { baseEligibility, relayLimits, ZERO_FEE } from './sponsorship.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Price a single zero-deposit function call, before signing, at one finalized block.
|
|
2
|
+
* NEP-642: send fees burn at block price; receipt gas is bought at the protocol minimum.
|
|
3
|
+
* https://near.github.io/nearcore/architecture/how/gas.html
|
|
4
|
+
*/
|
|
5
|
+
export declare function keeperBalance(o: {
|
|
6
|
+
nearRpc: string;
|
|
7
|
+
accountId: string;
|
|
8
|
+
contractId: string;
|
|
9
|
+
block: string;
|
|
10
|
+
gasPrice: bigint;
|
|
11
|
+
method: string;
|
|
12
|
+
args: object;
|
|
13
|
+
gas: bigint;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
balance: bigint;
|
|
16
|
+
cost: bigint;
|
|
17
|
+
}>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { rpcReply } from '../near/rpc.js';
|
|
2
|
+
const object = (v) => {
|
|
3
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
4
|
+
throw new Error('keeper balance/protocol data unavailable');
|
|
5
|
+
return v;
|
|
6
|
+
};
|
|
7
|
+
const integer = (v) => {
|
|
8
|
+
if (!((typeof v === 'string' && /^(0|[1-9][0-9]*)$/.test(v)) ||
|
|
9
|
+
(typeof v === 'number' && Number.isSafeInteger(v) && v >= 0)))
|
|
10
|
+
throw new Error('invalid keeper balance/protocol integer');
|
|
11
|
+
return BigInt(v);
|
|
12
|
+
};
|
|
13
|
+
/** Price a single zero-deposit function call, before signing, at one finalized block.
|
|
14
|
+
* NEP-642: send fees burn at block price; receipt gas is bought at the protocol minimum.
|
|
15
|
+
* https://near.github.io/nearcore/architecture/how/gas.html
|
|
16
|
+
*/
|
|
17
|
+
export async function keeperBalance(o) {
|
|
18
|
+
const [account, protocol] = await Promise.all([
|
|
19
|
+
rpcReply(o.nearRpc, 'query', {
|
|
20
|
+
request_type: 'view_account',
|
|
21
|
+
block_id: o.block,
|
|
22
|
+
account_id: o.accountId,
|
|
23
|
+
}),
|
|
24
|
+
rpcReply(o.nearRpc, 'EXPERIMENTAL_protocol_config', { block_id: o.block }),
|
|
25
|
+
]);
|
|
26
|
+
if (account.error || protocol.error)
|
|
27
|
+
throw new Error('final keeper balance/protocol unavailable');
|
|
28
|
+
const a = object(account.result);
|
|
29
|
+
const runtime = object(object(protocol.result).runtime_config);
|
|
30
|
+
const fees = object(runtime.transaction_costs);
|
|
31
|
+
const actions = object(fees.action_creation_config);
|
|
32
|
+
const receipt = object(fees.action_receipt_creation_config);
|
|
33
|
+
const call = object(actions.function_call_cost);
|
|
34
|
+
const byte = object(actions.function_call_cost_per_byte);
|
|
35
|
+
const size = BigInt(new TextEncoder().encode(o.method + JSON.stringify(o.args)).length);
|
|
36
|
+
const send = o.accountId === o.contractId ? 'send_sir' : 'send_not_sir';
|
|
37
|
+
const sendGas = integer(receipt[send]) + integer(call[send]) + size * integer(byte[send]);
|
|
38
|
+
const receiptGas = o.gas + integer(receipt.execution) + integer(call.execution) + size * integer(byte.execution);
|
|
39
|
+
const minPrice = integer(runtime.min_gas_purchase_price);
|
|
40
|
+
const cost = sendGas * o.gasPrice + receiptGas * (minPrice > o.gasPrice ? minPrice : o.gasPrice);
|
|
41
|
+
const amount = integer(a.amount), locked = integer(a.locked), usage = integer(a.storage_usage);
|
|
42
|
+
// NEP-448 permits accounts using at most 770 bytes to have zero storage stake.
|
|
43
|
+
const stake = usage <= 770n ? 0n : usage * integer(runtime.storage_amount_per_byte);
|
|
44
|
+
const held = stake > locked ? stake - locked : 0n;
|
|
45
|
+
return { cost, balance: amount > held ? amount - held : 0n };
|
|
46
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { type BudgetRecord, type SpendReservation } from '../node/budget.js';
|
|
2
|
+
import type { Store, Versioned } from '../pay/store.js';
|
|
3
|
+
import { type MaterializeRefundArgs } from './keeper.js';
|
|
4
|
+
import type { RelayResult, Sender } from './relay.js';
|
|
5
|
+
export interface KeeperRecord extends Versioned {
|
|
6
|
+
handled?: string;
|
|
7
|
+
tries?: number;
|
|
8
|
+
nextAt?: number;
|
|
9
|
+
observed?: {
|
|
10
|
+
funded: string;
|
|
11
|
+
paid: number[];
|
|
12
|
+
};
|
|
13
|
+
stage?: 'ready' | 'preparing' | 'finalizing' | 'submitted' | 'retired';
|
|
14
|
+
active?: {
|
|
15
|
+
id: string;
|
|
16
|
+
deliveryId: number;
|
|
17
|
+
}[];
|
|
18
|
+
tx?: {
|
|
19
|
+
hash: string;
|
|
20
|
+
signer: string;
|
|
21
|
+
signed: string;
|
|
22
|
+
method: 'settle' | 'materialize_refund';
|
|
23
|
+
args: object;
|
|
24
|
+
phases: string[];
|
|
25
|
+
/** Persist before broadcast, so expiry remains provable after RPC block garbage collection. */
|
|
26
|
+
reference?: {
|
|
27
|
+
hash: string;
|
|
28
|
+
height: number;
|
|
29
|
+
};
|
|
30
|
+
reservation?: SpendReservation;
|
|
31
|
+
storageReserved?: string;
|
|
32
|
+
} | null;
|
|
33
|
+
sweep?: {
|
|
34
|
+
block: string;
|
|
35
|
+
count: number;
|
|
36
|
+
from: number;
|
|
37
|
+
};
|
|
38
|
+
balanceLow?: boolean;
|
|
39
|
+
reserveCheckedAt?: number;
|
|
40
|
+
inFlightAlerted?: boolean;
|
|
41
|
+
uncertainAlerted?: boolean;
|
|
42
|
+
belowMinimum?: boolean;
|
|
43
|
+
firstSeenAt?: number;
|
|
44
|
+
oldDeliveryAlerted?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface RefundSource {
|
|
47
|
+
/** Supply current proofs from durable event history. No indexer is bundled. Retry stale proofs. */
|
|
48
|
+
leaves(context: {
|
|
49
|
+
nearRpc: string;
|
|
50
|
+
contractId: string;
|
|
51
|
+
limit: number;
|
|
52
|
+
}): Promise<MaterializeRefundArgs[]>;
|
|
53
|
+
}
|
|
54
|
+
export interface KeepOptions {
|
|
55
|
+
nearRpc: string;
|
|
56
|
+
/** Stable chain identity, independent of the RPC provider. */
|
|
57
|
+
network: string;
|
|
58
|
+
contractId: string;
|
|
59
|
+
sender: Sender;
|
|
60
|
+
store: Store<KeeperRecord>;
|
|
61
|
+
budgetStore: Store<BudgetRecord>;
|
|
62
|
+
hourlyBudget: bigint;
|
|
63
|
+
dailyBudget: bigint;
|
|
64
|
+
/** Maximum yoctoNEAR per gas unit. Refuse if current finalized gas price exceeds this. */
|
|
65
|
+
gasPrice: bigint;
|
|
66
|
+
/** USDC base units; refunds bypass this minimum. */
|
|
67
|
+
minPayout?: bigint;
|
|
68
|
+
oldestDeliverySeconds?: number;
|
|
69
|
+
maxPages?: number;
|
|
70
|
+
maxTransactions?: number;
|
|
71
|
+
maxUnresolved?: number;
|
|
72
|
+
storagePerRefund?: bigint;
|
|
73
|
+
reserveLowWater?: bigint;
|
|
74
|
+
/** Minimum liquid yoctoNEAR before signing; requires max(this, submission cost). */
|
|
75
|
+
balanceLowWater?: bigint;
|
|
76
|
+
alert?: (alert: {
|
|
77
|
+
type: 'reserve_low' | 'in_flight' | 'uncertain' | 'breaker' | 'low_balance' | 'old_delivery';
|
|
78
|
+
balance?: string;
|
|
79
|
+
needed?: string;
|
|
80
|
+
reserve?: string;
|
|
81
|
+
deliveryId?: number;
|
|
82
|
+
seconds?: number;
|
|
83
|
+
}) => void;
|
|
84
|
+
refunds?: RefundSource;
|
|
85
|
+
now?: () => number;
|
|
86
|
+
jitter?: () => number;
|
|
87
|
+
}
|
|
88
|
+
export interface KeepResult {
|
|
89
|
+
sent: {
|
|
90
|
+
method: 'settle' | 'materialize_refund';
|
|
91
|
+
args: object;
|
|
92
|
+
result: RelayResult;
|
|
93
|
+
}[];
|
|
94
|
+
errors: {
|
|
95
|
+
from?: number;
|
|
96
|
+
deliveryId?: number;
|
|
97
|
+
error: unknown;
|
|
98
|
+
}[];
|
|
99
|
+
incidents: {
|
|
100
|
+
classification: 'budget' | 'transport' | 'finalized_failure' | 'in_flight' | 'uncertain' | 'rejected';
|
|
101
|
+
deliveryId?: number;
|
|
102
|
+
reason?: string;
|
|
103
|
+
}[];
|
|
104
|
+
}
|
|
105
|
+
export declare function retryDelay(tries: number, jitter?: number): number;
|
|
106
|
+
interface Transport {
|
|
107
|
+
sign(method: string, args: object, gas: bigint): Promise<{
|
|
108
|
+
txHash: string;
|
|
109
|
+
signed: string;
|
|
110
|
+
}>;
|
|
111
|
+
send(hash: string, signed: string): Promise<RelayResult>;
|
|
112
|
+
}
|
|
113
|
+
export declare function runKeeper(o: KeepOptions, transport: Transport): Promise<KeepResult>;
|
|
114
|
+
export {};
|