@gibs/bridge-sdk 1.6.27 → 1.6.34

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.
@@ -0,0 +1,312 @@
1
+ import { encodeFunctionData, getContract, multicall3Abi, zeroAddress } from "viem";
2
+ import { nativeAssetOut, pathway, toChain, uniV2Routers } from "./config.js";
3
+ import _ from "lodash";
4
+ import { chainsMetadata } from "./chains.js";
5
+ import * as abis from "./abis.js";
6
+ import { multicallRead } from "@gibs/common/multicall";
7
+ export const links = _.memoize(async ({ chainId, target, address, client }) => {
8
+ return multicallRead({
9
+ client,
10
+ chain: chainsMetadata[toChain(chainId)],
11
+ abi: abis.inputBridge,
12
+ target,
13
+ calls: [
14
+ { functionName: 'bridgedTokenAddress', args: [address] },
15
+ { functionName: 'nativeTokenAddress', args: [address] },
16
+ ],
17
+ });
18
+ }, ({ chainId, target, address }) => `${chainId}-${target}-${address}`.toLowerCase());
19
+ export const tokenBridgeInfo = async ({ bridgeKey, assetIn, isProd, fromChainClient, toChainClient, }) => {
20
+ const bridgePathway = pathway(bridgeKey, isProd);
21
+ if (!assetIn || !bridgePathway) {
22
+ console.log('missing asset in or bridge pathway');
23
+ return null;
24
+ }
25
+ const [, fromChain, toChain] = bridgeKey;
26
+ const assetInAddress = assetIn.address === zeroAddress ? nativeAssetOut[fromChain] : assetIn.address;
27
+ const [toMappings, fromMappings] = await Promise.all([
28
+ links({
29
+ chainId: Number(toChain),
30
+ target: bridgePathway.to,
31
+ address: assetInAddress,
32
+ client: toChainClient,
33
+ }),
34
+ links({
35
+ chainId: Number(fromChain),
36
+ target: bridgePathway.from,
37
+ address: assetInAddress,
38
+ client: fromChainClient,
39
+ }),
40
+ ]);
41
+ const [toBridged, toNative] = toMappings;
42
+ // const notGas = !Object.values(nativeAssetOut).find(
43
+ // (v) => v.toLowerCase() === assetInAddress.toLowerCase(),
44
+ // )
45
+ if (toBridged !== zeroAddress) {
46
+ // if (notGas) console.log('toBridged')
47
+ return {
48
+ originationChainId: fromChain,
49
+ assetInAddress: assetInAddress,
50
+ assetOutAddress: toBridged,
51
+ toForeign: {
52
+ foreign: toBridged,
53
+ home: assetInAddress,
54
+ },
55
+ };
56
+ }
57
+ if (toNative !== zeroAddress) {
58
+ // if (notGas) console.log('toNative')
59
+ return {
60
+ originationChainId: toChain,
61
+ assetInAddress: assetInAddress,
62
+ assetOutAddress: toNative,
63
+ toHome: {
64
+ home: toNative,
65
+ foreign: assetInAddress,
66
+ },
67
+ };
68
+ }
69
+ const [fromBridged, fromNative] = fromMappings;
70
+ if (fromNative !== zeroAddress) {
71
+ // if (notGas) console.log('fromNative')
72
+ return {
73
+ originationChainId: toChain,
74
+ assetInAddress: assetInAddress,
75
+ assetOutAddress: fromNative,
76
+ toHome: {
77
+ home: assetInAddress,
78
+ foreign: fromNative,
79
+ },
80
+ };
81
+ }
82
+ if (fromBridged !== zeroAddress) {
83
+ // if (notGas) console.log('fromBridged')
84
+ return {
85
+ originationChainId: fromChain,
86
+ assetInAddress: assetInAddress,
87
+ assetOutAddress: fromBridged,
88
+ toForeign: {
89
+ foreign: fromBridged,
90
+ home: assetInAddress,
91
+ },
92
+ };
93
+ }
94
+ // the token has not been bridged yet
95
+ return {
96
+ originationChainId: toChain,
97
+ assetInAddress: assetInAddress,
98
+ assetOutAddress: null,
99
+ ...(bridgePathway.toHome
100
+ ? {
101
+ toHome: {
102
+ foreign: assetInAddress,
103
+ home: null,
104
+ },
105
+ }
106
+ : {
107
+ toForeign: {
108
+ foreign: null,
109
+ home: assetInAddress,
110
+ },
111
+ }),
112
+ };
113
+ };
114
+ /**
115
+ * check the prices that each router offers for the given paths
116
+ * @param assetInAddress the address of the asset going into the bridge
117
+ * @param oneTokenInt the number of tokens to push into the bridge (before fees)
118
+ * @param chain the chain of the asset going into the bridge
119
+ * @param bridgeKey the bridge key
120
+ * @param blockNumber the block number to use for the price
121
+ * @param paths the paths to check
122
+ * @returns the prices as bigint for each path, hex whenever the price is not available
123
+ */
124
+ const readAmountOut = ({ amountIn, chain, paths, client, uniV2Router, }) => {
125
+ return multicallRead({
126
+ chain: chainsMetadata[chain],
127
+ client,
128
+ abi: abis.univ2Router,
129
+ // pulsex router
130
+ calls: _.flatMap(uniV2Router ? [uniV2Router] : uniV2Routers[chain], (target) => paths.map((path) => ({
131
+ functionName: 'getAmountsOut',
132
+ allowFailure: true,
133
+ args: [amountIn, path],
134
+ target,
135
+ }))),
136
+ });
137
+ };
138
+ export const fetchPriceCorrective = ({
139
+ // bridgeKey,
140
+ // assetIn,
141
+ // isProd,
142
+ bridgeKeyPartner, assetOut, measurementToken, fromChainClient, toChainClient, assetLink, amountIn, isProd, }) => {
143
+ return {
144
+ getPaymentToken: async () => {
145
+ // fetch the token bridge info for the token that is about to be spent by the delivery service
146
+ return await tokenBridgeInfo({
147
+ bridgeKey: bridgeKeyPartner,
148
+ assetIn: {
149
+ ...assetOut,
150
+ address: measurementToken,
151
+ },
152
+ isProd,
153
+ // clients are reversed here because we are actually fetching the
154
+ // tokens going the opposite direction to the provided bridge key
155
+ fromChainClient: toChainClient,
156
+ toChainClient: fromChainClient,
157
+ });
158
+ },
159
+ getSwapResults: async ({ paymentTokenResult, chain, uniV2Router, client, }) => {
160
+ return await Promise.all([
161
+ readAmountOut({
162
+ amountIn,
163
+ chain,
164
+ uniV2Router,
165
+ client,
166
+ paths: [
167
+ // check the direct route
168
+ [assetLink.assetInAddress, paymentTokenResult.assetOutAddress],
169
+ // might have heavier liquidity going through the native asset on the home side
170
+ [assetLink.assetInAddress, nativeAssetOut[chain], paymentTokenResult.assetOutAddress],
171
+ ],
172
+ }),
173
+ readAmountOut({
174
+ amountIn,
175
+ chain,
176
+ uniV2Router,
177
+ client,
178
+ paths: [[assetOut.address, measurementToken]],
179
+ // only the direct route is available.
180
+ // might be able to create a whitelist for this at some point
181
+ }),
182
+ ]);
183
+ },
184
+ reduceResults: ({ results, oneToken, }) => {
185
+ const outputFromRouter = (result) => {
186
+ if (_.isString(result)) {
187
+ // the router failed to return a result
188
+ return 0n;
189
+ }
190
+ if (!result.length) {
191
+ // the router returned an empty result
192
+ return 0n;
193
+ }
194
+ // the last element is the amount of the output token
195
+ const last = result[result.length - 1];
196
+ return (last * oneToken) / amountIn;
197
+ };
198
+ const max = (amountsOut) => {
199
+ return _.compact(amountsOut).reduce((max, current) => (max < current ? current : max), 0n);
200
+ };
201
+ // could do with a refactor to unify the results
202
+ const [outputs, inputs] = results;
203
+ const outputAmounts = outputs.map(outputFromRouter);
204
+ const inputAmounts = inputs.map(outputFromRouter);
205
+ const outputToken = max(outputAmounts);
206
+ const inputToken = max(inputAmounts);
207
+ const result = outputToken && outputToken > 0n
208
+ ? inputToken && inputToken > outputToken && inputToken / outputToken === 1n
209
+ ? inputToken
210
+ : outputToken
211
+ : inputToken;
212
+ return result;
213
+ }
214
+ };
215
+ };
216
+ export const minBridgeAmountIn = async ({ assetIn, pathway, fromPublicClient, toPublicClient, }) => {
217
+ const publicClient = pathway?.feeManager === 'from' ? fromPublicClient : toPublicClient;
218
+ return publicClient.readContract({
219
+ abi: abis.inputBridge,
220
+ functionName: 'minPerTx',
221
+ args: [assetIn.address],
222
+ address: pathway[pathway.feeManager],
223
+ });
224
+ };
225
+ /** get the symbol of the native asset, unwrap it if it needs to be */
226
+ export const nativeSymbol = (asset, unwrap = false) => {
227
+ return asset ? (unwrap && asset.address === nativeAssetOut[toChain(asset.chainId)] ? asset.symbol.slice(1) : asset.symbol) : '';
228
+ };
229
+ export const chainMulticall = (client) => {
230
+ const chainId = client.chain.id;
231
+ const metadata = chainsMetadata[toChain(chainId)];
232
+ return getContract({
233
+ abi: multicall3Abi,
234
+ client: client,
235
+ address: metadata.contracts.multicall3.address,
236
+ });
237
+ };
238
+ export const loadBridgeFees = async ({ pathway, fromChainClient, toChainClient }) => {
239
+ const multicall = pathway.feeManager === 'from'
240
+ ? chainMulticall(fromChainClient)
241
+ : chainMulticall(toChainClient);
242
+ const [feeManagerResponse] = await multicall.read.aggregate3([
243
+ [
244
+ {
245
+ allowFailure: false,
246
+ target: pathway[pathway.feeManager],
247
+ callData: encodeFunctionData({
248
+ abi: abis.inputBridge,
249
+ functionName: 'feeManager',
250
+ }),
251
+ },
252
+ ],
253
+ ]);
254
+ const { success, returnData } = feeManagerResponse;
255
+ if (!success) {
256
+ throw new Error('unable to load feeManager');
257
+ }
258
+ if (returnData === '0x') {
259
+ throw new Error('unable to read feeManager');
260
+ }
261
+ const feeManager = (returnData.startsWith('0x000000000000000000000000')
262
+ ? `0x${returnData.slice(26)}`
263
+ : `0x${returnData.slice(-40)}`);
264
+ const [keyH2F, keyF2H] = await multicall.read.aggregate3([
265
+ [
266
+ {
267
+ allowFailure: false,
268
+ target: feeManager,
269
+ callData: encodeFunctionData({
270
+ abi: abis.feeManager,
271
+ functionName: 'HOME_TO_FOREIGN_FEE',
272
+ }),
273
+ },
274
+ {
275
+ allowFailure: false,
276
+ target: feeManager,
277
+ callData: encodeFunctionData({
278
+ abi: abis.feeManager,
279
+ functionName: 'FOREIGN_TO_HOME_FEE',
280
+ }),
281
+ },
282
+ ],
283
+ ]);
284
+ const [feeH2F, feeF2H] = await multicall.read.aggregate3([
285
+ [
286
+ {
287
+ allowFailure: false,
288
+ target: feeManager,
289
+ callData: encodeFunctionData({
290
+ abi: abis.feeManager,
291
+ functionName: 'getFee',
292
+ args: [keyH2F.returnData, zeroAddress],
293
+ }),
294
+ },
295
+ {
296
+ allowFailure: false,
297
+ target: feeManager,
298
+ callData: encodeFunctionData({
299
+ abi: abis.feeManager,
300
+ functionName: 'getFee',
301
+ args: [keyF2H.returnData, zeroAddress],
302
+ }),
303
+ },
304
+ ],
305
+ ]);
306
+ const setting = {
307
+ feeManager,
308
+ feeH2F: BigInt(feeH2F.returnData),
309
+ feeF2H: BigInt(feeF2H.returnData),
310
+ };
311
+ return setting;
312
+ };
@@ -0,0 +1,6 @@
1
+ import type { ChainsMetadata } from '@gibs/bridge-sdk/types';
2
+ /**
3
+ * all of the chains that are enabled in this dapp
4
+ * and their corresponding useful metadata
5
+ */
6
+ export declare const chainsMetadata: ChainsMetadata;
@@ -0,0 +1,86 @@
1
+ import { mainnet, pulsechain, bsc, sepolia, pulsechainV4 } from '@reown/appkit/networks';
2
+ import { Chains } from '@gibs/bridge-sdk/config';
3
+ import * as imageLinks from '@gibs/bridge-sdk/image-links';
4
+ /**
5
+ * all of the chains that are enabled in this dapp
6
+ * and their corresponding useful metadata
7
+ */
8
+ export const chainsMetadata = {
9
+ [Chains.PLS]: {
10
+ ...pulsechain,
11
+ chainId: Chains.PLS,
12
+ logoURI: imageLinks.network(pulsechain.id),
13
+ alt: 'a pink blue and purple hexagon with an ekg line running horizontally',
14
+ rpcUrls: {
15
+ default: {
16
+ http: ['https://rpc.pulsechain.com'],
17
+ },
18
+ },
19
+ contracts: {
20
+ ...pulsechain.contracts,
21
+ ensRegistry: {
22
+ address: '0xbd5133993FCDED5945c5539D9f032261F0d13170',
23
+ },
24
+ ensUniversalResolver: {
25
+ address: '0x2FDBb906b4FE68e31D928C1ED0b6a3bD1f204374',
26
+ blockCreated: 19_400_610,
27
+ },
28
+ },
29
+ },
30
+ [Chains.ETH]: {
31
+ ...mainnet,
32
+ chainId: Chains.ETH,
33
+ logoURI: imageLinks.network(mainnet.id),
34
+ alt: 'two pyramids joined on one side',
35
+ rpcUrls: {
36
+ default: {
37
+ http: ['https://rpc-ethereum.g4mm4.io'],
38
+ },
39
+ },
40
+ },
41
+ [Chains.BNB]: {
42
+ ...bsc,
43
+ name: 'Binance Smart Chain',
44
+ chainId: Chains.BNB,
45
+ logoURI: imageLinks.network(bsc.id),
46
+ alt: 'a gold cube with slits in the middle of each side',
47
+ rpcUrls: {
48
+ default: {
49
+ http: ['https://bsc-pokt.nodies.app'],
50
+ },
51
+ },
52
+ },
53
+ [Chains.SEP]: {
54
+ ...sepolia,
55
+ name: 'Sepolia',
56
+ chainId: Chains.SEP,
57
+ logoURI: imageLinks.network(sepolia.id),
58
+ alt: 'two pyramids joined on one side',
59
+ rpcUrls: {
60
+ default: {
61
+ http: ['https://ethereum-sepolia-rpc.publicnode.com'],
62
+ },
63
+ },
64
+ },
65
+ [Chains.V4PLS]: {
66
+ ...pulsechainV4,
67
+ name: 'Pulsechain V4',
68
+ chainId: Chains.V4PLS,
69
+ logoURI: imageLinks.network(pulsechainV4.id),
70
+ alt: 'a pink blue and purple hexagon with an ekg line running horizontally',
71
+ rpcUrls: {
72
+ default: {
73
+ http: ['https://rpc-testnet-pulsechain.g4mm4.io'],
74
+ },
75
+ },
76
+ contracts: {
77
+ ...pulsechainV4.contracts,
78
+ ensRegistry: {
79
+ address: '0xaDB38309aF7F85034FDC35bd6E6B45d1216CfB56',
80
+ },
81
+ ensUniversalResolver: {
82
+ address: '0xDDFde3fA938dE0cdff82645579d03Ed750DE2e69',
83
+ },
84
+ },
85
+ },
86
+ };
@@ -0,0 +1,51 @@
1
+ import { type Hex } from "viem";
2
+ import type { BridgeKey, Token, TokenOut } from "./types.js";
3
+ /**
4
+ * Add here your chain id as hex, be sure to add your chain in the chainsMetadata object too
5
+ */
6
+ export declare enum Chains {
7
+ PLS = "0x171",
8
+ ETH = "0x1",
9
+ BNB = "0x38",
10
+ SEP = "0xaa36a7",
11
+ V4PLS = "0x3af"
12
+ }
13
+ export type ChainKey = keyof typeof Chains;
14
+ export declare const chainIdToKey: Map<Chains, "ETH" | "PLS" | "BNB" | "SEP" | "V4PLS">;
15
+ export declare const toChain: (chainId: number | string) => Chains;
16
+ export declare const toChainKey: (chainId: number | string) => "ETH" | "PLS" | "BNB" | "SEP" | "V4PLS" | undefined;
17
+ export declare enum Provider {
18
+ PULSECHAIN = "pulsechain",
19
+ TOKENSEX = "tokensex"
20
+ }
21
+ export declare const nativeAssetOut: Record<Chains, Hex>;
22
+ export declare const uniV2Routers: Record<Chains, Hex[]>;
23
+ export declare const nativeTokenSymbol: Record<Chains, string>;
24
+ export declare const nativeTokenName: Record<Chains, string>;
25
+ export type Pathway = {
26
+ from: Hex;
27
+ to: Hex;
28
+ destinationRouter: Hex | null;
29
+ nativeRouter: Hex;
30
+ validator: Hex;
31
+ defaultAssetIn: Token;
32
+ usesExtraParam: boolean;
33
+ feeManager: 'from' | 'to';
34
+ toHome: boolean;
35
+ requiresDelivery: boolean;
36
+ };
37
+ export type DeepPathwayConfig = Record<Provider, Partial<Record<Chains, Partial<Record<Chains, Pathway>>>>>;
38
+ export declare const pathways: DeepPathwayConfig;
39
+ export declare const testnetPathways: DeepPathwayConfig;
40
+ export declare const validBridgeKeys: (isProd: boolean) => BridgeKey[];
41
+ export declare const inferBridgeKey: ({ currentKey, provider, fromChain, toChain, }: {
42
+ currentKey: BridgeKey;
43
+ provider: Provider;
44
+ fromChain?: Chains;
45
+ toChain?: Chains;
46
+ }) => BridgeKey;
47
+ export declare const pathway: (bridgeKey: BridgeKey | null, isProd: boolean) => Pathway | undefined;
48
+ export declare const defaultAssetIn: ($bridgeKey: BridgeKey | null, isProd: boolean) => Token | undefined;
49
+ export declare const isNative: (asset: Token | TokenOut | null, bridgeKey: BridgeKey | null) => boolean;
50
+ export declare const isUnwrappable: (asset: Pick<Token, "extensions"> | null, bridgeKey: BridgeKey | null) => boolean;
51
+ export declare const canChangeUnwrap: (bridgeKey: BridgeKey, assetIn: Token | null) => boolean;