@getomnichain/omnichain 0.2.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/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/abstract_gas_pricing.d.ts +31 -0
- package/dist/abstract_gas_pricing.d.ts.map +1 -0
- package/dist/abstract_gas_pricing.js +22 -0
- package/dist/abstract_gas_pricing.js.map +1 -0
- package/dist/address.d.ts +10 -0
- package/dist/address.d.ts.map +1 -0
- package/dist/address.factory.d.ts +26 -0
- package/dist/address.factory.d.ts.map +1 -0
- package/dist/address.factory.js +75 -0
- package/dist/address.factory.js.map +1 -0
- package/dist/address.js +13 -0
- package/dist/address.js.map +1 -0
- package/dist/address_field.decorator.d.ts +17 -0
- package/dist/address_field.decorator.d.ts.map +1 -0
- package/dist/address_field.decorator.js +34 -0
- package/dist/address_field.decorator.js.map +1 -0
- package/dist/chain.base.d.ts +79 -0
- package/dist/chain.base.d.ts.map +1 -0
- package/dist/chain.base.js +113 -0
- package/dist/chain.base.js.map +1 -0
- package/dist/chain_ids.d.ts +122 -0
- package/dist/chain_ids.d.ts.map +1 -0
- package/dist/chain_ids.js +193 -0
- package/dist/chain_ids.js.map +1 -0
- package/dist/errors.d.ts +42 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +62 -0
- package/dist/errors.js.map +1 -0
- package/dist/evm/evm_address.d.ts +11 -0
- package/dist/evm/evm_address.d.ts.map +1 -0
- package/dist/evm/evm_address.js +56 -0
- package/dist/evm/evm_address.js.map +1 -0
- package/dist/evm/evm_chain.d.ts +123 -0
- package/dist/evm/evm_chain.d.ts.map +1 -0
- package/dist/evm/evm_chain.js +685 -0
- package/dist/evm/evm_chain.js.map +1 -0
- package/dist/evm/evm_chains.d.ts +83 -0
- package/dist/evm/evm_chains.d.ts.map +1 -0
- package/dist/evm/evm_chains.js +493 -0
- package/dist/evm/evm_chains.js.map +1 -0
- package/dist/evm/evm_gas_estimate.d.ts +45 -0
- package/dist/evm/evm_gas_estimate.d.ts.map +1 -0
- package/dist/evm/evm_gas_estimate.js +70 -0
- package/dist/evm/evm_gas_estimate.js.map +1 -0
- package/dist/evm/evm_gas_pricing.d.ts +40 -0
- package/dist/evm/evm_gas_pricing.d.ts.map +1 -0
- package/dist/evm/evm_gas_pricing.js +56 -0
- package/dist/evm/evm_gas_pricing.js.map +1 -0
- package/dist/evm/evm_token.d.ts +7 -0
- package/dist/evm/evm_token.d.ts.map +1 -0
- package/dist/evm/evm_token.js +22 -0
- package/dist/evm/evm_token.js.map +1 -0
- package/dist/evm/evm_tokens.d.ts +81 -0
- package/dist/evm/evm_tokens.d.ts.map +1 -0
- package/dist/evm/evm_tokens.js +104 -0
- package/dist/evm/evm_tokens.js.map +1 -0
- package/dist/evm/evm_transaction_status.d.ts +108 -0
- package/dist/evm/evm_transaction_status.d.ts.map +1 -0
- package/dist/evm/evm_transaction_status.js +177 -0
- package/dist/evm/evm_transaction_status.js.map +1 -0
- package/dist/evm/index.d.ts +10 -0
- package/dist/evm/index.d.ts.map +1 -0
- package/dist/evm/index.js +10 -0
- package/dist/evm/index.js.map +1 -0
- package/dist/evm/unsigned_evm_transaction.d.ts +19 -0
- package/dist/evm/unsigned_evm_transaction.d.ts.map +1 -0
- package/dist/evm/unsigned_evm_transaction.js +18 -0
- package/dist/evm/unsigned_evm_transaction.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/is_address.decorator.d.ts +3 -0
- package/dist/is_address.decorator.d.ts.map +1 -0
- package/dist/is_address.decorator.js +51 -0
- package/dist/is_address.decorator.js.map +1 -0
- package/dist/network_type.d.ts +73 -0
- package/dist/network_type.d.ts.map +1 -0
- package/dist/network_type.js +144 -0
- package/dist/network_type.js.map +1 -0
- package/dist/priority.d.ts +23 -0
- package/dist/priority.d.ts.map +1 -0
- package/dist/priority.js +23 -0
- package/dist/priority.js.map +1 -0
- package/dist/solana/index.d.ts +9 -0
- package/dist/solana/index.d.ts.map +1 -0
- package/dist/solana/index.js +9 -0
- package/dist/solana/index.js.map +1 -0
- package/dist/solana/solana_address.d.ts +20 -0
- package/dist/solana/solana_address.d.ts.map +1 -0
- package/dist/solana/solana_address.js +45 -0
- package/dist/solana/solana_address.js.map +1 -0
- package/dist/solana/solana_chain.d.ts +213 -0
- package/dist/solana/solana_chain.d.ts.map +1 -0
- package/dist/solana/solana_chain.js +704 -0
- package/dist/solana/solana_chain.js.map +1 -0
- package/dist/solana/solana_chains.d.ts +24 -0
- package/dist/solana/solana_chains.d.ts.map +1 -0
- package/dist/solana/solana_chains.js +84 -0
- package/dist/solana/solana_chains.js.map +1 -0
- package/dist/solana/solana_gas_pricing.d.ts +30 -0
- package/dist/solana/solana_gas_pricing.d.ts.map +1 -0
- package/dist/solana/solana_gas_pricing.js +26 -0
- package/dist/solana/solana_gas_pricing.js.map +1 -0
- package/dist/solana/solana_token.d.ts +19 -0
- package/dist/solana/solana_token.d.ts.map +1 -0
- package/dist/solana/solana_token.js +34 -0
- package/dist/solana/solana_token.js.map +1 -0
- package/dist/solana/solana_tokens.d.ts +22 -0
- package/dist/solana/solana_tokens.d.ts.map +1 -0
- package/dist/solana/solana_tokens.js +26 -0
- package/dist/solana/solana_tokens.js.map +1 -0
- package/dist/solana/solana_transaction_status.d.ts +59 -0
- package/dist/solana/solana_transaction_status.d.ts.map +1 -0
- package/dist/solana/solana_transaction_status.js +144 -0
- package/dist/solana/solana_transaction_status.js.map +1 -0
- package/dist/solana/unsigned_solana_transaction.d.ts +31 -0
- package/dist/solana/unsigned_solana_transaction.d.ts.map +1 -0
- package/dist/solana/unsigned_solana_transaction.js +26 -0
- package/dist/solana/unsigned_solana_transaction.js.map +1 -0
- package/dist/token.d.ts +35 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +62 -0
- package/dist/token.js.map +1 -0
- package/dist/ton/index.d.ts +2 -0
- package/dist/ton/index.d.ts.map +1 -0
- package/dist/ton/index.js +2 -0
- package/dist/ton/index.js.map +1 -0
- package/dist/ton/ton_address.d.ts +10 -0
- package/dist/ton/ton_address.d.ts.map +1 -0
- package/dist/ton/ton_address.js +53 -0
- package/dist/ton/ton_address.js.map +1 -0
- package/dist/transaction_status.d.ts +140 -0
- package/dist/transaction_status.d.ts.map +1 -0
- package/dist/transaction_status.js +271 -0
- package/dist/transaction_status.js.map +1 -0
- package/dist/unsigned_transaction.d.ts +7 -0
- package/dist/unsigned_transaction.d.ts.map +1 -0
- package/dist/unsigned_transaction.js +9 -0
- package/dist/unsigned_transaction.js.map +1 -0
- package/dist/utxo/btc/btc_address.d.ts +15 -0
- package/dist/utxo/btc/btc_address.d.ts.map +1 -0
- package/dist/utxo/btc/btc_address.js +39 -0
- package/dist/utxo/btc/btc_address.js.map +1 -0
- package/dist/utxo/btc/btc_chain.d.ts +34 -0
- package/dist/utxo/btc/btc_chain.d.ts.map +1 -0
- package/dist/utxo/btc/btc_chain.js +62 -0
- package/dist/utxo/btc/btc_chain.js.map +1 -0
- package/dist/utxo/btc/btc_chains.d.ts +25 -0
- package/dist/utxo/btc/btc_chains.d.ts.map +1 -0
- package/dist/utxo/btc/btc_chains.js +43 -0
- package/dist/utxo/btc/btc_chains.js.map +1 -0
- package/dist/utxo/btc/btc_token.d.ts +6 -0
- package/dist/utxo/btc/btc_token.d.ts.map +1 -0
- package/dist/utxo/btc/btc_token.js +11 -0
- package/dist/utxo/btc/btc_token.js.map +1 -0
- package/dist/utxo/btc/index.d.ts +14 -0
- package/dist/utxo/btc/index.d.ts.map +1 -0
- package/dist/utxo/btc/index.js +14 -0
- package/dist/utxo/btc/index.js.map +1 -0
- package/dist/utxo/btc/network_params.d.ts +50 -0
- package/dist/utxo/btc/network_params.d.ts.map +1 -0
- package/dist/utxo/btc/network_params.js +188 -0
- package/dist/utxo/btc/network_params.js.map +1 -0
- package/dist/utxo/btc/tools/asset_outpoint.d.ts +7 -0
- package/dist/utxo/btc/tools/asset_outpoint.d.ts.map +1 -0
- package/dist/utxo/btc/tools/asset_outpoint.js +12 -0
- package/dist/utxo/btc/tools/asset_outpoint.js.map +1 -0
- package/dist/utxo/btc/tools/bitcoin_core.tool.d.ts +41 -0
- package/dist/utxo/btc/tools/bitcoin_core.tool.d.ts.map +1 -0
- package/dist/utxo/btc/tools/bitcoin_core.tool.js +172 -0
- package/dist/utxo/btc/tools/bitcoin_core.tool.js.map +1 -0
- package/dist/utxo/btc/tools/esplora.tool.d.ts +27 -0
- package/dist/utxo/btc/tools/esplora.tool.d.ts.map +1 -0
- package/dist/utxo/btc/tools/esplora.tool.js +113 -0
- package/dist/utxo/btc/tools/esplora.tool.js.map +1 -0
- package/dist/utxo/btc/tools/inscription_index.d.ts +6 -0
- package/dist/utxo/btc/tools/inscription_index.d.ts.map +1 -0
- package/dist/utxo/btc/tools/inscription_index.js +2 -0
- package/dist/utxo/btc/tools/inscription_index.js.map +1 -0
- package/dist/utxo/btc/tools/ordiscan.tool.d.ts +19 -0
- package/dist/utxo/btc/tools/ordiscan.tool.d.ts.map +1 -0
- package/dist/utxo/btc/tools/ordiscan.tool.js +53 -0
- package/dist/utxo/btc/tools/ordiscan.tool.js.map +1 -0
- package/dist/utxo/btc/tools/rare_sat_index.d.ts +6 -0
- package/dist/utxo/btc/tools/rare_sat_index.d.ts.map +1 -0
- package/dist/utxo/btc/tools/rare_sat_index.js +2 -0
- package/dist/utxo/btc/tools/rare_sat_index.js.map +1 -0
- package/dist/utxo/btc/tools/rune_index.d.ts +6 -0
- package/dist/utxo/btc/tools/rune_index.d.ts.map +1 -0
- package/dist/utxo/btc/tools/rune_index.js +2 -0
- package/dist/utxo/btc/tools/rune_index.js.map +1 -0
- package/dist/utxo/btc/tools/unisat.tool.d.ts +18 -0
- package/dist/utxo/btc/tools/unisat.tool.d.ts.map +1 -0
- package/dist/utxo/btc/tools/unisat.tool.js +49 -0
- package/dist/utxo/btc/tools/unisat.tool.js.map +1 -0
- package/dist/utxo/coin_selection.d.ts +29 -0
- package/dist/utxo/coin_selection.d.ts.map +1 -0
- package/dist/utxo/coin_selection.js +156 -0
- package/dist/utxo/coin_selection.js.map +1 -0
- package/dist/utxo/doge/doge_chains.d.ts +17 -0
- package/dist/utxo/doge/doge_chains.d.ts.map +1 -0
- package/dist/utxo/doge/doge_chains.js +28 -0
- package/dist/utxo/doge/doge_chains.js.map +1 -0
- package/dist/utxo/doge/index.d.ts +3 -0
- package/dist/utxo/doge/index.d.ts.map +1 -0
- package/dist/utxo/doge/index.js +3 -0
- package/dist/utxo/doge/index.js.map +1 -0
- package/dist/utxo/doge/network_params.d.ts +4 -0
- package/dist/utxo/doge/network_params.d.ts.map +1 -0
- package/dist/utxo/doge/network_params.js +24 -0
- package/dist/utxo/doge/network_params.js.map +1 -0
- package/dist/utxo/ecc.d.ts +2 -0
- package/dist/utxo/ecc.d.ts.map +1 -0
- package/dist/utxo/ecc.js +11 -0
- package/dist/utxo/ecc.js.map +1 -0
- package/dist/utxo/fee.d.ts +12 -0
- package/dist/utxo/fee.d.ts.map +1 -0
- package/dist/utxo/fee.js +51 -0
- package/dist/utxo/fee.js.map +1 -0
- package/dist/utxo/index.d.ts +20 -0
- package/dist/utxo/index.d.ts.map +1 -0
- package/dist/utxo/index.js +20 -0
- package/dist/utxo/index.js.map +1 -0
- package/dist/utxo/ltc/index.d.ts +3 -0
- package/dist/utxo/ltc/index.d.ts.map +1 -0
- package/dist/utxo/ltc/index.js +3 -0
- package/dist/utxo/ltc/index.js.map +1 -0
- package/dist/utxo/ltc/ltc_chains.d.ts +17 -0
- package/dist/utxo/ltc/ltc_chains.d.ts.map +1 -0
- package/dist/utxo/ltc/ltc_chains.js +28 -0
- package/dist/utxo/ltc/ltc_chains.js.map +1 -0
- package/dist/utxo/ltc/network_params.d.ts +4 -0
- package/dist/utxo/ltc/network_params.d.ts.map +1 -0
- package/dist/utxo/ltc/network_params.js +27 -0
- package/dist/utxo/ltc/network_params.js.map +1 -0
- package/dist/utxo/script.d.ts +16 -0
- package/dist/utxo/script.d.ts.map +1 -0
- package/dist/utxo/script.js +52 -0
- package/dist/utxo/script.js.map +1 -0
- package/dist/utxo/tools/broadcaster.d.ts +6 -0
- package/dist/utxo/tools/broadcaster.d.ts.map +1 -0
- package/dist/utxo/tools/broadcaster.js +2 -0
- package/dist/utxo/tools/broadcaster.js.map +1 -0
- package/dist/utxo/tools/chain_tip_provider.d.ts +5 -0
- package/dist/utxo/tools/chain_tip_provider.d.ts.map +1 -0
- package/dist/utxo/tools/chain_tip_provider.js +2 -0
- package/dist/utxo/tools/chain_tip_provider.js.map +1 -0
- package/dist/utxo/tools/fee_estimator.d.ts +6 -0
- package/dist/utxo/tools/fee_estimator.d.ts.map +1 -0
- package/dist/utxo/tools/fee_estimator.js +2 -0
- package/dist/utxo/tools/fee_estimator.js.map +1 -0
- package/dist/utxo/tools/raw_transaction_provider.d.ts +8 -0
- package/dist/utxo/tools/raw_transaction_provider.d.ts.map +1 -0
- package/dist/utxo/tools/raw_transaction_provider.js +2 -0
- package/dist/utxo/tools/raw_transaction_provider.js.map +1 -0
- package/dist/utxo/tools/utxo_provider.d.ts +7 -0
- package/dist/utxo/tools/utxo_provider.d.ts.map +1 -0
- package/dist/utxo/tools/utxo_provider.js +2 -0
- package/dist/utxo/tools/utxo_provider.js.map +1 -0
- package/dist/utxo/unsigned_utxo_transaction.d.ts +32 -0
- package/dist/utxo/unsigned_utxo_transaction.d.ts.map +1 -0
- package/dist/utxo/unsigned_utxo_transaction.js +32 -0
- package/dist/utxo/unsigned_utxo_transaction.js.map +1 -0
- package/dist/utxo/utxo.d.ts +43 -0
- package/dist/utxo/utxo.d.ts.map +1 -0
- package/dist/utxo/utxo.js +2 -0
- package/dist/utxo/utxo.js.map +1 -0
- package/dist/utxo/utxo_chain.d.ts +102 -0
- package/dist/utxo/utxo_chain.d.ts.map +1 -0
- package/dist/utxo/utxo_chain.js +524 -0
- package/dist/utxo/utxo_chain.js.map +1 -0
- package/dist/utxo/utxo_chains.d.ts +29 -0
- package/dist/utxo/utxo_chains.d.ts.map +1 -0
- package/dist/utxo/utxo_chains.js +63 -0
- package/dist/utxo/utxo_chains.js.map +1 -0
- package/dist/utxo/utxo_gas_pricing.d.ts +22 -0
- package/dist/utxo/utxo_gas_pricing.d.ts.map +1 -0
- package/dist/utxo/utxo_gas_pricing.js +20 -0
- package/dist/utxo/utxo_gas_pricing.js.map +1 -0
- package/dist/utxo/utxo_network_params.d.ts +25 -0
- package/dist/utxo/utxo_network_params.d.ts.map +1 -0
- package/dist/utxo/utxo_network_params.js +15 -0
- package/dist/utxo/utxo_network_params.js.map +1 -0
- package/dist/utxo/utxo_transaction_status.d.ts +73 -0
- package/dist/utxo/utxo_transaction_status.d.ts.map +1 -0
- package/dist/utxo/utxo_transaction_status.js +89 -0
- package/dist/utxo/utxo_transaction_status.js.map +1 -0
- package/docs/PUBLISHING.md +119 -0
- package/docs/README.md +273 -0
- package/docs/evm.md +205 -0
- package/docs/solana.md +220 -0
- package/docs/utxo.md +364 -0
- package/package.json +94 -0
package/docs/README.md
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# omnichain
|
|
2
|
+
|
|
3
|
+
A service-agnostic abstraction layer for talking to blockchain networks. One
|
|
4
|
+
shape (`Chain`, `Token`, `Address`, `UnsignedTransaction`) covers EVM
|
|
5
|
+
(Ethereum / Arbitrum / Base / BNB / …), UTXO (Bitcoin / Litecoin /
|
|
6
|
+
Dogecoin), Solana, and TON, with the same call surface regardless of
|
|
7
|
+
underlying network.
|
|
8
|
+
|
|
9
|
+
This repository is the canonical source for the SDK. It is consumed by
|
|
10
|
+
sister services (depositron, pluton-back-end, gasless) as a git submodule
|
|
11
|
+
mounted at each service's own `chain` directory.
|
|
12
|
+
|
|
13
|
+
## What's in here
|
|
14
|
+
|
|
15
|
+
| Concept | File | Purpose |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `Chain` | [chain.base.ts](../chain.base.ts) | Abstract base for any chain — exposes `getBalance`, `createTransferUnsignedTransaction`, `getTransactionStatus`, address/token validation, explorer URLs |
|
|
18
|
+
| `Token` | [token.ts](../token.ts) | Abstract base for an on-chain asset — `chainId`, `symbol`, `identifier` (or `NATIVE_TOKEN_IDENTIFIER`), `decimals` |
|
|
19
|
+
| `Address` | [address.ts](../address.ts) | Abstract address with `canonical()` + `networkType` |
|
|
20
|
+
| `UnsignedTransaction` | [unsigned_transaction.ts](../unsigned_transaction.ts) | Abstract base for an unsigned tx; per-chain subclasses add the bytes |
|
|
21
|
+
| `NetworkType` | [network_type.ts](../network_type.ts) | Enum (`EVM`, `COSMOS`, `TON`, `SOLANA`, `BTC`) + `registerNonEvmChain(chainId, type)` registry |
|
|
22
|
+
| `TransactionStatus` | [transaction_status.ts](../transaction_status.ts) | Abstract base — chainId, status, inclusionAt, error, balanceChanges. Per-network subclasses: `EvmTransactionStatus` (+logs, +fees), `SolanaTransactionStatus` (+fees), `UtxoTransactionStatus` (+outputs, +vsize, +confirmations, +fees) |
|
|
23
|
+
| `AssetBalanceChange` | [transaction_status.ts](../transaction_status.ts) | Per-(wallet, asset) balance delta. `.balanceChangeMr: bigint` is the source of truth; `.balanceChangeHr: Decimal` is a lazy accessor derived via exact string-shift (never loses wei). `.fromHr(hr, decimals)` factory for Python-parity construction. `NestedBalanceChanges = Map<wallet, Map<assetHash, {token, change}>>` |
|
|
24
|
+
| `AbstractGasPricing` + subclasses | [abstract_gas_pricing.ts](../abstract_gas_pricing.ts), [evm/evm_gas_pricing.ts](../evm/evm_gas_pricing.ts), [solana/solana_gas_pricing.ts](../solana/solana_gas_pricing.ts), [utxo/utxo_gas_pricing.ts](../utxo/utxo_gas_pricing.ts) | Python-parity explicit-fee override types. `GasPricingType = FeePriority \| AbstractGasPricing`. Type surface only in Wave 2B — per-chain builders don't consume `CreateTransferRequest.gasPricing` yet |
|
|
25
|
+
| `Priority` | [priority.ts](../priority.ts) | Shared `Priority` enum (`SLOW`/`NORMAL`/`FAST`) used by per-chain `suggestGas` / `suggestFeeRate` / `suggestPriorityFeeMicroLamports` |
|
|
26
|
+
| `ChainError` | [errors.ts](../errors.ts) | Module-internal error with a `kind` discriminator |
|
|
27
|
+
| `addressFor(chainId, raw)` | [address.factory.ts](../address.factory.ts) | Network-aware address constructor |
|
|
28
|
+
|
|
29
|
+
## Per-chain implementations
|
|
30
|
+
|
|
31
|
+
| Network | Where | What it is |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| **EVM** (Ethereum, Arbitrum, Base, BNB, …) | [evm/](../evm) | `EvmChain` over `ethers.js` v6 + ERC-20 support — see [evm.md](./evm.md) |
|
|
34
|
+
| **UTXO** (Bitcoin, Litecoin, Dogecoin + testnets) | [utxo/](../utxo) | `UtxoChain` + `BtcChain` over `bitcoinjs-lib` v7 + `coininfo` — see [utxo.md](./utxo.md) |
|
|
35
|
+
| **Solana** (mainnet, devnet, testnet) | [solana/](../solana) | `SolanaChain` over `@solana/web3.js` v1 + SPL Token / Token-2022 — see [solana.md](./solana.md) |
|
|
36
|
+
| **TON** | [ton/](../ton) | `TonAddress` only (no full chain yet) |
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
39
|
+
|
|
40
|
+
### Construct a chain instance
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { arbitrumChain } from '@getomnichain/omnichain';
|
|
44
|
+
|
|
45
|
+
const arb = arbitrumChain('https://arb1.arbitrum.io/rpc');
|
|
46
|
+
await arb.getBalance('0xabc…', /* tokenIdentifier */ 'NATIVE');
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For BTC:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import {
|
|
53
|
+
bitcoinMainnetChain,
|
|
54
|
+
BitcoinCoreTool,
|
|
55
|
+
BITCOIN_MAINNET_PARAMS,
|
|
56
|
+
} from '@getomnichain/omnichain';
|
|
57
|
+
|
|
58
|
+
const core = new BitcoinCoreTool({
|
|
59
|
+
baseUrl: 'http://localhost:8332',
|
|
60
|
+
user: 'user',
|
|
61
|
+
password: 'pass',
|
|
62
|
+
params: BITCOIN_MAINNET_PARAMS,
|
|
63
|
+
wallet: 'pluton-hot-wallet',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const btc = bitcoinMainnetChain({
|
|
67
|
+
chainId: -1,
|
|
68
|
+
utxoProvider: core,
|
|
69
|
+
rawTxProvider: core,
|
|
70
|
+
feeEstimator: core,
|
|
71
|
+
broadcaster: core,
|
|
72
|
+
chainTipProvider: core,
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For Solana:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { SolanaMainnet, Priority } from '@getomnichain/omnichain';
|
|
80
|
+
|
|
81
|
+
const cuMicroLamports = await SolanaMainnet.suggestPriorityFeeMicroLamports(Priority.NORMAL);
|
|
82
|
+
const balance = await SolanaMainnet.getBalance('5gUu…');
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Network-aware fee suggestion (Priority)
|
|
86
|
+
|
|
87
|
+
Each chain owns its own fee-suggestion math; the consumer just passes a
|
|
88
|
+
`Priority` and signs whatever envelope is returned.
|
|
89
|
+
|
|
90
|
+
| Chain | Method | Result | RPC call under the hood |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| EVM (`supportsEip1559`) | `chain.suggestGas(priority)` | `EvmGasEstimate { maxFeePerGas, maxPriorityFeePerGas }` | Single `eth_feeHistory` call at percentile `{SLOW:25, NORMAL:50, FAST:75}` over 10 blocks; sort tips + pick p90; 2 gwei fallback if reward rows empty; `MIN_GAS_PRICE_FLOOR` (0.05 gwei) clamp on the returned tip; RPC failure bubbles as `ChainError(RpcError)` (no defensive fallback) |
|
|
93
|
+
| EVM (legacy — `!supportsEip1559`) | `chain.suggestGas(priority)` | `EvmGasEstimate { gasPrice }` only — `maxFee*` fields **undefined** (Python parity) | `eth_gasPrice` × `{SLOW:1.0, NORMAL:1.2, FAST:1.5}`; clamped up to `MIN_GAS_PRICE_FLOOR`. Consumers that always sign EIP-1559 must dispatch on `supportsEip1559` or use `isLegacyGasEstimate` and fall back to a type-0 transaction on these chains. |
|
|
94
|
+
| BTC / UTXO | `chain.suggestFeeRate(priority)` | `number` (sats/vByte) | `feeEstimator.getFeeEstimate(targetBlocks)` with `FAST:1, NORMAL:3, SLOW:6` |
|
|
95
|
+
| Solana | `chain.suggestPriorityFeeMicroLamports(priority)` | `number` (microlamports/CU) | `getRecentPrioritizationFees({ lockedWritableAccounts: [] })` percentiles `{SLOW:p25, NORMAL:p50, FAST:p90}`; `0` on a quiet cluster |
|
|
96
|
+
|
|
97
|
+
### Build a transfer
|
|
98
|
+
|
|
99
|
+
The unified entry point is `Chain.createTransferUnsignedTransaction(req)`,
|
|
100
|
+
where `req` is the same shape for every chain:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const unsigned = await chain.createTransferUnsignedTransaction({
|
|
104
|
+
from: senderAddress,
|
|
105
|
+
to: recipientAddress,
|
|
106
|
+
tokenIdentifier: 'NATIVE', // or an ERC-20 / SPL / etc. identifier
|
|
107
|
+
amount: 1_000_000n, // smallest unit (wei / sats / lamports)
|
|
108
|
+
memo: 'optional UTF-8 memo', // BTC: emits OP_RETURN; EVM: ignored
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The returned object is a chain-specific subclass of `UnsignedTransaction`:
|
|
113
|
+
|
|
114
|
+
- EVM → `UnsignedEvmTransaction` (`{ to, value, data, chainId }`)
|
|
115
|
+
- UTXO → `UnsignedUtxoTransaction` (`{ psbtBase64, selectedInputs, feeSats, inputsToSign, … }`)
|
|
116
|
+
- Solana → `UnsignedSolanaTransaction` (`{ transaction: VersionedTransaction, … }`)
|
|
117
|
+
|
|
118
|
+
Signers (wallets, hardware devices, the protocol itself) take this and
|
|
119
|
+
sign it externally; the chain module **does not hold keys** and **does not
|
|
120
|
+
read environment variables** for credentials.
|
|
121
|
+
|
|
122
|
+
### Check transaction status
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { isSuccess } from '@getomnichain/omnichain';
|
|
126
|
+
|
|
127
|
+
const status = await chain.getTransactionStatus(txHash);
|
|
128
|
+
// EvmTransactionStatus | SolanaTransactionStatus | UtxoTransactionStatus
|
|
129
|
+
// Base fields: { chainId, status, inclusionAt, error, balanceChanges }
|
|
130
|
+
// Subclass extras vary per chain (see the type table above).
|
|
131
|
+
|
|
132
|
+
if (isSuccess(status)) {
|
|
133
|
+
// balanceChanges is now Map<wallet, Map<assetHash, {token, change}>>
|
|
134
|
+
for (const [wallet, perAsset] of status.balanceChanges) {
|
|
135
|
+
for (const { token, change } of perAsset.values()) {
|
|
136
|
+
// balanceChangeMr is the exact source of truth (bigint minor units);
|
|
137
|
+
// balanceChangeHr is a lazy Decimal accessor for display (Wave 2B).
|
|
138
|
+
console.log(wallet, token.symbol, change.balanceChangeMr, change.balanceChangeHr.toFixed());
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Wallet-key case normalization** — the wallet key used in
|
|
145
|
+
`NestedBalanceChanges` is chain-specific: EVM addresses are lowercased,
|
|
146
|
+
Solana keys are the raw base58 (case-sensitive), UTXO keys are the raw
|
|
147
|
+
provider-returned address (bech32 lower-case in practice; providers
|
|
148
|
+
don't uppercase). If you do `status.balanceChanges.get(userAddress)`
|
|
149
|
+
directly, normalise `userAddress` to the same form (`toLowerCase()` for
|
|
150
|
+
EVM).
|
|
151
|
+
|
|
152
|
+
## Error model
|
|
153
|
+
|
|
154
|
+
All errors thrown by this module are `ChainError` with a typed
|
|
155
|
+
`kind` field — see [errors.ts](../errors.ts):
|
|
156
|
+
|
|
157
|
+
- `NotSupported`, `RpcError`, `RpcChainIdMismatch`, `TransactionDecodeFailed`,
|
|
158
|
+
`InvalidAddress`, `InvalidTokenIdentifier`, `RpcNotConfigured`
|
|
159
|
+
|
|
160
|
+
Use `isChainError(err, ChainErrorKinds.InvalidAddress)` to narrow.
|
|
161
|
+
|
|
162
|
+
## NetworkType registry
|
|
163
|
+
|
|
164
|
+
Some addresses look identical across networks (e.g. all `0x…` 20-byte hex
|
|
165
|
+
addresses are valid EVM-shaped, but a real EVM-vs-non-EVM dispatch needs
|
|
166
|
+
context). The module maintains a small registry mapping `chainId →
|
|
167
|
+
NetworkType` (`network_type.ts`).
|
|
168
|
+
|
|
169
|
+
Behavior in v0:
|
|
170
|
+
|
|
171
|
+
- **Static seeds at module load** (from `chain_ids.ts`): BTC family
|
|
172
|
+
(`-1/-2/-3`), Solana family (`-2000/-2001/-2002`), TON family
|
|
173
|
+
(`-4000/-4001`), Tron family (`728126428/2494104990`).
|
|
174
|
+
- **`networkTypeOf(chainId)`**: returns the registered NetworkType if
|
|
175
|
+
present. For unregistered positive ids returns `EVM`. For unregistered
|
|
176
|
+
negative ids throws `ChainError(ChainNotSupported)` — fail-closed so a
|
|
177
|
+
stale `-100` doesn't silently misroute.
|
|
178
|
+
- **`tryNetworkTypeOf(chainId)`**: non-throwing sibling; returns
|
|
179
|
+
`undefined` on unregistered negatives.
|
|
180
|
+
- **`registerNonEvmChain(id, type)`**: idempotent for same type; throws
|
|
181
|
+
`ChainError(InvalidArgument)` on conflict. Since positive ids
|
|
182
|
+
synthesize EVM by default, claiming a positive id for a non-EVM
|
|
183
|
+
family requires `unregisterChain(id)` first.
|
|
184
|
+
- **`unregisterChain(id)`** + **`networkTypeRegistrations()`**: escape
|
|
185
|
+
hatch and diagnostics.
|
|
186
|
+
- Chain constructors (`SolanaChain`, `UtxoChain`) self-register in
|
|
187
|
+
their constructors. `EvmChain` does NOT self-register (its 48
|
|
188
|
+
pre-baked entries at module load would otherwise turn a consumer's
|
|
189
|
+
earlier `registerNonEvmChain(sameId, OTHER)` into an unrecoverable
|
|
190
|
+
import-time crash).
|
|
191
|
+
|
|
192
|
+
`addressFor(chainId, raw)` reads from this registry via `networkTypeOf`
|
|
193
|
+
and dispatches to the appropriate `Address` subclass — currently EVM,
|
|
194
|
+
Solana, BTC, TON. TRON and COSMOS families throw `ChainNotSupported`
|
|
195
|
+
(no Address parser yet).
|
|
196
|
+
|
|
197
|
+
## Chain IDs
|
|
198
|
+
|
|
199
|
+
- **EVM chains** use real EVM `chainId` values (1, 42161, 8453, 56, …).
|
|
200
|
+
- **Non-EVM chains** — the SDK now owns a reserved negative-ID range,
|
|
201
|
+
matching `omnichain-py/chain_ids.py`:
|
|
202
|
+
- BTC family: `-1` mainnet, `-2` testnet, `-3` signet
|
|
203
|
+
- LTC `-10`, DOGE `-12`, DASH `-14`, ZEC `-16`, BCH `-18` (mainnets only — testnets for these families are not defined in v0)
|
|
204
|
+
- Solana `-2000` mainnet, `-2001` testnet, `-2002` devnet
|
|
205
|
+
- TON `-4000` mainnet, `-4001` testnet
|
|
206
|
+
Consumers **should not** invent overlapping negative IDs for their own
|
|
207
|
+
chains. `registerNonEvmChain` throws `ChainError(InvalidArgument)` on
|
|
208
|
+
family conflict. Notable overlap: TON's native `global_id = -3` collides
|
|
209
|
+
with BTC signet in this SDK — use `CHAIN_ID_TON_TESTNET` (`-4001`) for
|
|
210
|
+
TON.
|
|
211
|
+
|
|
212
|
+
## Boundary: this module reads env only for RPC URL resolution
|
|
213
|
+
|
|
214
|
+
Constructors take plain config objects (`baseUrl`, `user`, `password`,
|
|
215
|
+
`rpcUrl`, …). The **one** exception is RPC URL fallback resolution:
|
|
216
|
+
`EvmChain.readRpcUrl` and `SolanaChain.readRpcUrl` will read
|
|
217
|
+
`<NAME>_RPC_URL` / `EVM_<chainId>_RPC_URL` / `SOLANA_<chainId>_RPC_URL`
|
|
218
|
+
env vars if no `rpcUrl` was passed at construction. Adding a
|
|
219
|
+
`process.env` read anywhere else in this SDK is a review-blocker.
|
|
220
|
+
|
|
221
|
+
## Adding a new chain
|
|
222
|
+
|
|
223
|
+
### New EVM chain
|
|
224
|
+
Add a factory function to [evm/evm_chains.ts](../evm/evm_chains.ts):
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
export const POLYGON_CHAIN_ID = 137;
|
|
228
|
+
export const Polygon = new EvmChain({
|
|
229
|
+
chainId: POLYGON_CHAIN_ID,
|
|
230
|
+
name: 'Polygon',
|
|
231
|
+
blockTimeSeconds: 2,
|
|
232
|
+
explorerBaseUrl: 'https://polygonscan.com',
|
|
233
|
+
nativeSymbol: 'MATIC',
|
|
234
|
+
// rpcUrl is optional. When omitted, the SDK resolves via env-var fallback:
|
|
235
|
+
// POLYGON_RPC_URL, then EVM_137_RPC_URL, then throws RpcNotConfigured.
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The RPC URL precedence for every `EvmChain` is:
|
|
240
|
+
|
|
241
|
+
1. `rpcUrl` passed at construction (wins).
|
|
242
|
+
2. `<NAME_UPPERCASE_UNDERSCORED>_RPC_URL` env var (e.g. `POLYGON_RPC_URL`).
|
|
243
|
+
3. `EVM_<chainId>_RPC_URL` env var.
|
|
244
|
+
4. Throws `ChainError(RpcNotConfigured)` on first RPC call.
|
|
245
|
+
|
|
246
|
+
Solana is similar but has a required `defaultRpcUrl` that acts as the final
|
|
247
|
+
fallback instead of throwing — see [solana.md](./solana.md).
|
|
248
|
+
|
|
249
|
+
### New UTXO chain
|
|
250
|
+
See [utxo.md](./utxo.md) — define `UtxoNetworkParams` (use `coininfo` for
|
|
251
|
+
network bytes), write a factory under the appropriate subdirectory.
|
|
252
|
+
|
|
253
|
+
### New chain family entirely (Cosmos, Aptos, etc.)
|
|
254
|
+
Add `Foo` enum value to [network_type.ts](../network_type.ts), create
|
|
255
|
+
`foo/` with `FooChain extends Chain`, `FooAddress extends Address`,
|
|
256
|
+
`UnsignedFooTransaction extends UnsignedTransaction`. Wire into
|
|
257
|
+
[address.factory.ts](../address.factory.ts).
|
|
258
|
+
|
|
259
|
+
## Testing layout
|
|
260
|
+
|
|
261
|
+
| Spec | What it verifies |
|
|
262
|
+
|---|---|
|
|
263
|
+
| [test/](../test) | Address factory + registry + cross-chain glue |
|
|
264
|
+
| [evm/test/](../evm/test) | EVM address parsing + EVM chain RPC mocks + `suggestGas` percentile / fallback / legacy paths |
|
|
265
|
+
| [solana/test/](../solana/test) | Solana address validators + `suggestPriorityFeeMicroLamports` percentile / empty-cluster |
|
|
266
|
+
| [utxo/btc/test/](../utxo/btc/test) | BTC parsing, fee math, coin selection, chain transfer round-trip with mocked providers + `suggestFeeRate` targetBlocks mapping |
|
|
267
|
+
| `<consumer>/test/integration/` | Live-network smoke checks belong in the consuming service, not this SDK |
|
|
268
|
+
|
|
269
|
+
## See also
|
|
270
|
+
|
|
271
|
+
- [evm.md](./evm.md) — EVM chains and ERC-20
|
|
272
|
+
- [utxo.md](./utxo.md) — Bitcoin / Litecoin / Dogecoin, fee estimation, asset filtering
|
|
273
|
+
- [solana.md](./solana.md) — Solana, SPL Token, Token-2022, priority-fee model
|
package/docs/evm.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# EVM chains
|
|
2
|
+
|
|
3
|
+
`EvmChain` covers any Ethereum-compatible chain (Mainnet, Arbitrum, Base,
|
|
4
|
+
BNB, Polygon, …). It wraps `ethers.js` v6 for RPC and ERC-20 transfers.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- [evm_chain.ts](../evm/evm_chain.ts) — `EvmChain extends Chain`
|
|
9
|
+
- [evm_address.ts](../evm/evm_address.ts) — `EvmAddress extends Address`; EIP-55 checksum-aware
|
|
10
|
+
- [evm_token.ts](../evm/evm_token.ts) — `EvmToken extends Token`; native + ERC-20
|
|
11
|
+
- [evm_chains.ts](../evm/evm_chains.ts) — per-chain factories (`arbitrumChain`, `baseChain`, `bnbChain`)
|
|
12
|
+
- [evm_tokens.ts](../evm/evm_tokens.ts) — predefined token instances per chain
|
|
13
|
+
- [unsigned_evm_transaction.ts](../evm/unsigned_evm_transaction.ts) — `UnsignedEvmTransaction` with `{ chainId, to, value, data, from? }`
|
|
14
|
+
|
|
15
|
+
## Constructing a chain
|
|
16
|
+
|
|
17
|
+
Each chain has a factory function that takes an optional `rpcUrl`. If
|
|
18
|
+
omitted, falls back to the `<CHAIN>_RPC_URL` env var, then to
|
|
19
|
+
`http://127.0.0.1:8545`.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { arbitrumChain, baseChain, bnbChain } from 'src/modules/chain';
|
|
23
|
+
|
|
24
|
+
const arb = arbitrumChain('https://arb1.arbitrum.io/rpc');
|
|
25
|
+
const base = baseChain(); // reads BASE_RPC_URL
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For chains without a pre-built factory, construct `EvmChain` directly:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { EvmChain } from 'src/modules/chain';
|
|
32
|
+
|
|
33
|
+
const polygon = new EvmChain({
|
|
34
|
+
chainId: 137n,
|
|
35
|
+
name: 'Polygon',
|
|
36
|
+
blockTimeSeconds: 2,
|
|
37
|
+
explorerBaseUrl: 'https://polygonscan.com',
|
|
38
|
+
nativeSymbol: 'MATIC',
|
|
39
|
+
rpcUrl: process.env.POLYGON_RPC_URL!,
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Use `EvmChain.create(init)` instead of `new EvmChain(...)` to verify the
|
|
44
|
+
RPC actually serves the chainId you declared:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const arb = await EvmChain.create({ chainId: 42161n, /* ... */ });
|
|
48
|
+
// throws ChainError(RpcChainIdMismatch) if the RPC reports a different chainId
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Tokens
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { EvmToken } from 'src/modules/chain';
|
|
55
|
+
|
|
56
|
+
const ethArb = EvmToken.native(42161n, 'ETH');
|
|
57
|
+
const usdcArb = EvmToken.erc20(
|
|
58
|
+
42161n,
|
|
59
|
+
'USDC',
|
|
60
|
+
'0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
|
|
61
|
+
6
|
|
62
|
+
);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
ERC-20 identifiers are normalized to EIP-55 checksum form. Constructing
|
|
66
|
+
with a mixed-case address that fails the checksum throws.
|
|
67
|
+
|
|
68
|
+
## Predefined tokens
|
|
69
|
+
|
|
70
|
+
[evm_tokens.ts](../evm/evm_tokens.ts) exports common stablecoins per
|
|
71
|
+
chain — e.g. `ARBITRUM_USDC`, `BASE_USDC`. Use these instead of typing
|
|
72
|
+
addresses by hand where possible.
|
|
73
|
+
|
|
74
|
+
## Addresses
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { EvmAddress } from 'src/modules/chain';
|
|
78
|
+
|
|
79
|
+
const addr = new EvmAddress('0xabc…'); // throws if not 20 bytes of hex
|
|
80
|
+
addr.canonical(); // lowercase, '0x'-prefixed
|
|
81
|
+
addr.toChecksum(); // EIP-55 mixed case
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If a caller passes a mixed-case address that doesn't match the EIP-55
|
|
85
|
+
checksum, the constructor throws — this catches typos before they reach
|
|
86
|
+
the chain.
|
|
87
|
+
|
|
88
|
+
## Transfers
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const unsigned = await arb.createTransferUnsignedTransaction({
|
|
92
|
+
from: '0xsender…',
|
|
93
|
+
to: '0xrecipient…',
|
|
94
|
+
tokenIdentifier: 'NATIVE', // or USDC's checksum address
|
|
95
|
+
amount: 1_000_000_000_000_000_000n, // 1 ETH = 1e18 wei
|
|
96
|
+
});
|
|
97
|
+
// unsigned: UnsignedEvmTransaction { chainId, to, value, data, from? }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
For ERC-20 transfers, set `tokenIdentifier` to the token's contract
|
|
101
|
+
address. The chain emits the calldata for the standard `transfer(to,
|
|
102
|
+
value)` selector against that contract:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const unsigned = await arb.createTransferUnsignedTransaction({
|
|
106
|
+
from: senderAddr,
|
|
107
|
+
to: recipientAddr,
|
|
108
|
+
tokenIdentifier: ARBITRUM_USDC.identifier,
|
|
109
|
+
amount: 5_000_000n, // 5 USDC (6 decimals)
|
|
110
|
+
});
|
|
111
|
+
// unsigned.to === ARBITRUM_USDC.identifier
|
|
112
|
+
// unsigned.data === '0xa9059cbb...' (transfer(address,uint256) selector + args)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Sign with whatever you'd normally use for EVM (ethers wallet, hardware
|
|
116
|
+
wallet, MPC service). The chain module never touches keys.
|
|
117
|
+
|
|
118
|
+
## Balances
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const ethBalance = await arb.getBalance('0xabc…', 'NATIVE');
|
|
122
|
+
const usdcBalance = await arb.getBalance('0xabc…', ARBITRUM_USDC.identifier);
|
|
123
|
+
// returns bigint in wei / smallest token unit
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
For unknown ERC-20s, the chain auto-resolves `decimals()` and `symbol()`
|
|
127
|
+
from the contract on first balance call and caches the result.
|
|
128
|
+
|
|
129
|
+
## Transaction status
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const status = await arb.getTransactionStatus(txHash);
|
|
133
|
+
// EvmTransactionStatus (extends TransactionStatus):
|
|
134
|
+
// status: 'Pending' | 'Success' | 'Failed' | 'NotFound'
|
|
135
|
+
// inclusionAt: Date | null
|
|
136
|
+
// error: TransactionErrorInfo | null
|
|
137
|
+
// balanceChanges: NestedBalanceChanges | null
|
|
138
|
+
// = Map<walletLowercase, Map<assetHash, {token, change}>>
|
|
139
|
+
// logs: EvmParsedTransactionLog[] | null
|
|
140
|
+
// fees: EvmTransactionGasFees | null
|
|
141
|
+
// .gasLimitUsed, .effectiveGasPrice, .totalGasInWei
|
|
142
|
+
// .l1FeeWei (OP-stack rollups only)
|
|
143
|
+
// .totalNativeDebitWei = totalGasInWei + (l1FeeWei ?? 0)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
For ERC-20 transfers, `decodeBalanceChanges` decodes `Transfer(from, to, value)`
|
|
147
|
+
logs into the nested balance-change map. On OP-stack rollups
|
|
148
|
+
(Optimism/Base/Unichain/WorldChain/Boba/Sonic) the sender's native debit
|
|
149
|
+
includes the receipt's `l1Fee` — this is exposed on `fees.l1FeeWei` and
|
|
150
|
+
folded into the sender's `balanceChanges` row so the emitted delta
|
|
151
|
+
matches the on-chain net.
|
|
152
|
+
|
|
153
|
+
`Failed` status carries `balanceChanges: null` per Python parity — the
|
|
154
|
+
gas that was burned is still on `fees.totalNativeDebitWei` and consumers
|
|
155
|
+
reconstruct sender debits from there for reverted transactions.
|
|
156
|
+
|
|
157
|
+
## Registry / wiring
|
|
158
|
+
|
|
159
|
+
The `ChainRegistry` auto-loads EVM chains from `config.yaml` on module
|
|
160
|
+
init — see [chain.config.ts](../chain.config.ts) for the schema. Each
|
|
161
|
+
entry needs at least one provider:
|
|
162
|
+
|
|
163
|
+
```yaml
|
|
164
|
+
chains:
|
|
165
|
+
"42161":
|
|
166
|
+
name: Arbitrum
|
|
167
|
+
blockTimeSeconds: 0.25
|
|
168
|
+
explorerBaseUrl: https://arbiscan.io
|
|
169
|
+
nativeSymbol: ETH
|
|
170
|
+
providers:
|
|
171
|
+
- name: ankr
|
|
172
|
+
url: https://rpc.ankr.com/arbitrum/${ANKR_API_KEY}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
To register at runtime instead:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
@Injectable()
|
|
179
|
+
export class MyService {
|
|
180
|
+
constructor(private readonly chains: ChainRegistry) {}
|
|
181
|
+
|
|
182
|
+
init() {
|
|
183
|
+
const polygon = new EvmChain({ /* … */ });
|
|
184
|
+
this.chains.register(polygon);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Address validation
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
arb.validateWalletAddress('0xabc…'); // true / false
|
|
193
|
+
arb.validateTokenIdentifier('NATIVE'); // true
|
|
194
|
+
arb.validateTokenIdentifier('0xUSDC…'); // true if valid EVM address
|
|
195
|
+
arb.validateTokenIdentifier('not-hex'); // false
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Cross-chain address parsing
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { addressFor } from 'src/modules/chain';
|
|
202
|
+
|
|
203
|
+
const a = addressFor(42161, '0xabc…'); // EvmAddress
|
|
204
|
+
const b = addressFor(-1, 'bc1q…'); // BtcAddress (BTC registered → -1)
|
|
205
|
+
```
|