@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.
Files changed (296) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/abstract_gas_pricing.d.ts +31 -0
  4. package/dist/abstract_gas_pricing.d.ts.map +1 -0
  5. package/dist/abstract_gas_pricing.js +22 -0
  6. package/dist/abstract_gas_pricing.js.map +1 -0
  7. package/dist/address.d.ts +10 -0
  8. package/dist/address.d.ts.map +1 -0
  9. package/dist/address.factory.d.ts +26 -0
  10. package/dist/address.factory.d.ts.map +1 -0
  11. package/dist/address.factory.js +75 -0
  12. package/dist/address.factory.js.map +1 -0
  13. package/dist/address.js +13 -0
  14. package/dist/address.js.map +1 -0
  15. package/dist/address_field.decorator.d.ts +17 -0
  16. package/dist/address_field.decorator.d.ts.map +1 -0
  17. package/dist/address_field.decorator.js +34 -0
  18. package/dist/address_field.decorator.js.map +1 -0
  19. package/dist/chain.base.d.ts +79 -0
  20. package/dist/chain.base.d.ts.map +1 -0
  21. package/dist/chain.base.js +113 -0
  22. package/dist/chain.base.js.map +1 -0
  23. package/dist/chain_ids.d.ts +122 -0
  24. package/dist/chain_ids.d.ts.map +1 -0
  25. package/dist/chain_ids.js +193 -0
  26. package/dist/chain_ids.js.map +1 -0
  27. package/dist/errors.d.ts +42 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +62 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/evm/evm_address.d.ts +11 -0
  32. package/dist/evm/evm_address.d.ts.map +1 -0
  33. package/dist/evm/evm_address.js +56 -0
  34. package/dist/evm/evm_address.js.map +1 -0
  35. package/dist/evm/evm_chain.d.ts +123 -0
  36. package/dist/evm/evm_chain.d.ts.map +1 -0
  37. package/dist/evm/evm_chain.js +685 -0
  38. package/dist/evm/evm_chain.js.map +1 -0
  39. package/dist/evm/evm_chains.d.ts +83 -0
  40. package/dist/evm/evm_chains.d.ts.map +1 -0
  41. package/dist/evm/evm_chains.js +493 -0
  42. package/dist/evm/evm_chains.js.map +1 -0
  43. package/dist/evm/evm_gas_estimate.d.ts +45 -0
  44. package/dist/evm/evm_gas_estimate.d.ts.map +1 -0
  45. package/dist/evm/evm_gas_estimate.js +70 -0
  46. package/dist/evm/evm_gas_estimate.js.map +1 -0
  47. package/dist/evm/evm_gas_pricing.d.ts +40 -0
  48. package/dist/evm/evm_gas_pricing.d.ts.map +1 -0
  49. package/dist/evm/evm_gas_pricing.js +56 -0
  50. package/dist/evm/evm_gas_pricing.js.map +1 -0
  51. package/dist/evm/evm_token.d.ts +7 -0
  52. package/dist/evm/evm_token.d.ts.map +1 -0
  53. package/dist/evm/evm_token.js +22 -0
  54. package/dist/evm/evm_token.js.map +1 -0
  55. package/dist/evm/evm_tokens.d.ts +81 -0
  56. package/dist/evm/evm_tokens.d.ts.map +1 -0
  57. package/dist/evm/evm_tokens.js +104 -0
  58. package/dist/evm/evm_tokens.js.map +1 -0
  59. package/dist/evm/evm_transaction_status.d.ts +108 -0
  60. package/dist/evm/evm_transaction_status.d.ts.map +1 -0
  61. package/dist/evm/evm_transaction_status.js +177 -0
  62. package/dist/evm/evm_transaction_status.js.map +1 -0
  63. package/dist/evm/index.d.ts +10 -0
  64. package/dist/evm/index.d.ts.map +1 -0
  65. package/dist/evm/index.js +10 -0
  66. package/dist/evm/index.js.map +1 -0
  67. package/dist/evm/unsigned_evm_transaction.d.ts +19 -0
  68. package/dist/evm/unsigned_evm_transaction.d.ts.map +1 -0
  69. package/dist/evm/unsigned_evm_transaction.js +18 -0
  70. package/dist/evm/unsigned_evm_transaction.js.map +1 -0
  71. package/dist/index.d.ts +18 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +18 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/is_address.decorator.d.ts +3 -0
  76. package/dist/is_address.decorator.d.ts.map +1 -0
  77. package/dist/is_address.decorator.js +51 -0
  78. package/dist/is_address.decorator.js.map +1 -0
  79. package/dist/network_type.d.ts +73 -0
  80. package/dist/network_type.d.ts.map +1 -0
  81. package/dist/network_type.js +144 -0
  82. package/dist/network_type.js.map +1 -0
  83. package/dist/priority.d.ts +23 -0
  84. package/dist/priority.d.ts.map +1 -0
  85. package/dist/priority.js +23 -0
  86. package/dist/priority.js.map +1 -0
  87. package/dist/solana/index.d.ts +9 -0
  88. package/dist/solana/index.d.ts.map +1 -0
  89. package/dist/solana/index.js +9 -0
  90. package/dist/solana/index.js.map +1 -0
  91. package/dist/solana/solana_address.d.ts +20 -0
  92. package/dist/solana/solana_address.d.ts.map +1 -0
  93. package/dist/solana/solana_address.js +45 -0
  94. package/dist/solana/solana_address.js.map +1 -0
  95. package/dist/solana/solana_chain.d.ts +213 -0
  96. package/dist/solana/solana_chain.d.ts.map +1 -0
  97. package/dist/solana/solana_chain.js +704 -0
  98. package/dist/solana/solana_chain.js.map +1 -0
  99. package/dist/solana/solana_chains.d.ts +24 -0
  100. package/dist/solana/solana_chains.d.ts.map +1 -0
  101. package/dist/solana/solana_chains.js +84 -0
  102. package/dist/solana/solana_chains.js.map +1 -0
  103. package/dist/solana/solana_gas_pricing.d.ts +30 -0
  104. package/dist/solana/solana_gas_pricing.d.ts.map +1 -0
  105. package/dist/solana/solana_gas_pricing.js +26 -0
  106. package/dist/solana/solana_gas_pricing.js.map +1 -0
  107. package/dist/solana/solana_token.d.ts +19 -0
  108. package/dist/solana/solana_token.d.ts.map +1 -0
  109. package/dist/solana/solana_token.js +34 -0
  110. package/dist/solana/solana_token.js.map +1 -0
  111. package/dist/solana/solana_tokens.d.ts +22 -0
  112. package/dist/solana/solana_tokens.d.ts.map +1 -0
  113. package/dist/solana/solana_tokens.js +26 -0
  114. package/dist/solana/solana_tokens.js.map +1 -0
  115. package/dist/solana/solana_transaction_status.d.ts +59 -0
  116. package/dist/solana/solana_transaction_status.d.ts.map +1 -0
  117. package/dist/solana/solana_transaction_status.js +144 -0
  118. package/dist/solana/solana_transaction_status.js.map +1 -0
  119. package/dist/solana/unsigned_solana_transaction.d.ts +31 -0
  120. package/dist/solana/unsigned_solana_transaction.d.ts.map +1 -0
  121. package/dist/solana/unsigned_solana_transaction.js +26 -0
  122. package/dist/solana/unsigned_solana_transaction.js.map +1 -0
  123. package/dist/token.d.ts +35 -0
  124. package/dist/token.d.ts.map +1 -0
  125. package/dist/token.js +62 -0
  126. package/dist/token.js.map +1 -0
  127. package/dist/ton/index.d.ts +2 -0
  128. package/dist/ton/index.d.ts.map +1 -0
  129. package/dist/ton/index.js +2 -0
  130. package/dist/ton/index.js.map +1 -0
  131. package/dist/ton/ton_address.d.ts +10 -0
  132. package/dist/ton/ton_address.d.ts.map +1 -0
  133. package/dist/ton/ton_address.js +53 -0
  134. package/dist/ton/ton_address.js.map +1 -0
  135. package/dist/transaction_status.d.ts +140 -0
  136. package/dist/transaction_status.d.ts.map +1 -0
  137. package/dist/transaction_status.js +271 -0
  138. package/dist/transaction_status.js.map +1 -0
  139. package/dist/unsigned_transaction.d.ts +7 -0
  140. package/dist/unsigned_transaction.d.ts.map +1 -0
  141. package/dist/unsigned_transaction.js +9 -0
  142. package/dist/unsigned_transaction.js.map +1 -0
  143. package/dist/utxo/btc/btc_address.d.ts +15 -0
  144. package/dist/utxo/btc/btc_address.d.ts.map +1 -0
  145. package/dist/utxo/btc/btc_address.js +39 -0
  146. package/dist/utxo/btc/btc_address.js.map +1 -0
  147. package/dist/utxo/btc/btc_chain.d.ts +34 -0
  148. package/dist/utxo/btc/btc_chain.d.ts.map +1 -0
  149. package/dist/utxo/btc/btc_chain.js +62 -0
  150. package/dist/utxo/btc/btc_chain.js.map +1 -0
  151. package/dist/utxo/btc/btc_chains.d.ts +25 -0
  152. package/dist/utxo/btc/btc_chains.d.ts.map +1 -0
  153. package/dist/utxo/btc/btc_chains.js +43 -0
  154. package/dist/utxo/btc/btc_chains.js.map +1 -0
  155. package/dist/utxo/btc/btc_token.d.ts +6 -0
  156. package/dist/utxo/btc/btc_token.d.ts.map +1 -0
  157. package/dist/utxo/btc/btc_token.js +11 -0
  158. package/dist/utxo/btc/btc_token.js.map +1 -0
  159. package/dist/utxo/btc/index.d.ts +14 -0
  160. package/dist/utxo/btc/index.d.ts.map +1 -0
  161. package/dist/utxo/btc/index.js +14 -0
  162. package/dist/utxo/btc/index.js.map +1 -0
  163. package/dist/utxo/btc/network_params.d.ts +50 -0
  164. package/dist/utxo/btc/network_params.d.ts.map +1 -0
  165. package/dist/utxo/btc/network_params.js +188 -0
  166. package/dist/utxo/btc/network_params.js.map +1 -0
  167. package/dist/utxo/btc/tools/asset_outpoint.d.ts +7 -0
  168. package/dist/utxo/btc/tools/asset_outpoint.d.ts.map +1 -0
  169. package/dist/utxo/btc/tools/asset_outpoint.js +12 -0
  170. package/dist/utxo/btc/tools/asset_outpoint.js.map +1 -0
  171. package/dist/utxo/btc/tools/bitcoin_core.tool.d.ts +41 -0
  172. package/dist/utxo/btc/tools/bitcoin_core.tool.d.ts.map +1 -0
  173. package/dist/utxo/btc/tools/bitcoin_core.tool.js +172 -0
  174. package/dist/utxo/btc/tools/bitcoin_core.tool.js.map +1 -0
  175. package/dist/utxo/btc/tools/esplora.tool.d.ts +27 -0
  176. package/dist/utxo/btc/tools/esplora.tool.d.ts.map +1 -0
  177. package/dist/utxo/btc/tools/esplora.tool.js +113 -0
  178. package/dist/utxo/btc/tools/esplora.tool.js.map +1 -0
  179. package/dist/utxo/btc/tools/inscription_index.d.ts +6 -0
  180. package/dist/utxo/btc/tools/inscription_index.d.ts.map +1 -0
  181. package/dist/utxo/btc/tools/inscription_index.js +2 -0
  182. package/dist/utxo/btc/tools/inscription_index.js.map +1 -0
  183. package/dist/utxo/btc/tools/ordiscan.tool.d.ts +19 -0
  184. package/dist/utxo/btc/tools/ordiscan.tool.d.ts.map +1 -0
  185. package/dist/utxo/btc/tools/ordiscan.tool.js +53 -0
  186. package/dist/utxo/btc/tools/ordiscan.tool.js.map +1 -0
  187. package/dist/utxo/btc/tools/rare_sat_index.d.ts +6 -0
  188. package/dist/utxo/btc/tools/rare_sat_index.d.ts.map +1 -0
  189. package/dist/utxo/btc/tools/rare_sat_index.js +2 -0
  190. package/dist/utxo/btc/tools/rare_sat_index.js.map +1 -0
  191. package/dist/utxo/btc/tools/rune_index.d.ts +6 -0
  192. package/dist/utxo/btc/tools/rune_index.d.ts.map +1 -0
  193. package/dist/utxo/btc/tools/rune_index.js +2 -0
  194. package/dist/utxo/btc/tools/rune_index.js.map +1 -0
  195. package/dist/utxo/btc/tools/unisat.tool.d.ts +18 -0
  196. package/dist/utxo/btc/tools/unisat.tool.d.ts.map +1 -0
  197. package/dist/utxo/btc/tools/unisat.tool.js +49 -0
  198. package/dist/utxo/btc/tools/unisat.tool.js.map +1 -0
  199. package/dist/utxo/coin_selection.d.ts +29 -0
  200. package/dist/utxo/coin_selection.d.ts.map +1 -0
  201. package/dist/utxo/coin_selection.js +156 -0
  202. package/dist/utxo/coin_selection.js.map +1 -0
  203. package/dist/utxo/doge/doge_chains.d.ts +17 -0
  204. package/dist/utxo/doge/doge_chains.d.ts.map +1 -0
  205. package/dist/utxo/doge/doge_chains.js +28 -0
  206. package/dist/utxo/doge/doge_chains.js.map +1 -0
  207. package/dist/utxo/doge/index.d.ts +3 -0
  208. package/dist/utxo/doge/index.d.ts.map +1 -0
  209. package/dist/utxo/doge/index.js +3 -0
  210. package/dist/utxo/doge/index.js.map +1 -0
  211. package/dist/utxo/doge/network_params.d.ts +4 -0
  212. package/dist/utxo/doge/network_params.d.ts.map +1 -0
  213. package/dist/utxo/doge/network_params.js +24 -0
  214. package/dist/utxo/doge/network_params.js.map +1 -0
  215. package/dist/utxo/ecc.d.ts +2 -0
  216. package/dist/utxo/ecc.d.ts.map +1 -0
  217. package/dist/utxo/ecc.js +11 -0
  218. package/dist/utxo/ecc.js.map +1 -0
  219. package/dist/utxo/fee.d.ts +12 -0
  220. package/dist/utxo/fee.d.ts.map +1 -0
  221. package/dist/utxo/fee.js +51 -0
  222. package/dist/utxo/fee.js.map +1 -0
  223. package/dist/utxo/index.d.ts +20 -0
  224. package/dist/utxo/index.d.ts.map +1 -0
  225. package/dist/utxo/index.js +20 -0
  226. package/dist/utxo/index.js.map +1 -0
  227. package/dist/utxo/ltc/index.d.ts +3 -0
  228. package/dist/utxo/ltc/index.d.ts.map +1 -0
  229. package/dist/utxo/ltc/index.js +3 -0
  230. package/dist/utxo/ltc/index.js.map +1 -0
  231. package/dist/utxo/ltc/ltc_chains.d.ts +17 -0
  232. package/dist/utxo/ltc/ltc_chains.d.ts.map +1 -0
  233. package/dist/utxo/ltc/ltc_chains.js +28 -0
  234. package/dist/utxo/ltc/ltc_chains.js.map +1 -0
  235. package/dist/utxo/ltc/network_params.d.ts +4 -0
  236. package/dist/utxo/ltc/network_params.d.ts.map +1 -0
  237. package/dist/utxo/ltc/network_params.js +27 -0
  238. package/dist/utxo/ltc/network_params.js.map +1 -0
  239. package/dist/utxo/script.d.ts +16 -0
  240. package/dist/utxo/script.d.ts.map +1 -0
  241. package/dist/utxo/script.js +52 -0
  242. package/dist/utxo/script.js.map +1 -0
  243. package/dist/utxo/tools/broadcaster.d.ts +6 -0
  244. package/dist/utxo/tools/broadcaster.d.ts.map +1 -0
  245. package/dist/utxo/tools/broadcaster.js +2 -0
  246. package/dist/utxo/tools/broadcaster.js.map +1 -0
  247. package/dist/utxo/tools/chain_tip_provider.d.ts +5 -0
  248. package/dist/utxo/tools/chain_tip_provider.d.ts.map +1 -0
  249. package/dist/utxo/tools/chain_tip_provider.js +2 -0
  250. package/dist/utxo/tools/chain_tip_provider.js.map +1 -0
  251. package/dist/utxo/tools/fee_estimator.d.ts +6 -0
  252. package/dist/utxo/tools/fee_estimator.d.ts.map +1 -0
  253. package/dist/utxo/tools/fee_estimator.js +2 -0
  254. package/dist/utxo/tools/fee_estimator.js.map +1 -0
  255. package/dist/utxo/tools/raw_transaction_provider.d.ts +8 -0
  256. package/dist/utxo/tools/raw_transaction_provider.d.ts.map +1 -0
  257. package/dist/utxo/tools/raw_transaction_provider.js +2 -0
  258. package/dist/utxo/tools/raw_transaction_provider.js.map +1 -0
  259. package/dist/utxo/tools/utxo_provider.d.ts +7 -0
  260. package/dist/utxo/tools/utxo_provider.d.ts.map +1 -0
  261. package/dist/utxo/tools/utxo_provider.js +2 -0
  262. package/dist/utxo/tools/utxo_provider.js.map +1 -0
  263. package/dist/utxo/unsigned_utxo_transaction.d.ts +32 -0
  264. package/dist/utxo/unsigned_utxo_transaction.d.ts.map +1 -0
  265. package/dist/utxo/unsigned_utxo_transaction.js +32 -0
  266. package/dist/utxo/unsigned_utxo_transaction.js.map +1 -0
  267. package/dist/utxo/utxo.d.ts +43 -0
  268. package/dist/utxo/utxo.d.ts.map +1 -0
  269. package/dist/utxo/utxo.js +2 -0
  270. package/dist/utxo/utxo.js.map +1 -0
  271. package/dist/utxo/utxo_chain.d.ts +102 -0
  272. package/dist/utxo/utxo_chain.d.ts.map +1 -0
  273. package/dist/utxo/utxo_chain.js +524 -0
  274. package/dist/utxo/utxo_chain.js.map +1 -0
  275. package/dist/utxo/utxo_chains.d.ts +29 -0
  276. package/dist/utxo/utxo_chains.d.ts.map +1 -0
  277. package/dist/utxo/utxo_chains.js +63 -0
  278. package/dist/utxo/utxo_chains.js.map +1 -0
  279. package/dist/utxo/utxo_gas_pricing.d.ts +22 -0
  280. package/dist/utxo/utxo_gas_pricing.d.ts.map +1 -0
  281. package/dist/utxo/utxo_gas_pricing.js +20 -0
  282. package/dist/utxo/utxo_gas_pricing.js.map +1 -0
  283. package/dist/utxo/utxo_network_params.d.ts +25 -0
  284. package/dist/utxo/utxo_network_params.d.ts.map +1 -0
  285. package/dist/utxo/utxo_network_params.js +15 -0
  286. package/dist/utxo/utxo_network_params.js.map +1 -0
  287. package/dist/utxo/utxo_transaction_status.d.ts +73 -0
  288. package/dist/utxo/utxo_transaction_status.d.ts.map +1 -0
  289. package/dist/utxo/utxo_transaction_status.js +89 -0
  290. package/dist/utxo/utxo_transaction_status.js.map +1 -0
  291. package/docs/PUBLISHING.md +119 -0
  292. package/docs/README.md +273 -0
  293. package/docs/evm.md +205 -0
  294. package/docs/solana.md +220 -0
  295. package/docs/utxo.md +364 -0
  296. 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
+ ```