@forgezero/runtime 0.1.14 → 0.1.15
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/README.md +27 -388
- package/dist/notify-templates.js +1 -1
- package/dist/schema-typebox.js +116 -7
- package/dist/schema.d.ts +14 -1
- package/dist/schema.js +116 -7
- package/package.json +2 -58
- package/dist/compliance.d.ts +0 -172
- package/dist/compliance.js +0 -168
- package/dist/finance/chain-addresses.d.ts +0 -130
- package/dist/finance/chain-addresses.js +0 -462
- package/dist/finance/chain-deposits.d.ts +0 -193
- package/dist/finance/chain-deposits.js +0 -600
- package/dist/finance/chain-reconcile.d.ts +0 -112
- package/dist/finance/chain-reconcile.js +0 -76
- package/dist/finance/chain-withdrawals.d.ts +0 -223
- package/dist/finance/chain-withdrawals.js +0 -635
- package/dist/finance/chain.d.ts +0 -116
- package/dist/finance/chain.js +0 -316
- package/dist/finance/commission.d.ts +0 -155
- package/dist/finance/commission.js +0 -423
- package/dist/finance/custody.d.ts +0 -68
- package/dist/finance/custody.js +0 -107
- package/dist/finance/derive.d.ts +0 -115
- package/dist/finance/derive.js +0 -116
- package/dist/finance/ledger.d.ts +0 -227
- package/dist/finance/ledger.js +0 -313
- package/dist/finance/market.d.ts +0 -209
- package/dist/finance/market.js +0 -112
- package/dist/finance/rates.d.ts +0 -178
- package/dist/finance/rates.js +0 -292
- package/dist/finance/transfers.d.ts +0 -153
- package/dist/finance/transfers.js +0 -292
- package/dist/finance/venues.d.ts +0 -190
- package/dist/finance/venues.js +0 -251
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { type Money } from './money';
|
|
2
|
-
/**
|
|
3
|
-
* Comparing what the chain holds against what the ledger says it owes.
|
|
4
|
-
*
|
|
5
|
-
* Every other guard in this package is preventive: the confirmation depth, the
|
|
6
|
-
* idempotency key, the hold placed with acceptance. This is the one that tells
|
|
7
|
-
* you a preventive guard FAILED — and it is the difference between finding a
|
|
8
|
-
* shortfall in a scheduled report and finding it in a withdrawal queue, where
|
|
9
|
-
* the person who finds it is a customer.
|
|
10
|
-
*
|
|
11
|
-
* ## Why a surplus is a finding too
|
|
12
|
-
*
|
|
13
|
-
* The instinct is to alert on "less on chain than we owe" and treat the other
|
|
14
|
-
* direction as a happy accident. It is not. A surplus means money arrived that
|
|
15
|
-
* nothing credited — a deposit the scanner missed, an asset nobody configured,
|
|
16
|
-
* a transfer to an address the book no longer derives. Somebody is owed that
|
|
17
|
-
* money and does not know it, and the longer it sits the harder it is to
|
|
18
|
-
* attribute. Both directions are reported, with the reason each matters.
|
|
19
|
-
*
|
|
20
|
-
* ## Why this holds no I/O
|
|
21
|
-
*
|
|
22
|
-
* `@forgezero/runtime` has no credentials and no network. Both sides are passed
|
|
23
|
-
* in, so the same comparison runs against a live node, a snapshot taken during
|
|
24
|
-
* an incident, or a fixture — and the tests exercise real discrepancies rather
|
|
25
|
-
* than a mock of a reconciler.
|
|
26
|
-
*/
|
|
27
|
-
export declare class ReconcileError extends Error {
|
|
28
|
-
readonly code: 'ASSET_MISMATCH' | 'MALFORMED';
|
|
29
|
-
constructor(code: 'ASSET_MISMATCH' | 'MALFORMED', message: string);
|
|
30
|
-
}
|
|
31
|
-
/** What the chain actually holds, per asset, across every address in custody. */
|
|
32
|
-
export interface OnChainHolding {
|
|
33
|
-
chain: string;
|
|
34
|
-
asset: string;
|
|
35
|
-
/** Deposit addresses plus the hot wallet plus cold storage — everything. */
|
|
36
|
-
units: bigint;
|
|
37
|
-
}
|
|
38
|
-
/** What the ledger says is owed, per asset. */
|
|
39
|
-
export interface LedgerLiability {
|
|
40
|
-
chain: string;
|
|
41
|
-
asset: string;
|
|
42
|
-
/**
|
|
43
|
-
* Every user balance in this asset, available and held together.
|
|
44
|
-
*
|
|
45
|
-
* Held is INCLUDED. A withdrawal awaiting approval is money the platform
|
|
46
|
-
* still owes somebody; excluding it would make the platform look solvent
|
|
47
|
-
* exactly while a payout queue is building up against it.
|
|
48
|
-
*/
|
|
49
|
-
units: bigint;
|
|
50
|
-
/** Dispatched and not yet confirmed — in flight, and no longer on chain. */
|
|
51
|
-
inFlightUnits?: bigint;
|
|
52
|
-
}
|
|
53
|
-
export declare const DISCREPANCY_KINDS: readonly ["shortfall", "surplus"];
|
|
54
|
-
export type DiscrepancyKind = (typeof DISCREPANCY_KINDS)[number];
|
|
55
|
-
export interface Discrepancy {
|
|
56
|
-
chain: string;
|
|
57
|
-
asset: string;
|
|
58
|
-
kind: DiscrepancyKind;
|
|
59
|
-
/** Always positive — `kind` carries the direction. */
|
|
60
|
-
difference: Money;
|
|
61
|
-
onChain: Money;
|
|
62
|
-
owed: Money;
|
|
63
|
-
/** What this means, in the terms an operator has to act on. */
|
|
64
|
-
meaning: string;
|
|
65
|
-
}
|
|
66
|
-
export interface ReconcileReport {
|
|
67
|
-
atMs: number;
|
|
68
|
-
/** Every (chain, asset) pair compared, whether or not it differed. */
|
|
69
|
-
checked: number;
|
|
70
|
-
discrepancies: readonly Discrepancy[];
|
|
71
|
-
/** True when nothing differed. The only result that needs no action. */
|
|
72
|
-
balanced: boolean;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Compare one snapshot of both sides.
|
|
76
|
-
*
|
|
77
|
-
* A pair present on one side and absent from the other is treated as zero on
|
|
78
|
-
* the missing side rather than skipped. Skipping is how the most serious case
|
|
79
|
-
* hides: an asset that exists on chain and is entirely unknown to the ledger
|
|
80
|
-
* has no ledger row to iterate over, so a loop driven by ledger rows never
|
|
81
|
-
* looks at it.
|
|
82
|
-
*/
|
|
83
|
-
export declare function reconcileOnce(args: {
|
|
84
|
-
onChain: readonly OnChainHolding[];
|
|
85
|
-
owed: readonly LedgerLiability[];
|
|
86
|
-
/**
|
|
87
|
-
* How much difference is not worth reporting, per asset in smallest units.
|
|
88
|
-
*
|
|
89
|
-
* Not a way to hide a shortfall — a tolerance covers rounding on chains that
|
|
90
|
-
* charge fees in the transferred asset, and nothing larger. It is per asset
|
|
91
|
-
* because "a thousand units" is dust in one and a fortune in another.
|
|
92
|
-
*/
|
|
93
|
-
tolerance?: Record<string, bigint>;
|
|
94
|
-
atMs: number;
|
|
95
|
-
}): ReconcileReport;
|
|
96
|
-
/**
|
|
97
|
-
* The report, as an operator reads it.
|
|
98
|
-
*
|
|
99
|
-
* Shortfalls first and named individually. A summary that leads with "3
|
|
100
|
-
* discrepancies" buries the one that matters underneath two that do not, and
|
|
101
|
-
* this text exists to be the first line of an alert.
|
|
102
|
-
*/
|
|
103
|
-
export declare function reconcileReport(report: ReconcileReport): string;
|
|
104
|
-
/**
|
|
105
|
-
* The one number an operator watches.
|
|
106
|
-
*
|
|
107
|
-
* Total shortfall across every pair, in USD, using a rate function the caller
|
|
108
|
-
* supplies. Per-asset figures are what you act on; this is what tells you
|
|
109
|
-
* whether to act now — and summing units across assets without converting
|
|
110
|
-
* would add satoshis to cents and produce a number that means nothing.
|
|
111
|
-
*/
|
|
112
|
-
export declare function shortfallUsd(report: ReconcileReport, usdValueOf: (amount: Money) => number): number;
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined")
|
|
5
|
-
return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
// src/finance/chain-reconcile.ts
|
|
10
|
-
class ReconcileError extends Error {
|
|
11
|
-
code;
|
|
12
|
-
constructor(code, message) {
|
|
13
|
-
super(message);
|
|
14
|
-
this.code = code;
|
|
15
|
-
this.name = "ReconcileError";
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
var DISCREPANCY_KINDS = ["shortfall", "surplus"];
|
|
19
|
-
var key = (item) => `${item.chain}:${item.asset}`;
|
|
20
|
-
function reconcileOnce(args) {
|
|
21
|
-
const chainSide = new Map(args.onChain.map((item) => [key(item), item]));
|
|
22
|
-
const ledgerSide = new Map(args.owed.map((item) => [key(item), item]));
|
|
23
|
-
const pairs = [...new Set([...chainSide.keys(), ...ledgerSide.keys()])].sort();
|
|
24
|
-
const discrepancies = [];
|
|
25
|
-
for (const pair of pairs) {
|
|
26
|
-
const held = chainSide.get(pair);
|
|
27
|
-
const liability = ledgerSide.get(pair);
|
|
28
|
-
const [chain, asset] = pair.split(":");
|
|
29
|
-
const onChainUnits = held?.units ?? 0n;
|
|
30
|
-
const owedUnits = (liability?.units ?? 0n) + (liability?.inFlightUnits ?? 0n);
|
|
31
|
-
const difference = onChainUnits - owedUnits;
|
|
32
|
-
const allowed = args.tolerance?.[asset] ?? 0n;
|
|
33
|
-
if (difference >= -allowed && difference <= allowed)
|
|
34
|
-
continue;
|
|
35
|
-
const kind = difference < 0n ? "shortfall" : "surplus";
|
|
36
|
-
discrepancies.push({
|
|
37
|
-
chain,
|
|
38
|
-
asset,
|
|
39
|
-
kind,
|
|
40
|
-
difference: { units: difference < 0n ? -difference : difference, asset },
|
|
41
|
-
onChain: { units: onChainUnits, asset },
|
|
42
|
-
owed: { units: owedUnits, asset },
|
|
43
|
-
meaning: kind === "shortfall" ? "The chain holds less than is owed. Withdrawals will fail once the balance is drawn down — " + "this is the state that becomes a customer-facing incident." : "Money arrived that nothing credited. Somebody is owed it and does not know, and attribution " + "gets harder the longer it sits."
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
atMs: args.atMs,
|
|
48
|
-
checked: pairs.length,
|
|
49
|
-
discrepancies,
|
|
50
|
-
balanced: discrepancies.length === 0
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
function reconcileReport(report) {
|
|
54
|
-
if (report.balanced) {
|
|
55
|
-
return `Reconciled: ${report.checked} chain/asset pair(s) agree.`;
|
|
56
|
-
}
|
|
57
|
-
const shortfalls = report.discrepancies.filter((item) => item.kind === "shortfall");
|
|
58
|
-
const surpluses = report.discrepancies.filter((item) => item.kind === "surplus");
|
|
59
|
-
const line = (item) => ` ${item.kind === "shortfall" ? "SHORT" : "over "} ${item.chain}/${item.asset}: ` + `${item.difference.units} units (chain ${item.onChain.units}, owed ${item.owed.units})`;
|
|
60
|
-
return [
|
|
61
|
-
shortfalls.length > 0 ? `SHORTFALL on ${shortfalls.length} pair(s) — withdrawals will fail once drawn down.` : `No shortfall. ${surpluses.length} pair(s) hold more than is owed.`,
|
|
62
|
-
...shortfalls.map(line),
|
|
63
|
-
...surpluses.map(line)
|
|
64
|
-
].join(`
|
|
65
|
-
`);
|
|
66
|
-
}
|
|
67
|
-
function shortfallUsd(report, usdValueOf) {
|
|
68
|
-
return report.discrepancies.filter((item) => item.kind === "shortfall").reduce((total, item) => total + usdValueOf(item.difference), 0);
|
|
69
|
-
}
|
|
70
|
-
export {
|
|
71
|
-
shortfallUsd,
|
|
72
|
-
reconcileReport,
|
|
73
|
-
reconcileOnce,
|
|
74
|
-
ReconcileError,
|
|
75
|
-
DISCREPANCY_KINDS
|
|
76
|
-
};
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { type Transaction } from './ledger';
|
|
2
|
-
import { type Money } from './money';
|
|
3
|
-
import { type ChainSpec } from './chain';
|
|
4
|
-
/**
|
|
5
|
-
* Getting money out, which is the most dangerous path in the product.
|
|
6
|
-
*
|
|
7
|
-
* Everything else here can be wrong and cost a correction. This one can be
|
|
8
|
-
* wrong and cost the money. Four things go wrong in practice and all four are
|
|
9
|
-
* arranged against explicitly:
|
|
10
|
-
*
|
|
11
|
-
* THE HOLD placed as part of accepting the request, never after. A gap
|
|
12
|
-
* between "accepted" and "held" is a window in which the same
|
|
13
|
-
* balance funds two withdrawals.
|
|
14
|
-
* THE BROADCAST keyed, so a retry after a timeout cannot send twice. A
|
|
15
|
-
* dispatcher that cannot tell "did not send" from "sent, reply
|
|
16
|
-
* lost" will eventually send twice.
|
|
17
|
-
* THE REFUND a transaction that fails AFTER the balance was debited leaves
|
|
18
|
-
* the user short. The prior art reconciled failed and rejected
|
|
19
|
-
* refunds in two separate passes, because both were found
|
|
20
|
-
* missing in production — separately.
|
|
21
|
-
* THE SWEEP on demand to fund a payout, never on a timer. A timer burns
|
|
22
|
-
* gas continuously moving dust nobody asked to move.
|
|
23
|
-
*
|
|
24
|
-
* ## A hold is not a lock
|
|
25
|
-
*
|
|
26
|
-
* It is a posting between the owner's `available` and `held` sub-accounts. So
|
|
27
|
-
* `availableOf()` is an ordinary balance query, there is no reservation table,
|
|
28
|
-
* no expiry sweeper, and a process that dies mid-withdrawal strands nothing —
|
|
29
|
-
* the posting either happened or it did not.
|
|
30
|
-
*/
|
|
31
|
-
export declare class WithdrawalError extends Error {
|
|
32
|
-
readonly code: 'BAD_STATE' | 'BAD_ADDRESS' | 'BAD_AMOUNT' | 'INSUFFICIENT' | 'CHAIN_DISABLED' | 'MALFORMED';
|
|
33
|
-
constructor(code: 'BAD_STATE' | 'BAD_ADDRESS' | 'BAD_AMOUNT' | 'INSUFFICIENT' | 'CHAIN_DISABLED' | 'MALFORMED', message: string);
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Every state a withdrawal can be in, and there are no others.
|
|
37
|
-
*
|
|
38
|
-
* Written down as a list because the transitions are checked against it. An
|
|
39
|
-
* undeclared state reachable by assignment is how a withdrawal ends up
|
|
40
|
-
* `approved` twice, or dispatched from a state that never held the funds.
|
|
41
|
-
*/
|
|
42
|
-
export declare const WITHDRAWAL_STATES: readonly ["requested", "approved", "dispatched", "confirmed", "rejected", "failed", "refunded"];
|
|
43
|
-
export type WithdrawalState = (typeof WITHDRAWAL_STATES)[number];
|
|
44
|
-
export interface Withdrawal {
|
|
45
|
-
key: string;
|
|
46
|
-
owner: string;
|
|
47
|
-
chain: string;
|
|
48
|
-
/** Where the money is going. Validated against the chain's scheme. */
|
|
49
|
-
destination: string;
|
|
50
|
-
amount: Money;
|
|
51
|
-
state: WithdrawalState;
|
|
52
|
-
requestedAtMs: number;
|
|
53
|
-
/** The broadcast idempotency key. Present from `approved` onward. */
|
|
54
|
-
broadcastKey?: string;
|
|
55
|
-
txHash?: string;
|
|
56
|
-
decidedByUserKey?: string;
|
|
57
|
-
reason?: string;
|
|
58
|
-
}
|
|
59
|
-
/** May this withdrawal move there? */
|
|
60
|
-
export declare const canTransition: (from: WithdrawalState, to: WithdrawalState) => boolean;
|
|
61
|
-
/** The reference for the hold, and therefore the queue's dedupe key. */
|
|
62
|
-
export declare const holdReference: (key: string) => string;
|
|
63
|
-
/**
|
|
64
|
-
* The key a broadcast is made under.
|
|
65
|
-
*
|
|
66
|
-
* Derived from the withdrawal, so the same withdrawal produces the same key on
|
|
67
|
-
* every attempt. A dispatcher that timed out and retries presents this again;
|
|
68
|
-
* anything downstream that has seen it already knows this is the same send
|
|
69
|
-
* rather than a second one. Deriving it rather than generating it is the whole
|
|
70
|
-
* point — a generated key is a new key on every retry, which is exactly the
|
|
71
|
-
* failure it was meant to prevent.
|
|
72
|
-
*/
|
|
73
|
-
export declare const broadcastKeyFor: (withdrawal: Pick<Withdrawal, "key" | "chain">) => string;
|
|
74
|
-
/**
|
|
75
|
-
* Accept a request, and hold the funds in the same breath.
|
|
76
|
-
*
|
|
77
|
-
* Returns the withdrawal AND the posting together, so a caller cannot persist
|
|
78
|
-
* one without the other. Two functions — "create the request" then "place the
|
|
79
|
-
* hold" — would leave a window in which the balance is still available and a
|
|
80
|
-
* second request can spend it. They are one operation because they must not be
|
|
81
|
-
* separable.
|
|
82
|
-
*/
|
|
83
|
-
export declare function requestWithdrawal(args: {
|
|
84
|
-
key: string;
|
|
85
|
-
owner: string;
|
|
86
|
-
chain: ChainSpec;
|
|
87
|
-
destination: string;
|
|
88
|
-
amount: Money;
|
|
89
|
-
/** From `availableOf()` — what the owner can actually spend right now. */
|
|
90
|
-
available: Money;
|
|
91
|
-
atMs: number;
|
|
92
|
-
}): {
|
|
93
|
-
withdrawal: Withdrawal;
|
|
94
|
-
hold: Transaction;
|
|
95
|
-
};
|
|
96
|
-
/**
|
|
97
|
-
* A human says yes.
|
|
98
|
-
*
|
|
99
|
-
* No posting: the funds were held at request time and stay held. Approval mints
|
|
100
|
-
* the broadcast key, which is the moment the send acquires an identity — before
|
|
101
|
-
* this there is nothing to be idempotent about.
|
|
102
|
-
*/
|
|
103
|
-
export declare function approveWithdrawal(withdrawal: Withdrawal, args: {
|
|
104
|
-
actorUserKey: string;
|
|
105
|
-
atMs: number;
|
|
106
|
-
}): Withdrawal;
|
|
107
|
-
/**
|
|
108
|
-
* A human says no, and the hold comes back.
|
|
109
|
-
*
|
|
110
|
-
* Reachable from `approved` as well as `requested`: a withdrawal approved and
|
|
111
|
-
* not yet broadcast can still be stopped, and that is exactly when somebody
|
|
112
|
-
* notices something is wrong.
|
|
113
|
-
*/
|
|
114
|
-
export declare function rejectWithdrawal(withdrawal: Withdrawal, args: {
|
|
115
|
-
actorUserKey: string;
|
|
116
|
-
reason: string;
|
|
117
|
-
atMs: number;
|
|
118
|
-
}): {
|
|
119
|
-
withdrawal: Withdrawal;
|
|
120
|
-
release: Transaction;
|
|
121
|
-
};
|
|
122
|
-
/** What a broadcaster must provide. Injected — runtime holds no credentials. */
|
|
123
|
-
export interface Broadcaster {
|
|
124
|
-
/**
|
|
125
|
-
* Send, or report that this key was already sent.
|
|
126
|
-
*
|
|
127
|
-
* The KEY is the argument that matters. An implementation that ignores it
|
|
128
|
-
* and sends anyway defeats the only protection against a double payout, so
|
|
129
|
-
* it is passed first and named for what it does.
|
|
130
|
-
*/
|
|
131
|
-
send(args: {
|
|
132
|
-
broadcastKey: string;
|
|
133
|
-
chain: string;
|
|
134
|
-
destination: string;
|
|
135
|
-
amount: Money;
|
|
136
|
-
}): Promise<{
|
|
137
|
-
txHash: string;
|
|
138
|
-
alreadySent: boolean;
|
|
139
|
-
}>;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Broadcast one approved withdrawal.
|
|
143
|
-
*
|
|
144
|
-
* `alreadySent` is treated as success, not as an error. A dispatcher that timed
|
|
145
|
-
* out and retried is the ordinary case, and treating the second attempt as a
|
|
146
|
-
* failure would move a withdrawal that IS on-chain into `failed` and then
|
|
147
|
-
* refund it — paying the same money twice, which is the exact outcome the key
|
|
148
|
-
* exists to prevent.
|
|
149
|
-
*/
|
|
150
|
-
export declare function processOnce(withdrawal: Withdrawal, args: {
|
|
151
|
-
broadcaster: Broadcaster;
|
|
152
|
-
atMs: number;
|
|
153
|
-
}): Promise<Withdrawal>;
|
|
154
|
-
/**
|
|
155
|
-
* Seen on-chain, deep enough. The hold becomes a real debit.
|
|
156
|
-
*
|
|
157
|
-
* `captureHold` is what actually removes the money: until now it was held, not
|
|
158
|
-
* spent, and a withdrawal abandoned before this point costs the owner nothing.
|
|
159
|
-
*/
|
|
160
|
-
export declare function confirmWithdrawal(withdrawal: Withdrawal, args: {
|
|
161
|
-
txHash: string;
|
|
162
|
-
atMs: number;
|
|
163
|
-
}): {
|
|
164
|
-
withdrawal: Withdrawal;
|
|
165
|
-
capture: Transaction;
|
|
166
|
-
};
|
|
167
|
-
/** Broadcast and failed on-chain. Marked, not yet made good. */
|
|
168
|
-
export declare function markFailed(withdrawal: Withdrawal, args: {
|
|
169
|
-
reason: string;
|
|
170
|
-
atMs: number;
|
|
171
|
-
}): Withdrawal;
|
|
172
|
-
/**
|
|
173
|
-
* Make a failed withdrawal good.
|
|
174
|
-
*
|
|
175
|
-
* A separate pass from rejection on purpose. They look like one problem — money
|
|
176
|
-
* to return — and they are two: a rejection never left the platform, whereas a
|
|
177
|
-
* failure was broadcast and did not land. The prior art ran them as one and
|
|
178
|
-
* found BOTH refund paths missing in production, separately, which is the
|
|
179
|
-
* strongest argument available for keeping them apart.
|
|
180
|
-
*/
|
|
181
|
-
export declare function refundFailed(withdrawal: Withdrawal, args: {
|
|
182
|
-
atMs: number;
|
|
183
|
-
}): {
|
|
184
|
-
withdrawal: Withdrawal;
|
|
185
|
-
release: Transaction;
|
|
186
|
-
};
|
|
187
|
-
/** A deposit address holding funds that have not been moved to the hot wallet. */
|
|
188
|
-
export interface SweepCandidate {
|
|
189
|
-
address: string;
|
|
190
|
-
owner: string;
|
|
191
|
-
amount: Money;
|
|
192
|
-
/** What it costs to move it, in the same asset terms as `amount`. */
|
|
193
|
-
estimatedGas: Money;
|
|
194
|
-
}
|
|
195
|
-
export interface SweepPlan {
|
|
196
|
-
/** Addresses worth sweeping, largest first. */
|
|
197
|
-
sweep: readonly SweepCandidate[];
|
|
198
|
-
/** Skipped because moving them costs more than they hold. */
|
|
199
|
-
uneconomic: readonly SweepCandidate[];
|
|
200
|
-
/** What the plan raises, net of gas. */
|
|
201
|
-
raised: Money;
|
|
202
|
-
/** True when the plan cannot fund the payout even after sweeping everything. */
|
|
203
|
-
short: boolean;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Which deposit addresses to sweep to fund one payout.
|
|
207
|
-
*
|
|
208
|
-
* ON DEMAND, never on a timer. A timer sweeps continuously and burns gas moving
|
|
209
|
-
* dust nobody asked to move; funding a specific payout sweeps the least it can
|
|
210
|
-
* and stops. That is why this takes a target rather than a schedule.
|
|
211
|
-
*
|
|
212
|
-
* Largest first, so the fewest transactions raise the amount — each sweep is
|
|
213
|
-
* its own gas cost, and twenty small ones cost more than two large ones raising
|
|
214
|
-
* the same total.
|
|
215
|
-
*/
|
|
216
|
-
export declare function sweepForPayout(args: {
|
|
217
|
-
need: Money;
|
|
218
|
-
/** Already in the hot wallet and spendable without sweeping anything. */
|
|
219
|
-
hot: Money;
|
|
220
|
-
candidates: readonly SweepCandidate[];
|
|
221
|
-
}): SweepPlan;
|
|
222
|
-
/** Human-readable, for an operator screen. */
|
|
223
|
-
export declare const describeSweep: (plan: SweepPlan) => string;
|