@black-market/sdk 0.6.4 → 0.7.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/README.md +28 -4
- package/dist/addresses.d.ts +1 -1
- package/dist/addresses.js +4 -4
- package/dist/launch.d.ts +61 -0
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +171 -6
- package/dist/launch.js.map +1 -1
- package/dist/lifecycle/abi.d.ts +1999 -253
- package/dist/lifecycle/abi.d.ts.map +1 -1
- package/dist/lifecycle/abi.js +43 -14
- package/dist/lifecycle/abi.js.map +1 -1
- package/dist/lifecycle/allocations.d.ts +11 -0
- package/dist/lifecycle/allocations.d.ts.map +1 -0
- package/dist/lifecycle/allocations.js +36 -0
- package/dist/lifecycle/allocations.js.map +1 -0
- package/dist/lifecycle/calibration.d.ts +19 -0
- package/dist/lifecycle/calibration.d.ts.map +1 -0
- package/dist/lifecycle/calibration.js +65 -0
- package/dist/lifecycle/calibration.js.map +1 -0
- package/dist/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.d.ts +11 -0
- package/dist/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.d.ts.map +1 -0
- package/dist/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.js +10 -0
- package/dist/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.js.map +1 -0
- package/dist/lifecycle/index.d.ts +4 -2
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -2
- package/dist/lifecycle/index.js.map +1 -1
- package/dist/lifecycle/markets.d.ts +170 -6
- package/dist/lifecycle/markets.d.ts.map +1 -1
- package/dist/lifecycle/markets.js +164 -37
- package/dist/lifecycle/markets.js.map +1 -1
- package/dist/lifecycle/planner.d.ts +7 -14
- package/dist/lifecycle/planner.d.ts.map +1 -1
- package/dist/lifecycle/planner.js +354 -400
- package/dist/lifecycle/planner.js.map +1 -1
- package/dist/lifecycle/presets.d.ts +62 -0
- package/dist/lifecycle/presets.d.ts.map +1 -0
- package/dist/lifecycle/presets.js +195 -0
- package/dist/lifecycle/presets.js.map +1 -0
- package/dist/lifecycle/progress.d.ts +5 -1
- package/dist/lifecycle/progress.d.ts.map +1 -1
- package/dist/lifecycle/progress.js +212 -53
- package/dist/lifecycle/progress.js.map +1 -1
- package/dist/lifecycle/rpc.d.ts +13 -2
- package/dist/lifecycle/rpc.d.ts.map +1 -1
- package/dist/lifecycle/rpc.js +92 -8
- package/dist/lifecycle/rpc.js.map +1 -1
- package/dist/lifecycle/simulation.d.ts +14 -4
- package/dist/lifecycle/simulation.d.ts.map +1 -1
- package/dist/lifecycle/simulation.js +381 -118
- package/dist/lifecycle/simulation.js.map +1 -1
- package/dist/lifecycle/transactions.d.ts +6 -0
- package/dist/lifecycle/transactions.d.ts.map +1 -0
- package/dist/lifecycle/transactions.js +40 -0
- package/dist/lifecycle/transactions.js.map +1 -0
- package/dist/lifecycle/types.d.ts +39 -2
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js.map +1 -1
- package/docs/launch-lifecycle.md +228 -54
- package/examples/README.md +108 -18
- package/examples/generate-lifecycle-commitment-fixture.mjs +3 -1
- package/examples/launch-example-support.mjs +1 -1
- package/examples/launch-example.mjs +73 -26
- package/examples/launch-lifecycle-smoke.mjs +13 -11
- package/examples/launch.mjs +12 -11
- package/examples/quickstart.mjs +1 -1
- package/package.json +1 -1
- package/src/addresses.ts +4 -4
- package/src/launch.ts +213 -6
- package/src/lifecycle/abi.ts +43 -14
- package/src/lifecycle/allocations.ts +35 -0
- package/src/lifecycle/calibration.ts +59 -0
- package/src/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.ts +11 -0
- package/src/lifecycle/index.ts +4 -2
- package/src/lifecycle/markets.ts +169 -35
- package/src/lifecycle/planner.ts +326 -330
- package/src/lifecycle/presets.ts +243 -0
- package/src/lifecycle/progress.ts +193 -50
- package/src/lifecycle/rpc.ts +97 -9
- package/src/lifecycle/simulation.ts +369 -111
- package/src/lifecycle/transactions.ts +34 -0
- package/src/lifecycle/types.ts +35 -3
package/src/lifecycle/rpc.ts
CHANGED
|
@@ -17,6 +17,8 @@ class InvocationLifecycleClient implements LifecycleRpcClient {
|
|
|
17
17
|
private closed = false;
|
|
18
18
|
private readonly concurrency: number;
|
|
19
19
|
private diagnosticSequence = 0;
|
|
20
|
+
private chainId?: Promise<bigint>;
|
|
21
|
+
private readonly limitObservations = new Map<LifecycleLimitSource | undefined, Map<string, Promise<LifecycleLimitObservation>>>();
|
|
20
22
|
constructor(readonly source: LifecycleRpcClient) {
|
|
21
23
|
this.concurrency = source.supportsReadBatching === true ? lifecycleReadConcurrency : 1;
|
|
22
24
|
}
|
|
@@ -28,7 +30,7 @@ class InvocationLifecycleClient implements LifecycleRpcClient {
|
|
|
28
30
|
return result;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
assertOpen(): void {
|
|
32
34
|
if (this.closed) throw new LifecyclePlanningError("READ_SCOPE_CLOSED", "Lifecycle read invocation has already settled");
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -59,6 +61,34 @@ class InvocationLifecycleClient implements LifecycleRpcClient {
|
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
observeChainId(): Promise<bigint> {
|
|
65
|
+
this.assertOpen();
|
|
66
|
+
return this.chainId ??= this.run("eth_chainId", []).then((value) => rpcQuantity(value, "chain ID"));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
observeLimits(context: LifecycleLimitContext, source?: LifecycleLimitSource): Promise<LifecycleLimitObservation> {
|
|
70
|
+
this.assertOpen();
|
|
71
|
+
// A caller callback is live policy, not an immutable pinned observation.
|
|
72
|
+
// Its current result must validate provenance; native fixed-state reads still reuse their wire cache.
|
|
73
|
+
if (typeof source === "function") return resolveLifecycleLimitsFresh(context, source).then((result) => {
|
|
74
|
+
this.assertOpen();
|
|
75
|
+
result.assertCurrent();
|
|
76
|
+
return result;
|
|
77
|
+
});
|
|
78
|
+
const key = JSON.stringify([context.chainId.toString(), context.account.toLowerCase(), context.orchestrator.toLowerCase(), context.block.number.toString(), context.block.hash.toLowerCase(), context.block.timestamp.toString(), context.block.gasLimit.toString(), context.block.baseFeePerGas?.toString()]);
|
|
79
|
+
let byContext = this.limitObservations.get(source);
|
|
80
|
+
if (byContext === undefined) { byContext = new Map(); this.limitObservations.set(source, byContext); }
|
|
81
|
+
const contexts = byContext;
|
|
82
|
+
let observation = byContext.get(key);
|
|
83
|
+
if (observation === undefined) {
|
|
84
|
+
observation = resolveLifecycleLimitsFresh(context, source);
|
|
85
|
+
byContext.set(key, observation);
|
|
86
|
+
const owned = observation;
|
|
87
|
+
void owned.catch(() => { if (contexts.get(key) === owned) contexts.delete(key); });
|
|
88
|
+
}
|
|
89
|
+
return observation.then((result) => { this.assertOpen(); result.assertCurrent(); return result; });
|
|
90
|
+
}
|
|
91
|
+
|
|
62
92
|
read(method: string, params: readonly unknown[], block: LifecycleBlock): Promise<unknown> {
|
|
63
93
|
this.assertOpen();
|
|
64
94
|
// Source identity is this scope's original client. The complete wire call includes
|
|
@@ -79,6 +109,8 @@ class InvocationLifecycleClient implements LifecycleRpcClient {
|
|
|
79
109
|
close(): void {
|
|
80
110
|
this.closed = true;
|
|
81
111
|
this.reads.clear();
|
|
112
|
+
this.chainId = undefined;
|
|
113
|
+
this.limitObservations.clear();
|
|
82
114
|
}
|
|
83
115
|
}
|
|
84
116
|
|
|
@@ -94,6 +126,13 @@ export function lifecycleSourceClient(client: LifecycleRpcClient): LifecycleRpcC
|
|
|
94
126
|
return client instanceof InvocationLifecycleClient ? client.source : client;
|
|
95
127
|
}
|
|
96
128
|
|
|
129
|
+
/** Internal continuations cannot retain proof after their read owner settles. */
|
|
130
|
+
export function assertLifecycleReadClientOpen(client: LifecycleRpcClient): boolean {
|
|
131
|
+
if (!(client instanceof InvocationLifecycleClient)) return false;
|
|
132
|
+
client.assertOpen();
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
97
136
|
function emitLifecycleDiagnostic(listener: LifecycleDiagnosticListener, event: LifecycleDiagnosticEvent): void {
|
|
98
137
|
try {
|
|
99
138
|
const pending = listener(event);
|
|
@@ -129,6 +168,13 @@ export async function lifecyclePinnedRpc(client: LifecycleRpcClient, method: "et
|
|
|
129
168
|
export async function lifecycleRpc(client: LifecycleRpcClient, method: string, params: readonly unknown[] = []): Promise<unknown> {
|
|
130
169
|
return client instanceof InvocationLifecycleClient ? client.run(method, params) : client.request({ method, params });
|
|
131
170
|
}
|
|
171
|
+
/** One invocation's observed identity. End-of-operation and submission checks
|
|
172
|
+
* deliberately use uncached eth_chainId so a switched provider cannot retain proof. */
|
|
173
|
+
export function readLifecycleChainId(client: LifecycleRpcClient): Promise<bigint> {
|
|
174
|
+
return client instanceof InvocationLifecycleClient
|
|
175
|
+
? client.observeChainId()
|
|
176
|
+
: lifecycleRpc(client, "eth_chainId").then((value) => rpcQuantity(value, "chain ID"));
|
|
177
|
+
}
|
|
132
178
|
export function rpcObject(value: unknown, label: string): Record<string, unknown> {
|
|
133
179
|
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new LifecyclePlanningError("INVALID_RPC_RESPONSE", `${label} must be an object`);
|
|
134
180
|
// The object guard establishes the RPC record boundary; fields remain untrusted unknowns.
|
|
@@ -144,6 +190,19 @@ export function rpcQuantity(value: unknown, label: string): bigint {
|
|
|
144
190
|
if (!/^0x[0-9a-fA-F]+$/.test(hex)) throw new LifecyclePlanningError("INVALID_RPC_RESPONSE", `${label} must be a quantity`);
|
|
145
191
|
return BigInt(hex);
|
|
146
192
|
}
|
|
193
|
+
|
|
194
|
+
/** Public refusal details contain only fixed text and numeric RPC/HTTP codes, never provider messages. */
|
|
195
|
+
export function lifecycleFailureReason(failure: unknown): string {
|
|
196
|
+
for (let depth = 0; depth < 8 && failure !== null && typeof failure === "object"; depth += 1) {
|
|
197
|
+
const error = failure as { code?: unknown; status?: unknown; statusCode?: unknown; cause?: unknown };
|
|
198
|
+
const status = error.status ?? error.statusCode;
|
|
199
|
+
if (typeof status === "number" && Number.isSafeInteger(status) && status >= 400 && status <= 599) return `HTTP read failed (status ${status})`;
|
|
200
|
+
if (typeof error.code === "number" && Number.isSafeInteger(error.code)) return `RPC request failed (code ${error.code})`;
|
|
201
|
+
if (failure instanceof LifecyclePlanningError) return "Lifecycle planning guard rejected the request";
|
|
202
|
+
failure = error.cause;
|
|
203
|
+
}
|
|
204
|
+
return "Read or execution proof failed";
|
|
205
|
+
}
|
|
147
206
|
export async function readLifecycleBlock(client: LifecycleRpcClient, tag: Hex | "latest" = "latest"): Promise<LifecycleBlock> {
|
|
148
207
|
const value = rpcObject(await lifecycleRpc(client, "eth_getBlockByNumber", [tag, false]), "block");
|
|
149
208
|
const hash = rpcHex(value.hash, "block hash");
|
|
@@ -205,12 +264,37 @@ async function readNitroScalar(context: LifecycleLimitContext, to: Address, abi:
|
|
|
205
264
|
return decodeFunctionResult({ abi, functionName, data: result }) as bigint;
|
|
206
265
|
}
|
|
207
266
|
|
|
208
|
-
export
|
|
267
|
+
export type LifecycleLimitObservation = {
|
|
268
|
+
limits: ResolvedLifecycleLimits;
|
|
269
|
+
configuration: LifecycleLimits;
|
|
270
|
+
assertCurrent: () => void;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/** Reuse only SDK-owned, pinned observations inside the still-open invocation.
|
|
274
|
+
* Caller-supplied policies remain checked for mutation on every consumption. */
|
|
275
|
+
export function resolveLifecycleLimits(context: LifecycleLimitContext, source?: LifecycleLimitSource): Promise<LifecycleLimitObservation> {
|
|
276
|
+
return context.client instanceof InvocationLifecycleClient
|
|
277
|
+
? context.client.observeLimits(context, source)
|
|
278
|
+
: resolveLifecycleLimitsFresh(context, source);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function resolveLifecycleLimitsFresh(context: LifecycleLimitContext, source?: LifecycleLimitSource): Promise<LifecycleLimitObservation> {
|
|
209
282
|
let configuration: LifecycleLimits;
|
|
210
283
|
const policyContext = typeof source === "function" && context.client instanceof InvocationLifecycleClient ? { ...context, client: context.client.source } : context;
|
|
211
284
|
try { configuration = source === undefined ? {} : typeof source === "function" ? await source(policyContext) : source; }
|
|
212
|
-
catch (failure) {
|
|
285
|
+
catch (failure) {
|
|
286
|
+
if (failure instanceof Error && failure.name === "AbortError") throw failure;
|
|
287
|
+
throw new LifecyclePlanningError("LIMIT_SOURCE_FAILED", `Supplied execution policy could not be resolved: ${lifecycleFailureReason(failure)}`);
|
|
288
|
+
}
|
|
213
289
|
if (configuration === null || typeof configuration !== "object" || Array.isArray(configuration)) throw new LifecyclePlanningError("INVALID_LIMITS", "Supplied execution policy must be an object");
|
|
290
|
+
const suppliedConfiguration = configuration;
|
|
291
|
+
configuration = Object.freeze({ ...configuration });
|
|
292
|
+
const configurationKeys = Object.keys(configuration) as (keyof LifecycleLimits)[];
|
|
293
|
+
const assertCurrent = () => {
|
|
294
|
+
if (Object.keys(suppliedConfiguration).length !== configurationKeys.length || configurationKeys.some((key) => suppliedConfiguration[key] !== configuration[key])) {
|
|
295
|
+
throw new LifecyclePlanningError("LIMIT_CONTEXT_MISMATCH", "Supplied execution policy changed after its pinned observation");
|
|
296
|
+
}
|
|
297
|
+
};
|
|
214
298
|
const headroomBps = configuration.headroomBps === undefined ? 1500 : configuration.headroomBps;
|
|
215
299
|
if (!Number.isSafeInteger(headroomBps) || headroomBps < 0 || headroomBps > 10000) throw new LifecyclePlanningError("INVALID_LIMITS", "Headroom must be between zero and 10000 basis points");
|
|
216
300
|
for (const limit of [context.block.gasLimit, configuration.chainGasLimit, configuration.rpcGasLimit, configuration.accountGasLimit, configuration.maxSimulationGas]) {
|
|
@@ -240,7 +324,10 @@ export async function resolveLifecycleLimits(context: LifecycleLimitContext, sou
|
|
|
240
324
|
maxTxComputeGas = txComputeGas;
|
|
241
325
|
maxBlockComputeGas = blockComputeGas;
|
|
242
326
|
if ([maxTxComputeGas, maxBlockComputeGas].some((limit) => typeof limit !== "bigint" || limit <= 0n || limit > maxUint64)) throw new Error("Nitro compute getters must be positive uint64 values");
|
|
243
|
-
} catch (failure) {
|
|
327
|
+
} catch (failure) {
|
|
328
|
+
if (failure instanceof Error && failure.name === "AbortError") throw failure;
|
|
329
|
+
throw new LifecyclePlanningError("NITRO_LIMITS_UNAVAILABLE", `Pinned Nitro compute limits could not be established: ${lifecycleFailureReason(failure)}; header gasLimit is not a fallback`);
|
|
330
|
+
}
|
|
244
331
|
if (configuration.estimateDataFee !== undefined) throw new LifecyclePlanningError("INVALID_LIMITS", "Nitro poster fees are already included in gas; an external data-fee estimator would double-charge them");
|
|
245
332
|
}
|
|
246
333
|
const envelopeCaps = [configuration.chainGasLimit, configuration.rpcGasLimit, configuration.accountGasLimit].filter((limit): limit is bigint => limit !== undefined);
|
|
@@ -256,15 +343,16 @@ export async function resolveLifecycleLimits(context: LifecycleLimitContext, sou
|
|
|
256
343
|
...(configuration.maxCalldataBytes === undefined ? ["Applicable chain/account/RPC calldata byte cap is unknown"] : []),
|
|
257
344
|
...(configuration.maxSimulationGas === undefined ? ["RPC aggregate simulation gas cap is unknown"] : []),
|
|
258
345
|
];
|
|
346
|
+
assertCurrent();
|
|
259
347
|
return {
|
|
260
|
-
configuration,
|
|
261
|
-
limits: {
|
|
348
|
+
configuration, assertCurrent,
|
|
349
|
+
limits: Object.freeze({
|
|
262
350
|
protocol, executionGasCeiling, transactionGasCeiling, arbOSVersion, maxTxComputeGas, maxBlockComputeGas, headroomBps,
|
|
263
351
|
maxCalldataBytes: configuration.maxCalldataBytes, maxSimulationGas: configuration.maxSimulationGas,
|
|
264
352
|
blockGasLimit: context.block.gasLimit, chainGasLimit: configuration.chainGasLimit,
|
|
265
353
|
rpcGasLimit: configuration.rpcGasLimit, accountGasLimit: configuration.accountGasLimit,
|
|
266
|
-
unknownExecutionConstraints,
|
|
267
|
-
unknownConstraints: [...unknownExecutionConstraints, ...(protocol === "evm" && configuration.estimateDataFee === undefined ? ["External chain-specific data fee is unavailable"] : [])],
|
|
268
|
-
},
|
|
354
|
+
unknownExecutionConstraints: Object.freeze(unknownExecutionConstraints),
|
|
355
|
+
unknownConstraints: Object.freeze([...unknownExecutionConstraints, ...(protocol === "evm" && configuration.estimateDataFee === undefined ? ["External chain-specific data fee is unavailable"] : [])]),
|
|
356
|
+
}),
|
|
269
357
|
};
|
|
270
358
|
}
|