hookr-sdk 0.2.1 → 1.0.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.
- package/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/MIGRATING.md +89 -0
- package/README.md +286 -105
- package/dist/core/abi.d.ts +10930 -0
- package/dist/core/abi.js +455 -0
- package/dist/core/advisories.d.ts +184 -0
- package/dist/core/advisories.js +291 -0
- package/dist/core/client.d.ts +55 -0
- package/dist/core/client.js +107 -0
- package/dist/core/generated.d.ts +14295 -0
- package/dist/core/generated.js +1058 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.js +17 -0
- package/dist/core/launch.d.ts +200 -0
- package/dist/core/launch.js +427 -0
- package/dist/core/pair.d.ts +64 -0
- package/dist/core/pair.js +136 -0
- package/dist/core/params.d.ts +415 -0
- package/dist/core/params.js +1235 -0
- package/dist/core/policies.d.ts +136 -0
- package/dist/core/policies.js +125 -0
- package/dist/core/proofs.d.ts +25 -0
- package/dist/core/proofs.js +118 -0
- package/dist/core/quotes.d.ts +185 -0
- package/dist/core/quotes.js +550 -0
- package/dist/core/referrals.d.ts +32 -0
- package/dist/core/referrals.js +36 -0
- package/dist/core/registry.d.ts +357 -0
- package/dist/core/registry.js +378 -0
- package/dist/core/rules.d.ts +675 -0
- package/dist/core/rules.js +1049 -0
- package/dist/core/schema.d.ts +358 -0
- package/dist/core/schema.js +105 -0
- package/dist/core/stocks.generated.d.ts +11 -0
- package/dist/core/stocks.generated.js +1228 -0
- package/dist/core/vault.d.ts +59 -0
- package/dist/core/vault.js +112 -0
- package/dist/core/wallet.d.ts +142 -0
- package/dist/core/wallet.js +304 -0
- package/dist/deployments.d.ts +8 -0
- package/dist/deployments.js +26 -0
- package/dist/index.d.ts +6 -15
- package/dist/index.js +6 -15
- package/dist/releases.d.ts +172 -0
- package/dist/releases.js +268 -0
- package/package.json +46 -10
- package/provenance.json +31 -0
- package/dist/abi.d.ts +0 -6404
- package/dist/abi.js +0 -8300
- package/dist/chain.d.ts +0 -87
- package/dist/chain.js +0 -86
- package/dist/claims.d.ts +0 -32
- package/dist/claims.js +0 -31
- package/dist/fees.d.ts +0 -59
- package/dist/fees.js +0 -142
- package/dist/links.d.ts +0 -48
- package/dist/links.js +0 -93
- package/dist/market.d.ts +0 -264
- package/dist/market.js +0 -310
- package/dist/reads.d.ts +0 -136
- package/dist/reads.js +0 -219
- package/dist/rules.d.ts +0 -84
- package/dist/rules.js +0 -296
- package/dist/swap.d.ts +0 -108
- package/dist/swap.js +0 -144
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { type Hex } from "viem";
|
|
2
|
+
import { type Address, type Call, type ContractRole, type DeploymentManifest, type Hash } from "./schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* HookrFeeAdvisory.Config, the bind data of a fee-advisory pool. Pips of the pool input.
|
|
5
|
+
* cap: largest surcharge either way; surcharge: default while no keeper target is live; premium0/premium1: default
|
|
6
|
+
* per-direction premiums; maxStep: largest change per update; minInterval/maxTtl: seconds (minInterval at most
|
|
7
|
+
* MAX_MIN_INTERVAL, one day).
|
|
8
|
+
*/
|
|
9
|
+
export interface FeeAdvisoryConfig {
|
|
10
|
+
cap: number;
|
|
11
|
+
surcharge: number;
|
|
12
|
+
premium0: number;
|
|
13
|
+
premium1: number;
|
|
14
|
+
maxStep: number;
|
|
15
|
+
minInterval: number;
|
|
16
|
+
maxTtl: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* HookrSessionAdvisory.Schedule, the bind data of a session-advisory pool: the surcharge per US equity session.
|
|
20
|
+
* Ramps are seconds after the open (pre-market to regular) and before the close (regular to after-hours); flags is
|
|
21
|
+
* `SESSION_UNCOVERED_AS_CLOSED` or zero.
|
|
22
|
+
*/
|
|
23
|
+
export interface SessionSchedule {
|
|
24
|
+
regularPips: number;
|
|
25
|
+
preMarketPips: number;
|
|
26
|
+
afterHoursPips: number;
|
|
27
|
+
overnightPips: number;
|
|
28
|
+
closedPips: number;
|
|
29
|
+
openRampSeconds: number;
|
|
30
|
+
closeRampSeconds: number;
|
|
31
|
+
flags: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* HookrSessionTiers.Tiers: a higher LP fee outside US regular hours that a fee-advisory pool may bind next to its
|
|
35
|
+
* Config. The surcharge is the keeper's (or the default) plus the tier in force, clamped to the Config cap. Same
|
|
36
|
+
* fields and units as SessionSchedule; the tiers read the session advisory's shared market calendar.
|
|
37
|
+
*/
|
|
38
|
+
export type SessionTiers = SessionSchedule;
|
|
39
|
+
/** Flag: a weekday outside the calendar is charged the closed surcharge. */
|
|
40
|
+
export declare const SESSION_UNCOVERED_AS_CLOSED: 1;
|
|
41
|
+
/** HookrFeeAdvisory.MIN_TIERED_GAS: the advisory refuses session tiers on a pool with less advisory gas. */
|
|
42
|
+
export declare const FEE_ADVISORY_MIN_TIERED_GAS: 60000;
|
|
43
|
+
/**
|
|
44
|
+
* Advisory gas a HookrRoot fee-advisory pool needs for the advisory's own bind. HookrRoot requires the pool's
|
|
45
|
+
* advisory gas to equal the admission's and runs `bind` within it; the bind reads the admission and the Rules
|
|
46
|
+
* configuration and writes the pool's state, bounds and, when tiered, its session tiers, measured at 71,974 and
|
|
47
|
+
* 99,306 gas on this release. These floors carry about 10% margin and sit above the advisory's own MIN_ADVISORY_GAS
|
|
48
|
+
* (30,000) and MIN_TIERED_GAS (60,000). A pair root calls `bindPair` with its full gas, so no bind floor applies there.
|
|
49
|
+
*/
|
|
50
|
+
export declare const FEE_ADVISORY_ROOT_BIND_GAS: {
|
|
51
|
+
readonly plain: 80000;
|
|
52
|
+
readonly tiered: 110000;
|
|
53
|
+
};
|
|
54
|
+
/** The root a fee-advisory pool binds through: a HookrRoot launch member (`bind`) or a HookrPairRoot (`bindPair`). */
|
|
55
|
+
export type FeeAdvisoryRoot = "root" | "pair";
|
|
56
|
+
/**
|
|
57
|
+
* Smallest advisory gas a fee-advisory pool binds with. HookrRoot: the bind floor `FEE_ADVISORY_ROOT_BIND_GAS`,
|
|
58
|
+
* which clears `bind`'s MIN_ADVISORY_GAS and, tiered, MIN_TIERED_GAS. Pair root: `bindPair`'s MIN_PAIR_GAS
|
|
59
|
+
* (10,000), or MIN_TIERED_GAS (60,000) with session tiers.
|
|
60
|
+
*/
|
|
61
|
+
export declare function feeAdvisoryMinGas(root: FeeAdvisoryRoot, tiered: boolean): number;
|
|
62
|
+
/** ABI-encodes a fee-advisory Config: the bind data for `launchAdvised` and for a pair root's `bindPair`. */
|
|
63
|
+
export declare function encodeFeeAdvisoryConfig(c: FeeAdvisoryConfig): Hex;
|
|
64
|
+
/** Decodes `PoolBound.config` / `PairBound.config`, refusing any non-canonical encoding as the advisory does. */
|
|
65
|
+
export declare function decodeFeeAdvisoryConfig(data: Hex): FeeAdvisoryConfig;
|
|
66
|
+
/**
|
|
67
|
+
* Refuses a Config HookrFeeAdvisory would refuse under the largest cap `limit`: the pair root's advisory cap, or
|
|
68
|
+
* on a HookrRoot pool min(admission cap, pool room above the Rules' native ceiling).
|
|
69
|
+
*/
|
|
70
|
+
export declare function checkFeeAdvisoryConfig(c: FeeAdvisoryConfig, limit: number): void;
|
|
71
|
+
/**
|
|
72
|
+
* Refuses session tiers HookrFeeAdvisory would refuse under the pool's Config cap `cap`: known flags, ramps within
|
|
73
|
+
* the shortest regular session, every tier at most the cap, and at least one tier above zero (all-zero tiers bind as
|
|
74
|
+
* a plain Config instead).
|
|
75
|
+
*/
|
|
76
|
+
export declare function checkSessionTiers(t: SessionTiers, cap: number): void;
|
|
77
|
+
/**
|
|
78
|
+
* ABI-encodes a fee Config with session tiers (480 bytes): the bind data of a fee-advisory pool that charges more
|
|
79
|
+
* outside US regular hours, for `launchAdvised` and a pair root's `bindPair`. Checks no gas: the pool needs
|
|
80
|
+
* `feeAdvisoryMinGas(root, true)` of advisory gas, which `encodeFeeAdvisoryBind` checks.
|
|
81
|
+
*/
|
|
82
|
+
export declare function encodeTieredFeeAdvisoryConfig(c: FeeAdvisoryConfig, t: SessionTiers): Hex;
|
|
83
|
+
/**
|
|
84
|
+
* Decodes fee-advisory bind data (`PoolBound.config` / `PairBound.config`): a Config, or a Config and session
|
|
85
|
+
* tiers. Refuses any other size and any non-canonical encoding, as the advisory does.
|
|
86
|
+
*/
|
|
87
|
+
export declare function decodeFeeAdvisoryBindData(data: Hex): {
|
|
88
|
+
config: FeeAdvisoryConfig;
|
|
89
|
+
tiers?: SessionTiers;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Fee-advisory bind data: the Config, with session tiers when given, checked against the largest cap `limit` and
|
|
93
|
+
* the pool's advisory gas `gasLimit` against `feeAdvisoryMinGas(root, tiered)`. On a HookrRoot pool (`launchAdvised`,
|
|
94
|
+
* the default) `gasLimit` is the advisory admission's, which HookrRoot requires exactly and runs the bind within:
|
|
95
|
+
* at least 80,000 plain and 110,000 tiered. On a pair root (`"pair"`, `bindPair`) it is the root's advisoryGasLimit:
|
|
96
|
+
* at least 10,000 plain and 60,000 tiered.
|
|
97
|
+
*/
|
|
98
|
+
export declare function encodeFeeAdvisoryBind(c: FeeAdvisoryConfig, limit: number, gasLimit: number, tiers?: SessionTiers, root?: FeeAdvisoryRoot): Hex;
|
|
99
|
+
/** HookrFeeAdvisory.State: the swap-path state of a pool; `tiered` when it bound session tiers. */
|
|
100
|
+
export interface FeePoolState {
|
|
101
|
+
target0: number;
|
|
102
|
+
target1: number;
|
|
103
|
+
expiry: number;
|
|
104
|
+
default0: number;
|
|
105
|
+
default1: number;
|
|
106
|
+
guardCeiling: number;
|
|
107
|
+
guardEnd: number;
|
|
108
|
+
updatedAt: number;
|
|
109
|
+
bound: boolean;
|
|
110
|
+
tiered: boolean;
|
|
111
|
+
}
|
|
112
|
+
/** `poolState(id)` calldata for HookrFeeAdvisory. */
|
|
113
|
+
export declare function encodeFeePoolStateCall(id: Hash): Hex;
|
|
114
|
+
/** Decodes a `poolState(id)` result. */
|
|
115
|
+
export declare function decodeFeePoolState(data: Hex): FeePoolState;
|
|
116
|
+
/** ABI-encodes a session Schedule (always 256 bytes): the bind data for `launchAdvised` and a pair root's `bindPair`. */
|
|
117
|
+
export declare function encodeSessionSchedule(s: SessionSchedule): Hex;
|
|
118
|
+
/** Decodes session bind data; the advisory accepts exactly 256 bytes. */
|
|
119
|
+
export declare function decodeSessionSchedule(data: Hex): SessionSchedule;
|
|
120
|
+
/**
|
|
121
|
+
* Refuses a Schedule HookrSessionAdvisory would refuse with tier cap `cap`: the pair root's advisory cap, or on a
|
|
122
|
+
* HookrRoot pool the advisory's admitted LP cap.
|
|
123
|
+
*/
|
|
124
|
+
export declare function checkSessionSchedule(s: SessionSchedule, cap: number): void;
|
|
125
|
+
/** A pair root's advisory terms and the parameters its advisory binds the pool with. */
|
|
126
|
+
export type PairAdvisoryBinding =
|
|
127
|
+
/** `tiers`: optional session tiers, a higher fee outside US regular hours next to the keeper surcharge. */
|
|
128
|
+
{
|
|
129
|
+
advisory: "fee";
|
|
130
|
+
config: FeeAdvisoryConfig;
|
|
131
|
+
tiers?: SessionTiers;
|
|
132
|
+
} | {
|
|
133
|
+
advisory: "session";
|
|
134
|
+
schedule: SessionSchedule;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* `advisoryData` for HookrRootFactory.deploy: the bytes the pair root passes to its advisory's `bindPair`, checked
|
|
138
|
+
* against the root's immutable advisory cap and gas limit the way the advisory checks them (fee: MIN_PAIR_GAS, or
|
|
139
|
+
* MIN_TIERED_GAS with tiers; session: MIN_PAIR_GAS).
|
|
140
|
+
*/
|
|
141
|
+
export declare function encodeBindPairData(binding: PairAdvisoryBinding, capPips: number, gasLimit: number): Hex;
|
|
142
|
+
/**
|
|
143
|
+
* Full `bindPair(id, capPips, gasLimit, data)` calldata, the call a pair root makes when it opens. Useful for
|
|
144
|
+
* reviewing a trace; the advisory only accepts it from the root itself.
|
|
145
|
+
*/
|
|
146
|
+
export declare function encodeBindPairCall(id: Hash, capPips: number, gasLimit: number, data: Hex): Hex;
|
|
147
|
+
/**
|
|
148
|
+
* Keeper call: sets the keeper target and per-direction premiums of a fee-advisory pool for `ttl` seconds. Each
|
|
149
|
+
* directional surcharge stays within the pool cap and moves at most maxStep from the surcharge in force.
|
|
150
|
+
*/
|
|
151
|
+
export declare function buildFeeReprice(m: DeploymentManifest, id: Hash, target: number, premium0: number, premium1: number, ttl: number): Call;
|
|
152
|
+
/** A timelocked HookrGoverned operation on a later-stage contract. */
|
|
153
|
+
export type GovernedOperation = {
|
|
154
|
+
kind: "ADD_KEEPER";
|
|
155
|
+
keeper: Address;
|
|
156
|
+
}
|
|
157
|
+
/** Dates are month * 100 + day, strictly ascending weekdays; `early` lists 13:00 closes that are not closures. */
|
|
158
|
+
| {
|
|
159
|
+
kind: "APPEND_YEAR";
|
|
160
|
+
year: number;
|
|
161
|
+
closed: readonly number[];
|
|
162
|
+
early: readonly number[];
|
|
163
|
+
} | {
|
|
164
|
+
kind: "SET_SKIM";
|
|
165
|
+
bps: number;
|
|
166
|
+
} | {
|
|
167
|
+
kind: "TRANSFER_OWNER";
|
|
168
|
+
nextOwner: Address;
|
|
169
|
+
};
|
|
170
|
+
/** `queue(kind, arguments)` inputs of a governed operation, with its canonical ABI arguments. */
|
|
171
|
+
export declare function encodeGovernedOperation(op: GovernedOperation): {
|
|
172
|
+
kind: Hash;
|
|
173
|
+
arguments: Hex;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* HookrGoverned.operationHash: keccak256(abi.encode(chainId, contract, kind, epoch, arguments)). Read `epoch` from
|
|
177
|
+
* the contract's `epoch(key)`; a restrictive action (removing a keeper, lowering the skim) raises it and voids what
|
|
178
|
+
* was queued before.
|
|
179
|
+
*/
|
|
180
|
+
export declare function governedOperationHash(chainId: bigint, target: Address, kind: Hash, epoch: bigint, args: Hex): Hash;
|
|
181
|
+
/** Owner call queuing a governed operation on its pinned contract. */
|
|
182
|
+
export declare function buildGovernedQueue(m: DeploymentManifest, op: GovernedOperation, role?: ContractRole): Call;
|
|
183
|
+
/** Owner call executing a matured governed operation with the arguments it was queued with. */
|
|
184
|
+
export declare function buildGovernedExecute(m: DeploymentManifest, op: GovernedOperation, role?: ContractRole): Call;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { decodeAbiParameters, decodeFunctionResult, encodeAbiParameters, encodeFunctionData, keccak256, } from "viem";
|
|
2
|
+
import { ARTIFACT_ABIS, RELEASE_CONSTANTS, STRUCT_COMPONENTS } from "./generated.js";
|
|
3
|
+
import { address, assert, hash, uint, } from "./schema.js";
|
|
4
|
+
import { buildCall } from "./wallet.js";
|
|
5
|
+
/** Flag: a weekday outside the calendar is charged the closed surcharge. */
|
|
6
|
+
export const SESSION_UNCOVERED_AS_CLOSED = RELEASE_CONSTANTS.SESSION_UNCOVERED_AS_CLOSED;
|
|
7
|
+
/** HookrFeeAdvisory.MIN_TIERED_GAS: the advisory refuses session tiers on a pool with less advisory gas. */
|
|
8
|
+
export const FEE_ADVISORY_MIN_TIERED_GAS = RELEASE_CONSTANTS.FEE_ADVISORY_MIN_TIERED_GAS;
|
|
9
|
+
/**
|
|
10
|
+
* Advisory gas a HookrRoot fee-advisory pool needs for the advisory's own bind. HookrRoot requires the pool's
|
|
11
|
+
* advisory gas to equal the admission's and runs `bind` within it; the bind reads the admission and the Rules
|
|
12
|
+
* configuration and writes the pool's state, bounds and, when tiered, its session tiers, measured at 71,974 and
|
|
13
|
+
* 99,306 gas on this release. These floors carry about 10% margin and sit above the advisory's own MIN_ADVISORY_GAS
|
|
14
|
+
* (30,000) and MIN_TIERED_GAS (60,000). A pair root calls `bindPair` with its full gas, so no bind floor applies there.
|
|
15
|
+
*/
|
|
16
|
+
export const FEE_ADVISORY_ROOT_BIND_GAS = { plain: 80_000, tiered: 110_000 };
|
|
17
|
+
/**
|
|
18
|
+
* Smallest advisory gas a fee-advisory pool binds with. HookrRoot: the bind floor `FEE_ADVISORY_ROOT_BIND_GAS`,
|
|
19
|
+
* which clears `bind`'s MIN_ADVISORY_GAS and, tiered, MIN_TIERED_GAS. Pair root: `bindPair`'s MIN_PAIR_GAS
|
|
20
|
+
* (10,000), or MIN_TIERED_GAS (60,000) with session tiers.
|
|
21
|
+
*/
|
|
22
|
+
export function feeAdvisoryMinGas(root, tiered) {
|
|
23
|
+
if (root === "pair")
|
|
24
|
+
return tiered ? RELEASE_CONSTANTS.FEE_ADVISORY_MIN_TIERED_GAS : RELEASE_CONSTANTS.FEE_ADVISORY_MIN_PAIR_GAS;
|
|
25
|
+
return tiered
|
|
26
|
+
? Math.max(FEE_ADVISORY_ROOT_BIND_GAS.tiered, RELEASE_CONSTANTS.FEE_ADVISORY_MIN_TIERED_GAS)
|
|
27
|
+
: Math.max(FEE_ADVISORY_ROOT_BIND_GAS.plain, RELEASE_CONSTANTS.FEE_ADVISORY_MIN_ADVISORY_GAS);
|
|
28
|
+
}
|
|
29
|
+
const FEE_TUPLE = [{ type: "tuple", components: STRUCT_COMPONENTS.FeeAdvisoryConfig }];
|
|
30
|
+
const SESSION_TUPLE = [{ type: "tuple", components: STRUCT_COMPONENTS.SessionSchedule }];
|
|
31
|
+
const TIERED_FEE_TUPLE = [
|
|
32
|
+
{ type: "tuple", components: STRUCT_COMPONENTS.FeeAdvisoryConfig },
|
|
33
|
+
{ type: "tuple", components: STRUCT_COMPONENTS.SessionTiers },
|
|
34
|
+
];
|
|
35
|
+
/** ABI size of a fee Config alone; tiered bind data appends the 256-byte Tiers. */
|
|
36
|
+
const FEE_CONFIG_BYTES = 32 * STRUCT_COMPONENTS.FeeAdvisoryConfig.length;
|
|
37
|
+
function int(value, bits, label) {
|
|
38
|
+
assert(Number.isSafeInteger(value) && value >= 0 && value < 2 ** bits, `Invalid ${label}`);
|
|
39
|
+
}
|
|
40
|
+
function fields(components, value) {
|
|
41
|
+
for (const c of components) {
|
|
42
|
+
const bits = Number(/^uint(\d+)$/.exec(c.type)?.[1]);
|
|
43
|
+
assert(bits > 0 && bits <= 48, `Unsupported ${c.type} field ${c.name}`);
|
|
44
|
+
int(value[c.name], bits, c.name);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** ABI-encodes a fee-advisory Config: the bind data for `launchAdvised` and for a pair root's `bindPair`. */
|
|
48
|
+
export function encodeFeeAdvisoryConfig(c) {
|
|
49
|
+
fields(STRUCT_COMPONENTS.FeeAdvisoryConfig, c);
|
|
50
|
+
return encodeAbiParameters(FEE_TUPLE, [c]);
|
|
51
|
+
}
|
|
52
|
+
/** Decodes `PoolBound.config` / `PairBound.config`, refusing any non-canonical encoding as the advisory does. */
|
|
53
|
+
export function decodeFeeAdvisoryConfig(data) {
|
|
54
|
+
const [c] = decodeAbiParameters(FEE_TUPLE, data);
|
|
55
|
+
assert(encodeAbiParameters(FEE_TUPLE, [c]).toLowerCase() === data.toLowerCase(), "Non-canonical fee config");
|
|
56
|
+
return { ...c };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Refuses a Config HookrFeeAdvisory would refuse under the largest cap `limit`: the pair root's advisory cap, or
|
|
60
|
+
* on a HookrRoot pool min(admission cap, pool room above the Rules' native ceiling).
|
|
61
|
+
*/
|
|
62
|
+
export function checkFeeAdvisoryConfig(c, limit) {
|
|
63
|
+
fields(STRUCT_COMPONENTS.FeeAdvisoryConfig, c);
|
|
64
|
+
assert(c.cap > 0 && c.cap <= limit, "Fee cap outside (0, limit]");
|
|
65
|
+
assert(c.surcharge + c.premium0 <= c.cap && c.surcharge + c.premium1 <= c.cap, "Default surcharge above the cap");
|
|
66
|
+
assert(c.maxStep > 0 && c.minInterval > 0, "Fee updates need a step and an interval");
|
|
67
|
+
assert(c.minInterval <= RELEASE_CONSTANTS.FEE_ADVISORY_MAX_MIN_INTERVAL, "Fee update interval above 1 day");
|
|
68
|
+
assert(c.maxTtl > 0 && c.maxTtl <= RELEASE_CONSTANTS.FEE_ADVISORY_MAX_TTL, "Fee TTL outside (0, 72 hours]");
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Refuses session tiers HookrFeeAdvisory would refuse under the pool's Config cap `cap`: known flags, ramps within
|
|
72
|
+
* the shortest regular session, every tier at most the cap, and at least one tier above zero (all-zero tiers bind as
|
|
73
|
+
* a plain Config instead).
|
|
74
|
+
*/
|
|
75
|
+
export function checkSessionTiers(t, cap) {
|
|
76
|
+
fields(STRUCT_COMPONENTS.SessionTiers, t);
|
|
77
|
+
const tiers = [t.regularPips, t.preMarketPips, t.afterHoursPips, t.overnightPips, t.closedPips];
|
|
78
|
+
assert(tiers.some((x) => x > 0), "Session tiers are all zero; bind the Config alone");
|
|
79
|
+
assert((t.flags & ~RELEASE_CONSTANTS.SESSION_TIERS_UNCOVERED_AS_CLOSED) === 0, "Unknown tier flags");
|
|
80
|
+
assert(t.openRampSeconds + t.closeRampSeconds <= RELEASE_CONSTANTS.SESSION_TIERS_MAX_RAMP_SECONDS, "Ramps longer than the shortest regular session");
|
|
81
|
+
assert(tiers.every((x) => x <= cap), "Session tier above the fee cap");
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* ABI-encodes a fee Config with session tiers (480 bytes): the bind data of a fee-advisory pool that charges more
|
|
85
|
+
* outside US regular hours, for `launchAdvised` and a pair root's `bindPair`. Checks no gas: the pool needs
|
|
86
|
+
* `feeAdvisoryMinGas(root, true)` of advisory gas, which `encodeFeeAdvisoryBind` checks.
|
|
87
|
+
*/
|
|
88
|
+
export function encodeTieredFeeAdvisoryConfig(c, t) {
|
|
89
|
+
fields(STRUCT_COMPONENTS.FeeAdvisoryConfig, c);
|
|
90
|
+
checkSessionTiers(t, c.cap);
|
|
91
|
+
return encodeAbiParameters(TIERED_FEE_TUPLE, [c, t]);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Decodes fee-advisory bind data (`PoolBound.config` / `PairBound.config`): a Config, or a Config and session
|
|
95
|
+
* tiers. Refuses any other size and any non-canonical encoding, as the advisory does.
|
|
96
|
+
*/
|
|
97
|
+
export function decodeFeeAdvisoryBindData(data) {
|
|
98
|
+
const size = (data.length - 2) / 2;
|
|
99
|
+
if (size === FEE_CONFIG_BYTES)
|
|
100
|
+
return { config: decodeFeeAdvisoryConfig(data) };
|
|
101
|
+
assert(size === FEE_CONFIG_BYTES + RELEASE_CONSTANTS.SESSION_TIERS_ENCODED_SIZE, "Fee bind data is neither a Config nor a Config with tiers");
|
|
102
|
+
const [config, tiers] = decodeAbiParameters(TIERED_FEE_TUPLE, data);
|
|
103
|
+
assert(encodeAbiParameters(TIERED_FEE_TUPLE, [config, tiers]).toLowerCase() === data.toLowerCase(), "Non-canonical fee config");
|
|
104
|
+
return { config: { ...config }, tiers: { ...tiers } };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Fee-advisory bind data: the Config, with session tiers when given, checked against the largest cap `limit` and
|
|
108
|
+
* the pool's advisory gas `gasLimit` against `feeAdvisoryMinGas(root, tiered)`. On a HookrRoot pool (`launchAdvised`,
|
|
109
|
+
* the default) `gasLimit` is the advisory admission's, which HookrRoot requires exactly and runs the bind within:
|
|
110
|
+
* at least 80,000 plain and 110,000 tiered. On a pair root (`"pair"`, `bindPair`) it is the root's advisoryGasLimit:
|
|
111
|
+
* at least 10,000 plain and 60,000 tiered.
|
|
112
|
+
*/
|
|
113
|
+
export function encodeFeeAdvisoryBind(c, limit, gasLimit, tiers, root = "root") {
|
|
114
|
+
checkFeeAdvisoryConfig(c, limit);
|
|
115
|
+
if (tiers)
|
|
116
|
+
checkSessionTiers(tiers, c.cap);
|
|
117
|
+
int(gasLimit, 32, "advisory gas");
|
|
118
|
+
const floor = feeAdvisoryMinGas(root, tiers !== undefined);
|
|
119
|
+
assert(gasLimit >= floor, `Advisory gas ${gasLimit} below the fee advisory's ${root === "pair" ? "pair root" : "HookrRoot"} ${tiers ? "tiered " : ""}floor ${floor}`);
|
|
120
|
+
return tiers ? encodeTieredFeeAdvisoryConfig(c, tiers) : encodeFeeAdvisoryConfig(c);
|
|
121
|
+
}
|
|
122
|
+
/** `poolState(id)` calldata for HookrFeeAdvisory. */
|
|
123
|
+
export function encodeFeePoolStateCall(id) {
|
|
124
|
+
return encodeFunctionData({ abi: ARTIFACT_ABIS.HookrFeeAdvisory, functionName: "poolState", args: [hash(id)] });
|
|
125
|
+
}
|
|
126
|
+
/** Decodes a `poolState(id)` result. */
|
|
127
|
+
export function decodeFeePoolState(data) {
|
|
128
|
+
const s = decodeFunctionResult({ abi: ARTIFACT_ABIS.HookrFeeAdvisory, functionName: "poolState", data });
|
|
129
|
+
return {
|
|
130
|
+
target0: s.target0,
|
|
131
|
+
target1: s.target1,
|
|
132
|
+
expiry: Number(s.expiry),
|
|
133
|
+
default0: s.default0,
|
|
134
|
+
default1: s.default1,
|
|
135
|
+
guardCeiling: s.guardCeiling,
|
|
136
|
+
guardEnd: Number(s.guardEnd),
|
|
137
|
+
updatedAt: Number(s.updatedAt),
|
|
138
|
+
bound: s.bound,
|
|
139
|
+
tiered: s.tiered,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** ABI-encodes a session Schedule (always 256 bytes): the bind data for `launchAdvised` and a pair root's `bindPair`. */
|
|
143
|
+
export function encodeSessionSchedule(s) {
|
|
144
|
+
fields(STRUCT_COMPONENTS.SessionSchedule, s);
|
|
145
|
+
return encodeAbiParameters(SESSION_TUPLE, [s]);
|
|
146
|
+
}
|
|
147
|
+
/** Decodes session bind data; the advisory accepts exactly 256 bytes. */
|
|
148
|
+
export function decodeSessionSchedule(data) {
|
|
149
|
+
assert(data.length === 2 + 2 * 256, "Session schedule must be 256 bytes");
|
|
150
|
+
const [s] = decodeAbiParameters(SESSION_TUPLE, data);
|
|
151
|
+
assert(encodeAbiParameters(SESSION_TUPLE, [s]).toLowerCase() === data.toLowerCase(), "Non-canonical schedule");
|
|
152
|
+
return { ...s };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Refuses a Schedule HookrSessionAdvisory would refuse with tier cap `cap`: the pair root's advisory cap, or on a
|
|
156
|
+
* HookrRoot pool the advisory's admitted LP cap.
|
|
157
|
+
*/
|
|
158
|
+
export function checkSessionSchedule(s, cap) {
|
|
159
|
+
fields(STRUCT_COMPONENTS.SessionSchedule, s);
|
|
160
|
+
assert((s.flags & ~SESSION_UNCOVERED_AS_CLOSED) === 0, "Unknown schedule flags");
|
|
161
|
+
assert(s.openRampSeconds + s.closeRampSeconds <= RELEASE_CONSTANTS.SESSION_ADVISORY_MAX_RAMP_SECONDS, "Ramps longer than the shortest regular session");
|
|
162
|
+
assert([s.regularPips, s.preMarketPips, s.afterHoursPips, s.overnightPips, s.closedPips].every((t) => t <= cap), "Session tier above the cap");
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* `advisoryData` for HookrRootFactory.deploy: the bytes the pair root passes to its advisory's `bindPair`, checked
|
|
166
|
+
* against the root's immutable advisory cap and gas limit the way the advisory checks them (fee: MIN_PAIR_GAS, or
|
|
167
|
+
* MIN_TIERED_GAS with tiers; session: MIN_PAIR_GAS).
|
|
168
|
+
*/
|
|
169
|
+
export function encodeBindPairData(binding, capPips, gasLimit) {
|
|
170
|
+
if (binding.advisory === "fee")
|
|
171
|
+
return encodeFeeAdvisoryBind(binding.config, capPips, gasLimit, binding.tiers, "pair");
|
|
172
|
+
assert(gasLimit >= RELEASE_CONSTANTS.SESSION_ADVISORY_MIN_PAIR_GAS, "Pair gas below the session advisory minimum");
|
|
173
|
+
checkSessionSchedule(binding.schedule, capPips);
|
|
174
|
+
return encodeSessionSchedule(binding.schedule);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Full `bindPair(id, capPips, gasLimit, data)` calldata, the call a pair root makes when it opens. Useful for
|
|
178
|
+
* reviewing a trace; the advisory only accepts it from the root itself.
|
|
179
|
+
*/
|
|
180
|
+
export function encodeBindPairCall(id, capPips, gasLimit, data) {
|
|
181
|
+
int(capPips, 24, "cap");
|
|
182
|
+
int(gasLimit, 32, "gas limit");
|
|
183
|
+
return encodeFunctionData({
|
|
184
|
+
abi: ARTIFACT_ABIS.HookrFeeAdvisory,
|
|
185
|
+
functionName: "bindPair",
|
|
186
|
+
args: [hash(id), capPips, gasLimit, data],
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Keeper call: sets the keeper target and per-direction premiums of a fee-advisory pool for `ttl` seconds. Each
|
|
191
|
+
* directional surcharge stays within the pool cap and moves at most maxStep from the surcharge in force.
|
|
192
|
+
*/
|
|
193
|
+
export function buildFeeReprice(m, id, target, premium0, premium1, ttl) {
|
|
194
|
+
int(target, 24, "target");
|
|
195
|
+
int(premium0, 24, "premium0");
|
|
196
|
+
int(premium1, 24, "premium1");
|
|
197
|
+
assert(Number.isInteger(ttl) && ttl > 0 && ttl <= RELEASE_CONSTANTS.FEE_ADVISORY_MAX_TTL, "TTL outside (0, 72 hours]");
|
|
198
|
+
return buildCall(m, "feeAdvisory", ARTIFACT_ABIS.HookrFeeAdvisory, "reprice", [
|
|
199
|
+
hash(id),
|
|
200
|
+
target,
|
|
201
|
+
premium0,
|
|
202
|
+
premium1,
|
|
203
|
+
ttl,
|
|
204
|
+
]);
|
|
205
|
+
}
|
|
206
|
+
/** Which pinned contract runs each governed kind. TRANSFER_OWNER runs on the role passed to the builders. */
|
|
207
|
+
const GOVERNED = {
|
|
208
|
+
ADD_KEEPER: "feeAdvisory",
|
|
209
|
+
APPEND_YEAR: "sessionAdvisory",
|
|
210
|
+
SET_SKIM: "liquidityVault",
|
|
211
|
+
};
|
|
212
|
+
function governedAbi(role) {
|
|
213
|
+
if (role === "feeAdvisory")
|
|
214
|
+
return ARTIFACT_ABIS.HookrFeeAdvisory;
|
|
215
|
+
if (role === "sessionAdvisory")
|
|
216
|
+
return ARTIFACT_ABIS.HookrSessionAdvisory;
|
|
217
|
+
assert(role === "liquidityVault", `No governed ABI for ${role}`);
|
|
218
|
+
return ARTIFACT_ABIS.HookrLiquidityVault;
|
|
219
|
+
}
|
|
220
|
+
function dates(values) {
|
|
221
|
+
values.forEach((d, i) => {
|
|
222
|
+
int(d, 16, "date");
|
|
223
|
+
assert(d >= 101 && d <= 1231 && d % 100 >= 1 && d % 100 <= 31, "Date outside month * 100 + day");
|
|
224
|
+
assert(i === 0 || d > values[i - 1], "Dates must be strictly ascending");
|
|
225
|
+
});
|
|
226
|
+
return [...values];
|
|
227
|
+
}
|
|
228
|
+
function governed(op) {
|
|
229
|
+
switch (op.kind) {
|
|
230
|
+
case "ADD_KEEPER":
|
|
231
|
+
return {
|
|
232
|
+
kind: RELEASE_CONSTANTS.ADD_KEEPER,
|
|
233
|
+
types: [{ type: "address" }],
|
|
234
|
+
values: [address(op.keeper)],
|
|
235
|
+
call: "addKeeper",
|
|
236
|
+
};
|
|
237
|
+
case "APPEND_YEAR":
|
|
238
|
+
int(op.year, 16, "year");
|
|
239
|
+
assert(dates(op.early).every((d) => !op.closed.includes(d)), "An early close cannot also be a closure");
|
|
240
|
+
return {
|
|
241
|
+
kind: RELEASE_CONSTANTS.APPEND_YEAR,
|
|
242
|
+
types: [{ type: "uint16" }, { type: "uint16[]" }, { type: "uint16[]" }],
|
|
243
|
+
values: [op.year, dates(op.closed), [...op.early]],
|
|
244
|
+
call: "appendYear",
|
|
245
|
+
};
|
|
246
|
+
case "SET_SKIM":
|
|
247
|
+
int(op.bps, 16, "skim");
|
|
248
|
+
assert(op.bps <= RELEASE_CONSTANTS.VAULT_SKIM_CEILING_BPS, "Skim above the ceiling");
|
|
249
|
+
return { kind: RELEASE_CONSTANTS.SET_SKIM, types: [{ type: "uint16" }], values: [op.bps], call: "setSkim" };
|
|
250
|
+
case "TRANSFER_OWNER":
|
|
251
|
+
return {
|
|
252
|
+
kind: RELEASE_CONSTANTS.TRANSFER_OWNER,
|
|
253
|
+
types: [{ type: "address" }],
|
|
254
|
+
values: [address(op.nextOwner)],
|
|
255
|
+
call: "transferOwnership",
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function roleOf(op, role) {
|
|
260
|
+
if (op.kind === "TRANSFER_OWNER") {
|
|
261
|
+
assert(role !== undefined, "TRANSFER_OWNER needs the governed contract role");
|
|
262
|
+
return role;
|
|
263
|
+
}
|
|
264
|
+
assert(role === undefined || role === GOVERNED[op.kind], `${op.kind} runs on ${GOVERNED[op.kind]}`);
|
|
265
|
+
return GOVERNED[op.kind];
|
|
266
|
+
}
|
|
267
|
+
/** `queue(kind, arguments)` inputs of a governed operation, with its canonical ABI arguments. */
|
|
268
|
+
export function encodeGovernedOperation(op) {
|
|
269
|
+
const g = governed(op);
|
|
270
|
+
return { kind: g.kind, arguments: encodeAbiParameters(g.types, g.values) };
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* HookrGoverned.operationHash: keccak256(abi.encode(chainId, contract, kind, epoch, arguments)). Read `epoch` from
|
|
274
|
+
* the contract's `epoch(key)`; a restrictive action (removing a keeper, lowering the skim) raises it and voids what
|
|
275
|
+
* was queued before.
|
|
276
|
+
*/
|
|
277
|
+
export function governedOperationHash(chainId, target, kind, epoch, args) {
|
|
278
|
+
return keccak256(encodeAbiParameters([{ type: "uint256" }, { type: "address" }, { type: "bytes32" }, { type: "uint256" }, { type: "bytes" }], [uint(chainId), address(target), hash(kind), uint(epoch), args]));
|
|
279
|
+
}
|
|
280
|
+
/** Owner call queuing a governed operation on its pinned contract. */
|
|
281
|
+
export function buildGovernedQueue(m, op, role) {
|
|
282
|
+
const r = roleOf(op, role);
|
|
283
|
+
const { kind, arguments: args } = encodeGovernedOperation(op);
|
|
284
|
+
return buildCall(m, r, governedAbi(r), "queue", [kind, args]);
|
|
285
|
+
}
|
|
286
|
+
/** Owner call executing a matured governed operation with the arguments it was queued with. */
|
|
287
|
+
export function buildGovernedExecute(m, op, role) {
|
|
288
|
+
const r = roleOf(op, role);
|
|
289
|
+
const g = governed(op);
|
|
290
|
+
return buildCall(m, r, governedAbi(r), g.call, g.values);
|
|
291
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type Address, type Coverage, type AccountView, type FamilyView, type ProofView, type Hash, type ApiEnvelope, type ApiPage, type ProgramListing } from "./schema.js";
|
|
2
|
+
/** Pages a list client-side before `programs()`/`families()`/`campaigns()` refuse to guess the rest. */
|
|
3
|
+
export declare const MAX_CLIENT_PAGES = 40;
|
|
4
|
+
export interface PageQuery {
|
|
5
|
+
cursor?: string | null;
|
|
6
|
+
limit?: number;
|
|
7
|
+
includeUnqualified?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class HookrClient {
|
|
10
|
+
readonly base: string;
|
|
11
|
+
readonly expected: {
|
|
12
|
+
releaseId: string;
|
|
13
|
+
chainId: string;
|
|
14
|
+
manifestHash: Hash;
|
|
15
|
+
};
|
|
16
|
+
readonly fetcher: typeof fetch;
|
|
17
|
+
constructor(base: string, expected: {
|
|
18
|
+
releaseId: string;
|
|
19
|
+
chainId: string;
|
|
20
|
+
manifestHash: Hash;
|
|
21
|
+
}, fetcher?: typeof fetch);
|
|
22
|
+
envelope<T>(path: string, query?: Record<string, string>): Promise<ApiEnvelope<T>>;
|
|
23
|
+
get<T>(path: string): Promise<T>;
|
|
24
|
+
coverage(): Promise<Coverage>;
|
|
25
|
+
page<T>(path: string, { cursor, limit, includeUnqualified }?: PageQuery, extra?: Record<string, string>): Promise<{
|
|
26
|
+
items: T[];
|
|
27
|
+
page: ApiPage;
|
|
28
|
+
}>;
|
|
29
|
+
/** Follows `page.next` to the end; refuses rather than returning a silently partial list. */
|
|
30
|
+
all<T>(path: string, query?: PageQuery, extra?: Record<string, string>): Promise<T[]>;
|
|
31
|
+
/** Qualified programs by default; `includeUnqualified` adds the rest with their reasons. */
|
|
32
|
+
programPage(query?: PageQuery): Promise<{
|
|
33
|
+
items: ProgramListing[];
|
|
34
|
+
page: ApiPage;
|
|
35
|
+
}>;
|
|
36
|
+
programs(query?: PageQuery): Promise<ProgramListing[]>;
|
|
37
|
+
program(id: string): Promise<ProgramListing>;
|
|
38
|
+
campaignPage(query?: PageQuery): Promise<{
|
|
39
|
+
items: Record<string, unknown>[];
|
|
40
|
+
page: ApiPage;
|
|
41
|
+
}>;
|
|
42
|
+
campaigns(query?: PageQuery): Promise<Record<string, unknown>[]>;
|
|
43
|
+
familyPage(query?: PageQuery & {
|
|
44
|
+
owner?: Address;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
items: FamilyView[];
|
|
47
|
+
page: ApiPage;
|
|
48
|
+
}>;
|
|
49
|
+
families(query?: PageQuery & {
|
|
50
|
+
owner?: Address;
|
|
51
|
+
}): Promise<FamilyView[]>;
|
|
52
|
+
account(user: Address): Promise<AccountView>;
|
|
53
|
+
proof(programId: string, day: string, user: Address): Promise<ProofView>;
|
|
54
|
+
commissionProof(id: string, day: string, user: Address): Promise<ProofView>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { assert, address, uint, hash, } from "./schema.js";
|
|
2
|
+
/** Pages a list client-side before `programs()`/`families()`/`campaigns()` refuse to guess the rest. */
|
|
3
|
+
export const MAX_CLIENT_PAGES = 40;
|
|
4
|
+
export class HookrClient {
|
|
5
|
+
base;
|
|
6
|
+
expected;
|
|
7
|
+
fetcher;
|
|
8
|
+
constructor(base, expected, fetcher = fetch) {
|
|
9
|
+
const u = new URL(base);
|
|
10
|
+
assert(u.protocol === "https:" || (u.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(u.hostname)), "HTTPS required");
|
|
11
|
+
assert(!u.username && !u.password && !u.search && !u.hash, "Invalid API origin");
|
|
12
|
+
uint(expected.chainId);
|
|
13
|
+
hash(expected.manifestHash);
|
|
14
|
+
this.base = u.href.replace(/\/$/, "");
|
|
15
|
+
this.expected = expected;
|
|
16
|
+
this.fetcher = fetcher;
|
|
17
|
+
}
|
|
18
|
+
async envelope(path, query = {}) {
|
|
19
|
+
assert(/^\/v1\/[a-z0-9/-]+$/i.test(path), "Invalid API path");
|
|
20
|
+
const q = new URLSearchParams();
|
|
21
|
+
for (const [k, v] of Object.entries(query)) {
|
|
22
|
+
assert((k === "cursor" && /^\d{1,15}-\d{1,9}$/.test(v)) ||
|
|
23
|
+
(k === "limit" && /^\d{1,3}$/.test(v)) ||
|
|
24
|
+
(k === "include" && v === "unqualified") ||
|
|
25
|
+
(k === "owner" && address(v) === v), "Invalid API query");
|
|
26
|
+
q.set(k, v);
|
|
27
|
+
}
|
|
28
|
+
const r = await this.fetcher(this.base + path + (q.size ? `?${q}` : ""), {
|
|
29
|
+
signal: AbortSignal.timeout(15000),
|
|
30
|
+
});
|
|
31
|
+
assert(r.ok, `Service unavailable (${r.status})`);
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
assert(b &&
|
|
34
|
+
b.schema === "hookr.api.v1" &&
|
|
35
|
+
b.releaseId === this.expected.releaseId &&
|
|
36
|
+
b.coverage?.chainId === this.expected.chainId &&
|
|
37
|
+
b.coverage?.manifestHash === this.expected.manifestHash, "Service deployment identity mismatch");
|
|
38
|
+
return b;
|
|
39
|
+
}
|
|
40
|
+
async get(path) {
|
|
41
|
+
return (await this.envelope(path)).data;
|
|
42
|
+
}
|
|
43
|
+
coverage() {
|
|
44
|
+
return this.get("/v1/coverage");
|
|
45
|
+
}
|
|
46
|
+
async page(path, { cursor, limit, includeUnqualified } = {}, extra = {}) {
|
|
47
|
+
const b = await this.envelope(path, {
|
|
48
|
+
...extra,
|
|
49
|
+
...(cursor ? { cursor } : {}),
|
|
50
|
+
...(limit !== undefined ? { limit: String(limit) } : {}),
|
|
51
|
+
...(includeUnqualified ? { include: "unqualified" } : {}),
|
|
52
|
+
});
|
|
53
|
+
assert(Array.isArray(b.data) && b.page, "Service list response lacks a page");
|
|
54
|
+
return { items: b.data, page: b.page };
|
|
55
|
+
}
|
|
56
|
+
/** Follows `page.next` to the end; refuses rather than returning a silently partial list. */
|
|
57
|
+
async all(path, query = {}, extra = {}) {
|
|
58
|
+
const out = [];
|
|
59
|
+
let cursor = query.cursor;
|
|
60
|
+
for (let i = 0; i < MAX_CLIENT_PAGES; i++) {
|
|
61
|
+
const r = await this.page(path, { ...query, cursor }, extra);
|
|
62
|
+
out.push(...r.items);
|
|
63
|
+
if (!r.page.next)
|
|
64
|
+
return out;
|
|
65
|
+
cursor = r.page.next;
|
|
66
|
+
}
|
|
67
|
+
throw new Error("List exceeds the client page bound; page it with a cursor");
|
|
68
|
+
}
|
|
69
|
+
/** Qualified programs by default; `includeUnqualified` adds the rest with their reasons. */
|
|
70
|
+
programPage(query = {}) {
|
|
71
|
+
return this.page("/v1/programs", query);
|
|
72
|
+
}
|
|
73
|
+
programs(query = {}) {
|
|
74
|
+
return this.all("/v1/programs", query);
|
|
75
|
+
}
|
|
76
|
+
program(id) {
|
|
77
|
+
uint(id);
|
|
78
|
+
return this.get(`/v1/programs/${id}`);
|
|
79
|
+
}
|
|
80
|
+
campaignPage(query = {}) {
|
|
81
|
+
return this.page("/v1/referral-campaigns", query);
|
|
82
|
+
}
|
|
83
|
+
campaigns(query = {}) {
|
|
84
|
+
return this.all("/v1/referral-campaigns", query);
|
|
85
|
+
}
|
|
86
|
+
familyPage(query = {}) {
|
|
87
|
+
const { owner, ...rest } = query;
|
|
88
|
+
return this.page("/v1/families", rest, owner ? { owner: address(owner) } : {});
|
|
89
|
+
}
|
|
90
|
+
families(query = {}) {
|
|
91
|
+
const { owner, ...rest } = query;
|
|
92
|
+
return this.all("/v1/families", rest, owner ? { owner: address(owner) } : {});
|
|
93
|
+
}
|
|
94
|
+
account(user) {
|
|
95
|
+
return this.get(`/v1/accounts/${address(user)}`);
|
|
96
|
+
}
|
|
97
|
+
proof(programId, day, user) {
|
|
98
|
+
uint(programId);
|
|
99
|
+
uint(day);
|
|
100
|
+
return this.get(`/v1/proofs/${programId}/${day}/${address(user)}`);
|
|
101
|
+
}
|
|
102
|
+
commissionProof(id, day, user) {
|
|
103
|
+
uint(id);
|
|
104
|
+
uint(day);
|
|
105
|
+
return this.get(`/v1/commission-proofs/${id}/${day}/${address(user)}`);
|
|
106
|
+
}
|
|
107
|
+
}
|