@getomnichain/omnichain 0.3.6 → 0.5.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.
- package/CHANGELOG.md +80 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/rango_chain_names.js +209 -0
- package/dist/cjs/rango_chain_names.js.map +1 -0
- package/dist/cjs/solana/solana_chain.js +89 -25
- package/dist/cjs/solana/solana_chain.js.map +1 -1
- package/dist/cjs/solana/solana_transaction_status.js +39 -3
- package/dist/cjs/solana/solana_transaction_status.js.map +1 -1
- package/dist/cjs/utxo/btc/tools/bitcoin_core.tool.js +129 -0
- package/dist/cjs/utxo/btc/tools/bitcoin_core.tool.js.map +1 -1
- package/dist/cjs/utxo/btc/tools/esplora.tool.js +84 -0
- package/dist/cjs/utxo/btc/tools/esplora.tool.js.map +1 -1
- package/dist/cjs/utxo/utxo_chain.js +18 -49
- package/dist/cjs/utxo/utxo_chain.js.map +1 -1
- package/dist/cjs/utxo/utxo_transaction_status.js +20 -16
- package/dist/cjs/utxo/utxo_transaction_status.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/rango_chain_names.d.ts +76 -0
- package/dist/esm/rango_chain_names.d.ts.map +1 -0
- package/dist/esm/rango_chain_names.js +171 -0
- package/dist/esm/rango_chain_names.js.map +1 -0
- package/dist/esm/solana/solana_chain.d.ts.map +1 -1
- package/dist/esm/solana/solana_chain.js +90 -26
- package/dist/esm/solana/solana_chain.js.map +1 -1
- package/dist/esm/solana/solana_transaction_status.d.ts +40 -2
- package/dist/esm/solana/solana_transaction_status.d.ts.map +1 -1
- package/dist/esm/solana/solana_transaction_status.js +38 -2
- package/dist/esm/solana/solana_transaction_status.js.map +1 -1
- package/dist/esm/utxo/btc/tools/bitcoin_core.tool.d.ts +4 -1
- package/dist/esm/utxo/btc/tools/bitcoin_core.tool.d.ts.map +1 -1
- package/dist/esm/utxo/btc/tools/bitcoin_core.tool.js +129 -0
- package/dist/esm/utxo/btc/tools/bitcoin_core.tool.js.map +1 -1
- package/dist/esm/utxo/btc/tools/esplora.tool.d.ts +2 -1
- package/dist/esm/utxo/btc/tools/esplora.tool.d.ts.map +1 -1
- package/dist/esm/utxo/btc/tools/esplora.tool.js +84 -0
- package/dist/esm/utxo/btc/tools/esplora.tool.js.map +1 -1
- package/dist/esm/utxo/tools/raw_transaction_provider.d.ts +2 -1
- package/dist/esm/utxo/tools/raw_transaction_provider.d.ts.map +1 -1
- package/dist/esm/utxo/utxo.d.ts +27 -0
- package/dist/esm/utxo/utxo.d.ts.map +1 -1
- package/dist/esm/utxo/utxo_chain.d.ts.map +1 -1
- package/dist/esm/utxo/utxo_chain.js +18 -49
- package/dist/esm/utxo/utxo_chain.js.map +1 -1
- package/dist/esm/utxo/utxo_transaction_status.d.ts +21 -16
- package/dist/esm/utxo/utxo_transaction_status.d.ts.map +1 -1
- package/dist/esm/utxo/utxo_transaction_status.js +20 -16
- package/dist/esm/utxo/utxo_transaction_status.js.map +1 -1
- package/docs/README.md +1 -0
- package/docs/solana.md +23 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,77 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## [0.5.1] — 2026-09-22
|
|
12
|
+
|
|
13
|
+
Surfaces Solana finality on `SolanaTransactionStatus` (RIN-296, SDK half of RIN-285). Non-blocking pollers can now read one status snapshot and see how close to finality a tx is, matching the pattern EVM (`blockNumber` + tip derivation) and UTXO (RIN-195's carried `confirmations`) already use.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **`SolanaTransactionStatus.slot: number | null`** — from `getTransaction`'s `slot` on the success path; from `getSignatureStatus`'s `slot` on the ledger-pruned fallback.
|
|
18
|
+
- **`SolanaTransactionStatus.confirmations: number | null`** — the raw RPC count for non-final states, normalised to the exported `SOLANA_FINALIZED_CONFIRMATIONS = 32` when the RPC returns `null` (rooted, per the Solana RPC contract). `null` when the sig-status read was unavailable.
|
|
19
|
+
- **`SolanaTransactionStatus.confirmationStatus: 'processed' | 'confirmed' | 'finalized' | null`** — the raw RPC string.
|
|
20
|
+
- **`SolanaTransactionStatus.signers: readonly string[]`** — base58 signer keys in message order (the first `header.numRequiredSignatures` `staticAccountKeys`). Mirrors omnichain-py `signer_keys = account_keys[: header.num_required_signatures]` at `impl/solana/base.py:1175`. Empty on the ledger-pruned fallback and on `notFound`.
|
|
21
|
+
- **`SOLANA_FINALIZED_CONFIRMATIONS = 32`** — exported constant.
|
|
22
|
+
- **`SolanaConfirmationStatus`** — union of the three RPC states.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **`SolanaChain.getTransactionStatus`** fires `getTransaction` and `getSignatureStatus` in parallel via `Promise.allSettled` (RIN-296 D1) — the common Success case pays one round-trip of latency instead of two. Sig-status failure on the Success path degrades the four new fields to `null`/`[]` rather than failing the whole read (D2). The ledger-pruned Pending branch carries whatever finality it has (D3).
|
|
27
|
+
|
|
28
|
+
### Note
|
|
29
|
+
|
|
30
|
+
- No `blockNumber` field. Solana slots are not block heights (skipped slots exist) and `getChainTipHeight` returns a block height (0.3.3 fix); consumers deriving finality from `blockNumber` on other families must gate Solana on `confirmations`.
|
|
31
|
+
- **Additive only.** All new fields optional in the init with safe defaults (`null` / `[]`); every pre-0.5.1 `SolanaTransactionStatus.successful/failed/pending/notFound` call site compiles unchanged.
|
|
32
|
+
- **Note on version bump.** RIN-285 R4 specifies a **minor** bump. This release ships as a **patch** (0.5.0 → 0.5.1) per requester decision. Under SemVer this is a documented divergence — the change is additive to the public surface (new optional fields, new exported constant/type) and a minor bump would be more strictly correct.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## [0.5.0] — 2026-09-21
|
|
37
|
+
|
|
38
|
+
Adds a single canonical bidirectional map between omnichain chain ids and Rango's uppercase `blockchains[].name` identifiers. Removes the need for downstream consumers that talk to Rango to maintain their own partial copies of the mapping.
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **`CHAIN_ID_TO_RANGO_NAME: ReadonlyMap<number, string>`** on `rango_chain_names.ts` — primary map for every omnichain chain that appears in Rango's `/meta`. Sourced from `GET https://public-api.rango.exchange/basic/meta`, snapshot dated 2026-09-21.
|
|
43
|
+
- **`RANGO_NAME_TO_CHAIN_ID: ReadonlyMap<string, number>`** — the inverse map, keyed by uppercase name.
|
|
44
|
+
- **`NOT_ON_RANGO: ReadonlySet<number>`** — explicit "not on Rango" decisions for testnets + mainnet chains omnichain declares but Rango's `/meta` does not carry (e.g. `MANTLE`, `OPBNB`, `SEI_EVM`, `WORLD_CHAIN`, `WANCHAIN`, `ABSTRACT`, `INK`, `BOB`, `ZKLINK_NOVA`, `TEMPO`). Each id must appear in the map or here; the coverage test fails otherwise, and `prepublishOnly` now runs the suite so an undecided chain id cannot ship.
|
|
45
|
+
- **`rangoNameForChainId(chainId)`** and **`chainIdForRangoName(name)`** — accessors. The name-side normalises `trim() + toUpperCase()` and returns `undefined` for any non-string input, so callers looping over untrusted Rango feed rows can skip bad entries instead of aborting.
|
|
46
|
+
- **Fixture** `test/fixtures/rango_meta_blockchains.json` — compact wrapper `{fetchedAt, source, blockchains: [{name, chainId, type}, …]}` of Rango's `/meta`, ~8 KB. The map's substance is verified against it: every mapped name exists, every positive-id row's parsed `chainId` (hex `0x…` or decimal) equals the omnichain id, every non-positive mapped id lives on the family predicate the fixture type implies (`SOLANA→isSolana`, `SUI→isSui`, `XRPL→isXrpl`, `STELLAR→isStellar`, `TON→isTon`, `TRANSFER→isUtxo`; TRON is positive and covered by the hex check), and no `NOT_ON_RANGO` id is actually present in the fixture. Refreshing the map becomes "replace fixture, rerun tests."
|
|
47
|
+
- **Tests** in `test/rango_chain_names.spec.ts`: bijection, no-duplicate names, name-format invariant (`/^[A-Z0-9_]+$/`), critical anchors (BTC=-1, SOLANA=-2000, ETH, BSC, ARBITRUM, BASE, LINEA, AVAX_CCHAIN, LTC, DOGE, BCH, TRON), case/whitespace normalisation, unknown-and-non-string handling, disjoint map/`NOT_ON_RANGO`, exhaustive `chain_ids.ts` coverage, and the three fixture-backed feed checks.
|
|
48
|
+
|
|
49
|
+
### Note
|
|
50
|
+
|
|
51
|
+
- Aliases (alternate Rango spellings for the same chain) are out of scope. If a consumer hits one in the wild, opening a follow-up to add it is a separate decision.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## [0.4.0] — 2026-09-16
|
|
56
|
+
|
|
57
|
+
Brings TS `UtxoTransactionStatus` in line with the Python SDK's already-shipped UTXO shape: per-input `{address, value}` surfaced, and `balanceChanges` becomes net per-address (matching EVM/Solana). Every hydration detail lives in the per-tool provider, mirroring Python's `AbstractUtxoTool.get_tx`.
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- **`UtxoTransaction` interface** on `utxo/utxo.ts` — the intermediate type returned by the tool layer, mirroring Python's `class UtxoTransaction`: `{ inputs, outputs, netChangesHr: Record<string, Decimal>, size, vsize, confirmations, confirmationDatetime }`.
|
|
62
|
+
- **`UtxoTransactionInput` type** — `{ txid, vout, scriptPubkeyHex, address, valueSats, valueBtcHr, coinbase? }`. Both `valueSats` (bigint, `@deprecated`) and `valueBtcHr` (Decimal) are populated this release; the deprecated `valueSats` is scheduled for removal in a future major, with `valueBtcHr` renamed → `value` at that point to align with Python. Mirrors the `AssetBalanceChange.balanceChangeMr`/`Hr` idiom.
|
|
63
|
+
- **`UtxoRawTransactionProvider.getTransactionWithInputs(txid)`** — new provider method returning a fully-hydrated `UtxoTransaction`. Every adapter is responsible for hydration per its API, matching Python's per-tool approach.
|
|
64
|
+
- **`EsploraTool.getTransactionWithInputs`** — parses inline `vin[*].prevout` from Esplora's `/tx/{txid}` response; zero extra RPCs.
|
|
65
|
+
- **`BitcoinCoreTool.getTransactionWithInputs`** — hydrates via a hybrid path that works on every Core version: whichever verbosity the caller requests (`bitcoinCoreVerbose?: 1 | 2`, default `1`), the adapter uses any `vin[*].prevout` the response inlines and runs a batched `getrawtransaction verbose=1` walk for the vins that do not carry a prevout (older Core, mempool txs, pruned-block txs, Core < 25). Zero extra RPCs when block undo data is available; N extra otherwise. Matches Python's `impl/utxo/tools/bitcoin_core.py:453` cost profile on the fallback path. Setting `bitcoinCoreVerbose: 2` on Core ≥ 25 is the recommended production configuration for confirmed-heavy status polling.
|
|
66
|
+
- **`UtxoTransactionStatus.inputs`** — `readonly UtxoTransactionInput[] | null`. Populated when the provider tool hydrated every non-coinbase input; `null` (with `inputsUnresolvedReason: 'pending'`) on `Pending` statuses whose provider skips hydration.
|
|
67
|
+
- **`UtxoTransactionStatus.inputsUnresolvedReason`** — `'provider_error' | 'parent_missing' | 'pending' | null`.
|
|
68
|
+
- **`UtxoInputsUnresolvedReason`** type on `utxo/utxo.ts`.
|
|
69
|
+
- **8 new RIN-226 unit tests** covering: inputs order + dual units, net semantics on a mixed-owner tx, self-send fee-only debit, no legacy accessor, hydration failure → thrown `RpcError`, coinbase input shape, `Pending` + hydration unavailable, `valueSats`/`valueBtcHr` agreement.
|
|
70
|
+
|
|
71
|
+
### Changed (breaking on the UTXO surface)
|
|
72
|
+
|
|
73
|
+
- **`UtxoTransactionStatus.balanceChanges`** is now **net per-address** (from `UtxoTransaction.netChangesHr`) on `Success` statuses — inputs debited, outputs credited. Previously gross output credits per receiving address. Matches `EvmTransactionStatus`, `SolanaTransactionStatus`, Python's UTXO status, and Clydner's BlockCypher parser.
|
|
74
|
+
- On confirmed + hydration failure, `UtxoChain.getTransactionStatus` **throws `ChainError(RpcError)`** rather than returning a `Success` with `null` balance changes (preserves the base `TransactionStatus(Success)` invariant that requires `balanceChanges` present).
|
|
75
|
+
- Rewrote the `UtxoTransactionStatus` class docstring — the "gross output credits" caveat and the "`inputs` … comes back in the 2C UTXO port" TODO are removed.
|
|
76
|
+
- No `outputCreditsByAddress()` accessor and no `grossOutputCreditsLegacy` field: deposit detectors that only need who-got-credited iterate `status.outputs[]` directly (Python has neither).
|
|
77
|
+
|
|
78
|
+
### Note
|
|
79
|
+
|
|
80
|
+
- Migration: consumers that summed positive UTXO credits from `balanceChanges` need to switch to iterating `status.outputs[]` per address. A hot-wallet withdrawal that previously read as a large positive credit (from the change output) now reads as a net debit for the fee — the correct sign.
|
|
81
|
+
- Companion cards to file: rango-intents BTC-8 workaround deletion (drop `enrichInputTxFromChain` and the parent-tx resolver; consume `status.inputs` + iterate `status.outputs[]` for deposit detection) and Clydner BlockCypher-parser netting (drop the parser's own netting or document why it stays because it ingests BlockCypher payloads directly, not SDK statuses).
|
|
12
82
|
|
|
13
83
|
---
|
|
14
84
|
|
|
@@ -324,6 +394,15 @@ Initial npm release of `@getomnichain/omnichain`. Replaces prior vendored-submod
|
|
|
324
394
|
|
|
325
395
|
---
|
|
326
396
|
|
|
397
|
+
[0.5.1]: https://github.com/getomnichain/omnichain/releases/tag/v0.5.1
|
|
398
|
+
[0.5.0]: https://github.com/getomnichain/omnichain/releases/tag/v0.5.0
|
|
399
|
+
[0.4.0]: https://github.com/getomnichain/omnichain/releases/tag/v0.4.0
|
|
400
|
+
[0.3.6]: https://github.com/getomnichain/omnichain/releases/tag/v0.3.6
|
|
401
|
+
[0.3.5]: https://github.com/getomnichain/omnichain/releases/tag/v0.3.5
|
|
402
|
+
[0.3.4]: https://github.com/getomnichain/omnichain/releases/tag/v0.3.4
|
|
403
|
+
[0.3.3]: https://github.com/getomnichain/omnichain/releases/tag/v0.3.3
|
|
404
|
+
[0.3.2]: https://github.com/getomnichain/omnichain/releases/tag/v0.3.2
|
|
405
|
+
[0.3.1]: https://github.com/getomnichain/omnichain/releases/tag/v0.3.1
|
|
327
406
|
[0.3.0]: https://github.com/getomnichain/omnichain/releases/tag/v0.3.0
|
|
328
407
|
[0.2.1]: https://github.com/getomnichain/omnichain/releases/tag/v0.2.1
|
|
329
408
|
[0.2.0]: https://github.com/getomnichain/omnichain/releases/tag/v0.2.0
|
package/dist/cjs/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __exportStar(require("./errors.js"), exports);
|
|
|
24
24
|
__exportStar(require("./is_address.decorator.js"), exports);
|
|
25
25
|
__exportStar(require("./network_type.js"), exports);
|
|
26
26
|
__exportStar(require("./priority.js"), exports);
|
|
27
|
+
__exportStar(require("./rango_chain_names.js"), exports);
|
|
27
28
|
__exportStar(require("./token.js"), exports);
|
|
28
29
|
__exportStar(require("./transaction_status.js"), exports);
|
|
29
30
|
__exportStar(require("./unsigned_transaction.js"), exports);
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4DAA0C;AAC1C,+CAA6B;AAC7B,uDAAqC;AACrC,+DAA6C;AAC7C,kDAAgC;AAChC,iDAA+B;AAC/B,8CAA4B;AAC5B,4DAA0C;AAC1C,oDAAkC;AAClC,gDAA8B;AAC9B,6CAA2B;AAC3B,0DAAwC;AACxC,4DAA0C;AAC1C,iDAA+B;AAC/B,oDAAkC;AAClC,iDAA+B;AAC/B,kDAAgC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4DAA0C;AAC1C,+CAA6B;AAC7B,uDAAqC;AACrC,+DAA6C;AAC7C,kDAAgC;AAChC,iDAA+B;AAC/B,8CAA4B;AAC5B,4DAA0C;AAC1C,oDAAkC;AAClC,gDAA8B;AAC9B,yDAAuC;AACvC,6CAA2B;AAC3B,0DAAwC;AACxC,4DAA0C;AAC1C,iDAA+B;AAC/B,oDAAkC;AAClC,iDAA+B;AAC/B,kDAAgC"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.NOT_ON_RANGO = exports.RANGO_NAME_TO_CHAIN_ID = exports.CHAIN_ID_TO_RANGO_NAME = void 0;
|
|
37
|
+
exports.rangoNameForChainId = rangoNameForChainId;
|
|
38
|
+
exports.chainIdForRangoName = chainIdForRangoName;
|
|
39
|
+
const C = __importStar(require("./chain_ids.js"));
|
|
40
|
+
const MAX_RANGO_NAME_LENGTH = 64;
|
|
41
|
+
/**
|
|
42
|
+
* Bidirectional lookup between omnichain chain ids and Rango's canonical
|
|
43
|
+
* uppercase blockchain names (the `blockchains[].name` field of Rango's
|
|
44
|
+
* `/meta` response).
|
|
45
|
+
*
|
|
46
|
+
* TypeScript-only module — no `omnichain-py` counterpart. `chain_ids.ts`
|
|
47
|
+
* mirrors the Python catalogue, but the Rango-name mapping is a
|
|
48
|
+
* TS-consumer concern and does not exist on the Python side.
|
|
49
|
+
*
|
|
50
|
+
* Source of truth: `GET https://public-api.rango.exchange/basic/meta`.
|
|
51
|
+
* The snapshot backing this module lives at
|
|
52
|
+
* `test/fixtures/rango_meta_blockchains.json`; its `fetchedAt` field is
|
|
53
|
+
* the single source-of-date. Refresh via `npm run rango:refresh`.
|
|
54
|
+
*
|
|
55
|
+
* Downstream consumers that talk to Rango should import from here
|
|
56
|
+
* instead of maintaining a local copy. `test/rango_chain_names.spec.ts`
|
|
57
|
+
* fails the suite whenever `chain_ids.ts` gains a new id that is
|
|
58
|
+
* neither mapped here nor listed in `NOT_ON_RANGO`, so a new chain
|
|
59
|
+
* forces an explicit decision. That suite runs on every PR/push via
|
|
60
|
+
* `.github/workflows/test.yml` and on `npm publish` via
|
|
61
|
+
* `prepublishOnly`. When the `test` check is required on `main` via
|
|
62
|
+
* branch protection, consumers vendoring omnichain from `main` inherit
|
|
63
|
+
* that check as their guarantee; until then, the workflow signals but
|
|
64
|
+
* does not block.
|
|
65
|
+
*
|
|
66
|
+
* Aliases (alternate spellings Rango may use in other endpoints) are
|
|
67
|
+
* deliberately out of scope; if a consumer hits one, add it as a
|
|
68
|
+
* separate decision.
|
|
69
|
+
*/
|
|
70
|
+
exports.CHAIN_ID_TO_RANGO_NAME = new Map([
|
|
71
|
+
[C.CHAIN_ID_BITCOIN_MAINNET, 'BTC'],
|
|
72
|
+
[C.CHAIN_ID_LITECOIN_MAINNET, 'LTC'],
|
|
73
|
+
[C.CHAIN_ID_DOGECOIN_MAINNET, 'DOGE'],
|
|
74
|
+
[C.CHAIN_ID_DASH_MAINNET, 'DASH'],
|
|
75
|
+
[C.CHAIN_ID_ZCASH_MAINNET, 'ZCASH'],
|
|
76
|
+
[C.CHAIN_ID_BITCOIN_CASH_MAINNET, 'BCH'],
|
|
77
|
+
[C.CHAIN_ID_SOLANA_MAINNET, 'SOLANA'],
|
|
78
|
+
[C.CHAIN_ID_SUI_MAINNET, 'SUI'],
|
|
79
|
+
[C.CHAIN_ID_XRPL_MAINNET, 'XRPL'],
|
|
80
|
+
[C.CHAIN_ID_STELLAR_MAINNET, 'STELLAR'],
|
|
81
|
+
[C.CHAIN_ID_TON_MAINNET, 'TON'],
|
|
82
|
+
[C.CHAIN_ID_TRON_MAINNET, 'TRON'],
|
|
83
|
+
[C.CHAIN_ID_ETHEREUM, 'ETH'],
|
|
84
|
+
[C.CHAIN_ID_OPTIMISM, 'OPTIMISM'],
|
|
85
|
+
[C.CHAIN_ID_CRONOS, 'CRONOS'],
|
|
86
|
+
[C.CHAIN_ID_BNB_CHAIN, 'BSC'],
|
|
87
|
+
[C.CHAIN_ID_OKX_CHAIN, 'OKC'],
|
|
88
|
+
[C.CHAIN_ID_GNOSIS, 'GNOSIS'],
|
|
89
|
+
[C.CHAIN_ID_UNICHAIN, 'UNICHAIN'],
|
|
90
|
+
[C.CHAIN_ID_POLYGON, 'POLYGON'],
|
|
91
|
+
[C.CHAIN_ID_MONAD, 'MONAD'],
|
|
92
|
+
[C.CHAIN_ID_SONIC, 'SONIC'],
|
|
93
|
+
[C.CHAIN_ID_SHIMMER, 'SHIMMER'],
|
|
94
|
+
[C.CHAIN_ID_XLAYER, 'XLAYER'],
|
|
95
|
+
[C.CHAIN_ID_FANTOM, 'FANTOM'],
|
|
96
|
+
[C.CHAIN_ID_BOBA, 'BOBA'],
|
|
97
|
+
[C.CHAIN_ID_ZKSYNC, 'ZKSYNC'],
|
|
98
|
+
[C.CHAIN_ID_STABLE, 'STABLE'],
|
|
99
|
+
[C.CHAIN_ID_HYPER_EVM, 'HYPEREVM'],
|
|
100
|
+
[C.CHAIN_ID_METIS, 'METIS'],
|
|
101
|
+
[C.CHAIN_ID_POLYGON_ZKEVM, 'POLYGONZK'],
|
|
102
|
+
[C.CHAIN_ID_MOONBEAM, 'MOONBEAM'],
|
|
103
|
+
[C.CHAIN_ID_MOONRIVER, 'MOONRIVER'],
|
|
104
|
+
[C.CHAIN_ID_SONEIUM, 'SONEIUM'],
|
|
105
|
+
[C.CHAIN_ID_CITREA, 'CITREA'],
|
|
106
|
+
[C.CHAIN_ID_MEGA_ETH, 'MEGAETH'],
|
|
107
|
+
[C.CHAIN_ID_ZETACHAIN, 'ZETA_CHAIN'],
|
|
108
|
+
[C.CHAIN_ID_BASE, 'BASE'],
|
|
109
|
+
[C.CHAIN_ID_IOTA_EVM, 'IOTA'],
|
|
110
|
+
[C.CHAIN_ID_PLASMA, 'PLASMA'],
|
|
111
|
+
[C.CHAIN_ID_MODE, 'MODE'],
|
|
112
|
+
[C.CHAIN_ID_ARBITRUM, 'ARBITRUM'],
|
|
113
|
+
[C.CHAIN_ID_CELO, 'CELO'],
|
|
114
|
+
[C.CHAIN_ID_AVALANCHE_C_CHAIN, 'AVAX_CCHAIN'],
|
|
115
|
+
[C.CHAIN_ID_LINEA, 'LINEA'],
|
|
116
|
+
[C.CHAIN_ID_BERA_CHAIN, 'BERACHAIN'],
|
|
117
|
+
[C.CHAIN_ID_BLAST, 'BLAST'],
|
|
118
|
+
[C.CHAIN_ID_TAIKO, 'TAIKO'],
|
|
119
|
+
[C.CHAIN_ID_SCROLL, 'SCROLL'],
|
|
120
|
+
[C.CHAIN_ID_KATANA, 'KATANA'],
|
|
121
|
+
[C.CHAIN_ID_ZORA, 'ZORA'],
|
|
122
|
+
[C.CHAIN_ID_AURORA, 'AURORA'],
|
|
123
|
+
]);
|
|
124
|
+
/**
|
|
125
|
+
* Inverse of `CHAIN_ID_TO_RANGO_NAME`, keyed by uppercase name.
|
|
126
|
+
* Prefer `chainIdForRangoName` for case- and whitespace-tolerant lookup.
|
|
127
|
+
*/
|
|
128
|
+
exports.RANGO_NAME_TO_CHAIN_ID = new Map(Array.from(exports.CHAIN_ID_TO_RANGO_NAME, ([id, name]) => [name, id]));
|
|
129
|
+
/**
|
|
130
|
+
* Explicit decisions for chain ids omnichain declares but Rango's `/meta`
|
|
131
|
+
* does not carry, verified against the same snapshot as
|
|
132
|
+
* `CHAIN_ID_TO_RANGO_NAME`.
|
|
133
|
+
*
|
|
134
|
+
* Every id in `chain_ids.ts` must appear in either `CHAIN_ID_TO_RANGO_NAME`
|
|
135
|
+
* or here; the coverage test fails the PR check and `npm publish` (via
|
|
136
|
+
* `prepublishOnly`) otherwise. Adding an id to `chain_ids.ts` without a
|
|
137
|
+
* decision is a bug.
|
|
138
|
+
*
|
|
139
|
+
* Grouped by reason:
|
|
140
|
+
* - Testnets/devnets Rango's `/meta` intentionally omits.
|
|
141
|
+
* - Mainnet EVM chains not on Rango's `/meta` as of the snapshot; add to
|
|
142
|
+
* `CHAIN_ID_TO_RANGO_NAME` once Rango starts publishing them.
|
|
143
|
+
*/
|
|
144
|
+
exports.NOT_ON_RANGO = new Set([
|
|
145
|
+
// testnets/devnets — Rango's /meta never carries these.
|
|
146
|
+
C.CHAIN_ID_BITCOIN_TESTNET,
|
|
147
|
+
C.CHAIN_ID_BITCOIN_SIGNET,
|
|
148
|
+
C.CHAIN_ID_SOLANA_TESTNET,
|
|
149
|
+
C.CHAIN_ID_SOLANA_DEVNET,
|
|
150
|
+
C.CHAIN_ID_SUI_TESTNET,
|
|
151
|
+
C.CHAIN_ID_SUI_DEVNET,
|
|
152
|
+
C.CHAIN_ID_XRPL_TESTNET,
|
|
153
|
+
C.CHAIN_ID_STELLAR_TESTNET,
|
|
154
|
+
C.CHAIN_ID_TON_TESTNET,
|
|
155
|
+
C.CHAIN_ID_TRON_SHASTA,
|
|
156
|
+
C.CHAIN_ID_GOERLI,
|
|
157
|
+
C.CHAIN_ID_SEPOLIA,
|
|
158
|
+
C.CHAIN_ID_CELO_SEPOLIA,
|
|
159
|
+
// Mainnet, absent from /meta as of the fixture snapshot — move to the map when Rango publishes them.
|
|
160
|
+
C.CHAIN_ID_OPBNB,
|
|
161
|
+
C.CHAIN_ID_WORLD_CHAIN,
|
|
162
|
+
C.CHAIN_ID_WANCHAIN,
|
|
163
|
+
C.CHAIN_ID_SEI_EVM,
|
|
164
|
+
C.CHAIN_ID_ABSTRACT,
|
|
165
|
+
C.CHAIN_ID_TEMPO,
|
|
166
|
+
C.CHAIN_ID_MANTLE,
|
|
167
|
+
C.CHAIN_ID_INK,
|
|
168
|
+
C.CHAIN_ID_BOB,
|
|
169
|
+
C.CHAIN_ID_ZKLINK_NOVA,
|
|
170
|
+
]);
|
|
171
|
+
/**
|
|
172
|
+
* Rango's canonical uppercase blockchain name for a given omnichain chain
|
|
173
|
+
* id, or `undefined` when the chain is not on Rango, when the input is not
|
|
174
|
+
* a real number (`NaN`), or when it is `null` / `undefined`.
|
|
175
|
+
*
|
|
176
|
+
* `bigint` input is rejected at runtime and returns `undefined`. Callers
|
|
177
|
+
* bridging from `ethers` v6's `Network.chainId: bigint` should narrow with
|
|
178
|
+
* `Number(x)` and check `Number.isSafeInteger` first.
|
|
179
|
+
*/
|
|
180
|
+
function rangoNameForChainId(chainId) {
|
|
181
|
+
if (typeof chainId !== 'number' || Number.isNaN(chainId))
|
|
182
|
+
return undefined;
|
|
183
|
+
return exports.CHAIN_ID_TO_RANGO_NAME.get(chainId);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Omnichain chain id for a Rango blockchain name. Input is normalised
|
|
187
|
+
* (`trim()` + `toUpperCase()`) so `' bsc '`, `'BSC'`, `'Bsc'` all resolve
|
|
188
|
+
* to `56`. Returns `undefined` for an unknown name, for any non-string
|
|
189
|
+
* input, for raw input longer than 64 characters (rejected before any
|
|
190
|
+
* per-character work — the bound is several times the longest name in
|
|
191
|
+
* the snapshot, so it is a DoS guard, not a contract), or for input
|
|
192
|
+
* carrying non-ASCII characters (Unicode-aware `toUpperCase` folds
|
|
193
|
+
* `'ſ' → 'S'`
|
|
194
|
+
* and `'ı' → 'I'`, which would resolve homoglyph inputs to real chain
|
|
195
|
+
* ids — the accessor fails closed instead). Callers looping over
|
|
196
|
+
* untrusted Rango feed rows can skip bad entries without a `TypeError`.
|
|
197
|
+
*/
|
|
198
|
+
function chainIdForRangoName(name) {
|
|
199
|
+
if (typeof name !== 'string')
|
|
200
|
+
return undefined;
|
|
201
|
+
if (name.length > MAX_RANGO_NAME_LENGTH)
|
|
202
|
+
return undefined;
|
|
203
|
+
for (let i = 0; i < name.length; i++) {
|
|
204
|
+
if (name.charCodeAt(i) > 0x7f)
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
return exports.RANGO_NAME_TO_CHAIN_ID.get(name.trim().toUpperCase());
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=rango_chain_names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rango_chain_names.js","sourceRoot":"","sources":["../../rango_chain_names.ts"],"names":[],"mappings":"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"}
|
|
@@ -587,56 +587,85 @@ class SolanaChain extends chain_base_ts_1.Chain {
|
|
|
587
587
|
catch {
|
|
588
588
|
scrubUrl = null;
|
|
589
589
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
590
|
+
// RIN-296 D1: fetch `getTransaction` and `getSignatureStatus` in
|
|
591
|
+
// parallel so the common (Success) case pays one round-trip of
|
|
592
|
+
// latency instead of two. `getSignatureStatus` populates the four
|
|
593
|
+
// new finality fields (slot / confirmations / confirmationStatus /
|
|
594
|
+
// signers) on the status; `slot` and `signers` also come from
|
|
595
|
+
// `getTransaction` when it succeeds.
|
|
596
|
+
const [txResult, sigResult] = await Promise.allSettled([
|
|
597
|
+
connection.getTransaction(txHash, {
|
|
593
598
|
commitment: 'confirmed',
|
|
594
599
|
maxSupportedTransactionVersion: 0,
|
|
595
|
-
})
|
|
596
|
-
|
|
597
|
-
|
|
600
|
+
}),
|
|
601
|
+
connection.getSignatureStatus(txHash, { searchTransactionHistory: true }),
|
|
602
|
+
]);
|
|
603
|
+
if (txResult.status === 'rejected') {
|
|
604
|
+
const err = txResult.reason;
|
|
598
605
|
const rawMsg = err instanceof Error ? err.message : String(err);
|
|
599
606
|
throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Failed to read Solana tx ${txHash}: ${rawMsg}`, scrubUrl), { chainId: this.chainId, txHash }, (0, errors_ts_1.sanitizeCause)(err, scrubUrl));
|
|
600
607
|
}
|
|
608
|
+
const tx = txResult.value;
|
|
609
|
+
// RIN-296 D2: sig-status failure on the Success path degrades to
|
|
610
|
+
// `null` fields rather than throwing — a working tx read must not
|
|
611
|
+
// fail because a metadata enrichment call failed.
|
|
612
|
+
const sigValue = sigResult.status === 'fulfilled' ? (sigResult.value?.value ?? null) : null;
|
|
613
|
+
const confirmationStatus = extractSolanaConfirmationStatus(sigValue?.confirmationStatus);
|
|
614
|
+
const confirmations = normaliseSolanaConfirmations(sigValue);
|
|
601
615
|
if (!tx) {
|
|
602
|
-
// Full tx not fetchable;
|
|
603
|
-
//
|
|
604
|
-
//
|
|
605
|
-
//
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
sig = await connection.getSignatureStatus(txHash, { searchTransactionHistory: true });
|
|
609
|
-
}
|
|
610
|
-
catch (err) {
|
|
616
|
+
// Full tx not fetchable; use signature status. Consume BOTH fields
|
|
617
|
+
// (`confirmationStatus` + `err`) — always returning Pending on
|
|
618
|
+
// settled-but-unfetchable would let a settled-FAILED tx poll
|
|
619
|
+
// indefinitely.
|
|
620
|
+
if (sigResult.status === 'rejected') {
|
|
621
|
+
const err = sigResult.reason;
|
|
611
622
|
const rawMsg = err instanceof Error ? err.message : String(err);
|
|
612
623
|
throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Failed to read Solana signature status ${txHash}: ${rawMsg}`, scrubUrl), { chainId: this.chainId, txHash }, (0, errors_ts_1.sanitizeCause)(err, scrubUrl));
|
|
613
624
|
}
|
|
614
|
-
if (!
|
|
625
|
+
if (!sigValue)
|
|
615
626
|
return solana_transaction_status_ts_1.SolanaTransactionStatus.notFound(this.chainId);
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
if (settled &&
|
|
619
|
-
// Fees are not reconstructable from a sig-status only — factory
|
|
620
|
-
// now accepts fees: null for exactly this case.
|
|
627
|
+
const slot = sigValue.slot ?? null;
|
|
628
|
+
const settled = confirmationStatus === 'finalized' || confirmationStatus === 'confirmed';
|
|
629
|
+
if (settled && sigValue.err) {
|
|
621
630
|
return solana_transaction_status_ts_1.SolanaTransactionStatus.failed({
|
|
622
631
|
chainId: this.chainId,
|
|
623
632
|
inclusionAt: null,
|
|
624
|
-
error: { code: 'REVERTED', reason: JSON.stringify(
|
|
633
|
+
error: { code: 'REVERTED', reason: JSON.stringify(sigValue.err) },
|
|
625
634
|
fees: null,
|
|
635
|
+
slot,
|
|
636
|
+
confirmations,
|
|
637
|
+
confirmationStatus,
|
|
626
638
|
});
|
|
627
639
|
}
|
|
628
640
|
// settled-Success without a fetchable body OR still propagating —
|
|
629
641
|
// keep polling. NotFound here would misreport a settled deposit.
|
|
630
|
-
|
|
642
|
+
// RIN-296 D3: pass finality through so consumers polling on
|
|
643
|
+
// Pending see the tx's position toward finality.
|
|
644
|
+
return solana_transaction_status_ts_1.SolanaTransactionStatus.pending(this.chainId, {
|
|
645
|
+
slot,
|
|
646
|
+
confirmations,
|
|
647
|
+
confirmationStatus,
|
|
648
|
+
});
|
|
631
649
|
}
|
|
650
|
+
// RIN-296 R2: `slot` and `signers` are read from the `getTransaction`
|
|
651
|
+
// response the SDK already fetches. `signers` = the first
|
|
652
|
+
// `numRequiredSignatures` static account keys, matching
|
|
653
|
+
// omnichain-py `signer_keys = account_keys[: header.num_required_signatures]`.
|
|
654
|
+
const slot = tx.slot;
|
|
655
|
+
const accounts = tx.transaction.message.staticAccountKeys.map((k) => k.toBase58());
|
|
656
|
+
const signers = accounts.slice(0, tx.transaction.message.header.numRequiredSignatures);
|
|
632
657
|
if (!tx.meta) {
|
|
633
658
|
// A returned tx without meta is a valid RPC response shape (some
|
|
634
659
|
// very old txs or unusual nodes). Report Pending so consumers keep
|
|
635
660
|
// polling rather than throwing from a status read; matches the
|
|
636
661
|
// _decodeBalanceChanges guard at the same file.
|
|
637
|
-
return solana_transaction_status_ts_1.SolanaTransactionStatus.pending(this.chainId
|
|
662
|
+
return solana_transaction_status_ts_1.SolanaTransactionStatus.pending(this.chainId, {
|
|
663
|
+
slot,
|
|
664
|
+
confirmations,
|
|
665
|
+
confirmationStatus,
|
|
666
|
+
signers,
|
|
667
|
+
});
|
|
638
668
|
}
|
|
639
|
-
const accounts = tx.transaction.message.staticAccountKeys.map((k) => k.toBase58());
|
|
640
669
|
const feePayer = accounts[0] ?? '';
|
|
641
670
|
if (feePayer.length === 0) {
|
|
642
671
|
throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionDecodeFailed, `Solana tx ${txHash} has no fee_payer account`, { chainId: this.chainId, txHash });
|
|
@@ -659,6 +688,10 @@ class SolanaChain extends chain_base_ts_1.Chain {
|
|
|
659
688
|
inclusionAt,
|
|
660
689
|
error: { code: 'REVERTED', reason: JSON.stringify(tx.meta.err) },
|
|
661
690
|
fees,
|
|
691
|
+
slot,
|
|
692
|
+
confirmations,
|
|
693
|
+
confirmationStatus,
|
|
694
|
+
signers,
|
|
662
695
|
});
|
|
663
696
|
}
|
|
664
697
|
// Wrap the decoder so a malformed uiTokenAmount.amount / pre/postBalance
|
|
@@ -679,6 +712,10 @@ class SolanaChain extends chain_base_ts_1.Chain {
|
|
|
679
712
|
inclusionAt,
|
|
680
713
|
balanceChanges,
|
|
681
714
|
fees,
|
|
715
|
+
slot,
|
|
716
|
+
confirmations,
|
|
717
|
+
confirmationStatus,
|
|
718
|
+
signers,
|
|
682
719
|
});
|
|
683
720
|
}
|
|
684
721
|
async getChainTipHeight() {
|
|
@@ -1297,6 +1334,33 @@ function signatureBase58FromBytes(txBytes) {
|
|
|
1297
1334
|
return bs58encode(sigBytes);
|
|
1298
1335
|
}
|
|
1299
1336
|
const ED25519_SPKI_PREFIX = Buffer.from('302a300506032b6570032100', 'hex');
|
|
1337
|
+
/**
|
|
1338
|
+
* Narrow `getSignatureStatuses` `confirmationStatus` (a raw string) to
|
|
1339
|
+
* the known union, falling back to `null` for unknown values so a
|
|
1340
|
+
* future upstream widening doesn't lie about the type.
|
|
1341
|
+
*/
|
|
1342
|
+
function extractSolanaConfirmationStatus(raw) {
|
|
1343
|
+
if (raw === 'processed' || raw === 'confirmed' || raw === 'finalized')
|
|
1344
|
+
return raw;
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Normalise `getSignatureStatuses` `value.confirmations` to a monotone
|
|
1349
|
+
* count consumers can gate on. Per the RPC contract, `null` means
|
|
1350
|
+
* rooted (past `finalized`); the SDK maps that to
|
|
1351
|
+
* {@link SOLANA_FINALIZED_CONFIRMATIONS} so `confirmations >= threshold`
|
|
1352
|
+
* checks behave the way callers expect. Passing a null `sigValue`
|
|
1353
|
+
* (sig-status unavailable — a metadata-enrichment failure on the
|
|
1354
|
+
* success path) returns `null` so consumers can distinguish "unknown"
|
|
1355
|
+
* from any real count.
|
|
1356
|
+
*/
|
|
1357
|
+
function normaliseSolanaConfirmations(sigValue) {
|
|
1358
|
+
if (sigValue === null)
|
|
1359
|
+
return null;
|
|
1360
|
+
if (sigValue.confirmations !== null)
|
|
1361
|
+
return sigValue.confirmations;
|
|
1362
|
+
return solana_transaction_status_ts_1.SOLANA_FINALIZED_CONFIRMATIONS;
|
|
1363
|
+
}
|
|
1300
1364
|
function parseSolanaSignature(raw) {
|
|
1301
1365
|
const hexCandidate = raw.startsWith('0x') ? raw.slice(2) : raw;
|
|
1302
1366
|
if (/^[0-9a-fA-F]+$/.test(hexCandidate) && hexCandidate.length === 128) {
|