@artblocks/abx-sdk 0.1.0-alpha.4 → 0.1.0-alpha.41
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 +72 -0
- package/README.md +130 -0
- package/dist/abi/generated.d.ts +8502 -129
- package/dist/abi/generated.d.ts.map +1 -1
- package/dist/abi/generated.js +40 -22
- package/dist/abi/generated.js.map +1 -1
- package/dist/abi/index.d.ts +2645 -168
- package/dist/abi/index.d.ts.map +1 -1
- package/dist/abi/index.js +34 -7
- package/dist/abi/index.js.map +1 -1
- package/dist/anchors.d.ts +325 -0
- package/dist/anchors.d.ts.map +1 -0
- package/dist/anchors.js +740 -0
- package/dist/anchors.js.map +1 -0
- package/dist/chain-support.d.ts +28 -0
- package/dist/chain-support.d.ts.map +1 -0
- package/dist/chain-support.js +65 -0
- package/dist/chain-support.js.map +1 -0
- package/dist/chain-support.json +60 -0
- package/dist/chains.d.ts +24 -10
- package/dist/chains.d.ts.map +1 -1
- package/dist/chains.js +51 -17
- package/dist/chains.js.map +1 -1
- package/dist/chunks.d.ts +162 -31
- package/dist/chunks.d.ts.map +1 -1
- package/dist/chunks.js +179 -14
- package/dist/chunks.js.map +1 -1
- package/dist/clients.d.ts +23 -11
- package/dist/clients.d.ts.map +1 -1
- package/dist/clients.js +17 -18
- package/dist/clients.js.map +1 -1
- package/dist/contract-generations.d.ts +58 -0
- package/dist/contract-generations.d.ts.map +1 -0
- package/dist/contract-generations.js +109 -0
- package/dist/contract-generations.js.map +1 -0
- package/dist/contract-generations.json +48 -0
- package/dist/create2.d.ts +84 -1
- package/dist/create2.d.ts.map +1 -1
- package/dist/create2.js +158 -2
- package/dist/create2.js.map +1 -1
- package/dist/creator-token.d.ts +132 -0
- package/dist/creator-token.d.ts.map +1 -0
- package/dist/creator-token.js +183 -0
- package/dist/creator-token.js.map +1 -0
- package/dist/deploy.d.ts +234 -14
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +289 -111
- package/dist/deploy.js.map +1 -1
- package/dist/deployments.d.ts +38 -5
- package/dist/deployments.d.ts.map +1 -1
- package/dist/deployments.js +157 -30
- package/dist/deployments.js.map +1 -1
- package/dist/deps.d.ts +123 -6
- package/dist/deps.d.ts.map +1 -1
- package/dist/deps.js +249 -7
- package/dist/deps.js.map +1 -1
- package/dist/env.d.ts +5 -4
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +20 -5
- package/dist/env.js.map +1 -1
- package/dist/errors.d.ts +109 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +148 -0
- package/dist/errors.js.map +1 -0
- package/dist/execute.d.ts +118 -0
- package/dist/execute.d.ts.map +1 -0
- package/dist/execute.js +159 -0
- package/dist/execute.js.map +1 -0
- package/dist/gateways.d.ts +79 -0
- package/dist/gateways.d.ts.map +1 -0
- package/dist/gateways.js +156 -0
- package/dist/gateways.js.map +1 -0
- package/dist/generator-document.d.ts +57 -0
- package/dist/generator-document.d.ts.map +1 -0
- package/dist/generator-document.js +123 -0
- package/dist/generator-document.js.map +1 -0
- package/dist/generator.d.ts +44 -2
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +29 -0
- package/dist/generator.js.map +1 -1
- package/dist/index.d.ts +19 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +48 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +295 -0
- package/dist/inspect.js.map +1 -0
- package/dist/migrate.d.ts +41 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +142 -0
- package/dist/migrate.js.map +1 -0
- package/dist/mime.d.ts +11 -0
- package/dist/mime.d.ts.map +1 -0
- package/dist/mime.js +40 -0
- package/dist/mime.js.map +1 -0
- package/dist/node.d.ts +69 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +92 -0
- package/dist/node.js.map +1 -0
- package/dist/onchain-uri.d.ts +110 -0
- package/dist/onchain-uri.d.ts.map +1 -0
- package/dist/onchain-uri.js +276 -0
- package/dist/onchain-uri.js.map +1 -0
- package/dist/ops.d.ts +507 -14
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +850 -34
- package/dist/ops.js.map +1 -1
- package/dist/policy.d.ts +57 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +35 -0
- package/dist/policy.js.map +1 -0
- package/dist/probe.d.ts +58 -0
- package/dist/probe.d.ts.map +1 -1
- package/dist/probe.js +190 -9
- package/dist/probe.js.map +1 -1
- package/dist/reconstruct.d.ts +141 -3
- package/dist/reconstruct.d.ts.map +1 -1
- package/dist/reconstruct.js +479 -100
- package/dist/reconstruct.js.map +1 -1
- package/dist/resume.d.ts +147 -0
- package/dist/resume.d.ts.map +1 -0
- package/dist/resume.js +146 -0
- package/dist/resume.js.map +1 -0
- package/dist/script-chunks.d.ts +105 -0
- package/dist/script-chunks.d.ts.map +1 -0
- package/dist/script-chunks.js +158 -0
- package/dist/script-chunks.js.map +1 -0
- package/dist/service.d.ts +155 -9
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +103 -11
- package/dist/service.js.map +1 -1
- package/dist/spine.d.ts +47 -4
- package/dist/spine.d.ts.map +1 -1
- package/dist/spine.js +0 -0
- package/dist/spine.js.map +1 -1
- package/dist/staging.d.ts +139 -0
- package/dist/staging.d.ts.map +1 -0
- package/dist/staging.js +139 -0
- package/dist/staging.js.map +1 -0
- package/dist/token.d.ts +11 -1
- package/dist/token.d.ts.map +1 -1
- package/dist/token.js +32 -1
- package/dist/token.js.map +1 -1
- package/dist/tokendata.d.ts +63 -3
- package/dist/tokendata.d.ts.map +1 -1
- package/dist/tokendata.js +81 -19
- package/dist/tokendata.js.map +1 -1
- package/dist/tokens.d.ts +119 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +317 -0
- package/dist/tokens.js.map +1 -0
- package/dist/types.d.ts +107 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/util.d.ts +74 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +106 -0
- package/dist/util.js.map +1 -0
- package/package.json +15 -6
package/dist/errors.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK's error taxonomy — deliberately minimal. A caller that wants to react to a SPECIFIC
|
|
3
|
+
* failure (retry a stale estimate, surface a revert's tx hash) should catch by TYPE, not by
|
|
4
|
+
* parsing `.message`; every SDK-thrown error a caller is expected to distinguish extends this,
|
|
5
|
+
* following the same `name` + typed-fields convention `AbxServiceError` already uses (service.ts).
|
|
6
|
+
* This module stays small on purpose — it grows only when a real caller needs a new type to catch,
|
|
7
|
+
* not as a speculative taxonomy of everything that could go wrong.
|
|
8
|
+
*/
|
|
9
|
+
export class AbxSdkError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'AbxSdkError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A sent transaction MINED with `status: 'reverted'`. Thrown by {@link makeHotSender} (and anything
|
|
17
|
+
* else driving `SendTx`) instead of letting a revert masquerade as a "confirmed" send — carries the
|
|
18
|
+
* tx hash so a caller can look it up on an explorer without re-parsing a message string.
|
|
19
|
+
*/
|
|
20
|
+
export class TxRevertedError extends AbxSdkError {
|
|
21
|
+
op;
|
|
22
|
+
txHash;
|
|
23
|
+
constructor(op, txHash) {
|
|
24
|
+
super(`${op} reverted (${txHash})`);
|
|
25
|
+
this.name = 'TxRevertedError';
|
|
26
|
+
this.op = op;
|
|
27
|
+
this.txHash = txHash;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `pinGas`'s refusal: every estimate attempt came back below the transaction's PROVABLE gas floor
|
|
32
|
+
* (`PreparedTx.gasFloor` — EVM code deposit at exactly 200 gas/byte, and nothing else is physics).
|
|
33
|
+
* That is proof the node answering the estimate hasn't seen the target's deploy block yet, not that
|
|
34
|
+
* the floor is wrong — see `pinGas` in `execute.ts` for the full reasoning. Carries both numbers so a
|
|
35
|
+
* caller can decide to retry against a different RPC rather than re-parse the message.
|
|
36
|
+
*/
|
|
37
|
+
export class GasEstimateBelowFloorError extends AbxSdkError {
|
|
38
|
+
estimate;
|
|
39
|
+
floor;
|
|
40
|
+
constructor(estimate, floor) {
|
|
41
|
+
super(`Refusing to send: the RPC estimated ${estimate} gas for a transaction that provably needs at least ${floor} ` +
|
|
42
|
+
`(storing ${floor / 200n} bytes on-chain costs 200 gas/byte in code deposit alone). An estimate below the floor means the node ` +
|
|
43
|
+
`is answering from stale state — usually it has not seen the contract's deploy block yet, so the call looks like it is going to ` +
|
|
44
|
+
`an empty account. Sending this would revert DeploymentFailed() and burn the gas. Retry in a few seconds, or use a different RPC.`);
|
|
45
|
+
this.name = 'GasEstimateBelowFloorError';
|
|
46
|
+
this.estimate = estimate;
|
|
47
|
+
this.floor = floor;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* No signing key resolved. SINGLE env name: `ABX_DEPLOYER_PK` — thrown by {@link makeWalletClient}
|
|
52
|
+
* and {@link envSigningKey} (clients.ts). Names the two retired names explicitly (`SEPOLIA_FUNDED_PK`
|
|
53
|
+
* / `SEPOLIA_WALLET_PK`) rather than just omitting them, because a caller upgrading from an older
|
|
54
|
+
* alpha has one of those set in their `.env` and would otherwise see a plain "no key found" with no
|
|
55
|
+
* clue that the key IS there, just under a name the SDK no longer reads.
|
|
56
|
+
*/
|
|
57
|
+
export class MissingSigningKeyError extends AbxSdkError {
|
|
58
|
+
constructor() {
|
|
59
|
+
super('No signing key found. Set ABX_DEPLOYER_PK in your .env. ' +
|
|
60
|
+
'(SEPOLIA_FUNDED_PK and SEPOLIA_WALLET_PK are no longer read — rename the key to ABX_DEPLOYER_PK.)');
|
|
61
|
+
this.name = 'MissingSigningKeyError';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A registry-resolved dependency's on-chain bytes are gzip'd, and no `inflate` was supplied to
|
|
66
|
+
* decompress them (`resolveRegistryDep`/`dependencyScriptTags`, deps.ts). SDK core carries no
|
|
67
|
+
* `node:zlib` — it has to stay reachable from a browser bundle (see `test/browser-bundle.test.ts`)
|
|
68
|
+
* — so decompression is an injected function rather than a direct call. Thrown instead of letting
|
|
69
|
+
* the caller either crash on `opts.inflate(...)` being undefined or silently serve a broken
|
|
70
|
+
* document; names the exact fix instead of a generic "cannot read properties of undefined".
|
|
71
|
+
*/
|
|
72
|
+
export class InflateRequiredError extends AbxSdkError {
|
|
73
|
+
constructor() {
|
|
74
|
+
super("A registry-resolved dependency's on-chain bytes are gzip-compressed, but no `inflate` was " +
|
|
75
|
+
'provided to decompress them. On a Node host, pass `nodeInflate` from `@artblocks/abx-sdk/node` ' +
|
|
76
|
+
"(e.g. `dependencyScriptTags(client, state, {inflate: nodeInflate})`). In a browser, pass a " +
|
|
77
|
+
'DecompressionStream-based implementation — SDK core carries no `node:zlib` so it never assumes one.');
|
|
78
|
+
this.name = 'InflateRequiredError';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A caller asked reconstruction to stop at the `"safe"` or `"finalized"` block tag
|
|
83
|
+
* ({@link resolveBlockTag}, reconstruct.ts) and the RPC either rejected `eth_getBlockByNumber` for
|
|
84
|
+
* that tag or answered with no block number. Thrown rather than silently falling back to `latest`:
|
|
85
|
+
* the whole point of naming `safe`/`finalized` is the reorg-safety guarantee that tag carries, and a
|
|
86
|
+
* quiet substitution would hand back a normal-looking reconstruction that scanned further than the
|
|
87
|
+
* caller asked for — indistinguishable from success until a reorg proves it wrong. Some
|
|
88
|
+
* chains/providers genuinely don't serve these tags: pre-merge chains have no post-merge finality,
|
|
89
|
+
* and some non-archive or indexer-backed RPCs answer neither tag at all. Expected and actionable,
|
|
90
|
+
* not a bug — names the fallback so a caller isn't stuck.
|
|
91
|
+
*/
|
|
92
|
+
export class BlockTagUnavailableError extends AbxSdkError {
|
|
93
|
+
tag;
|
|
94
|
+
constructor(tag, cause) {
|
|
95
|
+
super(`This RPC doesn't support the "${tag}" block tag${cause instanceof Error ? ` (${cause.message})` : ''}. ` +
|
|
96
|
+
`Reconstruction can't resolve a "${tag}" boundary here — use \`latest\` (the default) or an explicit ` +
|
|
97
|
+
`block number instead, or point at an RPC that serves "${tag}" (most L1 mainnets and their public ` +
|
|
98
|
+
`testnets do; some rollups and indexer-backed endpoints don't — \`abx doctor\` rates configured endpoints).`);
|
|
99
|
+
this.name = 'BlockTagUnavailableError';
|
|
100
|
+
this.tag = tag;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A trust anchor {@link AnchorKind} that `ensureFactory`/`ensureSeriesFactory`/
|
|
105
|
+
* `ensureSeriesCodeFactory` (anchors.ts) could not resolve to a usable address, with
|
|
106
|
+
* `allowBootstrap` false — the caller must either point at a working one (override / env /
|
|
107
|
+
* manifest) or opt in to deploying a private one. Carries structured `detail` + `address` instead
|
|
108
|
+
* of a formatted sentence: the SDK doesn't know a UX's flag names or chain-key strings, so it never
|
|
109
|
+
* composes the actual guidance text itself — that's the CLI's `refuseMissingFactory`, which catches
|
|
110
|
+
* this and names the env var / `--bootstrap-factory` / the chain.
|
|
111
|
+
*/
|
|
112
|
+
export class AnchorUnavailableError extends AbxSdkError {
|
|
113
|
+
anchor;
|
|
114
|
+
detail;
|
|
115
|
+
address;
|
|
116
|
+
constructor(anchor, detail, address) {
|
|
117
|
+
super(`${anchor} trust anchor unavailable (${detail}${address ? `: ${address}` : ''})`);
|
|
118
|
+
this.name = 'AnchorUnavailableError';
|
|
119
|
+
this.anchor = anchor;
|
|
120
|
+
this.detail = detail;
|
|
121
|
+
this.address = address;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A candidate seed source that {@link probeSeedSource} found unusable — thrown by
|
|
126
|
+
* `assertSeedSourceUsable` (ops.ts). Carries the whole probe (verdict + what was observed) rather
|
|
127
|
+
* than a formatted sentence, same reason as {@link AnchorUnavailableError}: the SDK doesn't know
|
|
128
|
+
* whether the caller got this address from a `--seed-source` flag, an env var, or a form field, so
|
|
129
|
+
* it never composes the guidance itself.
|
|
130
|
+
*
|
|
131
|
+
* Worth catching by type: this is the ONE misconfiguration whose consequence is "every mint of the
|
|
132
|
+
* collection reverts", and it is invisible on every read surface (`seedSource()` returns exactly
|
|
133
|
+
* what was set; the `SeedSourceSet` event fires) — so a UX should treat it as a hard stop, not a
|
|
134
|
+
* warning it can proceed past.
|
|
135
|
+
*/
|
|
136
|
+
export class SeedSourceUnusableError extends AbxSdkError {
|
|
137
|
+
verdict;
|
|
138
|
+
address;
|
|
139
|
+
probe;
|
|
140
|
+
constructor(probe) {
|
|
141
|
+
super(`seed source ${probe.address} is not usable (${probe.verdict}${probe.error ? `: ${probe.error}` : ''})`);
|
|
142
|
+
this.name = 'SeedSourceUnusableError';
|
|
143
|
+
this.verdict = probe.verdict;
|
|
144
|
+
this.address = probe.address;
|
|
145
|
+
this.probe = probe;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gas limits for a sequenced transaction — decided HERE, once, rather than re-read at signing time.
|
|
3
|
+
*
|
|
4
|
+
* The failure this exists to prevent, in full, because it is not obvious and it cost a reporter every
|
|
5
|
+
* `deploy-code` attempt of a session: a project deploys in two transactions, and the second targets
|
|
6
|
+
* the contract the first just created. If the node answering `eth_estimateGas` has not yet seen the
|
|
7
|
+
* deploy block, the target looks like an account with no code — and an estimate for a call to a
|
|
8
|
+
* codeless account is just the calldata cost, around 200k. That number is then sent as the gas limit.
|
|
9
|
+
* Once the contract does exist the same call needs ~941k, because storing a program on-chain is
|
|
10
|
+
* dominated by CREATE code deposit at ~200 gas per byte. The CREATE receives 63/64 of a budget that
|
|
11
|
+
* cannot cover the deposit, returns 0, and Solady's `SSTORE2.write` reverts `DeploymentFailed()` — a
|
|
12
|
+
* revert that reads like a contract bug and is really an out-of-gas.
|
|
13
|
+
*
|
|
14
|
+
* (Measured, from the real transactions: `gasLimit 201,616 / gasUsed 198,870` and
|
|
15
|
+
* `gasLimit 169,301 / gasUsed 166,810` — 98.6% and 98.5% of their limits. Replaying those payloads
|
|
16
|
+
* against a codeless address reproduces both limits to the gas; against the real contract the same
|
|
17
|
+
* calls estimate at 941,331.)
|
|
18
|
+
*
|
|
19
|
+
* The deploy loop already pinned the *nonce* against exactly this read-after-write lag on a
|
|
20
|
+
* distributed RPC. The rule generalizes, and it is the reason this module exists: **anything read at
|
|
21
|
+
* send time on a distributed RPC needs pinning, not just the nonce.**
|
|
22
|
+
*/
|
|
23
|
+
import type { Account, Address, Hex, PublicClient, TransactionReceipt, WalletClient } from 'viem';
|
|
24
|
+
import type { PreparedTx } from './ops.js';
|
|
25
|
+
/**
|
|
26
|
+
* Wait until `address` has code from THIS client's point of view. A deploy receipt proves the
|
|
27
|
+
* contract exists on chain; it does not prove the node answering the next request has caught up.
|
|
28
|
+
* Bounded — on timeout we proceed and let the gas floor carry it, since a slow RPC is not a reason
|
|
29
|
+
* to refuse to continue a deploy that already spent money.
|
|
30
|
+
*/
|
|
31
|
+
export declare function waitForCodeAt(client: PublicClient, address: Address, timeoutMs?: number): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Decide a transaction's gas limit once, here.
|
|
34
|
+
*
|
|
35
|
+
* The important design point, because the obvious approach is wrong: when an estimate looks too low
|
|
36
|
+
* we do **not** substitute a computed number. A caller can only compute the *provable* part of a
|
|
37
|
+
* payload's cost (see `PreparedTx.gasFloor` — code deposit is 200 gas/byte and nothing else is
|
|
38
|
+
* physics); the same setup multicall also carries schema writes, dependency legs, URI legs and
|
|
39
|
+
* mints, whose cost we cannot derive without simulating them. A "probably enough" constant is a
|
|
40
|
+
* number tuned to whatever case was in front of its author: it papers over the symptom, then
|
|
41
|
+
* under-funds the next payload that carries a few more legs, producing the identical
|
|
42
|
+
* `DeploymentFailed()` with a fresh mystery attached.
|
|
43
|
+
*
|
|
44
|
+
* So the floor is used as a **detector**. An estimate below a provable minimum is not "low", it is
|
|
45
|
+
* *impossible* — proof that the node answering us is looking at the wrong state (typically it has
|
|
46
|
+
* not seen the deploy block yet, so the target reads as an account with no code and the estimate
|
|
47
|
+
* comes back as the calldata cost alone). The right response to a broken measurement is to take it
|
|
48
|
+
* again, and if it stays broken, to refuse — sending a transaction we can prove is under-funded
|
|
49
|
+
* would burn the gas AND orphan the contract.
|
|
50
|
+
*
|
|
51
|
+
* When the estimate IS plausible it is trusted, plus headroom for state drift between estimate and
|
|
52
|
+
* inclusion.
|
|
53
|
+
*/
|
|
54
|
+
export declare function pinGas(client: PublicClient, tx: {
|
|
55
|
+
from: Address;
|
|
56
|
+
to: Address | null;
|
|
57
|
+
data: Hex;
|
|
58
|
+
value?: Hex;
|
|
59
|
+
gasFloor?: Hex;
|
|
60
|
+
}, opts?: {
|
|
61
|
+
attempts?: number;
|
|
62
|
+
delayMs?: number;
|
|
63
|
+
}): Promise<bigint>;
|
|
64
|
+
/** Sign + broadcast one {@link PreparedTx} and return its mined receipt (for event parsing). The
|
|
65
|
+
* SDK's one send-injection point: a hot key ({@link makeHotSender}), a browser wallet (a
|
|
66
|
+
* `WalletSession`), or anything else a caller wires up — the SDK never picks or performs the
|
|
67
|
+
* signing itself. */
|
|
68
|
+
export type SendTx = (tx: PreparedTx) => Promise<TransactionReceipt>;
|
|
69
|
+
/** Progress from a {@link SendTx} built by {@link makeHotSender}, so a caller can narrate without
|
|
70
|
+
* the SDK printing anything itself (the SDK never prints — see clients.ts / chunks.ts for the same
|
|
71
|
+
* `onEvent` pattern). */
|
|
72
|
+
export type SendEvent = {
|
|
73
|
+
kind: 'sending';
|
|
74
|
+
tx: PreparedTx;
|
|
75
|
+
} | {
|
|
76
|
+
kind: 'mined';
|
|
77
|
+
receipt: TransactionReceipt;
|
|
78
|
+
tx: PreparedTx;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Build a {@link SendTx} that signs with an env-style hot key and broadcasts — the generalization of
|
|
82
|
+
* what used to be hand-rolled per call site (a deploy-then-setup loop in the CLI, the hot lane in
|
|
83
|
+
* `signer.ts`, the chunk-staging sender in `ownerops.ts`): one function that gets the read-after-
|
|
84
|
+
* write-lag handling right ONCE.
|
|
85
|
+
*
|
|
86
|
+
* Two lags, pinned once at construction / tracked locally per send, rather than re-read at send time:
|
|
87
|
+
* - the NONCE — read once here as `max(pending, latest)`, then incremented locally per tx. Right
|
|
88
|
+
* after a tx is mined a distributed RPC (e.g. Alchemy, `sepolia.base.org`) can briefly serve a
|
|
89
|
+
* stale `pending` transaction count (read-after-write lag), so re-fetching it for the NEXT send
|
|
90
|
+
* in the same sequence risks reusing the just-spent nonce — the tx it already has in the mempool
|
|
91
|
+
* rejects the duplicate as "replacement transaction underpriced", and that send is silently
|
|
92
|
+
* lost. The `max` covers the cross-INVOCATION case the local increment cannot see: a node whose
|
|
93
|
+
* pending view has fallen behind its own head answers the first send of the next `abx` command
|
|
94
|
+
* with a nonce it has already spent.
|
|
95
|
+
* - GAS, one field over: a tx after the first in a sequence may target a contract an EARLIER tx in
|
|
96
|
+
* this same sequence just created. If the node answering `eth_estimateGas` hasn't seen that
|
|
97
|
+
* block yet, the target looks codeless and the estimate comes back as the calldata cost alone —
|
|
98
|
+
* see `pinGas` above for the full story (and the measured numbers). So every send after the
|
|
99
|
+
* first waits for its target's code to be visible before estimating.
|
|
100
|
+
*
|
|
101
|
+
* Every send: wait for code at the target when it's not the first send in this sender's sequence
|
|
102
|
+
* (mirrors the exact rule the deploy-code hot lane hand-rolled: every tx after the first, since the
|
|
103
|
+
* first is what creates what the rest target), pin the gas (a provable floor detector, never a
|
|
104
|
+
* substitute limit), broadcast, wait for the receipt, and throw a typed {@link TxRevertedError} —
|
|
105
|
+
* carrying the tx hash — on a `reverted` status rather than reporting a burned transaction as
|
|
106
|
+
* "confirmed".
|
|
107
|
+
*/
|
|
108
|
+
export declare function makeHotSender(args: {
|
|
109
|
+
wallet: WalletClient;
|
|
110
|
+
account: Account;
|
|
111
|
+
publicClient: PublicClient;
|
|
112
|
+
onEvent?: (e: SendEvent) => void;
|
|
113
|
+
}): SendTx;
|
|
114
|
+
/** Send a list of prepared txs, in order, via `send`; returns their receipts in the same order.
|
|
115
|
+
* A small convenience for the common "just run this sequence" case — narration (if any) rides
|
|
116
|
+
* `send`'s own `onEvent`, not a callback here. */
|
|
117
|
+
export declare function runPrepared(txs: PreparedTx[], send: SendTx): Promise<TransactionReceipt[]>;
|
|
118
|
+
//# sourceMappingURL=execute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../src/execute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAC,MAAM,MAAM,CAAC;AAChG,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,UAAU,CAAC;AAGzC;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAQhH;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,YAAY,EACpB,EAAE,EAAE;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,EAAE,GAAG,CAAA;CAAC,EAC/E,IAAI,GAAE;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAM,GAC/C,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAED;;;sBAGsB;AACtB,MAAM,MAAM,MAAM,GAAG,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAErE;;0BAE0B;AAC1B,MAAM,MAAM,SAAS,GACjB;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAC,GACjC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAC,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,YAAY,CAAC;IAC3B,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;CAClC,GAAG,MAAM,CAgDT;AAED;;mDAEmD;AACnD,wBAAsB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAIhG"}
|
package/dist/execute.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { GasEstimateBelowFloorError, TxRevertedError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wait until `address` has code from THIS client's point of view. A deploy receipt proves the
|
|
4
|
+
* contract exists on chain; it does not prove the node answering the next request has caught up.
|
|
5
|
+
* Bounded — on timeout we proceed and let the gas floor carry it, since a slow RPC is not a reason
|
|
6
|
+
* to refuse to continue a deploy that already spent money.
|
|
7
|
+
*/
|
|
8
|
+
export async function waitForCodeAt(client, address, timeoutMs = 15_000) {
|
|
9
|
+
const deadline = Date.now() + timeoutMs;
|
|
10
|
+
for (;;) {
|
|
11
|
+
const code = await client.getCode({ address }).catch(() => undefined);
|
|
12
|
+
if (code && code !== '0x')
|
|
13
|
+
return true;
|
|
14
|
+
if (Date.now() >= deadline)
|
|
15
|
+
return false;
|
|
16
|
+
await new Promise((r) => setTimeout(r, 750));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Decide a transaction's gas limit once, here.
|
|
21
|
+
*
|
|
22
|
+
* The important design point, because the obvious approach is wrong: when an estimate looks too low
|
|
23
|
+
* we do **not** substitute a computed number. A caller can only compute the *provable* part of a
|
|
24
|
+
* payload's cost (see `PreparedTx.gasFloor` — code deposit is 200 gas/byte and nothing else is
|
|
25
|
+
* physics); the same setup multicall also carries schema writes, dependency legs, URI legs and
|
|
26
|
+
* mints, whose cost we cannot derive without simulating them. A "probably enough" constant is a
|
|
27
|
+
* number tuned to whatever case was in front of its author: it papers over the symptom, then
|
|
28
|
+
* under-funds the next payload that carries a few more legs, producing the identical
|
|
29
|
+
* `DeploymentFailed()` with a fresh mystery attached.
|
|
30
|
+
*
|
|
31
|
+
* So the floor is used as a **detector**. An estimate below a provable minimum is not "low", it is
|
|
32
|
+
* *impossible* — proof that the node answering us is looking at the wrong state (typically it has
|
|
33
|
+
* not seen the deploy block yet, so the target reads as an account with no code and the estimate
|
|
34
|
+
* comes back as the calldata cost alone). The right response to a broken measurement is to take it
|
|
35
|
+
* again, and if it stays broken, to refuse — sending a transaction we can prove is under-funded
|
|
36
|
+
* would burn the gas AND orphan the contract.
|
|
37
|
+
*
|
|
38
|
+
* When the estimate IS plausible it is trusted, plus headroom for state drift between estimate and
|
|
39
|
+
* inclusion.
|
|
40
|
+
*/
|
|
41
|
+
export async function pinGas(client, tx, opts = {}) {
|
|
42
|
+
const floor = tx.gasFloor ? BigInt(tx.gasFloor) : 0n;
|
|
43
|
+
const attempts = opts.attempts ?? 3;
|
|
44
|
+
let lastEstimate = null;
|
|
45
|
+
let lastError;
|
|
46
|
+
for (let i = 0; i < attempts; i++) {
|
|
47
|
+
try {
|
|
48
|
+
const estimate = await client.estimateGas({
|
|
49
|
+
account: tx.from,
|
|
50
|
+
to: tx.to ?? undefined,
|
|
51
|
+
data: tx.data,
|
|
52
|
+
...(tx.value && tx.value !== '0x0' ? { value: BigInt(tx.value) } : {}),
|
|
53
|
+
});
|
|
54
|
+
// Plausible (or nothing provable to check it against) → trust it.
|
|
55
|
+
if (estimate >= floor)
|
|
56
|
+
return (estimate * 125n) / 100n;
|
|
57
|
+
lastEstimate = estimate;
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
lastError = err;
|
|
61
|
+
// A revert during estimation is a real answer about the transaction, not a lagging node —
|
|
62
|
+
// surface it immediately, since its message ("caller is not the owner") is the useful part.
|
|
63
|
+
if (floor === 0n)
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
if (i < attempts - 1)
|
|
67
|
+
await new Promise((r) => setTimeout(r, opts.delayMs ?? 1_500));
|
|
68
|
+
}
|
|
69
|
+
if (lastEstimate !== null) {
|
|
70
|
+
throw new GasEstimateBelowFloorError(lastEstimate, floor);
|
|
71
|
+
}
|
|
72
|
+
throw lastError instanceof Error ? lastError : new Error('gas estimation failed');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build a {@link SendTx} that signs with an env-style hot key and broadcasts — the generalization of
|
|
76
|
+
* what used to be hand-rolled per call site (a deploy-then-setup loop in the CLI, the hot lane in
|
|
77
|
+
* `signer.ts`, the chunk-staging sender in `ownerops.ts`): one function that gets the read-after-
|
|
78
|
+
* write-lag handling right ONCE.
|
|
79
|
+
*
|
|
80
|
+
* Two lags, pinned once at construction / tracked locally per send, rather than re-read at send time:
|
|
81
|
+
* - the NONCE — read once here as `max(pending, latest)`, then incremented locally per tx. Right
|
|
82
|
+
* after a tx is mined a distributed RPC (e.g. Alchemy, `sepolia.base.org`) can briefly serve a
|
|
83
|
+
* stale `pending` transaction count (read-after-write lag), so re-fetching it for the NEXT send
|
|
84
|
+
* in the same sequence risks reusing the just-spent nonce — the tx it already has in the mempool
|
|
85
|
+
* rejects the duplicate as "replacement transaction underpriced", and that send is silently
|
|
86
|
+
* lost. The `max` covers the cross-INVOCATION case the local increment cannot see: a node whose
|
|
87
|
+
* pending view has fallen behind its own head answers the first send of the next `abx` command
|
|
88
|
+
* with a nonce it has already spent.
|
|
89
|
+
* - GAS, one field over: a tx after the first in a sequence may target a contract an EARLIER tx in
|
|
90
|
+
* this same sequence just created. If the node answering `eth_estimateGas` hasn't seen that
|
|
91
|
+
* block yet, the target looks codeless and the estimate comes back as the calldata cost alone —
|
|
92
|
+
* see `pinGas` above for the full story (and the measured numbers). So every send after the
|
|
93
|
+
* first waits for its target's code to be visible before estimating.
|
|
94
|
+
*
|
|
95
|
+
* Every send: wait for code at the target when it's not the first send in this sender's sequence
|
|
96
|
+
* (mirrors the exact rule the deploy-code hot lane hand-rolled: every tx after the first, since the
|
|
97
|
+
* first is what creates what the rest target), pin the gas (a provable floor detector, never a
|
|
98
|
+
* substitute limit), broadcast, wait for the receipt, and throw a typed {@link TxRevertedError} —
|
|
99
|
+
* carrying the tx hash — on a `reverted` status rather than reporting a burned transaction as
|
|
100
|
+
* "confirmed".
|
|
101
|
+
*/
|
|
102
|
+
export function makeHotSender(args) {
|
|
103
|
+
const { wallet, account, publicClient } = args;
|
|
104
|
+
const notify = args.onEvent ?? (() => { });
|
|
105
|
+
let nonce;
|
|
106
|
+
let sent = 0;
|
|
107
|
+
return async (tx) => {
|
|
108
|
+
if (nonce === undefined) {
|
|
109
|
+
// Fetch once, then increment locally per send — see the read-after-write-lag reasoning above.
|
|
110
|
+
//
|
|
111
|
+
// Read BOTH views and take the higher. `pending` is by definition >= `latest` on a coherent
|
|
112
|
+
// node, but a distributed endpoint that has not caught up with its own head serves a *lower*
|
|
113
|
+
// pending count than its own latest-block count — measured on `sepolia.base.org` right after
|
|
114
|
+
// a confirmed write, and never on the publicnode fallback. That is the cross-invocation half
|
|
115
|
+
// of the same lag this sender already handles within one sequence: each `abx` command is a
|
|
116
|
+
// fresh process, so the first send of the NEXT command re-reads a nonce the node has already
|
|
117
|
+
// spent, and the broadcast is rejected as a duplicate — silently, because a viem simulation
|
|
118
|
+
// failure means nothing is ever sent. `max` is correct-or-better in every case: with real
|
|
119
|
+
// pending txs `pending` wins, and against a stale view `latest` floors it.
|
|
120
|
+
const [pending, latest] = await Promise.all([
|
|
121
|
+
publicClient.getTransactionCount({ address: account.address, blockTag: 'pending' }),
|
|
122
|
+
publicClient.getTransactionCount({ address: account.address, blockTag: 'latest' }),
|
|
123
|
+
]);
|
|
124
|
+
nonce = Math.max(pending, latest);
|
|
125
|
+
}
|
|
126
|
+
// The SAME lag, one field over: every send after the first in this sender's sequence may target
|
|
127
|
+
// what an earlier send just created.
|
|
128
|
+
if (sent > 0 && tx.to)
|
|
129
|
+
await waitForCodeAt(publicClient, tx.to);
|
|
130
|
+
const gas = await pinGas(publicClient, { from: account.address, to: tx.to, data: tx.data, value: tx.value, gasFloor: tx.gasFloor });
|
|
131
|
+
notify({ kind: 'sending', tx });
|
|
132
|
+
const hash = await wallet.sendTransaction({
|
|
133
|
+
to: tx.to ?? undefined,
|
|
134
|
+
data: tx.data,
|
|
135
|
+
value: tx.value && tx.value !== '0x0' ? BigInt(tx.value) : undefined,
|
|
136
|
+
account,
|
|
137
|
+
chain: wallet.chain,
|
|
138
|
+
nonce,
|
|
139
|
+
gas,
|
|
140
|
+
});
|
|
141
|
+
nonce += 1;
|
|
142
|
+
sent += 1;
|
|
143
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
144
|
+
if (receipt.status !== 'success')
|
|
145
|
+
throw new TxRevertedError(tx.op, hash);
|
|
146
|
+
notify({ kind: 'mined', receipt, tx });
|
|
147
|
+
return receipt;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** Send a list of prepared txs, in order, via `send`; returns their receipts in the same order.
|
|
151
|
+
* A small convenience for the common "just run this sequence" case — narration (if any) rides
|
|
152
|
+
* `send`'s own `onEvent`, not a callback here. */
|
|
153
|
+
export async function runPrepared(txs, send) {
|
|
154
|
+
const receipts = [];
|
|
155
|
+
for (const tx of txs)
|
|
156
|
+
receipts.push(await send(tx));
|
|
157
|
+
return receipts;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../src/execute.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { ProjectState } from './types.js';
|
|
2
|
+
/** The locator's network, which decides which gateways can be asked about it. `http` locators
|
|
3
|
+
* carry their own base (they are already an absolute URL) and resolve to `''` here — a caller
|
|
4
|
+
* returns them verbatim before consulting a gateway. */
|
|
5
|
+
export type LocatorNetwork = 'arweave' | 'ipfs' | 'http';
|
|
6
|
+
/** Resolved gateway overrides for the two content-addressed networks — the shape both
|
|
7
|
+
* {@link resolveGatewayBase} and {@link gatewayConfigFromEnv} speak. */
|
|
8
|
+
export interface GatewayOverrides {
|
|
9
|
+
ipfs?: string;
|
|
10
|
+
arweave?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The gateway BASE for a locator network, given already-resolved `overrides` — PURE, no env
|
|
14
|
+
* read. Defaults to the generic public gateways (`ipfs.io` / `arweave.net`) when `overrides`
|
|
15
|
+
* doesn't name one; any other `network` (e.g. `http`, which carries its own base) resolves to
|
|
16
|
+
* `''`. Callers that want override → env → default precedence compose this with
|
|
17
|
+
* {@link gatewayConfigFromEnv} themselves (see `@artblocks/abx-storage`'s `resolveGatewayBase`
|
|
18
|
+
* for the reference compose) — kept separate so a host with its own config source never has this
|
|
19
|
+
* function reach into `process.env` on its behalf.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveGatewayBase(network: 'ipfs' | 'arweave' | string, overrides?: GatewayOverrides): string;
|
|
22
|
+
/**
|
|
23
|
+
* The ONLY env-reading piece of this module: `ABX_IPFS_GATEWAY` / `ABX_ARWEAVE_GATEWAY`, read via
|
|
24
|
+
* {@link readEnv} (safe where `process` doesn't exist). Exported separately from
|
|
25
|
+
* {@link resolveGatewayBase} so a host with its own gateway configuration (a database row, a
|
|
26
|
+
* remote config service) never has to touch `process.env` through this package at all — it just
|
|
27
|
+
* never calls this function and passes its own overrides straight to the pure resolver instead.
|
|
28
|
+
*/
|
|
29
|
+
export declare function gatewayConfigFromEnv(): GatewayOverrides;
|
|
30
|
+
/** Build the URL that asks `gateway` for `id`. A locator that is already an absolute URL is asked
|
|
31
|
+
* verbatim — rewriting someone's URL would answer a question they didn't ask. */
|
|
32
|
+
export declare function gatewayUrlFor(network: LocatorNetwork, id: string, gateway: string): string;
|
|
33
|
+
/** A scheme whose locator is content-addressed, and therefore its own integrity anchor. */
|
|
34
|
+
export type ContentAddressedNetwork = 'ipfs' | 'arweave';
|
|
35
|
+
/**
|
|
36
|
+
* Normalize a gateway HOST (the `ABX_IPFS_GATEWAY` / `ABX_ARWEAVE_GATEWAY` shape, and what
|
|
37
|
+
* {@link resolveGatewayBase} returns) into the on-chain PREFIX shape — trailing path included, so
|
|
38
|
+
* projection is a raw concat. A value that already ends in the scheme's path segment is left
|
|
39
|
+
* alone, so an operator may configure either form.
|
|
40
|
+
*/
|
|
41
|
+
export declare function gatewayPrefixFrom(network: ContentAddressedNetwork, base: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* The gateway prefix this project prefers for a scheme: the collection's reserved
|
|
44
|
+
* `abx_gateway_*` field, else the host's env/override default, else the public floor.
|
|
45
|
+
*
|
|
46
|
+
* The env read is a FLOOR, never an override. When a project has stated a preference on chain,
|
|
47
|
+
* every conforming resolver must serve that one — otherwise the same token renders differently
|
|
48
|
+
* depending on who is serving it, which is the property `abx_provenance` exists to make checkable.
|
|
49
|
+
* When a project has stated nothing, a host with its own gateway is strictly better than
|
|
50
|
+
* `ipfs.io`, and a managed provider serving a token it does not own has no other way to say so.
|
|
51
|
+
*/
|
|
52
|
+
export declare function projectGatewayPrefix(state: Pick<ProjectState, 'collectionFields'>, network: ContentAddressedNetwork, overrides?: GatewayOverrides): string;
|
|
53
|
+
/**
|
|
54
|
+
* Project a content-addressed field value into its https URL, byte-identically to the deployed
|
|
55
|
+
* renderer. `null` when the value locates nothing (`ipfs://` with no CID) — the caller omits an
|
|
56
|
+
* optional field or falls back on a required one, rather than emitting a bare prefix that 404s.
|
|
57
|
+
*
|
|
58
|
+
* Order is load-bearing and mirrors the Solidity exactly: strip the scheme, THEN substitute
|
|
59
|
+
* `{id}`, THEN check for an absolute URL. That last check is what stops a value which already
|
|
60
|
+
* names its own host — precisely what a `backend.locator()` return looks like — from coming back
|
|
61
|
+
* as `https://arweave.net/https://arweave.net/<txid>`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function projectGatewayUrl(network: ContentAddressedNetwork, value: string, prefix: string, tokenId?: string): string | null;
|
|
64
|
+
/**
|
|
65
|
+
* The inverse of {@link gatewayUrlFor} / {@link projectGatewayUrl}: recover the bare content id
|
|
66
|
+
* (CID or txid, plus any path suffix) from whatever form a locator arrived in.
|
|
67
|
+
*
|
|
68
|
+
* This exists because a storage backend's `locator()` returns a **gateway HTTPS URL** — the form an
|
|
69
|
+
* operator can click — while an `ipfs` / `arweave` metadata field must hold IDENTITY, with the
|
|
70
|
+
* gateway supplied at read time. Baking the operator's gateway host into the field is exactly the
|
|
71
|
+
* weld this projection was built to remove: it makes a CID unmigratable and makes the chain report
|
|
72
|
+
* `source: url` for bytes that live on IPFS.
|
|
73
|
+
*
|
|
74
|
+
* `null` when no id can be recovered — notably a subdomain-style gateway
|
|
75
|
+
* (`https://<cid>.ipfs.dweb.link`), which ABX's own backends never produce. A caller that gets
|
|
76
|
+
* `null` should keep the URL as a plain `url` field and SAY so, rather than guess.
|
|
77
|
+
*/
|
|
78
|
+
export declare function contentIdFromLocator(network: ContentAddressedNetwork, locator: string): string | null;
|
|
79
|
+
//# sourceMappingURL=gateways.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateways.d.ts","sourceRoot":"","sources":["../src/gateways.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAgB,YAAY,EAAC,MAAM,YAAY,CAAC;AAE5D;;yDAEyD;AACzD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzD;yEACyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,SAAS,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAI7G;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,gBAAgB,CAOvD;AAED;kFACkF;AAClF,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAK1F;AAaD,2FAA2F;AAC3F,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAAC;AAIzD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKxF;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,kBAAkB,CAAC,EAC7C,OAAO,EAAE,uBAAuB,EAChC,SAAS,CAAC,EAAE,gBAAgB,GAC3B,MAAM,CAgBR;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAUf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBrG"}
|