@getomnichain/omnichain 0.2.1 → 0.3.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +256 -0
  2. package/dist/cjs/chain.base.js.map +1 -1
  3. package/dist/cjs/errors.js +55 -6
  4. package/dist/cjs/errors.js.map +1 -1
  5. package/dist/cjs/evm/evm_chain.js +421 -2
  6. package/dist/cjs/evm/evm_chain.js.map +1 -1
  7. package/dist/cjs/evm/evm_transaction_status.js +4 -0
  8. package/dist/cjs/evm/evm_transaction_status.js.map +1 -1
  9. package/dist/cjs/evm/unsigned_evm_transaction.js +12 -0
  10. package/dist/cjs/evm/unsigned_evm_transaction.js.map +1 -1
  11. package/dist/cjs/solana/solana_chain.js +659 -21
  12. package/dist/cjs/solana/solana_chain.js.map +1 -1
  13. package/dist/cjs/solana/unsigned_solana_transaction.js +33 -0
  14. package/dist/cjs/solana/unsigned_solana_transaction.js.map +1 -1
  15. package/dist/cjs/utxo/utxo_chain.js +94 -10
  16. package/dist/cjs/utxo/utxo_chain.js.map +1 -1
  17. package/dist/esm/chain.base.d.ts +38 -1
  18. package/dist/esm/chain.base.d.ts.map +1 -1
  19. package/dist/esm/chain.base.js.map +1 -1
  20. package/dist/esm/errors.d.ts +13 -0
  21. package/dist/esm/errors.d.ts.map +1 -1
  22. package/dist/esm/errors.js +51 -6
  23. package/dist/esm/errors.js.map +1 -1
  24. package/dist/esm/evm/evm_chain.d.ts +48 -2
  25. package/dist/esm/evm/evm_chain.d.ts.map +1 -1
  26. package/dist/esm/evm/evm_chain.js +423 -4
  27. package/dist/esm/evm/evm_chain.js.map +1 -1
  28. package/dist/esm/evm/evm_transaction_status.d.ts +4 -0
  29. package/dist/esm/evm/evm_transaction_status.d.ts.map +1 -1
  30. package/dist/esm/evm/evm_transaction_status.js +4 -0
  31. package/dist/esm/evm/evm_transaction_status.js.map +1 -1
  32. package/dist/esm/evm/unsigned_evm_transaction.d.ts +13 -0
  33. package/dist/esm/evm/unsigned_evm_transaction.d.ts.map +1 -1
  34. package/dist/esm/evm/unsigned_evm_transaction.js +12 -0
  35. package/dist/esm/evm/unsigned_evm_transaction.js.map +1 -1
  36. package/dist/esm/solana/solana_chain.d.ts +100 -3
  37. package/dist/esm/solana/solana_chain.d.ts.map +1 -1
  38. package/dist/esm/solana/solana_chain.js +659 -23
  39. package/dist/esm/solana/solana_chain.js.map +1 -1
  40. package/dist/esm/solana/unsigned_solana_transaction.d.ts +20 -1
  41. package/dist/esm/solana/unsigned_solana_transaction.d.ts.map +1 -1
  42. package/dist/esm/solana/unsigned_solana_transaction.js +33 -0
  43. package/dist/esm/solana/unsigned_solana_transaction.js.map +1 -1
  44. package/dist/esm/utxo/utxo_chain.d.ts +15 -2
  45. package/dist/esm/utxo/utxo_chain.d.ts.map +1 -1
  46. package/dist/esm/utxo/utxo_chain.js +96 -13
  47. package/dist/esm/utxo/utxo_chain.js.map +1 -1
  48. package/docs/CONNECTIONS.md +33 -0
  49. package/docs/EIP7702.md +77 -0
  50. package/docs/JITO.md +41 -0
  51. package/docs/MULTI_RPC.md +28 -0
  52. package/package.json +1 -1
@@ -0,0 +1,33 @@
1
+ # Chain connection surface (0.3.0)
2
+
3
+ Every wire-level operation a consumer needs is on `Chain` or its typed subclasses — no `ethers` / `@solana/web3.js` / `@solana/spl-token` import required in consumer code.
4
+
5
+ ## Escape-hatch → SDK expression
6
+
7
+ | Old pattern | 0.3.0 SDK expression |
8
+ |---|---|
9
+ | `chain.getProvider().broadcastTransaction(hex)` | `chain.broadcast(hex)` |
10
+ | `chain.getProvider().getTransactionCount(addr, 'pending')` | `chain.getPendingNonce(addr)` |
11
+ | `chain.getProvider().getCode(addr)` + custom `0xef0100` parse | `chain.getDelegation(addr)` |
12
+ | `chain.getProvider().call(tx)` | `chain.call({ to, data, ... })` |
13
+ | `chain.getProvider().estimateGas(tx)` | `chain.call({ to, data, ..., estimateGas: true })` |
14
+ | `chain.getConnection().sendRawTransaction(bytes, opts)` | `chain.broadcast(bytes, opts)` |
15
+ | `chain.getConnection().getAccountInfo(pk)` | `chain.getAccountInfo(pk)` |
16
+ | `chain.getConnection().getMultipleAccountsInfo(pks)` | `chain.getAccountInfo(pks)` (batch overload) |
17
+ | `getAssociatedTokenAddressSync(mint, owner)` + `getAccount(...)` | `chain.getTokenAccount(owner, mint)` |
18
+ | `chain.getConnection().getAddressLookupTable(alt)` | `chain.fetchAddressLookupTable(alt)` |
19
+ | Compile `MessageV0` + build tx from raw instructions | `chain.createUnsignedTransaction({ payer, instructions, addressLookupTables? })` |
20
+ | Build type-4 tx by hand with ethers RLP | `chain.createUnsignedTransaction({ from, to, data, authorizationList })` |
21
+ | Compute EIP-7702 digest by hand | `chain.buildAuthorizationDigest({ delegate, nonce, chainId })` |
22
+ | Custom Jito HTTP client | `chain.submitJitoBundle(bytes[])` + `chain.getBundleStatus(bundleId)` |
23
+ | Consumer-side receipt-poll loop | `chain.getTransactionStatus(hash, { wait: true, timeoutMs })` |
24
+ | Multiple `getTransactionStatus` calls | `chain.getTransactionStatus([hash1, hash2, ...])` batch |
25
+
26
+ ## Feature-gated surfaces
27
+
28
+ - **EIP-7702**: requires `EvmChain` constructor with `supports7702: true`. Methods on non-flagged chains throw `ChainError(FeatureNotSupported)`.
29
+ - **Jito**: requires `SolanaChain` constructor with `jito: { url, auth? }`. `broadcast({ via: 'jito' })`, `submitJitoBundle`, `getBundleStatus` all throw `FeatureNotSupported` when `chain.jito === null`.
30
+
31
+ ## Error taxonomy
32
+
33
+ New `ChainErrorKinds` values: `BroadcastRejected`, `NonceTooLow`, `InsufficientFunds`, `BlockhashExpired`, `SimulationFailed`, `TransactionTooLarge`, `FeatureNotSupported`. Discriminator helpers: `isBlockhashExpiredError`, `isSimulationError`, `isNonceError`, `isTransactionTooLargeError`.
@@ -0,0 +1,77 @@
1
+ # EIP-7702 (type-4) transactions
2
+
3
+ Opt-in per chain: pass `supports7702: true` to the `EvmChain` constructor. Methods below throw `ChainError(FeatureNotSupported)` on unflagged chains.
4
+
5
+ ## Flow
6
+
7
+ ```ts
8
+ import { EvmChain } from '@getomnichain/omnichain';
9
+
10
+ const chain = new EvmChain({
11
+ chainId: 1,
12
+ name: 'Mainnet',
13
+ blockTimeSeconds: 12,
14
+ nativeSymbol: 'ETH',
15
+ explorerBaseUrl: 'https://etherscan.io',
16
+ rpcUrl: process.env.MAINNET_RPC_URL,
17
+ supports7702: true,
18
+ });
19
+
20
+ // 1. Read current delegate (nullable)
21
+ const current = await chain.getDelegation(userAddress);
22
+ // current === null | { delegate: '0x...' }
23
+
24
+ // 2. Compute the authorization digest for the wallet to sign.
25
+ //
26
+ // SELF-DELEGATION SUBTLETY: when the tx sender IS the authority (as in this
27
+ // example, from == userAddress AND the authorization is for userAddress),
28
+ // the authorization nonce must be `pendingNonce + 1n` — the sender's nonce
29
+ // is incremented BEFORE the authorization list is processed. Using
30
+ // pendingNonce here would silently fail on-chain: the tx lands, gas is
31
+ // spent, no delegation is installed, and getDelegation returns null with
32
+ // no error surfaced.
33
+ //
34
+ // For third-party-sponsored 7702 (relayer sends, authority is a different
35
+ // account), use the authority's pendingNonce as-is.
36
+ const pendingNonce = await chain.getPendingNonce(userAddress);
37
+ const authNonce = pendingNonce + 1n;
38
+ const digest = chain.buildAuthorizationDigest({
39
+ delegate: newDelegateAddress,
40
+ nonce: authNonce,
41
+ chainId: 1,
42
+ });
43
+
44
+ // 3. Wallet signs the digest externally
45
+ const signature = await wallet.signRaw(digest); // returns { r, s, yParity }
46
+
47
+ // 4. Build the type-4 tx via the general primitive
48
+ const unsigned = await chain.createUnsignedTransaction({
49
+ from: userAddress,
50
+ to: userAddress, // 7702 self-call to establish delegation
51
+ data: '0x',
52
+ authorizationList: [{
53
+ chainId: 1,
54
+ address: newDelegateAddress, // ethers TransactionLike shape
55
+ nonce: authNonce,
56
+ signature,
57
+ }],
58
+ });
59
+
60
+ // 5. Sign + broadcast
61
+ const signedHex = await wallet.signTransaction(unsigned);
62
+ const txHash = await chain.broadcast(signedHex);
63
+
64
+ // 6. Wait for inclusion
65
+ const status = await chain.getTransactionStatus(txHash, { wait: true, timeoutMs: 30_000 });
66
+ ```
67
+
68
+ ## Guards
69
+
70
+ - `authorizationList` must be non-empty when provided (`InvalidArgument`).
71
+ - Every `authorizationList[i].chainId` must match `chain.chainId`. Cross-chain replay guard. **EIP-7702 permits `chainId = 0` as a "any chain" wildcard, but the SDK rejects it** — a chainId=0 authorization is replayable on every EVM chain forever, which is the exact hazard the guard exists to prevent.
72
+ - `supports7702 === false` → `FeatureNotSupported` before any wire call.
73
+ - `getDelegation` parses ONLY `0xef0100<20-byte-delegate>` (exact 24-byte code). Any other shape returns `null`.
74
+
75
+ ## Digest formula
76
+
77
+ `keccak256(0x05 || rlp([chainId, delegate, nonce]))` per the EIP.
package/docs/JITO.md ADDED
@@ -0,0 +1,41 @@
1
+ # Solana Jito bundles
2
+
3
+ Opt-in per chain: pass `jito: { url, auth? }` to the `SolanaChain` constructor. Methods below throw `ChainError(FeatureNotSupported)` when `chain.jito === null`.
4
+
5
+ ## Single-tx via broadcast
6
+
7
+ ```ts
8
+ import { SolanaChain } from '@getomnichain/omnichain';
9
+
10
+ const chain = new SolanaChain({
11
+ ...init,
12
+ jito: { url: 'https://mainnet.block-engine.jito.wtf/api/v1/bundles' },
13
+ });
14
+
15
+ // Broadcast a single signed tx as a 1-tx Jito bundle.
16
+ // Returns the base58-encoded transaction signature (NOT the bundle id).
17
+ const sig = await chain.broadcast(signedBytes, { via: 'jito' });
18
+ const status = await chain.getTransactionStatus(sig, { wait: true });
19
+ ```
20
+
21
+ `broadcast({ via: 'jito' })` submits the transaction as a 1-tx bundle and returns the tx **signature** (the base58 of the first 64 bytes of the serialized transaction). The signature is the same value `chain.getTransactionStatus` accepts — the two APIs are consistent. The corresponding bundle id is a wire-level detail owned by the SDK and not surfaced through this path.
22
+
23
+ ## Multi-tx bundle
24
+
25
+ For a real multi-tx bundle the bundle id is meaningful. Use `submitJitoBundle` explicitly:
26
+
27
+ ```ts
28
+ const bundleId = await chain.submitJitoBundle([signedTx1, signedTx2, signedTx3]);
29
+ const status = await chain.getBundleStatus(bundleId);
30
+ // { bundleId, state: 'Pending' | 'Landed' | 'Failed', slot?, err? }
31
+ ```
32
+
33
+ `getBundleStatus` accepts a single id OR an array (batch polling):
34
+
35
+ ```ts
36
+ const [a, b, c] = await chain.getBundleStatus(['id1', 'id2', 'id3']);
37
+ ```
38
+
39
+ ## Bundle composition + tip strategy
40
+
41
+ Consumer-owned. The SDK exposes wire-level submit + status only. Tip instructions (e.g. `SystemProgram.transfer` to a Jito tip account) must be composed into the transactions before signing.
@@ -0,0 +1,28 @@
1
+ # Multi-endpoint RPC config
2
+
3
+ Both `EvmChain` and `SolanaChain` constructors accept an optional `rpcUrls?: string[]` field alongside `rpcUrl?: string`.
4
+
5
+ ## 0.3.0 status — single-entry only
6
+
7
+ **`rpcUrls` accepts at most ONE entry in 0.3.0.** Passing a longer list throws `ChainError(InvalidArgument)` at construction time. The field ships as a stable API surface; automatic failover-retry lands in a follow-up release. Consumers pinning `rpcUrls: [primary]` today will benefit as soon as retry-on-transport-error ships.
8
+
9
+ ```ts
10
+ new EvmChain({ ..., rpcUrls: [process.env.ARBITRUM_PRIMARY_RPC_URL!] }); // OK
11
+ new EvmChain({ ..., rpcUrls: [primary, backup] }); // throws — deferred
12
+ ```
13
+
14
+ ## Precedence
15
+
16
+ `rpcUrl` (single) → `rpcUrls[0]` → env-var chain → `defaultRpcUrl` (Solana only).
17
+
18
+ When `rpcUrls` is non-empty but every entry is blank, resolution throws `ChainError(RpcNotConfigured)`. **The SDK never silently falls back to a URL that isn't in the configured list or env fallback chain.**
19
+
20
+ ## URL redactor
21
+
22
+ All error messages / thrown `ChainError.message` / logs pass through a redactor that strips:
23
+ - Query params ending in `key`, `token`, `secret` (`apiKey`, `api-key`, `api_key`, `key`, `access-token`, `secret`)
24
+ - `Authorization: Bearer <token>` (case-insensitive)
25
+ - Path-embedded keys under `/vN/` or `/api/` (≥16 alphanumeric or `_/-`)
26
+ - Full-URL match on known providers — Alchemy, Infura, QuickNode, Helius, Ankr, Blast, dRPC — collapses to `<scheme>://<host>/<redacted>`
27
+
28
+ Keyed URLs never appear in logs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getomnichain/omnichain",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Multi-chain SDK for EVM, Solana, UTXO (BTC/LTC/DOGE/DASH/ZEC/BCH), TON, and Tron. TypeScript parity port of omnichain-py.",
5
5
  "keywords": [
6
6
  "omnichain",