SDK v0.x → v1.0 Migration
Typed errors, retry/timeout options, and the mainnet network flag.
v1.0 is additive and opt-in for most setups, but the Error class hierarchy is a breaking change: callers that currently inspect raw viem/ethers error strings must migrate to instanceof ClearPactError (and its subclasses) before upgrading.
1. Typed Error Classes
v0.x only exposed a single base ClearPactError. Callers that wanted to
distinguish a 404 from a 422 had to string-match raw messages from viem or ethers,
which silently broke when chains renamed their revert reasons.
v1.0 ships a typed subclass hierarchy. Every error carries a stable
err.code, the upstream err.status (HTTP code), and
retry-aware fields (err.attempts, err.lastError) on
the retry-exhausted subclass.
| Class | Trigger | err.code |
|---|---|---|
NetworkError |
DNS / TLS failure or non-JSON body | NETWORK_ERROR |
EscrowNotFoundError |
HTTP 404 | ESCROW_NOT_FOUND |
ContractRevertedError |
HTTP 422, or body contains "revert" | CONTRACT_REVERTED |
InsufficientFundsError |
HTTP 402, or body contains "insufficient" | INSUFFICIENT_FUNDS |
InvalidAddressError |
assertAddress() failure |
INVALID_ADDRESS |
ClearPactTimeoutError |
Per-attempt timeoutMs reached |
CLEAR_PACT_TIMEOUT |
ClearPactRetryExhaustedError |
Final attempt failed extends ClearPactTimeoutError | CLEAR_PACT_RETRY_EXHAUSTED |
Catching the base ClearPactError catches every subclass. The retry-exhausted
class extends ClearPactTimeoutError, so a single
instanceof ClearPactTimeoutError branch handles both.
2. Retry & Timeout Constructor Options
Five new options on the new ClearPact({...}) constructor control
per-attempt timeout, total retry count, and the exponential backoff curve.
Defaults are conservative; tune them for the latency profile of your upstream RPC.
| Option | Default | Meaning |
|---|---|---|
timeoutMs |
30_000 |
Per-attempt timeout. AbortController cancels the fetch when this fires. |
retries |
3 |
Additional attempts after the initial (so 4 total when default). |
baseDelayMs |
200 |
Exponential backoff base. |
jitterMs |
±100 |
Random jitter added to each backoff sleep. |
backoffMaxMs |
5_000 |
Cap on any single backoff sleep so a high retry count can't queue a long wait. |
The SDK retries retriable failures only. The rule is:
NetworkError, ClearPactTimeoutError, HTTP 429,
and any 5xx response retry; everything else (including EscrowNotFoundError
and InvalidAddressError) does not — a retry on a 4xx risks
double-submitting a mutation, so the SDK surfaces it immediately.
3. Mainnet Network Config Flag
v1.0 makes the target network explicit through a network constructor
option. Allowed values: 'testnet' (default) and 'mainnet'.
The constructor throws a ClearPactError on any other value.
On 'testnet' the SDK resolves the Base Sepolia USDC contract and the
testnet escrow proxy automatically. On 'mainnet' the SDK targets Base
(chain 8453) USDC plus the mainnet escrow proxy.
Mainnet is server-gated. Calls against network: 'mainnet'
on the deployed platform only execute when MAINNET_ESCROW_CONTRACT_ADDRESS
and DEPLOYER_PRIVATE_KEY are configured on the server. You can
build and unit-test with network: 'mainnet' against any
ClearPact API instance, but live writes require the server-side env vars to be
set.
Before / After: new ClearPact() init
v0.x
const client = new ClearPact({
apiKey: 'cpk_live_…',
});
v1.0
const client = new ClearPact({
apiKey: 'cpk_live_…',
network: 'mainnet', // new; defaults to 'testnet'
timeoutMs: 15_000, // new; AbortController per attempt
retries: 5, // new; exponential backoff + jitter
});
All four new fields are optional. Omit them to inherit the defaults from
TIMEOUT_DEFAULTS: 30_000 ms timeout, 3 retries,
200 ms backoff base, ±100 ms jitter, 5 s cap.
Before / After: catch block
v0.x
try {
await client.escrow.get('not-a-real-id');
} catch (err) {
if (err.message?.includes('not found')) {
return null;
}
throw err;
}
v1.0
try {
await client.jobs.get('not-a-real-id');
} catch (err) {
if (err instanceof EscrowNotFoundError) { return null; }
if (err instanceof ClearPactRetryExhaustedError) { throw err.lastError; }
if (err instanceof ClearPactError) { /* err.code + err.status */ }
throw err;
}
The retry-exhausted branch unwraps err.lastError so handlers see the
underlying network/timeout failure rather than the wrapper. Reaching for
err.code lets you log by category without enumerating every subclass.