@gibs/bridge-sdk 1.19.0 → 1.20.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 (111) hide show
  1. package/dist/abis.js +0 -2
  2. package/dist/bridge-limits.js +0 -38
  3. package/dist/chain-info.js +0 -116
  4. package/dist/chain-names.js +0 -151
  5. package/dist/chains.js +0 -198
  6. package/dist/cjs/abis.js +0 -2
  7. package/dist/cjs/bridge-limits.js +0 -38
  8. package/dist/cjs/chain-info.js +0 -116
  9. package/dist/cjs/chain-names.js +0 -151
  10. package/dist/cjs/chains.js +0 -198
  11. package/dist/cjs/claims.js +0 -60
  12. package/dist/cjs/config.js +1 -208
  13. package/dist/cjs/consolidated-quote.js +0 -465
  14. package/dist/cjs/destination-calldata.js +0 -71
  15. package/dist/cjs/ecosystems.js +0 -183
  16. package/dist/cjs/execution.js +0 -92
  17. package/dist/cjs/fee-type.js +0 -1
  18. package/dist/cjs/hyperliquid-execution.js +0 -98
  19. package/dist/cjs/image-links.js +0 -108
  20. package/dist/cjs/lifi-execution-guard.js +65 -172
  21. package/dist/cjs/near-intents-conformance.js +0 -110
  22. package/dist/cjs/near-intents-route.js +0 -149
  23. package/dist/cjs/near-intents.js +0 -562
  24. package/dist/cjs/origin-deposit.js +0 -56
  25. package/dist/cjs/pre-quote.js +0 -80
  26. package/dist/cjs/prices.js +0 -113
  27. package/dist/cjs/providers.js +0 -83
  28. package/dist/cjs/quote-fees.js +0 -80
  29. package/dist/cjs/referral.js +0 -75
  30. package/dist/cjs/relay-currencies.js +0 -31
  31. package/dist/cjs/relay-depository.js +52 -77
  32. package/dist/cjs/relay-quote.js +0 -394
  33. package/dist/cjs/route-steps.js +0 -100
  34. package/dist/cjs/routing/amounts.js +0 -83
  35. package/dist/cjs/routing/candidate-route.js +0 -73
  36. package/dist/cjs/routing/candidate.js +0 -46
  37. package/dist/cjs/routing/carrier-provider-key.js +0 -27
  38. package/dist/cjs/routing/carriers/omnibridge.js +0 -120
  39. package/dist/cjs/routing/carriers/pulsex-paths.js +0 -57
  40. package/dist/cjs/routing/carriers/pulsex-split.js +0 -92
  41. package/dist/cjs/routing/carriers/pulsex.js +0 -279
  42. package/dist/cjs/routing/carriers/wrap.js +0 -57
  43. package/dist/cjs/routing/composed-route-quality.js +0 -95
  44. package/dist/cjs/routing/entry-chain-anchor.js +0 -27
  45. package/dist/cjs/routing/format.js +0 -50
  46. package/dist/cjs/routing/index.js +0 -28
  47. package/dist/cjs/routing/producers.js +0 -854
  48. package/dist/cjs/routing/providers.js +0 -27
  49. package/dist/cjs/routing/search/compose.js +0 -56
  50. package/dist/cjs/routing/search/graph.js +0 -364
  51. package/dist/cjs/routing/search/plan-route-data.js +0 -508
  52. package/dist/cjs/routing/search/score.js +0 -344
  53. package/dist/cjs/routing/swap-transactions.js +0 -74
  54. package/dist/cjs/routing/usd-value.js +0 -12
  55. package/dist/cjs/settings.js +1 -456
  56. package/dist/cjs/swap-venues.js +0 -17
  57. package/dist/cjs/ton-connect.js +0 -82
  58. package/dist/cjs/ton-execution.js +0 -6
  59. package/dist/cjs/ton.js +0 -42
  60. package/dist/cjs/tron-execution.js +0 -145
  61. package/dist/claims.js +0 -60
  62. package/dist/config.js +1 -211
  63. package/dist/consolidated-quote.js +0 -465
  64. package/dist/destination-calldata.js +0 -71
  65. package/dist/ecosystems.js +0 -183
  66. package/dist/execution.js +0 -92
  67. package/dist/fee-type.js +0 -2
  68. package/dist/hyperliquid-execution.js +0 -98
  69. package/dist/image-links.js +0 -108
  70. package/dist/lifi-execution-guard.js +65 -172
  71. package/dist/near-intents-conformance.js +0 -110
  72. package/dist/near-intents-route.js +0 -149
  73. package/dist/near-intents.js +0 -562
  74. package/dist/origin-deposit.js +0 -56
  75. package/dist/pre-quote.js +0 -80
  76. package/dist/prices.js +0 -113
  77. package/dist/providers.js +0 -83
  78. package/dist/quote-fees.js +0 -80
  79. package/dist/referral.js +0 -75
  80. package/dist/relay-currencies.js +0 -31
  81. package/dist/relay-depository.js +52 -77
  82. package/dist/relay-quote.js +0 -394
  83. package/dist/route-steps.js +0 -100
  84. package/dist/routing/amounts.js +0 -83
  85. package/dist/routing/candidate-route.js +0 -73
  86. package/dist/routing/candidate.js +0 -46
  87. package/dist/routing/carrier-provider-key.js +0 -27
  88. package/dist/routing/carriers/omnibridge.js +0 -120
  89. package/dist/routing/carriers/pulsex-paths.js +0 -57
  90. package/dist/routing/carriers/pulsex-split.js +0 -92
  91. package/dist/routing/carriers/pulsex.js +0 -279
  92. package/dist/routing/carriers/wrap.js +0 -57
  93. package/dist/routing/composed-route-quality.js +0 -95
  94. package/dist/routing/entry-chain-anchor.js +0 -27
  95. package/dist/routing/format.js +0 -50
  96. package/dist/routing/index.js +0 -28
  97. package/dist/routing/producers.js +0 -854
  98. package/dist/routing/providers.js +0 -27
  99. package/dist/routing/search/compose.js +0 -56
  100. package/dist/routing/search/graph.js +0 -364
  101. package/dist/routing/search/plan-route-data.js +0 -508
  102. package/dist/routing/search/score.js +0 -344
  103. package/dist/routing/swap-transactions.js +0 -74
  104. package/dist/routing/usd-value.js +0 -12
  105. package/dist/settings.js +1 -456
  106. package/dist/swap-venues.js +0 -17
  107. package/dist/ton-connect.js +0 -82
  108. package/dist/ton-execution.js +0 -6
  109. package/dist/ton.js +0 -42
  110. package/dist/tron-execution.js +0 -145
  111. package/package.json +3 -3
@@ -1,76 +1,17 @@
1
1
  import { getAddress } from 'viem';
2
2
  import { destinationDataParam, interactingWithBridgeToken, transactionInputs, } from './settings.js';
3
- /**
4
- * Conservative per-shape gas budgets for omnibridge entry calls executed by an
5
- * aggregator on the destination chain. An undershoot reverts the executor's
6
- * call and strands funds at the aggregator's fallback address, so these err
7
- * high; the aggregator prices unused gas into the quote rather than burning it.
8
- *
9
- * MEASURED against the deployed mediators on a fork of Ethereum mainnet,
10
- * 2026-08-31, by `destination-calldata.fork.test.ts`:
11
- *
12
- * relayTokens 195,967 USD Coin, an ordinary registered token
13
- * wrapAndRelayTokens 200,347 native ether through the router
14
- * transferAndCall 187,000 wrapped Pulse, a bridged representation home
15
- * to 204,000, depending on the fork block
16
- *
17
- * The figures move with block state — whether a storage slot the call touches
18
- * is already warm — so treat them as a band, not a constant. The spread
19
- * measured across two blocks minutes apart was about nine percent.
20
- *
21
- * THE BUDGET STAYS AT 400,000, and the headroom is deliberate. Every figure
22
- * above is a token the bridge has carried before. The first crossing of a
23
- * token the mediator has not seen writes its limits as well, and a token with
24
- * a transfer hook costs more again — neither is measured here, and both fail
25
- * by stranding the user's funds rather than by costing us a little gas. The
26
- * fork test asserts each shape stays under the budget, so a regression that
27
- * eats the headroom fails a test rather than a transfer.
28
- */
29
3
  export const destinationGasLimits = {
30
4
  relayTokens: 400000n,
31
5
  transferAndCall: 400000n,
32
6
  wrapAndRelayTokens: 400000n,
33
7
  };
34
- /**
35
- * Builds the call an aggregator's executor must perform on the bridge's
36
- * foreign chain (e.g. Ethereum) to relay just-bridged funds onward through the
37
- * omnibridge to the recipient on the home chain (e.g. PulseChain).
38
- *
39
- * This is the second leg of a consolidated route: the aggregator bridges the
40
- * user's funds to the foreign chain with a guaranteed minimum amount, then its
41
- * executor performs this call. Because the executor — not the user — is the
42
- * caller, the result always encodes the fixed `amount` and names the user as
43
- * `recipient`; delivery-fee encoding never applies (home-chain gas handout is
44
- * a separate automatic system).
45
- *
46
- * @param pathway - the bridge pathway whose foreign side the executor enters
47
- * @param assetIn - the foreign-chain token the aggregator delivers (zero address for native)
48
- * @param recipient - the user's address on the home chain
49
- * @param sender - the user's address, threaded through pathways that take an extra sender param
50
- * @param assetLink - the asset link for the token, used to detect bridged representations
51
- * @param amount - the exact token amount the executor will relay
52
- * @returns the destination call, or null when the inputs cannot form a valid call
53
- */
54
8
  export const buildDestinationCalldata = ({ pathway, assetIn, recipient, sender, assetLink, amount, unwrap = false, }) => {
55
9
  if (amount <= 0n) {
56
10
  return null;
57
11
  }
58
- // `transactionInputs` rejects both of these too, but it cannot say so to the
59
- // type checker, and the entry fields below are read straight off them. Asking
60
- // first keeps the answer honest rather than asserted.
61
12
  if (!pathway || !assetLink) {
62
13
  return null;
63
14
  }
64
- // THE UNWRAP RIDES THE DATA PARAMETER, which is the same slot the recipient
65
- // travels in. Naming the destination router there and the recipient after it
66
- // tells the home mediator to hand the arriving tokens to the router, which
67
- // unwraps them and forwards the coin on. `destinationDataParam` owns that
68
- // encoding for the direct path already, so it owns it here too rather than
69
- // being restated.
70
- //
71
- // The gate is the pathway's own bridged native asset. Anything else arrives
72
- // wrapped whatever the caller asked for, so obeying the request would build a
73
- // call that does something other than what the caller believes it does.
74
15
  const unwrapOnArrival = unwrap &&
75
16
  assetIn !== null &&
76
17
  pathway.destinationRouter !== null &&
@@ -87,17 +28,10 @@ export const buildDestinationCalldata = ({ pathway, assetIn, recipient, sender,
87
28
  unwrap: true,
88
29
  })
89
30
  : recipient;
90
- // Only reachable when the unwrap was asked for and could not be encoded.
91
- // Falling back to a plain recipient would quietly deliver the wrapped token.
92
31
  if (destinationData === null) {
93
32
  return null;
94
33
  }
95
34
  const inputs = transactionInputs({
96
- // NO READER COMPOSED THIS CALL, so there is no pair on screen for it to
97
- // disagree with. An aggregator's executor performs it on the entry chain
98
- // after the reader has already signed their own transaction, and the chain
99
- // pair IS this pathway. See `crossingRefusal` for why the field is nullable
100
- // rather than optional: every caller has to say which of the two it is.
101
35
  bridgeKey: null,
102
36
  signedCrossing: null,
103
37
  bridgePathway: pathway,
@@ -105,8 +39,6 @@ export const buildDestinationCalldata = ({ pathway, assetIn, recipient, sender,
105
39
  recipient,
106
40
  account: sender,
107
41
  assetLink,
108
- // for bridged tokens the data param rides transferAndCall and the
109
- // mediator decodes it as the home-chain recipient
110
42
  destinationDataParam: destinationData,
111
43
  amountToBridge: amount,
112
44
  feeDirectorStructEncoded: null,
@@ -120,9 +52,6 @@ export const buildDestinationCalldata = ({ pathway, assetIn, recipient, sender,
120
52
  assetLink,
121
53
  assetInAddress: assetIn?.address ?? null,
122
54
  });
123
- // Both are the same for every shape: the pathway's own mediator, and the
124
- // on-chain address the link resolved (already the wrapped one for a native
125
- // asset, because `tokenBridgeInfo` substitutes it before looking anything up).
126
55
  const entry = {
127
56
  entryMediator: pathway.from,
128
57
  entryTokenAddress: assetLink.assetInAddress,
@@ -1,45 +1,19 @@
1
1
  import { getAddress, isAddress } from 'viem';
2
- // Solana public keys are base58 (no 0, O, I, l) and 32–44 characters long.
3
2
  const base58Solana = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
4
- // Bitcoin mainnet: bech32 (bc1…) or base58 pay-to-key/script (starts 1 or 3).
5
3
  const bech32Bitcoin = /^bc1[0-9ac-hj-np-z]{6,87}$/;
6
4
  const base58Bitcoin = /^[13][1-9A-HJ-NP-Za-km-z]{25,39}$/;
7
- // Tron base58check addresses are always 34 characters and always begin with a
8
- // capital T (the encoding of the 0x41 mainnet prefix byte).
9
5
  const base58Tron = /^T[1-9A-HJ-NP-Za-km-z]{33}$/;
10
- // The Open Network has two interchangeable address encodings, and Relay uses
11
- // both: the 48-character "user-friendly" base64url form (EQ/UQ on mainnet,
12
- // kQ/0Q on testnet), and the "raw" form of a workchain, a colon, and a
13
- // 64-character hex account identifier.
14
6
  const friendlyTon = /^[EUk0]Q[A-Za-z0-9_-]{46}$/;
15
7
  const rawTon = /^-?\d+:[0-9a-fA-F]{64}$/;
16
- // HyperCore token identifiers are hex of a length Ethereum never uses: sixteen
17
- // bytes for most tokens, nineteen for a few. Twenty is allowed too so a token
18
- // that is addressed by a full Ethereum address still resolves.
19
8
  const hyperliquidTokenId = /^0x[0-9a-fA-F]{32,40}$/;
20
- // Zcash TRANSPARENT addresses only, and the restriction is the service's, not
21
- // ours: a shielded (`zs1…`) or unified (`u1…`) address is refused at the door
22
- // with `refundTo is not valid`, before a solver is ever reached. Both
23
- // transparent forms are base58check over a two-byte prefix, which always
24
- // encodes to exactly 35 characters: `t1` pays to a public key hash, `t3` to a
25
- // script hash. The pattern rejects the shielded forms by construction, because
26
- // neither begins with `t`.
27
9
  const transparentZcash = /^t[13][1-9A-HJ-NP-Za-km-z]{33}$/;
28
10
  const evmDescriptor = {
29
11
  id: 'evm',
30
12
  label: 'Ethereum Virtual Machine',
31
13
  caipNamespace: 'eip155',
32
- // The all-zero address, exactly as the Relay builder uses across every
33
- // Ethereum Virtual Machine chain for the native gas token.
34
14
  nativeAssetSentinel: '0x0000000000000000000000000000000000000000',
35
- // The only proof the product holds. The bridge's own contracts run on
36
- // PulseChain, Ethereum and Binance Smart Chain, and every aggregator origin
37
- // LI.FI carries is an Ethereum Virtual Machine chain, so this is the family
38
- // real transfers have crossed.
39
15
  endToEndProof: 'verified',
40
16
  isValidAddress: (value) => isAddress(value),
41
- // getAddress applies the mixed-case checksum; it throws on a non-address, so
42
- // fall back to the raw input to keep normalize total and side-effect-free.
43
17
  normalizeAddress: (value) => (isAddress(value) ? getAddress(value) : value),
44
18
  addressCasing: 'insensitive',
45
19
  };
@@ -47,16 +21,9 @@ const svmDescriptor = {
47
21
  id: 'svm',
48
22
  label: 'Solana',
49
23
  caipNamespace: 'solana',
50
- // Solana native currency (the System Program address), as Relay expects it.
51
24
  nativeAssetSentinel: '11111111111111111111111111111111',
52
- // Connects through the Reown AppKit Solana adapter and signs for real
53
- // (`useSolanaDepositSigner` builds a versioned transaction and calls the
54
- // wallet's sign-and-send). Every test around it drives a mocked wallet, and
55
- // Relay — the only provider that quotes this origin — has never had its
56
- // on-chain execution confirmed by a funded transfer.
57
25
  endToEndProof: 'pending',
58
26
  isValidAddress: (value) => base58Solana.test(value),
59
- // base58 is case-significant; canonical form is the input unchanged.
60
27
  normalizeAddress: (value) => value,
61
28
  addressCasing: 'significant',
62
29
  };
@@ -64,17 +31,9 @@ const bvmDescriptor = {
64
31
  id: 'bvm',
65
32
  label: 'Bitcoin',
66
33
  caipNamespace: 'bip122',
67
- // Bitcoin native currency string, as Relay expects it.
68
34
  nativeAssetSentinel: 'bc1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmql8k8',
69
- // The best-evidenced of the four, and still short of proof. A partially
70
- // signed transaction the pipeline builds is accepted by a real node, but
71
- // that test runs against a local regtest node behind `BITCOIN_REGTEST=1`
72
- // and never in the normal suite. The browser wallet that signs in
73
- // production is driven by nothing but a mock, and no funded mainnet
74
- // transfer has completed.
75
35
  endToEndProof: 'pending',
76
36
  isValidAddress: (value) => bech32Bitcoin.test(value) || base58Bitcoin.test(value),
77
- // bech32 and base58 are case-significant; canonical form is the input unchanged.
78
37
  normalizeAddress: (value) => value,
79
38
  addressCasing: 'significant',
80
39
  };
@@ -82,16 +41,9 @@ const tvmDescriptor = {
82
41
  id: 'tvm',
83
42
  label: 'Tron',
84
43
  caipNamespace: 'tron',
85
- // Tron native currency (TRX), as Relay expects it. Not a real contract: it is
86
- // the placeholder address Relay publishes for the chain's native asset.
87
44
  nativeAssetSentinel: 'T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb',
88
- // The first wallet the interface reaches outside Reown AppKit: it reads an
89
- // injected provider directly. That is more of the connection path owned
90
- // here, and therefore more of it unexercised outside a mock. No funded
91
- // transfer has completed.
92
45
  endToEndProof: 'pending',
93
46
  isValidAddress: (value) => base58Tron.test(value),
94
- // base58check is case-significant; canonical form is the input unchanged.
95
47
  normalizeAddress: (value) => value,
96
48
  addressCasing: 'significant',
97
49
  };
@@ -99,17 +51,9 @@ const tonvmDescriptor = {
99
51
  id: 'tonvm',
100
52
  label: 'The Open Network',
101
53
  caipNamespace: 'ton',
102
- // Relay publishes Gram — NOT the Toncoin gas asset — as this chain's native
103
- // currency, so a native-asset route on The Open Network is denominated in
104
- // Gram. Kept byte-equal to Relay's own value rather than "corrected".
105
54
  nativeAssetSentinel: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c',
106
- // The second wallet outside Reown AppKit, on a separate connection library,
107
- // and the one origin whose deposit identifier is derived from the returned
108
- // cell rather than handed back by the wallet. Only a mock has ever returned
109
- // that cell. No funded transfer has completed.
110
55
  endToEndProof: 'pending',
111
56
  isValidAddress: (value) => friendlyTon.test(value) || rawTon.test(value),
112
- // both encodings are case-significant; canonical form is the input unchanged.
113
57
  normalizeAddress: (value) => value,
114
58
  addressCasing: 'significant',
115
59
  };
@@ -117,71 +61,22 @@ const hypevmDescriptor = {
117
61
  id: 'hypevm',
118
62
  label: 'Hyperliquid',
119
63
  caipNamespace: 'hyperliquid',
120
- // Relay publishes USD Coin — NOT the Hype gas asset — as this chain's native
121
- // currency, and addresses it by a sixteen-byte HyperCore token identifier
122
- // rather than the twenty-byte all-zero address every Ethereum Virtual Machine
123
- // chain uses. The two sentinels are therefore different strings.
124
64
  nativeAssetSentinel: '0x00000000000000000000000000000000',
125
- // THE WORST OF THE SIX, and the one that was actively misleading. The
126
- // interface's source picker never offers Hyperliquid — nothing can quote it
127
- // — yet the landing page drew a pill for it that set the source chain to
128
- // Hyperliquid on click. A reader who pressed it arrived at a chain the form
129
- // does not list and no provider answers. Removing the pill removes that dead
130
- // end; the descriptor stays because the route layer still has to be able to
131
- // NAME the chain.
132
65
  endToEndProof: 'pending',
133
- // This predicate checks TOKEN addresses, and HyperCore identifies a token by a
134
- // hex string SHORTER than an Ethereum address — sixteen bytes for most,
135
- // nineteen for a few — so the twenty-byte `isAddress` check would reject the
136
- // very identifiers it exists to accept. Its only consumer silently falls back
137
- // to the native sentinel when this returns false, so an over-strict pattern
138
- // would quietly swallow valid token links instead of surfacing an error.
139
- // Accounts are a different namespace (ordinary twenty-byte Ethereum
140
- // addresses) and are validated by the wallet layer, not here.
141
66
  isValidAddress: (value) => hyperliquidTokenId.test(value),
142
- // Hex is case-insensitive and Relay emits lower case, so lower case is the
143
- // canonical form. It matters: the token-equality check compares non-Ethereum
144
- // addresses exactly, and only a single canonical case makes that correct.
145
67
  normalizeAddress: (value) => hyperliquidTokenId.test(value) ? value.toLowerCase() : value,
146
68
  addressCasing: 'significant',
147
69
  };
148
70
  const zcashDescriptor = {
149
71
  id: 'zcash',
150
72
  label: 'Zcash',
151
- // Zcash is not a Relay chain and has no widely-registered chain-agnostic
152
- // namespace. `zcash` is this repository's own key, and nothing maps a
153
- // connected wallet through it, because no wallet connects to this family.
154
73
  caipNamespace: 'zcash',
155
- // DERIVED, NOT INVENTED: the transparent pay-to-public-key-hash address of
156
- // an all-zero key hash, base58check over the mainnet prefix `1cb8` followed
157
- // by twenty zero bytes. Nobody holds a key for it. It follows the pattern of
158
- // Relay's Bitcoin sentinel, an all-zero witness program, and for the same
159
- // reason: the sentinel stands where a token address is expected, so it must
160
- // pass this family's own address check. Zcash carries no tokens on this
161
- // route, so the native asset is the only asset.
162
74
  nativeAssetSentinel: 't1Hsc1LR8yKnbbe3twRp88p6vFfC5t7DLbs',
163
- // THE FIRST ECOSYSTEM WITH NO WALLET AT ALL, and therefore the first whose
164
- // hold cannot be cleared by connecting one. The reader pays an address by
165
- // hand from wherever they hold the coin, so every part of the journey that
166
- // this repository owns — the quote, the address, the amount, the deadline,
167
- // the status poll — is exercised only by a fixture. Nothing funded has
168
- // crossed. Clearing this is one word, after one transfer.
169
75
  endToEndProof: 'pending',
170
76
  isValidAddress: (value) => transparentZcash.test(value),
171
- // base58check is case-significant; canonical form is the input unchanged.
172
77
  normalizeAddress: (value) => value,
173
78
  addressCasing: 'significant',
174
79
  };
175
- /**
176
- * Every ecosystem the bridge can originate from. LI.FI-compatible one first.
177
- *
178
- * Registration means the library can MODEL and QUOTE the ecosystem, not that an
179
- * interface can sign it — see the note on {@link Ecosystem}.
180
- *
181
- * It means less than that again where a public claim is concerned: read
182
- * {@link EndToEndProof} before listing this registry anywhere a reader will
183
- * take it as an offer.
184
- */
185
80
  export const ecosystemRegistry = [
186
81
  evmDescriptor,
187
82
  svmDescriptor,
@@ -192,14 +87,6 @@ export const ecosystemRegistry = [
192
87
  zcashDescriptor,
193
88
  ];
194
89
  const byId = new Map(ecosystemRegistry.map((descriptor) => [descriptor.id, descriptor]));
195
- /**
196
- * Looks up the descriptor for an ecosystem id. Total over the `Ecosystem` union;
197
- * throws only if the registry is ever left incomplete (a programming error the
198
- * round-trip test would catch).
199
- *
200
- * @param id - the ecosystem key
201
- * @returns the descriptor whose `id` equals `id`
202
- */
203
90
  export const ecosystemById = (id) => {
204
91
  const descriptor = byId.get(id);
205
92
  if (descriptor === undefined) {
@@ -207,82 +94,12 @@ export const ecosystemById = (id) => {
207
94
  }
208
95
  return descriptor;
209
96
  };
210
- /**
211
- * Whether a transfer has been carried through this ecosystem end to end.
212
- *
213
- * The predicate a surface calls before ADVERTISING an origin. It is separate
214
- * from anything the router asks, and deliberately so: the router must keep
215
- * quoting and naming every registered ecosystem, because a reader who reaches
216
- * an unproven chain through a link still needs the page to work. This governs
217
- * only what the product offers unprompted.
218
- *
219
- * @param id - the ecosystem key
220
- * @returns true when a real transfer has completed through it
221
- */
222
97
  export const isEcosystemProvenEndToEnd = (id) => ecosystemById(id).endToEndProof === 'verified';
223
98
  const byNamespace = new Map(ecosystemRegistry.map((descriptor) => [descriptor.caipNamespace, descriptor]));
224
- /**
225
- * Resolves an ecosystem from a chain-agnostic identifier namespace.
226
- *
227
- * @param namespace - the leading segment of a "namespace:reference:address" id
228
- * @returns the matching descriptor, or undefined when the namespace is unknown
229
- */
230
99
  export const ecosystemByCaipNamespace = (namespace) => byNamespace.get(namespace);
231
- /**
232
- * Whether two addresses in the same ecosystem name the same thing.
233
- *
234
- * THE ONE PLACE THIS QUESTION IS ANSWERED. It was answered in two: this
235
- * registry, which knows each family's address rules, and a ternary in the
236
- * interface's routing module that hard-coded "Ethereum is case-insensitive,
237
- * everything else is not". Two homes means a family added here is still
238
- * compared wrongly there, silently, because a loose comparison does not fail —
239
- * it merges two different accounts into one.
240
- *
241
- * @param a - an address
242
- * @param b - an address, in the same ecosystem
243
- * @param ecosystem - the ecosystem both belong to
244
- * @returns whether they are the same address
245
- */
246
100
  export const sameAddress = (a, b, ecosystem) => ecosystemById(ecosystem).addressCasing === 'insensitive'
247
101
  ? a.toLowerCase() === b.toLowerCase()
248
102
  : a === b;
249
- /**
250
- * Whether an address is the ecosystem's own native asset rather than a token.
251
- *
252
- * Every ecosystem already declares its `nativeAssetSentinel`, and this is the
253
- * predicate that reads it. The comparison goes through {@link sameAddress}
254
- * rather than `toLowerCase()`, which matters for exactly the families where the
255
- * hand-written checks were wrong to begin with: Solana's, Bitcoin's and The
256
- * Open Network's sentinels are case-significant, so lower-casing them before
257
- * comparing can match an address that is not the sentinel.
258
- *
259
- * @param value - the address to test
260
- * @param ecosystem - the ecosystem it belongs to
261
- * @returns whether it names the native asset
262
- */
263
103
  export const isNativeAsset = (value, ecosystem) => sameAddress(value, ecosystemById(ecosystem).nativeAssetSentinel, ecosystem);
264
- /**
265
- * Whether a string is a well-formed address in this ecosystem.
266
- *
267
- * A free function over the registry, so a caller needs the ecosystem key and
268
- * nothing else. Reaching for viem's `isAddress` directly is the thing this
269
- * replaces: it answers only for Ethereum and answers FALSE — confidently — for
270
- * every valid Solana, Bitcoin, Tron and Open Network address.
271
- *
272
- * @param value - the string to test
273
- * @param ecosystem - the ecosystem to test it against
274
- * @returns whether the string is a valid address there
275
- */
276
104
  export const isValidAddressIn = (value, ecosystem) => ecosystemById(ecosystem).isValidAddress(value);
277
- /**
278
- * The ecosystem's canonical spelling of an address.
279
- *
280
- * Total and side-effect-free: an input this ecosystem cannot parse comes back
281
- * unchanged rather than throwing, because normalizing is something callers do
282
- * on the way to a comparison and a throw there would be a crash on a typo.
283
- *
284
- * @param value - the address to normalize
285
- * @param ecosystem - the ecosystem it belongs to
286
- * @returns the canonical form, or the input when it cannot be parsed
287
- */
288
105
  export const normalizeAddressIn = (value, ecosystem) => ecosystemById(ecosystem).normalizeAddress(value);
package/dist/execution.js CHANGED
@@ -6,21 +6,14 @@ import { aggregatorOriginCalls, } from './consolidated-quote.js';
6
6
  import { pulseXSwapPathForHop, venueForEdge } from './routing/carriers/pulsex.js';
7
7
  import { buildPulseXSwapTransaction, buildWrapTransaction, swapMinimumOut } from './routing/swap-transactions.js';
8
8
  import { approvalAmounts, approveCall, bridgePathway, destinationDataParam, feeDirectorStructEncoded, feeTypeSettings, omnibridgeApproval, transactionInputs, } from './settings.js';
9
- /**
10
- * An {@link AllowanceReader} that reports no allowance anywhere, so the plan
11
- * includes every approval. For a caller that reads allowances itself at send
12
- * time.
13
- */
14
9
  export const assumeNoAllowance = async () => 0n;
15
10
  const refusal = (value) => ({ kind: 'refused', refusal: value });
16
- /** The fee director a delivery choice encodes, or null when the reader buys no delivery. */
17
11
  const encodeDelivery = ({ leg, pathway, }) => {
18
12
  const { delivery } = leg;
19
13
  if (!delivery.shouldDeliver)
20
14
  return { kind: 'encoded', feeDirector: null };
21
15
  if (!pathway.requiresDelivery)
22
16
  return refusal({ reason: 'delivery-not-offered' });
23
- // A zero fee pays no relayer, so the transfer would wait for a delivery that never comes.
24
17
  if (delivery.fee.maximumFee <= 0n)
25
18
  return refusal({ reason: 'delivery-fee-missing' });
26
19
  const feeDirector = feeDirectorStructEncoded({
@@ -95,16 +88,6 @@ const omnibridgeCalls = (leg, account) => {
95
88
  delivery: deliveryTermsOf(leg.delivery),
96
89
  };
97
90
  };
98
- /**
99
- * The venue a swap edge was priced on, from the venues execution was given.
100
- * A router that is not in those venues THROWS: signing on another router, on
101
- * PulseX most of all, would trade against pools the reader was never shown.
102
- *
103
- * @param venues - the venues passed to execution
104
- * @param edge - the swap edge
105
- * @returns the venue
106
- * @throws when the edge's router is not in `venues`, or its chain has no venue
107
- */
108
91
  const requireSwapVenue = (venues, edge) => {
109
92
  const venue = venueForEdge(venues, edge);
110
93
  if (venue !== null)
@@ -119,8 +102,6 @@ const pulseXSwapCalls = (leg, venues) => {
119
102
  if (edge.carrier !== 'PulseX')
120
103
  return refusal({ reason: 'swap-refused' });
121
104
  const routerAddress = requireSwapVenue(venues, edge).router;
122
- // The builder refuses a hop that records no priced path, a router other
123
- // than the edge's own, and a leg on screen that disagrees.
124
105
  const call = buildPulseXSwapTransaction({
125
106
  hop: leg.hop,
126
107
  amountIn: leg.amountIn,
@@ -185,7 +166,6 @@ const legCalls = (leg, account, venues) => {
185
166
  return wrapCalls(leg);
186
167
  return aggregatorCalls(leg);
187
168
  };
188
- /** The approve transactions an approval needs now: none when the allowance on chain covers it. */
189
169
  const approvalTransactions = async ({ approval, account, readAllowance, }) => {
190
170
  const allowance = await readAllowance(approval.chainId, approval.token, account, approval.spender);
191
171
  return approvalAmounts({ allowance, minimum: approval.minimum }).map((amount) => ({
@@ -195,20 +175,6 @@ const approvalTransactions = async ({ approval, account, readAllowance, }) => {
195
175
  gasLimit: null,
196
176
  }));
197
177
  };
198
- /**
199
- * Plans the transactions for a list of legs, in order.
200
- *
201
- * Each leg contributes the exact approval it needs when the allowance on
202
- * chain does not cover it (a reset to zero first when a smaller allowance
203
- * remains), then its own call. Nothing is sent.
204
- *
205
- * @param options.account - the account that signs every transaction
206
- * @param options.legs - the legs, in the order the wallet signs them
207
- * @param options.readAllowance - reads an allowance on chain; see {@link AllowanceReader}
208
- * @param options.swapVenues - the routers swaps may sign on, merged over the defaults; a swap whose router is in none of them throws
209
- * @returns the ordered transactions, or the first leg that cannot be planned
210
- * @throws when a swap leg's router is not in the swap venues
211
- */
212
178
  export const planExecution = async ({ account, legs, readAllowance, swapVenues, }) => {
213
179
  const venues = resolveSwapVenues(swapVenues);
214
180
  const calls = legs.map((leg) => legCalls(leg, account, venues));
@@ -274,7 +240,6 @@ const pulseXSectionLeg = (hop, inputs) => {
274
240
  if (quotedAmountOut !== undefined && quotedAmountOut <= 0n) {
275
241
  return { kind: 'refused', refusal: { reason: 'swap-quote-not-positive' } };
276
242
  }
277
- // Without a fresh quote the floor comes from the hop's own figure, so it must be a real quote.
278
243
  if (quotedAmountOut === undefined && hop.outcome.kind !== 'quote') {
279
244
  return { kind: 'refused', refusal: { reason: 'swap-estimate-only' } };
280
245
  }
@@ -293,7 +258,6 @@ const pulseXSectionLeg = (hop, inputs) => {
293
258
  },
294
259
  };
295
260
  };
296
- /** Turns one section of an engine plan into its leg, dispatching on the first hop's carrier. */
297
261
  const planSectionLeg = ({ hops, inputs, resolveToken, }) => {
298
262
  const firstHop = hops[0];
299
263
  if (firstHop === undefined)
@@ -301,8 +265,6 @@ const planSectionLeg = ({ hops, inputs, resolveToken, }) => {
301
265
  const { carrier, origin, from, to } = firstHop.edge;
302
266
  if (origin === 'by-hand')
303
267
  return { kind: 'refused', refusal: { reason: 'paid-by-hand' } };
304
- // An aggregator section may run several hops inside one signature (a fused
305
- // omnibridge entry); every other carrier signs exactly one hop.
306
268
  const singleHop = hops.length === 1;
307
269
  if (isOmnibridgeCarrier(carrier)) {
308
270
  if (!singleHop)
@@ -321,18 +283,7 @@ const planSectionLeg = ({ hops, inputs, resolveToken, }) => {
321
283
  }
322
284
  return aggregatorSectionLeg(carrier, inputs);
323
285
  };
324
- /** The sections of a plan, each as its hops. */
325
286
  const planSections = (plan) => plan.signatures.map((group) => group.flatMap((hopIndex) => plan.hops[hopIndex] ?? []));
326
- /**
327
- * A candidate built without an engine plan is one signature: either a
328
- * same-chain omnibridge crossing (`buildDirectCrossingCandidate`, whose quote
329
- * names the bridge) or an aggregator quote.
330
- *
331
- * THE CANDIDATE'S OWN QUOTE IS NEVER SIGNED. It priced the route on screen
332
- * and may be stale or a dry probe; only the live quote in
333
- * `inputs.aggregator` is. Without one the section is refused with
334
- * `live-quote-required`.
335
- */
336
287
  const planlessSectionLeg = (candidate, inputs) => {
337
288
  const provider = candidate.quote.provider;
338
289
  if (isOmnibridgeCarrier(provider)) {
@@ -346,10 +297,6 @@ const planlessSectionLeg = (candidate, inputs) => {
346
297
  }
347
298
  return aggregatorSectionLeg(provider, inputs);
348
299
  };
349
- /**
350
- * What is wrong with the requested window of sections, or null when it fits.
351
- * A window starts at `firstSectionIndex` and spans `plannedCount` sections.
352
- */
353
300
  const sectionWindowProblem = ({ firstSectionIndex, plannedCount, sectionCount, }) => {
354
301
  if (!Number.isSafeInteger(firstSectionIndex) || firstSectionIndex < 0) {
355
302
  return `firstSectionIndex must be a non-negative safe integer, received ${firstSectionIndex}`;
@@ -361,32 +308,6 @@ const sectionWindowProblem = ({ firstSectionIndex, plannedCount, sectionCount, }
361
308
  }
362
309
  return null;
363
310
  };
364
- /**
365
- * Plans the transactions for a chosen route candidate: the candidate
366
- * `buildRouteCandidateFromPlan` returns for an engine plan (the interface's
367
- * `buildCandidateFromEnginePlan`), or a candidate built without a plan.
368
- *
369
- * ONE SECTION IS ONE SIGNATURE. A plan's `signatures` groups its hops into
370
- * sections; the section's first hop names its carrier. An omnibridge section
371
- * becomes a crossing, a PulseX section a swap signed on its recorded path, a
372
- * wrap section a wrap or unwrap, and any other wallet-signed carrier an
373
- * aggregator deposit from the live quote the caller supplies. A section paid
374
- * by hand has nothing to sign and is refused.
375
- *
376
- * A LATER SECTION CAN BE PLANNED ALONE. Pass `firstSectionIndex` to skip the
377
- * sections that already landed. The wallet then plans the swap after the
378
- * crossing arrived, from the amount that arrived, with a fresh
379
- * `swap.quotedAmountOut` and a fresh `swap.deadline`. The slippage floor and the
380
- * deadline are not fixed minutes before the swap is signed. Every safety check
381
- * still applies to the later section: verified spenders, live quotes for
382
- * aggregator sections, exact approvals.
383
- *
384
- * Every `sectionIndex` in a refusal and every `legIndex` in `deliveries` is
385
- * the absolute section index.
386
- *
387
- * @param inputs - see {@link CandidateExecutionInputs}
388
- * @returns the ordered transactions with the deliveries they buy, or the first section that cannot be planned
389
- */
390
311
  export const planCandidateExecution = async (inputs) => {
391
312
  const { candidate, account, readAllowance, resolveToken, sections, swapVenues } = inputs;
392
313
  const firstSectionIndex = inputs.firstSectionIndex ?? 0;
@@ -422,19 +343,6 @@ export const planCandidateExecution = async (inputs) => {
422
343
  remainingSectionCount: sectionCount - firstSectionIndex - sections.length,
423
344
  };
424
345
  };
425
- /**
426
- * Reads a fresh quote for a PulseX section: what the router's `getAmountsOut`
427
- * says the swap delivers for `amountIn`, along the exact path the swap signs
428
- * ({@link pulseXSwapPathForHop}) on the edge's own router. A wallet passes the
429
- * result as `swap.quotedAmountOut` just before it signs.
430
- *
431
- * @param options.hop - the swap hop; its recorded path and edge name what to read
432
- * @param options.amountIn - the amount the swap spends, in base units
433
- * @param options.client - a client that can call `readContract`
434
- * @param options.swapVenues - the routers swaps may sign on, merged over the defaults
435
- * @returns the amount the last token of the path delivers, or null when the hop records no path, the amount is not positive, or the read fails or returns nothing
436
- * @throws when the hop records a path but its router is not in the swap venues
437
- */
438
346
  export const readPulseXSectionQuote = async ({ hop, amountIn, client, swapVenues, }) => {
439
347
  const path = pulseXSwapPathForHop(hop);
440
348
  if (path === null || amountIn <= 0n)
package/dist/fee-type.js CHANGED
@@ -1,9 +1,7 @@
1
- /** The type of fee to use for the bridge */
2
1
  export var FeeType;
3
2
  (function (FeeType) {
4
3
  FeeType["PERCENT"] = "%";
5
4
  FeeType["GAS_TIP"] = "gas+%";
6
5
  FeeType["FIXED"] = "fixed";
7
6
  })(FeeType || (FeeType = {}));
8
- /** A map of fee type values to keys */
9
7
  export const feeTypeValToKeyMap = new Map(Object.keys(FeeType).map((key) => [FeeType[key], key]));