@gibs/bridge-sdk 1.12.0 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +234 -432
  3. package/dist/abis.d.ts +1 -0
  4. package/dist/bridge-limits.d.ts +103 -0
  5. package/dist/bridge-limits.js +84 -0
  6. package/dist/chain-info.d.ts +169 -0
  7. package/dist/chain-info.js +410 -0
  8. package/dist/chain-names.d.ts +160 -0
  9. package/dist/chain-names.js +200 -0
  10. package/dist/chains.d.ts +65 -0
  11. package/dist/chains.js +284 -0
  12. package/dist/config.d.ts +216 -0
  13. package/dist/config.js +493 -0
  14. package/dist/consolidated-quote.d.ts +554 -0
  15. package/dist/consolidated-quote.js +773 -0
  16. package/dist/destination-calldata.d.ts +118 -0
  17. package/dist/destination-calldata.js +152 -0
  18. package/dist/ecosystems.d.ts +217 -0
  19. package/dist/ecosystems.js +288 -0
  20. package/dist/fee-type.d.ts +10 -0
  21. package/dist/fee-type.js +9 -0
  22. package/dist/hyperliquid-execution.d.ts +152 -0
  23. package/dist/hyperliquid-execution.js +168 -0
  24. package/dist/image-links.d.ts +117 -0
  25. package/dist/image-links.js +142 -0
  26. package/dist/index.d.ts +20 -0
  27. package/dist/index.js +20 -0
  28. package/dist/lifi-execution-guard.d.ts +199 -0
  29. package/dist/lifi-execution-guard.js +400 -0
  30. package/dist/near-intents-conformance.d.ts +85 -0
  31. package/dist/near-intents-conformance.js +329 -0
  32. package/dist/near-intents-route.d.ts +47 -0
  33. package/dist/near-intents-route.js +265 -0
  34. package/dist/near-intents.d.ts +524 -0
  35. package/dist/near-intents.js +1055 -0
  36. package/dist/origin-deposit.d.ts +326 -0
  37. package/dist/origin-deposit.js +68 -0
  38. package/dist/pre-quote.d.ts +198 -0
  39. package/dist/pre-quote.js +175 -0
  40. package/dist/prices.d.ts +107 -0
  41. package/dist/prices.js +162 -0
  42. package/dist/providers.d.ts +221 -0
  43. package/dist/providers.js +157 -0
  44. package/dist/quote-fees.d.ts +102 -0
  45. package/dist/quote-fees.js +195 -0
  46. package/dist/referral.d.ts +77 -0
  47. package/dist/referral.js +72 -0
  48. package/dist/relay-currencies.d.ts +47 -0
  49. package/dist/relay-currencies.js +89 -0
  50. package/dist/relay-quote.d.ts +301 -0
  51. package/dist/relay-quote.js +983 -0
  52. package/dist/route-steps.d.ts +138 -0
  53. package/dist/route-steps.js +305 -0
  54. package/dist/routing/amounts.d.ts +80 -0
  55. package/dist/routing/amounts.js +120 -0
  56. package/dist/routing/candidate-route.d.ts +43 -0
  57. package/dist/routing/candidate-route.js +45 -0
  58. package/dist/routing/candidate.d.ts +237 -0
  59. package/dist/routing/candidate.js +96 -0
  60. package/dist/routing/carriers/ask-answer.d.ts +60 -0
  61. package/dist/routing/carriers/ask-answer.js +1 -0
  62. package/dist/routing/carriers/omnibridge.d.ts +137 -0
  63. package/dist/routing/carriers/omnibridge.js +419 -0
  64. package/dist/routing/carriers/pulsex.d.ts +149 -0
  65. package/dist/routing/carriers/pulsex.js +453 -0
  66. package/dist/routing/carriers/wrap.d.ts +25 -0
  67. package/dist/routing/carriers/wrap.js +115 -0
  68. package/dist/routing/composed-route-quality.d.ts +103 -0
  69. package/dist/routing/composed-route-quality.js +108 -0
  70. package/dist/routing/entry-chain-anchor.d.ts +88 -0
  71. package/dist/routing/entry-chain-anchor.js +55 -0
  72. package/dist/routing/format.d.ts +31 -0
  73. package/dist/routing/format.js +58 -0
  74. package/dist/routing/index.d.ts +34 -0
  75. package/dist/routing/index.js +42 -0
  76. package/dist/routing/producers.d.ts +770 -0
  77. package/dist/routing/producers.js +1564 -0
  78. package/dist/routing/providers.d.ts +26 -0
  79. package/dist/routing/providers.js +38 -0
  80. package/dist/routing/route-data.d.ts +355 -0
  81. package/dist/routing/route-data.js +1 -0
  82. package/dist/routing/search/compose.d.ts +168 -0
  83. package/dist/routing/search/compose.js +98 -0
  84. package/dist/routing/search/graph.d.ts +653 -0
  85. package/dist/routing/search/graph.js +445 -0
  86. package/dist/routing/search/plan-route-data.d.ts +244 -0
  87. package/dist/routing/search/plan-route-data.js +767 -0
  88. package/dist/routing/search/score.d.ts +353 -0
  89. package/dist/routing/search/score.js +427 -0
  90. package/dist/routing/usd-value.d.ts +17 -0
  91. package/dist/routing/usd-value.js +25 -0
  92. package/dist/settings.d.ts +692 -0
  93. package/dist/settings.js +909 -0
  94. package/dist/ton-execution.d.ts +117 -0
  95. package/dist/ton-execution.js +247 -0
  96. package/dist/tron-execution.d.ts +146 -0
  97. package/dist/tron-execution.js +239 -0
  98. package/dist/types.d.ts +51 -0
  99. package/dist/types.js +1 -0
  100. package/llms.txt +169 -0
  101. package/package.json +204 -23
  102. package/src/chain-info.test.ts +0 -968
  103. package/src/chain-info.ts +0 -498
  104. package/src/chains.test.ts +0 -123
  105. package/src/chains.ts +0 -88
  106. package/src/config.test.ts +0 -679
  107. package/src/config.ts +0 -500
  108. package/src/fee-type.test.ts +0 -45
  109. package/src/fee-type.ts +0 -14
  110. package/src/image-links.test.ts +0 -160
  111. package/src/image-links.ts +0 -65
  112. package/src/index.ts +0 -8
  113. package/src/settings.test.ts +0 -2754
  114. package/src/settings.ts +0 -875
  115. package/src/types.ts +0 -48
  116. package/tsconfig.json +0 -17
  117. package/vitest.config.ts +0 -13
  118. /package/{src/abis.ts → dist/abis.js} +0 -0
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Executing a deposit on Tron, as pure data.
3
+ *
4
+ * Relay does not return a broadcastable Tron transaction. It returns the
5
+ * *parameters* of a `TriggerSmartContract` call — owner, contract, call value
6
+ * and calldata — and nothing else. A Tron transaction additionally needs a
7
+ * reference block, an expiration and a fee limit, none of which Relay supplies.
8
+ * Those are produced by asking a full node to build the transaction, which is
9
+ * what {@link buildTriggerSmartContractRequest} constructs the body for. The
10
+ * node answers with an UNSIGNED transaction; the wallet signs it and a second
11
+ * call broadcasts it.
12
+ *
13
+ * Three details of that exchange lose money or mislead if handled naively:
14
+ *
15
+ * **The `visible` flag defaults to true.** A node reads addresses as base58
16
+ * unless told otherwise, but Relay returns them as hex with Tron's `41` prefix.
17
+ * Omitting the flag therefore hands base58-parsing a hex string. The request
18
+ * built here always states `visible: false` rather than relying on a default
19
+ * that is the opposite of what the payload contains.
20
+ *
21
+ * **`fee_limit` is required and Relay omits it.** It caps the energy the caller
22
+ * will pay for. Set too low, the call runs out of energy, reverts, and the
23
+ * energy already burned is NOT refunded — the deposit fails and costs the user
24
+ * anyway.
25
+ *
26
+ * **A rejected call still answers HTTP 200**, with the reason hex-encoded in
27
+ * `result.message`. Reading the status code alone reports a failed deposit as a
28
+ * success, and printing the message unmodified shows the user a wall of hex.
29
+ * {@link tronErrorMessage} decodes it.
30
+ *
31
+ * Kept free of transport and of clock reads so it is fully testable: nothing
32
+ * here signs, fetches, or calls `Date.now`. The caller performs the requests.
33
+ */
34
+ /** Path of the full-node endpoint that builds an unsigned contract call. */
35
+ export const tronTriggerSmartContractPath = '/wallet/triggersmartcontract';
36
+ /**
37
+ * The fee limit used when a caller states none, in sun (1 TRX = 1,000,000 sun).
38
+ *
39
+ * Thirty TRX, matching Relay's own wallet adapter, which is the only reference
40
+ * implementation of this deposit path. It is an upper bound rather than a
41
+ * charge: unused energy is not taken, so the cost of setting it generously is
42
+ * only that the wallet shows a larger worst case.
43
+ */
44
+ export const defaultTronFeeLimitSun = 30_000_000;
45
+ /**
46
+ * The largest fee limit a node accepts, in sun — 15,000 TRX. Documented as a
47
+ * hard protocol cap, so a request above it is rejected outright rather than
48
+ * clamped.
49
+ */
50
+ export const maximumTronFeeLimitSun = 15_000_000_000;
51
+ /**
52
+ * Tron's `41` address prefix, the hex form's equivalent of the base58 `T`.
53
+ */
54
+ const tronHexAddressPrefix = '41';
55
+ /** A hex address is the prefix byte plus a twenty-byte account identifier. */
56
+ const tronHexAddressLength = 42;
57
+ /**
58
+ * Normalizes a Tron hex address for comparison.
59
+ *
60
+ * Wallets and quotes disagree on casing, and Relay's payload is not guaranteed
61
+ * to match what a wallet reports even when both name the same account.
62
+ *
63
+ * @param value - an address in Tron's `41`-prefixed hex form, in any casing,
64
+ * optionally written with a `0x` prefix
65
+ * @returns the lowercase `41` form, or null when the value is not that form
66
+ */
67
+ export const normalizeTronHexAddress = (value) => {
68
+ const withoutPrefix = value.trim().replace(/^0x/i, '').toLowerCase();
69
+ if (withoutPrefix.length !== tronHexAddressLength) {
70
+ return null;
71
+ }
72
+ if (!withoutPrefix.startsWith(tronHexAddressPrefix)) {
73
+ return null;
74
+ }
75
+ return /^[0-9a-f]+$/.test(withoutPrefix) ? withoutPrefix : null;
76
+ };
77
+ /**
78
+ * Whether two Tron hex addresses name the same account.
79
+ *
80
+ * Used to check a deposit's owner against the connected wallet before signing.
81
+ * A mismatch means the quote was built for a different account, and signing it
82
+ * would spend from an account the user did not choose — or simply fail after
83
+ * the user has already approved.
84
+ *
85
+ * @param left - an address in Tron's `41`-prefixed hex form
86
+ * @param right - an address in the same form
87
+ * @returns true only when both parse and name the same account
88
+ */
89
+ export const isSameTronAccount = (left, right) => {
90
+ const normalizedLeft = normalizeTronHexAddress(left);
91
+ const normalizedRight = normalizeTronHexAddress(right);
92
+ return normalizedLeft !== null && normalizedLeft === normalizedRight;
93
+ };
94
+ /**
95
+ * Strips the `0x` prefix Tron calldata never carries.
96
+ *
97
+ * Relay documents this field as bare hex, but its own adapter strips the prefix
98
+ * defensively, and a prefixed value is parsed by a node as calldata beginning
99
+ * with the bytes `0x` rather than being rejected — a silently wrong call.
100
+ */
101
+ const bareHex = (value) => value.replace(/^0x/i, '').toLowerCase();
102
+ /**
103
+ * Whether a numeric amount survives the trip through JSON as an exact integer.
104
+ *
105
+ * Sun amounts are 64-bit on the wire but JSON carries them as doubles, so a
106
+ * large enough value would be silently rounded — into a different amount of
107
+ * money than the one that was quoted.
108
+ */
109
+ const isExactInteger = (value) => value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER);
110
+ /**
111
+ * Builds the body that asks a full node to construct the unsigned deposit.
112
+ *
113
+ * @param deposit - the parsed origin deposit
114
+ * @param options.feeLimitSun - the energy spending cap in sun; defaults to
115
+ * {@link defaultTronFeeLimitSun}
116
+ * @returns the request body, or null when the deposit names an address that is
117
+ * not Tron hex, carries a call value that cannot be represented exactly, or
118
+ * the fee limit is outside what a node accepts — never a body that would be
119
+ * built against a default `visible` the payload contradicts
120
+ */
121
+ export const buildTriggerSmartContractRequest = (deposit, { feeLimitSun = defaultTronFeeLimitSun } = {}) => {
122
+ const ownerAddress = normalizeTronHexAddress(deposit.ownerAddress);
123
+ const contractAddress = normalizeTronHexAddress(deposit.contractAddress);
124
+ if (ownerAddress === null || contractAddress === null) {
125
+ return null;
126
+ }
127
+ if (!isExactInteger(deposit.callValue)) {
128
+ return null;
129
+ }
130
+ if (!Number.isInteger(feeLimitSun) || feeLimitSun <= 0 || feeLimitSun > maximumTronFeeLimitSun) {
131
+ return null;
132
+ }
133
+ return {
134
+ owner_address: ownerAddress,
135
+ contract_address: contractAddress,
136
+ data: bareHex(deposit.data),
137
+ call_value: Number(deposit.callValue),
138
+ fee_limit: feeLimitSun,
139
+ visible: false,
140
+ };
141
+ };
142
+ /**
143
+ * Decodes a node's hex-encoded failure message.
144
+ *
145
+ * A node reports why it refused a call in `result.message`, hex-encoded — the
146
+ * bytes `4e6f20636f6e7472616374…` rather than "No contract or not a valid smart
147
+ * contract". Broadcast failures report plain text in the same field, so both
148
+ * are handled and a value that is not hex is passed through unchanged.
149
+ */
150
+ const decodeTronMessage = (value) => {
151
+ const trimmed = value.trim();
152
+ if (trimmed.length === 0 || trimmed.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(trimmed)) {
153
+ return trimmed;
154
+ }
155
+ let decoded = '';
156
+ for (let index = 0; index < trimmed.length; index += 2) {
157
+ decoded += String.fromCharCode(Number.parseInt(trimmed.slice(index, index + 2), 16));
158
+ }
159
+ // A hex string that decodes to control characters was a number, not a
160
+ // message — return the original rather than a string of unprintable bytes.
161
+ return /^[\x20-\x7e]*$/.test(decoded) ? decoded : trimmed;
162
+ };
163
+ const asRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value)
164
+ ? value
165
+ : null;
166
+ /**
167
+ * Extracts a node's failure reason from either endpoint's response.
168
+ *
169
+ * @param payload - the parsed response body
170
+ * @returns the decoded message, or null when the response reports no failure
171
+ */
172
+ export const tronErrorMessage = (payload) => {
173
+ const body = asRecord(payload);
174
+ if (body === null) {
175
+ return null;
176
+ }
177
+ // The build endpoint nests its outcome under `result`; the broadcast endpoint
178
+ // reports at the top level.
179
+ const nested = asRecord(body.result);
180
+ const message = nested?.message ?? body.message;
181
+ if (typeof message !== 'string') {
182
+ return null;
183
+ }
184
+ const decoded = decodeTronMessage(message);
185
+ return decoded.length === 0 ? null : decoded;
186
+ };
187
+ /**
188
+ * Reads the unsigned transaction out of a build response.
189
+ *
190
+ * The response carries the transaction at the top level alongside a `result`
191
+ * object stating whether the build succeeded, so the presence of a `txID` is
192
+ * not on its own evidence of success.
193
+ *
194
+ * @param payload - the parsed `/wallet/triggersmartcontract` response
195
+ * @returns the unsigned transaction to hand a wallet, or null when the node
196
+ * refused the call
197
+ */
198
+ export const readTriggerSmartContractResponse = (payload) => {
199
+ const body = asRecord(payload);
200
+ if (body === null) {
201
+ return null;
202
+ }
203
+ const outcome = asRecord(body.result);
204
+ if (outcome === null || outcome.result !== true) {
205
+ return null;
206
+ }
207
+ const transaction = asRecord(body.transaction);
208
+ if (transaction === null || typeof transaction.txID !== 'string') {
209
+ return null;
210
+ }
211
+ return transaction;
212
+ };
213
+ /**
214
+ * Reads the transaction identifier out of a broadcast response.
215
+ *
216
+ * GOTCHA: a rejected broadcast answers HTTP 200, and `result` is OMITTED rather
217
+ * than set to false. Testing `result === false` therefore never fires, and
218
+ * trusting the status code records a deposit that never entered a block. Only
219
+ * an explicit `result: true` is accepted here.
220
+ *
221
+ * The identifier is read from `txid` where a node reports it, falling back to
222
+ * the echoed transaction's `txID`. The two spellings differ by casing and by
223
+ * position: a node answers `txid`, while a wallet library that echoes the
224
+ * transaction it broadcast carries `txID` inside it. Accepting only the first
225
+ * would report an accepted broadcast as a failure whenever the caller is one of
226
+ * those libraries.
227
+ *
228
+ * @param payload - the parsed `/wallet/broadcasttransaction` response
229
+ * @returns the transaction identifier, or null when the broadcast was refused
230
+ */
231
+ export const readBroadcastResponse = (payload) => {
232
+ const body = asRecord(payload);
233
+ if (body === null || body.result !== true) {
234
+ return null;
235
+ }
236
+ const echoed = asRecord(body.transaction);
237
+ const identifier = body.txid ?? echoed?.txID;
238
+ return typeof identifier === 'string' && identifier.length > 0 ? identifier : null;
239
+ };
@@ -0,0 +1,51 @@
1
+ import type { Chain, Hex } from 'viem';
2
+ import type { Chains, Provider } from './config.js';
3
+ export type { RouteStep, RouteStepKind } from './route-steps.js';
4
+ /** The per network bridge link */
5
+ export type PerNetworkBridgeLink = {
6
+ tokenAddress: Hex;
7
+ };
8
+ /** The extensions for a token */
9
+ export type Extensions = {
10
+ wrapped?: {
11
+ address: Hex;
12
+ };
13
+ bridgeInfo?: Record<number, PerNetworkBridgeLink>;
14
+ };
15
+ /** The metadata for a token */
16
+ export type TokenMetadata = {
17
+ name: string;
18
+ symbol: string;
19
+ decimals: number;
20
+ };
21
+ /** The token for a given asset */
22
+ export type Token = TokenMetadata & {
23
+ address: string;
24
+ chainId: number;
25
+ logoURI: string | null;
26
+ extensions?: Extensions;
27
+ /**
28
+ * Spot USD price as a decimal string, when a price source provides one inline
29
+ * (e.g. the LI.FI `/tokens` response). Absent/null for tokens from sources
30
+ * that carry no price (e.g. the dapp image lists).
31
+ */
32
+ priceUSD?: string | null;
33
+ };
34
+ /** The token out for a given asset */
35
+ export type TokenOut = Omit<Token, 'address'> & {
36
+ address: string | null;
37
+ };
38
+ /** The token list for a given asset */
39
+ export type TokenList = {
40
+ tokens: Token[];
41
+ };
42
+ /** The bridge key for a given provider, from chain, and to chain */
43
+ export type BridgeKey = [Provider, Chains, Chains];
44
+ /** The visual chain for a given chain */
45
+ export type VisualChain = Chain & {
46
+ chainId: Chains;
47
+ logoURI: string;
48
+ alt: string;
49
+ };
50
+ /** The metadata for a chain */
51
+ export type ChainsMetadata = Record<Chains, VisualChain>;
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/llms.txt ADDED
@@ -0,0 +1,169 @@
1
+ # @gibs/bridge-sdk
2
+
3
+ > A TypeScript library that prices and builds transfers across the Gibs
4
+ > Finance omnibridges (PulseChain with Ethereum, PulseChain with BNB Smart
5
+ > Chain). It also builds the requests for the aggregator routes (LI.FI,
6
+ > Relay, NEAR Intents) that reach those bridges from other chains. It does
7
+ > not sign or send anything. You bring a viem client and a wallet.
8
+
9
+ ## Install
10
+
11
+ - Run `npm install @gibs/bridge-sdk viem`.
12
+ - The package is ESM only. Use `import`, not `require`.
13
+ - It needs Node 20 or later (`engines.node` is `>=20`).
14
+ - viem is a peer dependency. The range is `>=2.37.0 <3`.
15
+ - Version note: 1.12.0 and older on npm are broken. 1.13.0 is the first usable version.
16
+ - In a browser, `@gibs/bridge-sdk/ton-execution` needs a global `Buffer`. No other entry point does.
17
+
18
+ ## Entry points
19
+
20
+ Import from the narrowest subpath. The root `@gibs/bridge-sdk` exports everything.
21
+
22
+ - `@gibs/bridge-sdk/config`: chains, providers, pathways, native-asset tables.
23
+ - `@gibs/bridge-sdk/types`: shared types such as `BridgeKey` and `Token`.
24
+ - `@gibs/bridge-sdk/chains`: viem chain objects with logos and RPC URLs.
25
+ - `@gibs/bridge-sdk/chain-info`: on-chain reads (token mappings, fees, minimum amounts).
26
+ - `@gibs/bridge-sdk/settings`: fee and amount arithmetic.
27
+ - `@gibs/bridge-sdk/consolidated-quote`: LI.FI request builders and parsers.
28
+ - `@gibs/bridge-sdk/relay-quote`: Relay request builders and parsers.
29
+ - `@gibs/bridge-sdk/near-intents`: NEAR Intents requests and parsers.
30
+ - `@gibs/bridge-sdk/pre-quote`: price checks before a binding quote.
31
+ - `@gibs/bridge-sdk/routing`: route search across carriers.
32
+ - `@gibs/bridge-sdk/image-links`: image URLs from gib.show.
33
+ - `@gibs/bridge-sdk/abis`: contract ABIs.
34
+
35
+ ## Chains and pathways
36
+
37
+ A chain is a hex id from the `Chains` enum. A route is a `BridgeKey`:
38
+ `[provider, fromChain, toChain]`. `pathway` returns the contract addresses
39
+ for a key, or `undefined` when no pathway exists.
40
+
41
+ ```ts
42
+ import { Chains, Providers, pathway } from '@gibs/bridge-sdk/config'
43
+ import type { BridgeKey } from '@gibs/bridge-sdk/types'
44
+
45
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS]
46
+ const route = pathway(bridgeKey, true) // true: mainnet only
47
+ if (route) {
48
+ route.from // the mediator on Ethereum, where funds enter
49
+ route.feeManager // 'to': the PulseChain mediator owns the fee manager
50
+ }
51
+ ```
52
+
53
+ ## Fees: direction and units
54
+
55
+ These rules must hold in every integration.
56
+
57
+ - PulseChain is the home side of both omnibridges.
58
+ - Foreign to home (`feeF2H`) means entering PulseChain.
59
+ - Home to foreign (`feeH2F`) means leaving PulseChain.
60
+ - Use the fee for your direction. Do not use one fee for both.
61
+ - Bridge fees are a rate per `1e18`. `1e18` is one hundred percent.
62
+ - Aggregator referral fees are basis points. `10_000n` is one hundred percent.
63
+ - LI.FI takes its fee as a decimal fraction (`0.001` is a tenth of a percent).
64
+ - Fees are on-chain settings. Read them. Do not hard-code them.
65
+
66
+ ```ts
67
+ import { loadBridgeFees } from '@gibs/bridge-sdk/chain-info'
68
+ import { Chains, pathway, Providers } from '@gibs/bridge-sdk/config'
69
+ import { amountAfterBridgeFee, bridgeCost } from '@gibs/bridge-sdk/settings'
70
+ import type { PublicClient } from 'viem'
71
+
72
+ declare const ethereumClient: PublicClient
73
+ declare const pulsechainClient: PublicClient
74
+
75
+ const route = pathway([Providers.PULSECHAIN, Chains.ETH, Chains.PLS], true)
76
+ if (route) {
77
+ const { feeF2H } = await loadBridgeFees({
78
+ pathway: route,
79
+ fromChainClient: ethereumClient,
80
+ toChainClient: pulsechainClient,
81
+ })
82
+ const amountToBridge = 1_000_000n
83
+ const cost = bridgeCost({ amountToBridge, bridgeFee: feeF2H })
84
+ const received = amountAfterBridgeFee({ amountToBridge, bridgeCost: cost })
85
+ }
86
+ ```
87
+
88
+ `loadBridgeFees` reads the default rate. It does not read per-token rates.
89
+
90
+ ## Tokens that are not bridged yet
91
+
92
+ `tokenBridgeInfo` asks both mediators how a token maps across the bridge.
93
+ A `null` `assetOutAddress` is a normal answer. The token gets an address on
94
+ the far side when its first transfer arrives. Always check for `null`.
95
+
96
+ ```ts
97
+ import { tokenBridgeInfo } from '@gibs/bridge-sdk/chain-info'
98
+ import { Chains, Providers } from '@gibs/bridge-sdk/config'
99
+ import type { Token } from '@gibs/bridge-sdk/types'
100
+ import type { PublicClient } from 'viem'
101
+
102
+ declare const assetIn: Token
103
+ declare const ethereumClient: PublicClient
104
+ declare const pulsechainClient: PublicClient
105
+
106
+ const info = await tokenBridgeInfo({
107
+ bridgeKey: [Providers.PULSECHAIN, Chains.ETH, Chains.PLS],
108
+ assetIn,
109
+ isProd: true,
110
+ fromChainClient: ethereumClient,
111
+ toChainClient: pulsechainClient,
112
+ })
113
+ if (info && info.assetOutAddress === null) {
114
+ // Not bridged yet. Do not treat this as an error.
115
+ }
116
+ ```
117
+
118
+ ## Aggregator identity: pass your own
119
+
120
+ The package ships no API key. Pass your own.
121
+
122
+ - Relay: pass your own `apiKey` to `relayRequestHeaders`. Name your own `relayReferrer`. The package sends no referrer by default, because Relay rejects a referrer without a key.
123
+ - LI.FI: set your own `lifiIntegrator`. A positive `lifiFee` without your own integrator throws.
124
+ - NEAR Intents: no key is needed. Pass your own `userAgent` if you want one.
125
+
126
+ ```ts
127
+ import {
128
+ buildLifiContractCallsRequest,
129
+ type ConsolidatedQuoteRequest,
130
+ } from '@gibs/bridge-sdk/consolidated-quote'
131
+ import { nearIntentsRequestHeaders } from '@gibs/bridge-sdk/near-intents'
132
+ import { buildRelayQuoteRequest, relayQuoteUrl, relayRequestHeaders } from '@gibs/bridge-sdk/relay-quote'
133
+
134
+ declare const baseRequest: ConsolidatedQuoteRequest
135
+ declare const myRelayApiKey: string
136
+
137
+ const request: ConsolidatedQuoteRequest = {
138
+ ...baseRequest,
139
+ lifiIntegrator: 'your-app.example',
140
+ lifiFee: 0.001,
141
+ relayReferrer: 'your-app.example',
142
+ }
143
+
144
+ const lifiBody = buildLifiContractCallsRequest(request)
145
+ const relayBody = buildRelayQuoteRequest(request)
146
+ if (relayBody) {
147
+ await fetch(relayQuoteUrl, {
148
+ method: 'POST',
149
+ body: JSON.stringify(relayBody),
150
+ headers: relayRequestHeaders(myRelayApiKey),
151
+ })
152
+ }
153
+
154
+ const nearHeaders = nearIntentsRequestHeaders(undefined, { userAgent: 'your-app/1.0' })
155
+ ```
156
+
157
+ ## Nulls and throws
158
+
159
+ - A question the package cannot answer returns `null`, `undefined` or `false`.
160
+ - A caller mistake or a failed chain read throws.
161
+ - Every request builder returns `null` for a request its provider cannot express.
162
+ - Every response parser returns `null` for an unexpected response shape.
163
+ - Check each return value before you use it.
164
+
165
+ ## Links
166
+
167
+ - [README](https://github.com/gibsfinance/bridge/blob/staging/packages/bridge-sdk/README.md): the full guide.
168
+ - [Gibs Finance llms.txt](https://next.gibs.finance/llms.txt): the whole platform.
169
+ - [@gibs/quotes](https://github.com/gibsfinance/bridge/blob/staging/packages/quotes/llms.txt): live multi-provider quotes.