@gibs/bridge-sdk 1.6.27
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/package.json +29 -0
- package/src/abis.ts +57 -0
- package/src/chain-info.ts +424 -0
- package/src/chains.ts +88 -0
- package/src/config.ts +369 -0
- package/src/fee-type.ts +12 -0
- package/src/image-links.ts +32 -0
- package/src/index.ts +6 -0
- package/src/types.ts +39 -0
- package/tsconfig.json +16 -0
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gibs/bridge-sdk",
|
|
3
|
+
"version": "1.6.27",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"access": "public",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./types": "./src/types.ts",
|
|
10
|
+
"./config": "./src/config.ts",
|
|
11
|
+
"./abis": "./src/abis.ts",
|
|
12
|
+
"./image-links": "./src/image-links.ts",
|
|
13
|
+
"./chains": "./src/chains.ts",
|
|
14
|
+
"./chain-info": "./src/chain-info.ts",
|
|
15
|
+
"./fee-type": "./src/fee-type.ts"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [],
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"description": "",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@gibs/common": "^1.0.0",
|
|
26
|
+
"lodash": "^4.17.21",
|
|
27
|
+
"viem": "^2.31.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/abis.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { parseAbi, parseAbiParameters } from 'viem'
|
|
2
|
+
|
|
3
|
+
/** the abi for the bridge */
|
|
4
|
+
export const inputBridge = parseAbi([
|
|
5
|
+
'function minPerTx(address token) external view returns(uint256)',
|
|
6
|
+
'function foreignTokenAddress(address token) external view returns(address)',
|
|
7
|
+
'function homeTokenAddress(address token) external view returns(address)',
|
|
8
|
+
'function bridgedTokenAddress(address token) external view returns(address)',
|
|
9
|
+
'function nativeTokenAddress(address token) external view returns(address)',
|
|
10
|
+
'function feeManager() external view returns(address)',
|
|
11
|
+
'function relayTokensAndCall(address token, address _receiver, uint256 _value, bytes memory _data) external',
|
|
12
|
+
'function relayTokens(address token, address _receiver, uint256 _value) external',
|
|
13
|
+
])
|
|
14
|
+
export const inputBridgeExtraInput = parseAbi([
|
|
15
|
+
'function relayTokensAndCall(address token, address _receiver, uint256 _value, bytes memory _data, address _senderOrigin) external',
|
|
16
|
+
'function relayTokens(address token, address _receiver, uint256 _value, address _senderOrigin) external',
|
|
17
|
+
])
|
|
18
|
+
export const erc677 = parseAbi([
|
|
19
|
+
'function transferAndCall(address, uint256, bytes calldata) external',
|
|
20
|
+
])
|
|
21
|
+
export const erc677ExtraInput = parseAbi([
|
|
22
|
+
'function transferAndCall(address, uint256, bytes calldata, address sender) external',
|
|
23
|
+
])
|
|
24
|
+
export const outputRouter = parseAbi([
|
|
25
|
+
'function onTokenBridged(address _token, uint256 _value, bytes memory _data) external payable',
|
|
26
|
+
'function WETH() external view returns(address)',
|
|
27
|
+
'function bridge() external view returns(address)',
|
|
28
|
+
])
|
|
29
|
+
export const outputBridge = parseAbi(['function bridgeContract() external view returns(address)'])
|
|
30
|
+
export const feeManager = parseAbi([
|
|
31
|
+
'function HOME_TO_FOREIGN_FEE() public view returns(bytes32)',
|
|
32
|
+
'function FOREIGN_TO_HOME_FEE() public view returns(bytes32)',
|
|
33
|
+
'function getFee(bytes32, address) public view returns(uint256)',
|
|
34
|
+
])
|
|
35
|
+
export const nativeRouter = parseAbi([
|
|
36
|
+
'function wrapAndRelayTokens(address receiver) external payable',
|
|
37
|
+
'function relayTokensAndCall(address receiver, bytes calldata data) external payable',
|
|
38
|
+
])
|
|
39
|
+
export const nativeRouterExtraInput = parseAbi([
|
|
40
|
+
'function wrapAndRelayTokens(address receiver, address sender) external payable',
|
|
41
|
+
'function relayTokensAndCall(address receiver, bytes calldata data, address sender) external payable',
|
|
42
|
+
])
|
|
43
|
+
export const univ2Router = parseAbi([
|
|
44
|
+
'function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns(uint256[] memory)',
|
|
45
|
+
])
|
|
46
|
+
export const feeDeliveryStruct = parseAbiParameters('(address, uint256, uint256, uint256)')
|
|
47
|
+
|
|
48
|
+
export const relayTokensDirect = parseAbi([
|
|
49
|
+
'function safeExecuteSignaturesWithAutoGasLimit(bytes calldata data, bytes calldata signatures) external',
|
|
50
|
+
])
|
|
51
|
+
export const homeAmb = parseAbi([
|
|
52
|
+
'function submitSignature(bytes message, bytes signature) external',
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
export const pair = parseAbi([
|
|
56
|
+
'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)',
|
|
57
|
+
])
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { encodeFunctionData, getContract, type Hex, multicall3Abi, type PublicClient, zeroAddress } from "viem"
|
|
2
|
+
import { Chains, nativeAssetOut, type Pathway, pathway, toChain, uniV2Routers } from "./config.js"
|
|
3
|
+
import type { BridgeKey, Token } from "./types.js"
|
|
4
|
+
import _ from "lodash"
|
|
5
|
+
import { chainsMetadata } from "./chains.js"
|
|
6
|
+
import * as abis from "./abis.js"
|
|
7
|
+
import { multicallRead } from "@gibs/common/multicall"
|
|
8
|
+
|
|
9
|
+
export type TokenBridgeInfo = {
|
|
10
|
+
originationChainId: Chains
|
|
11
|
+
assetInAddress: Hex
|
|
12
|
+
assetOutAddress: Hex | null
|
|
13
|
+
toForeign?: {
|
|
14
|
+
home: Hex
|
|
15
|
+
foreign: Hex | null
|
|
16
|
+
}
|
|
17
|
+
toHome?: {
|
|
18
|
+
home: Hex | null
|
|
19
|
+
foreign: Hex
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const links = _.memoize(
|
|
24
|
+
async ({ chainId, target, address, client }: { chainId: number; target: Hex; address: Hex, client: PublicClient }) => {
|
|
25
|
+
return multicallRead<Hex[]>({
|
|
26
|
+
client,
|
|
27
|
+
chain: chainsMetadata[toChain(chainId)],
|
|
28
|
+
abi: abis.inputBridge,
|
|
29
|
+
target,
|
|
30
|
+
calls: [
|
|
31
|
+
{ functionName: 'bridgedTokenAddress', args: [address] },
|
|
32
|
+
{ functionName: 'nativeTokenAddress', args: [address] },
|
|
33
|
+
],
|
|
34
|
+
})
|
|
35
|
+
},
|
|
36
|
+
({ chainId, target, address }) => `${chainId}-${target}-${address}`.toLowerCase(),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
export const tokenBridgeInfo = async ({
|
|
40
|
+
bridgeKey,
|
|
41
|
+
assetIn,
|
|
42
|
+
isProd,
|
|
43
|
+
fromChainClient,
|
|
44
|
+
toChainClient,
|
|
45
|
+
}: {
|
|
46
|
+
bridgeKey: BridgeKey,
|
|
47
|
+
assetIn: Token | null,
|
|
48
|
+
isProd: boolean,
|
|
49
|
+
fromChainClient: PublicClient,
|
|
50
|
+
toChainClient: PublicClient,
|
|
51
|
+
}): Promise<null | TokenBridgeInfo> => {
|
|
52
|
+
const bridgePathway = pathway(bridgeKey, isProd)
|
|
53
|
+
if (!assetIn || !bridgePathway) {
|
|
54
|
+
console.log('missing asset in or bridge pathway')
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
const [, fromChain, toChain] = bridgeKey
|
|
58
|
+
const assetInAddress =
|
|
59
|
+
assetIn.address === zeroAddress ? nativeAssetOut[fromChain] : assetIn.address
|
|
60
|
+
const [toMappings, fromMappings] = await Promise.all([
|
|
61
|
+
links({
|
|
62
|
+
chainId: Number(toChain),
|
|
63
|
+
target: bridgePathway.to,
|
|
64
|
+
address: assetInAddress as Hex,
|
|
65
|
+
client: toChainClient,
|
|
66
|
+
}),
|
|
67
|
+
links({
|
|
68
|
+
chainId: Number(fromChain),
|
|
69
|
+
target: bridgePathway.from,
|
|
70
|
+
address: assetInAddress as Hex,
|
|
71
|
+
client: fromChainClient,
|
|
72
|
+
}),
|
|
73
|
+
])
|
|
74
|
+
const [toBridged, toNative] = toMappings
|
|
75
|
+
// const notGas = !Object.values(nativeAssetOut).find(
|
|
76
|
+
// (v) => v.toLowerCase() === assetInAddress.toLowerCase(),
|
|
77
|
+
// )
|
|
78
|
+
if (toBridged !== zeroAddress) {
|
|
79
|
+
// if (notGas) console.log('toBridged')
|
|
80
|
+
return {
|
|
81
|
+
originationChainId: fromChain,
|
|
82
|
+
assetInAddress: assetInAddress as Hex,
|
|
83
|
+
assetOutAddress: toBridged,
|
|
84
|
+
toForeign: {
|
|
85
|
+
foreign: toBridged,
|
|
86
|
+
home: assetInAddress as Hex,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (toNative !== zeroAddress) {
|
|
91
|
+
// if (notGas) console.log('toNative')
|
|
92
|
+
return {
|
|
93
|
+
originationChainId: toChain,
|
|
94
|
+
assetInAddress: assetInAddress as Hex,
|
|
95
|
+
assetOutAddress: toNative,
|
|
96
|
+
toHome: {
|
|
97
|
+
home: toNative,
|
|
98
|
+
foreign: assetInAddress as Hex,
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const [fromBridged, fromNative] = fromMappings
|
|
103
|
+
if (fromNative !== zeroAddress) {
|
|
104
|
+
// if (notGas) console.log('fromNative')
|
|
105
|
+
return {
|
|
106
|
+
originationChainId: toChain,
|
|
107
|
+
assetInAddress: assetInAddress as Hex,
|
|
108
|
+
assetOutAddress: fromNative,
|
|
109
|
+
toHome: {
|
|
110
|
+
home: assetInAddress as Hex,
|
|
111
|
+
foreign: fromNative,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (fromBridged !== zeroAddress) {
|
|
116
|
+
// if (notGas) console.log('fromBridged')
|
|
117
|
+
return {
|
|
118
|
+
originationChainId: fromChain,
|
|
119
|
+
assetInAddress: assetInAddress as Hex,
|
|
120
|
+
assetOutAddress: fromBridged,
|
|
121
|
+
toForeign: {
|
|
122
|
+
foreign: fromBridged,
|
|
123
|
+
home: assetInAddress as Hex,
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// the token has not been bridged yet
|
|
128
|
+
return {
|
|
129
|
+
originationChainId: toChain,
|
|
130
|
+
assetInAddress: assetInAddress as Hex,
|
|
131
|
+
assetOutAddress: null,
|
|
132
|
+
...(bridgePathway.toHome
|
|
133
|
+
? {
|
|
134
|
+
toHome: {
|
|
135
|
+
foreign: assetInAddress as Hex,
|
|
136
|
+
home: null,
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
: {
|
|
140
|
+
toForeign: {
|
|
141
|
+
foreign: null,
|
|
142
|
+
home: assetInAddress as Hex,
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export type FetchResult = bigint[] | Hex
|
|
148
|
+
/**
|
|
149
|
+
* check the prices that each router offers for the given paths
|
|
150
|
+
* @param assetInAddress the address of the asset going into the bridge
|
|
151
|
+
* @param oneTokenInt the number of tokens to push into the bridge (before fees)
|
|
152
|
+
* @param chain the chain of the asset going into the bridge
|
|
153
|
+
* @param bridgeKey the bridge key
|
|
154
|
+
* @param blockNumber the block number to use for the price
|
|
155
|
+
* @param paths the paths to check
|
|
156
|
+
* @returns the prices as bigint for each path, hex whenever the price is not available
|
|
157
|
+
*/
|
|
158
|
+
const readAmountOut = ({
|
|
159
|
+
amountIn,
|
|
160
|
+
chain,
|
|
161
|
+
paths,
|
|
162
|
+
client,
|
|
163
|
+
uniV2Router,
|
|
164
|
+
}: {
|
|
165
|
+
amountIn: bigint,
|
|
166
|
+
chain: Chains,
|
|
167
|
+
paths: Hex[][],
|
|
168
|
+
client: PublicClient,
|
|
169
|
+
uniV2Router?: Hex,
|
|
170
|
+
}): Promise<FetchResult[]> => {
|
|
171
|
+
return multicallRead<FetchResult[]>({
|
|
172
|
+
chain: chainsMetadata[chain],
|
|
173
|
+
client,
|
|
174
|
+
abi: abis.univ2Router,
|
|
175
|
+
// pulsex router
|
|
176
|
+
calls: _.flatMap(uniV2Router ? [uniV2Router] : uniV2Routers[chain], (target) =>
|
|
177
|
+
paths.map((path) => ({
|
|
178
|
+
functionName: 'getAmountsOut',
|
|
179
|
+
allowFailure: true,
|
|
180
|
+
args: [amountIn, path],
|
|
181
|
+
target,
|
|
182
|
+
})),
|
|
183
|
+
),
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export const fetchPriceCorrective = ({
|
|
188
|
+
// bridgeKey,
|
|
189
|
+
// assetIn,
|
|
190
|
+
// isProd,
|
|
191
|
+
bridgeKeyPartner,
|
|
192
|
+
assetOut,
|
|
193
|
+
measurementToken,
|
|
194
|
+
fromChainClient,
|
|
195
|
+
toChainClient,
|
|
196
|
+
assetLink,
|
|
197
|
+
amountIn,
|
|
198
|
+
isProd,
|
|
199
|
+
}: {
|
|
200
|
+
bridgeKey: BridgeKey,
|
|
201
|
+
bridgeKeyPartner: BridgeKey,
|
|
202
|
+
measurementToken: Hex,
|
|
203
|
+
assetOut: Token,
|
|
204
|
+
fromChainClient: PublicClient,
|
|
205
|
+
toChainClient: PublicClient,
|
|
206
|
+
assetLink: TokenBridgeInfo,
|
|
207
|
+
amountIn: bigint,
|
|
208
|
+
isProd: boolean,
|
|
209
|
+
}) => {
|
|
210
|
+
return {
|
|
211
|
+
getPaymentToken: async () => {
|
|
212
|
+
// fetch the token bridge info for the token that is about to be spent by the delivery service
|
|
213
|
+
return await tokenBridgeInfo({
|
|
214
|
+
bridgeKey: bridgeKeyPartner,
|
|
215
|
+
assetIn: {
|
|
216
|
+
...assetOut,
|
|
217
|
+
address: measurementToken,
|
|
218
|
+
},
|
|
219
|
+
isProd,
|
|
220
|
+
// clients are reversed here because we are actually fetching the
|
|
221
|
+
// tokens going the opposite direction to the provided bridge key
|
|
222
|
+
fromChainClient: toChainClient,
|
|
223
|
+
toChainClient: fromChainClient,
|
|
224
|
+
})
|
|
225
|
+
},
|
|
226
|
+
getSwapResults: async ({
|
|
227
|
+
paymentTokenResult,
|
|
228
|
+
chain,
|
|
229
|
+
uniV2Router,
|
|
230
|
+
client,
|
|
231
|
+
}: {
|
|
232
|
+
paymentTokenResult: TokenBridgeInfo
|
|
233
|
+
chain: Chains
|
|
234
|
+
client: PublicClient
|
|
235
|
+
uniV2Router?: Hex
|
|
236
|
+
}) => {
|
|
237
|
+
return await Promise.all([
|
|
238
|
+
readAmountOut({
|
|
239
|
+
amountIn,
|
|
240
|
+
chain,
|
|
241
|
+
uniV2Router,
|
|
242
|
+
client,
|
|
243
|
+
paths: [
|
|
244
|
+
// check the direct route
|
|
245
|
+
[assetLink.assetInAddress, paymentTokenResult!.assetOutAddress!],
|
|
246
|
+
// might have heavier liquidity going through the native asset on the home side
|
|
247
|
+
[assetLink.assetInAddress, nativeAssetOut[chain], paymentTokenResult!.assetOutAddress!],
|
|
248
|
+
],
|
|
249
|
+
}),
|
|
250
|
+
readAmountOut({
|
|
251
|
+
amountIn,
|
|
252
|
+
chain,
|
|
253
|
+
uniV2Router,
|
|
254
|
+
client,
|
|
255
|
+
paths: [[assetOut.address as Hex, measurementToken]],
|
|
256
|
+
// only the direct route is available.
|
|
257
|
+
// might be able to create a whitelist for this at some point
|
|
258
|
+
}),
|
|
259
|
+
])
|
|
260
|
+
},
|
|
261
|
+
reduceResults: ({
|
|
262
|
+
results,
|
|
263
|
+
oneToken,
|
|
264
|
+
}: {
|
|
265
|
+
results: [FetchResult[], ...[FetchResult[]]]
|
|
266
|
+
oneToken: bigint
|
|
267
|
+
}) => {
|
|
268
|
+
const outputFromRouter = (result: FetchResult) => {
|
|
269
|
+
if (_.isString(result)) {
|
|
270
|
+
// the router failed to return a result
|
|
271
|
+
return 0n
|
|
272
|
+
}
|
|
273
|
+
if (!result.length) {
|
|
274
|
+
// the router returned an empty result
|
|
275
|
+
return 0n
|
|
276
|
+
}
|
|
277
|
+
// the last element is the amount of the output token
|
|
278
|
+
const last = result[result.length - 1]
|
|
279
|
+
return (last * oneToken) / amountIn
|
|
280
|
+
}
|
|
281
|
+
const max = (amountsOut: (bigint | undefined)[]) => {
|
|
282
|
+
return _.compact(amountsOut).reduce((max, current) => (max < current ? current : max), 0n)
|
|
283
|
+
}
|
|
284
|
+
// could do with a refactor to unify the results
|
|
285
|
+
const [outputs, inputs] = results
|
|
286
|
+
const outputAmounts = outputs.map(outputFromRouter)
|
|
287
|
+
const inputAmounts = inputs.map(outputFromRouter)
|
|
288
|
+
const outputToken = max(outputAmounts)
|
|
289
|
+
const inputToken = max(inputAmounts)
|
|
290
|
+
const result =
|
|
291
|
+
outputToken && outputToken > 0n
|
|
292
|
+
? inputToken && inputToken > outputToken && inputToken / outputToken === 1n
|
|
293
|
+
? inputToken
|
|
294
|
+
: outputToken
|
|
295
|
+
: inputToken
|
|
296
|
+
return result
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export const minBridgeAmountIn = async ({
|
|
302
|
+
assetIn,
|
|
303
|
+
pathway,
|
|
304
|
+
fromPublicClient,
|
|
305
|
+
toPublicClient,
|
|
306
|
+
}: {
|
|
307
|
+
assetIn: Token,
|
|
308
|
+
pathway: Pathway,
|
|
309
|
+
fromPublicClient: PublicClient,
|
|
310
|
+
toPublicClient: PublicClient,
|
|
311
|
+
}) => {
|
|
312
|
+
const publicClient = pathway?.feeManager === 'from' ? fromPublicClient : toPublicClient
|
|
313
|
+
return publicClient.readContract({
|
|
314
|
+
abi: abis.inputBridge,
|
|
315
|
+
functionName: 'minPerTx',
|
|
316
|
+
args: [assetIn.address as Hex],
|
|
317
|
+
address: pathway[pathway.feeManager],
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
/** get the symbol of the native asset, unwrap it if it needs to be */
|
|
321
|
+
export const nativeSymbol = (asset: { address: Hex, symbol: string, chainId: number } | null, unwrap = false) => {
|
|
322
|
+
return asset ? (unwrap && asset.address === nativeAssetOut[toChain(asset.chainId)] ? asset.symbol.slice(1) : asset.symbol) : ''
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export type InputLoadFeeFor = {
|
|
326
|
+
pathway: Pathway
|
|
327
|
+
fromChainClient: PublicClient
|
|
328
|
+
toChainClient: PublicClient
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export type PathwayExtendableConfig = {
|
|
332
|
+
feeManager: Hex
|
|
333
|
+
feeH2F: bigint
|
|
334
|
+
feeF2H: bigint
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export const chainMulticall = (client: PublicClient) => {
|
|
338
|
+
const chainId = client.chain!.id
|
|
339
|
+
const metadata = chainsMetadata[toChain(chainId)]
|
|
340
|
+
return getContract({
|
|
341
|
+
abi: multicall3Abi,
|
|
342
|
+
client: client as any,
|
|
343
|
+
address: metadata.contracts!.multicall3!.address,
|
|
344
|
+
}) as any
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export const loadBridgeFees = async ({ pathway, fromChainClient, toChainClient }: InputLoadFeeFor) => {
|
|
348
|
+
const multicall = pathway.feeManager === 'from'
|
|
349
|
+
? chainMulticall(fromChainClient)
|
|
350
|
+
: chainMulticall(toChainClient)
|
|
351
|
+
|
|
352
|
+
const [feeManagerResponse] = await multicall.read.aggregate3([
|
|
353
|
+
[
|
|
354
|
+
{
|
|
355
|
+
allowFailure: false,
|
|
356
|
+
target: pathway[pathway.feeManager],
|
|
357
|
+
callData: encodeFunctionData({
|
|
358
|
+
abi: abis.inputBridge,
|
|
359
|
+
functionName: 'feeManager',
|
|
360
|
+
}),
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
])
|
|
364
|
+
const { success, returnData } = feeManagerResponse
|
|
365
|
+
if (!success) {
|
|
366
|
+
throw new Error('unable to load feeManager')
|
|
367
|
+
}
|
|
368
|
+
if (returnData === '0x') {
|
|
369
|
+
throw new Error('unable to read feeManager')
|
|
370
|
+
}
|
|
371
|
+
const feeManager = (
|
|
372
|
+
returnData.startsWith('0x000000000000000000000000')
|
|
373
|
+
? `0x${returnData.slice(26)}`
|
|
374
|
+
: `0x${returnData.slice(-40)}`
|
|
375
|
+
) as Hex
|
|
376
|
+
const [keyH2F, keyF2H] = await multicall.read.aggregate3([
|
|
377
|
+
[
|
|
378
|
+
{
|
|
379
|
+
allowFailure: false,
|
|
380
|
+
target: feeManager,
|
|
381
|
+
callData: encodeFunctionData({
|
|
382
|
+
abi: abis.feeManager,
|
|
383
|
+
functionName: 'HOME_TO_FOREIGN_FEE',
|
|
384
|
+
}),
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
allowFailure: false,
|
|
388
|
+
target: feeManager,
|
|
389
|
+
callData: encodeFunctionData({
|
|
390
|
+
abi: abis.feeManager,
|
|
391
|
+
functionName: 'FOREIGN_TO_HOME_FEE',
|
|
392
|
+
}),
|
|
393
|
+
},
|
|
394
|
+
],
|
|
395
|
+
])
|
|
396
|
+
const [feeH2F, feeF2H] = await multicall.read.aggregate3([
|
|
397
|
+
[
|
|
398
|
+
{
|
|
399
|
+
allowFailure: false,
|
|
400
|
+
target: feeManager,
|
|
401
|
+
callData: encodeFunctionData({
|
|
402
|
+
abi: abis.feeManager,
|
|
403
|
+
functionName: 'getFee',
|
|
404
|
+
args: [keyH2F.returnData, zeroAddress],
|
|
405
|
+
}),
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
allowFailure: false,
|
|
409
|
+
target: feeManager,
|
|
410
|
+
callData: encodeFunctionData({
|
|
411
|
+
abi: abis.feeManager,
|
|
412
|
+
functionName: 'getFee',
|
|
413
|
+
args: [keyF2H.returnData, zeroAddress],
|
|
414
|
+
}),
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
])
|
|
418
|
+
const setting = {
|
|
419
|
+
feeManager,
|
|
420
|
+
feeH2F: BigInt(feeH2F.returnData),
|
|
421
|
+
feeF2H: BigInt(feeF2H.returnData),
|
|
422
|
+
} as PathwayExtendableConfig
|
|
423
|
+
return setting
|
|
424
|
+
}
|
package/src/chains.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { mainnet, pulsechain, bsc, sepolia, pulsechainV4 } from '@reown/appkit/networks'
|
|
2
|
+
import { Chains } from '@gibs/bridge-sdk/config'
|
|
3
|
+
import type { ChainsMetadata } from '@gibs/bridge-sdk/types'
|
|
4
|
+
import * as imageLinks from '@gibs/bridge-sdk/image-links'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* all of the chains that are enabled in this dapp
|
|
8
|
+
* and their corresponding useful metadata
|
|
9
|
+
*/
|
|
10
|
+
export const chainsMetadata = {
|
|
11
|
+
[Chains.PLS]: {
|
|
12
|
+
...pulsechain,
|
|
13
|
+
chainId: Chains.PLS,
|
|
14
|
+
logoURI: imageLinks.network(pulsechain.id),
|
|
15
|
+
alt: 'a pink blue and purple hexagon with an ekg line running horizontally',
|
|
16
|
+
rpcUrls: {
|
|
17
|
+
default: {
|
|
18
|
+
http: ['https://rpc.pulsechain.com'],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
contracts: {
|
|
22
|
+
...pulsechain.contracts,
|
|
23
|
+
ensRegistry: {
|
|
24
|
+
address: '0xbd5133993FCDED5945c5539D9f032261F0d13170',
|
|
25
|
+
},
|
|
26
|
+
ensUniversalResolver: {
|
|
27
|
+
address: '0x2FDBb906b4FE68e31D928C1ED0b6a3bD1f204374',
|
|
28
|
+
blockCreated: 19_400_610,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
[Chains.ETH]: {
|
|
33
|
+
...mainnet,
|
|
34
|
+
chainId: Chains.ETH,
|
|
35
|
+
logoURI: imageLinks.network(mainnet.id),
|
|
36
|
+
alt: 'two pyramids joined on one side',
|
|
37
|
+
rpcUrls: {
|
|
38
|
+
default: {
|
|
39
|
+
http: ['https://rpc-ethereum.g4mm4.io'],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
[Chains.BNB]: {
|
|
44
|
+
...bsc,
|
|
45
|
+
name: 'Binance Smart Chain',
|
|
46
|
+
chainId: Chains.BNB,
|
|
47
|
+
logoURI: imageLinks.network(bsc.id),
|
|
48
|
+
alt: 'a gold cube with slits in the middle of each side',
|
|
49
|
+
rpcUrls: {
|
|
50
|
+
default: {
|
|
51
|
+
http: ['https://bsc-pokt.nodies.app'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
[Chains.SEP]: {
|
|
56
|
+
...sepolia,
|
|
57
|
+
name: 'Sepolia',
|
|
58
|
+
chainId: Chains.SEP,
|
|
59
|
+
logoURI: imageLinks.network(sepolia.id),
|
|
60
|
+
alt: 'two pyramids joined on one side',
|
|
61
|
+
rpcUrls: {
|
|
62
|
+
default: {
|
|
63
|
+
http: ['https://ethereum-sepolia-rpc.publicnode.com'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
[Chains.V4PLS]: {
|
|
68
|
+
...pulsechainV4,
|
|
69
|
+
name: 'Pulsechain V4',
|
|
70
|
+
chainId: Chains.V4PLS,
|
|
71
|
+
logoURI: imageLinks.network(pulsechainV4.id),
|
|
72
|
+
alt: 'a pink blue and purple hexagon with an ekg line running horizontally',
|
|
73
|
+
rpcUrls: {
|
|
74
|
+
default: {
|
|
75
|
+
http: ['https://rpc-testnet-pulsechain.g4mm4.io'],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
contracts: {
|
|
79
|
+
...pulsechainV4.contracts,
|
|
80
|
+
ensRegistry: {
|
|
81
|
+
address: '0xaDB38309aF7F85034FDC35bd6E6B45d1216CfB56',
|
|
82
|
+
},
|
|
83
|
+
ensUniversalResolver: {
|
|
84
|
+
address: '0xDDFde3fA938dE0cdff82645579d03Ed750DE2e69',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
} as ChainsMetadata
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { zeroAddress, type Hex } from "viem";
|
|
2
|
+
import _ from 'lodash'
|
|
3
|
+
import * as imageLinks from '@gibs/bridge-sdk/image-links'
|
|
4
|
+
import type { BridgeKey, Token, TokenOut } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Add here your chain id as hex, be sure to add your chain in the chainsMetadata object too
|
|
8
|
+
*/
|
|
9
|
+
export enum Chains {
|
|
10
|
+
PLS = '0x171',
|
|
11
|
+
ETH = '0x1',
|
|
12
|
+
BNB = '0x38',
|
|
13
|
+
// testnets
|
|
14
|
+
SEP = '0xaa36a7',
|
|
15
|
+
V4PLS = '0x3af',
|
|
16
|
+
// TBNB = '0x61',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ChainKey = keyof typeof Chains
|
|
20
|
+
|
|
21
|
+
export const chainIdToKey = new Map<Chains, ChainKey>(
|
|
22
|
+
Object.entries(Chains).map(([key, chain]) => [chain, key] as [Chains, ChainKey]),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
export const toChain = (chainId: number | string) => `0x${Number(chainId).toString(16)}` as Chains
|
|
26
|
+
|
|
27
|
+
export const toChainKey = (chainId: number | string) => chainIdToKey.get(toChain(chainId))
|
|
28
|
+
|
|
29
|
+
export enum Provider {
|
|
30
|
+
PULSECHAIN = 'pulsechain',
|
|
31
|
+
TOKENSEX = 'tokensex',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const nativeAssetOut = {
|
|
35
|
+
[Chains.ETH]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
|
36
|
+
[Chains.BNB]: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
|
|
37
|
+
[Chains.PLS]: '0xA1077a294dDE1B09bB078844df40758a5D0f9a27',
|
|
38
|
+
[Chains.SEP]: '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9',
|
|
39
|
+
[Chains.V4PLS]: '0x70499adEBB11Efd915E3b69E700c331778628707',
|
|
40
|
+
} as Record<Chains, Hex>
|
|
41
|
+
|
|
42
|
+
export const uniV2Routers = {
|
|
43
|
+
[Chains.PLS]: [
|
|
44
|
+
'0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02',
|
|
45
|
+
'0x165C3410fC91EF562C50559f7d2289fEbed552d9',
|
|
46
|
+
],
|
|
47
|
+
[Chains.ETH]: ['0x7a250d5630b4cf539739df2c5dacb4c659f2488d'],
|
|
48
|
+
[Chains.BNB]: ['0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'],
|
|
49
|
+
[Chains.SEP]: ['0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3'],
|
|
50
|
+
[Chains.V4PLS]: [
|
|
51
|
+
'0xDaE9dd3d1A52CfCe9d5F2fAC7fDe164D500E50f7',
|
|
52
|
+
'0x636f6407B90661b73b1C0F7e24F4C79f624d0738',
|
|
53
|
+
],
|
|
54
|
+
} as Record<Chains, Hex[]>
|
|
55
|
+
|
|
56
|
+
export const nativeTokenSymbol = {
|
|
57
|
+
[Chains.PLS]: 'PLS',
|
|
58
|
+
[Chains.V4PLS]: 'V4PLS',
|
|
59
|
+
[Chains.ETH]: 'ETH',
|
|
60
|
+
[Chains.SEP]: 'sepETH',
|
|
61
|
+
[Chains.BNB]: 'BNB',
|
|
62
|
+
} as Record<Chains, string>
|
|
63
|
+
|
|
64
|
+
export const nativeTokenName = {
|
|
65
|
+
[Chains.PLS]: 'Pulse',
|
|
66
|
+
[Chains.V4PLS]: 'V4 Pulse',
|
|
67
|
+
[Chains.ETH]: 'Ether',
|
|
68
|
+
[Chains.SEP]: 'Sepolia Ether',
|
|
69
|
+
[Chains.BNB]: 'BNB',
|
|
70
|
+
} as Record<Chains, string>
|
|
71
|
+
|
|
72
|
+
export type Pathway = {
|
|
73
|
+
from: Hex
|
|
74
|
+
to: Hex
|
|
75
|
+
destinationRouter: Hex | null
|
|
76
|
+
nativeRouter: Hex
|
|
77
|
+
validator: Hex
|
|
78
|
+
defaultAssetIn: Token
|
|
79
|
+
usesExtraParam: boolean
|
|
80
|
+
feeManager: 'from' | 'to'
|
|
81
|
+
toHome: boolean
|
|
82
|
+
requiresDelivery: boolean
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type DeepPathwayConfig = Record<
|
|
86
|
+
Provider,
|
|
87
|
+
Partial<Record<Chains, Partial<Record<Chains, Pathway>>>>
|
|
88
|
+
>
|
|
89
|
+
|
|
90
|
+
export const pathways = {
|
|
91
|
+
[Provider.PULSECHAIN]: {
|
|
92
|
+
[Chains.PLS]: {
|
|
93
|
+
[Chains.ETH]: {
|
|
94
|
+
from: '0x4fD0aaa7506f3d9cB8274bdB946Ec42A1b8751Ef',
|
|
95
|
+
to: '0x1715a3E4A142d8b698131108995174F37aEBA10D',
|
|
96
|
+
destinationRouter: '0x2Ed02372C3efF1F239B22a901ceA8Cf6fbf82d8d',
|
|
97
|
+
nativeRouter: '0x612Da68aA7BcA625dD2F27f252cddB573674991A',
|
|
98
|
+
validator: '0x5ECfE77502317F3677f23C3b8Ab17929ACE3D74E',
|
|
99
|
+
usesExtraParam: false,
|
|
100
|
+
feeManager: 'from',
|
|
101
|
+
toHome: false,
|
|
102
|
+
requiresDelivery: true,
|
|
103
|
+
defaultAssetIn: {
|
|
104
|
+
symbol: 'WETH',
|
|
105
|
+
name: 'Wrapped Ether from Ethereum',
|
|
106
|
+
address: '0x02DcdD04e3F455D838cd1249292C58f3B79e3C3C',
|
|
107
|
+
decimals: 18,
|
|
108
|
+
chainId: 369,
|
|
109
|
+
logoURI: imageLinks.network(369),
|
|
110
|
+
extensions: {
|
|
111
|
+
bridgeInfo: {
|
|
112
|
+
'1': {
|
|
113
|
+
tokenAddress: nativeAssetOut[Chains.ETH],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
[Chains.ETH]: {
|
|
121
|
+
[Chains.PLS]: {
|
|
122
|
+
from: '0x1715a3E4A142d8b698131108995174F37aEBA10D',
|
|
123
|
+
to: '0x4fD0aaa7506f3d9cB8274bdB946Ec42A1b8751Ef',
|
|
124
|
+
nativeRouter: '0x2Ed02372C3efF1F239B22a901ceA8Cf6fbf82d8d',
|
|
125
|
+
destinationRouter: '0x612Da68aA7BcA625dD2F27f252cddB573674991A',
|
|
126
|
+
validator: '0x5ECfE77502317F3677f23C3b8Ab17929ACE3D74E',
|
|
127
|
+
feeManager: 'to',
|
|
128
|
+
usesExtraParam: false,
|
|
129
|
+
toHome: true,
|
|
130
|
+
requiresDelivery: false,
|
|
131
|
+
defaultAssetIn: {
|
|
132
|
+
chainId: 1,
|
|
133
|
+
address: nativeAssetOut[Chains.ETH],
|
|
134
|
+
name: 'Wrapped Ether',
|
|
135
|
+
symbol: 'WETH',
|
|
136
|
+
decimals: 18,
|
|
137
|
+
logoURI: imageLinks.network(1),
|
|
138
|
+
extensions: {
|
|
139
|
+
bridgeInfo: {
|
|
140
|
+
'369': {
|
|
141
|
+
tokenAddress: '0x02DcdD04e3F455D838cd1249292C58f3B79e3C3C',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
[Provider.TOKENSEX]: {
|
|
150
|
+
[Chains.PLS]: {
|
|
151
|
+
[Chains.BNB]: {
|
|
152
|
+
from: '0xf1DFc63e10fF01b8c3d307529b47AefaD2154C0e',
|
|
153
|
+
to: '0xb4005881e81a6ecd2c1f75d58e8e41f28d59c6b1',
|
|
154
|
+
destinationRouter: '0x9c50EfD36dF2DBD310fBC981d6D8478cF3484B85',
|
|
155
|
+
nativeRouter: '0xB80B80EBB375BD83a9d6c0761b197d16a8670f81',
|
|
156
|
+
validator: '0xc3c3d5d3ba946a2eb3906878ebe187418b0b524e',
|
|
157
|
+
feeManager: 'from',
|
|
158
|
+
usesExtraParam: true,
|
|
159
|
+
toHome: false,
|
|
160
|
+
requiresDelivery: true,
|
|
161
|
+
defaultAssetIn: {
|
|
162
|
+
symbol: 'WBNB',
|
|
163
|
+
name: 'Wrapped BNB',
|
|
164
|
+
address: '0x518076CCE3729eF1a3877EA3647a26e278e764FE',
|
|
165
|
+
decimals: 18,
|
|
166
|
+
chainId: 369,
|
|
167
|
+
logoURI: imageLinks.network(369),
|
|
168
|
+
extensions: {
|
|
169
|
+
bridgeInfo: {
|
|
170
|
+
'56': {
|
|
171
|
+
tokenAddress: nativeAssetOut[Chains.BNB],
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
[Chains.BNB]: {
|
|
179
|
+
[Chains.PLS]: {
|
|
180
|
+
from: '0xb4005881e81a6ecd2c1f75d58e8e41f28d59c6b1',
|
|
181
|
+
to: '0xf1DFc63e10fF01b8c3d307529b47AefaD2154C0e',
|
|
182
|
+
nativeRouter: '0x9c50EfD36dF2DBD310fBC981d6D8478cF3484B85',
|
|
183
|
+
destinationRouter: '0xB80B80EBB375BD83a9d6c0761b197d16a8670f81',
|
|
184
|
+
validator: '0xc3c3d5d3ba946a2eb3906878ebe187418b0b524e',
|
|
185
|
+
feeManager: 'to',
|
|
186
|
+
usesExtraParam: true,
|
|
187
|
+
toHome: true,
|
|
188
|
+
requiresDelivery: false,
|
|
189
|
+
defaultAssetIn: {
|
|
190
|
+
chainId: 56,
|
|
191
|
+
address: nativeAssetOut[Chains.BNB],
|
|
192
|
+
name: 'Wrapped BNB from BSC',
|
|
193
|
+
symbol: 'WBNB',
|
|
194
|
+
decimals: 18,
|
|
195
|
+
logoURI: imageLinks.network(56),
|
|
196
|
+
extensions: {
|
|
197
|
+
bridgeInfo: {
|
|
198
|
+
'369': {
|
|
199
|
+
tokenAddress: '0x518076CCE3729eF1a3877EA3647a26e278e764FE',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
} as DeepPathwayConfig
|
|
208
|
+
|
|
209
|
+
export const testnetPathways = {
|
|
210
|
+
[Provider.TOKENSEX]: {
|
|
211
|
+
// plsv4 to bnb home omnibridge: 0xfd0c07C376D27EA0E9fA64Ac5D2481439e36CF9C
|
|
212
|
+
// [Chains.BNB]: {
|
|
213
|
+
// [Chains.V4PLS]: {
|
|
214
|
+
// from: '0xDDf1b0CD7fb7AAA310c703C17995c6671F9DC806',
|
|
215
|
+
// to: '0xfd0c07C376D27EA0E9fA64Ac5D2481439e36CF9C',
|
|
216
|
+
// usesExtraParam: false,
|
|
217
|
+
// requiresDelivery: false,
|
|
218
|
+
// toHome: true,
|
|
219
|
+
// feeManager: 'to',
|
|
220
|
+
// defaultAssetIn: {
|
|
221
|
+
// chainId: 56,
|
|
222
|
+
// address: nativeAssetOut[Chains.TBNB],
|
|
223
|
+
// },
|
|
224
|
+
// },
|
|
225
|
+
// },
|
|
226
|
+
// [Chains.V4PLS]: {
|
|
227
|
+
// [Chains.BNB]: {
|
|
228
|
+
// from: '0xfd0c07C376D27EA0E9fA64Ac5D2481439e36CF9C',
|
|
229
|
+
// to: '0xDDf1b0CD7fb7AAA310c703C17995c6671F9DC806',
|
|
230
|
+
// },
|
|
231
|
+
// },
|
|
232
|
+
},
|
|
233
|
+
[Provider.PULSECHAIN]: {
|
|
234
|
+
[Chains.V4PLS]: {
|
|
235
|
+
[Chains.SEP]: {
|
|
236
|
+
from: '0x6B08a50865aDeCe6e3869D9AfbB316d0a0436B6c',
|
|
237
|
+
to: '0x546e37DAA15cdb82fd1a717E5dEEa4AF08D4349A',
|
|
238
|
+
nativeRouter: '0xCA1BDc038909038C4B3cbab0EA0ec3cC2839F106',
|
|
239
|
+
destinationRouter: '0xCb4057d7DA4CE521B93b71186DfE11f9FBC3e3b1',
|
|
240
|
+
validator: '0xDcd4D88552c86114b2ca029F7F8d4e1a7951d051',
|
|
241
|
+
usesExtraParam: false,
|
|
242
|
+
requiresDelivery: true,
|
|
243
|
+
toHome: false,
|
|
244
|
+
feeManager: 'from',
|
|
245
|
+
defaultAssetIn: {
|
|
246
|
+
chainId: 943,
|
|
247
|
+
address: '0x3677bd78CCf4d299328ECFBa61790cf8dBfcF686',
|
|
248
|
+
name: 'Wrapped Ether from Sepolia',
|
|
249
|
+
symbol: 'WsepETH',
|
|
250
|
+
decimals: 18,
|
|
251
|
+
logoURI: imageLinks.network(943),
|
|
252
|
+
extensions: {
|
|
253
|
+
bridgeInfo: {
|
|
254
|
+
'11155111': {
|
|
255
|
+
tokenAddress: nativeAssetOut[Chains.SEP],
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
[Chains.SEP]: {
|
|
263
|
+
[Chains.V4PLS]: {
|
|
264
|
+
from: '0x546e37DAA15cdb82fd1a717E5dEEa4AF08D4349A',
|
|
265
|
+
to: '0x6B08a50865aDeCe6e3869D9AfbB316d0a0436B6c',
|
|
266
|
+
nativeRouter: '0xCb4057d7DA4CE521B93b71186DfE11f9FBC3e3b1',
|
|
267
|
+
destinationRouter: '0xCA1BDc038909038C4B3cbab0EA0ec3cC2839F106',
|
|
268
|
+
validator: '0xDcd4D88552c86114b2ca029F7F8d4e1a7951d051',
|
|
269
|
+
usesExtraParam: false,
|
|
270
|
+
requiresDelivery: false,
|
|
271
|
+
toHome: true,
|
|
272
|
+
feeManager: 'to',
|
|
273
|
+
defaultAssetIn: {
|
|
274
|
+
chainId: 11_155_111,
|
|
275
|
+
address: nativeAssetOut[Chains.SEP],
|
|
276
|
+
name: 'Wrapped Ether',
|
|
277
|
+
symbol: 'sepWETH',
|
|
278
|
+
decimals: 18,
|
|
279
|
+
logoURI: imageLinks.network(11_155_111),
|
|
280
|
+
extensions: {
|
|
281
|
+
bridgeInfo: {
|
|
282
|
+
'943': {
|
|
283
|
+
tokenAddress: '0x3677bd78CCf4d299328ECFBa61790cf8dBfcF686',
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
} as DeepPathwayConfig
|
|
292
|
+
|
|
293
|
+
const mainnetBridgeKeys = [
|
|
294
|
+
[Provider.PULSECHAIN, Chains.PLS, Chains.ETH],
|
|
295
|
+
[Provider.PULSECHAIN, Chains.ETH, Chains.PLS],
|
|
296
|
+
[Provider.TOKENSEX, Chains.PLS, Chains.BNB],
|
|
297
|
+
[Provider.TOKENSEX, Chains.BNB, Chains.PLS],
|
|
298
|
+
] as BridgeKey[]
|
|
299
|
+
|
|
300
|
+
const testnetBridgeKeys = [
|
|
301
|
+
[Provider.PULSECHAIN, Chains.SEP, Chains.V4PLS],
|
|
302
|
+
[Provider.PULSECHAIN, Chains.V4PLS, Chains.SEP],
|
|
303
|
+
] as BridgeKey[]
|
|
304
|
+
|
|
305
|
+
export const validBridgeKeys = (isProd: boolean) => [
|
|
306
|
+
...mainnetBridgeKeys,
|
|
307
|
+
...(isProd ? [] : testnetBridgeKeys),
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
export const inferBridgeKey = ({
|
|
311
|
+
currentKey,
|
|
312
|
+
provider,
|
|
313
|
+
fromChain,
|
|
314
|
+
toChain,
|
|
315
|
+
}: {
|
|
316
|
+
currentKey: BridgeKey
|
|
317
|
+
provider: Provider
|
|
318
|
+
fromChain?: Chains
|
|
319
|
+
toChain?: Chains
|
|
320
|
+
}) => {
|
|
321
|
+
const [, fromChainKey, toChainKey] = currentKey
|
|
322
|
+
const from = fromChain ?? fromChainKey
|
|
323
|
+
const to = toChain ?? toChainKey
|
|
324
|
+
const currentFromChainIsValid = !!_.get(pathways, [provider, from])
|
|
325
|
+
const nextFromChain = currentFromChainIsValid
|
|
326
|
+
? from
|
|
327
|
+
: (Object.keys(pathways[provider])[0] as Chains)
|
|
328
|
+
const currentToChainIsValid = !!_.get(pathways, [provider, nextFromChain, to])
|
|
329
|
+
const nextToChain = currentToChainIsValid
|
|
330
|
+
? to
|
|
331
|
+
: (Object.keys(pathways[provider]![nextFromChain]!)[0] as Chains)
|
|
332
|
+
return [provider, nextFromChain, nextToChain] as BridgeKey
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export const pathway = (bridgeKey: BridgeKey | null, isProd: boolean) => {
|
|
336
|
+
if (!bridgeKey) return
|
|
337
|
+
return _.get(pathways, bridgeKey) || (!isProd ? _.get(testnetPathways, bridgeKey) : undefined)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export const defaultAssetIn = ($bridgeKey: BridgeKey | null, isProd: boolean) => {
|
|
341
|
+
const conf = pathway($bridgeKey, isProd)
|
|
342
|
+
const defaultAssetIn = _.get(conf, ['defaultAssetIn']) as Token | undefined
|
|
343
|
+
return defaultAssetIn
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export const isNative = (asset: Token | TokenOut | null, bridgeKey: BridgeKey | null) => {
|
|
347
|
+
if (!bridgeKey || !asset) {
|
|
348
|
+
return false
|
|
349
|
+
}
|
|
350
|
+
return (
|
|
351
|
+
(asset.address === zeroAddress ||
|
|
352
|
+
nativeAssetOut[toChain(asset.chainId)]?.toLowerCase() === asset.address?.toLowerCase()) &&
|
|
353
|
+
!asset.name.includes(' from Pulsechain')
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export const isUnwrappable = (
|
|
358
|
+
asset: Pick<Token, 'extensions'> | null,
|
|
359
|
+
bridgeKey: BridgeKey | null,
|
|
360
|
+
) => {
|
|
361
|
+
if (!bridgeKey || !asset) {
|
|
362
|
+
return false
|
|
363
|
+
}
|
|
364
|
+
const [, , toChain] = bridgeKey
|
|
365
|
+
return nativeAssetOut[toChain] === asset.extensions?.bridgeInfo?.[Number(toChain)]?.tokenAddress
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export const canChangeUnwrap = (bridgeKey: BridgeKey, assetIn: Token | null) =>
|
|
369
|
+
!!assetIn && isUnwrappable(assetIn, bridgeKey)
|
package/src/fee-type.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
export enum FeeType {
|
|
3
|
+
PERCENT = '%',
|
|
4
|
+
GAS_TIP = 'gas+%',
|
|
5
|
+
FIXED = 'fixed',
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type FeeTypeKeys = keyof typeof FeeType
|
|
9
|
+
|
|
10
|
+
export const feeTypeValToKeyMap = new Map<FeeType, FeeTypeKeys>(
|
|
11
|
+
(Object.keys(FeeType) as FeeTypeKeys[]).map((key) => [FeeType[key], key]),
|
|
12
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getAddress } from 'viem'
|
|
2
|
+
|
|
3
|
+
let imageRoot = 'https://gib.show'
|
|
4
|
+
|
|
5
|
+
export const setImageRoot = (root: string) => {
|
|
6
|
+
imageRoot = root
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const getImageRoot = () => imageRoot
|
|
10
|
+
|
|
11
|
+
interface MinimalTokenInfo {
|
|
12
|
+
chainId: number
|
|
13
|
+
address: string
|
|
14
|
+
logoURI?: string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const list = (path: string) => {
|
|
18
|
+
return `${imageRoot}/list${path}`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const network = (chainId: number) => {
|
|
22
|
+
if (!chainId) {
|
|
23
|
+
console.trace('network image')
|
|
24
|
+
}
|
|
25
|
+
return `${imageRoot}/image/${Number(chainId)}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const image = (t: MinimalTokenInfo) =>
|
|
29
|
+
t.logoURI || `${network(t.chainId)}/${getAddress(t.address)}`
|
|
30
|
+
|
|
31
|
+
export const images = (sources: string[]) =>
|
|
32
|
+
`${imageRoot}/image/?${sources.map((s) => `i=${s}`).join('&')}`
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Hex, Chain } from 'viem'
|
|
2
|
+
import type { Chains, Provider } from './config.js'
|
|
3
|
+
|
|
4
|
+
export type PerNetworkBridgeLink = {
|
|
5
|
+
tokenAddress: Hex
|
|
6
|
+
}
|
|
7
|
+
export type Extensions = {
|
|
8
|
+
wrapped?: { address: Hex }
|
|
9
|
+
bridgeInfo?: Record<number, PerNetworkBridgeLink>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type TokenMetadata = {
|
|
13
|
+
name: string
|
|
14
|
+
symbol: string
|
|
15
|
+
decimals: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type Token = TokenMetadata & {
|
|
19
|
+
address: string
|
|
20
|
+
chainId: number
|
|
21
|
+
logoURI: string | null
|
|
22
|
+
extensions?: Extensions
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type TokenOut = Omit<Token, 'address'> & { address: string | null }
|
|
26
|
+
|
|
27
|
+
export type TokenList = {
|
|
28
|
+
tokens: Token[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type BridgeKey = [Provider, Chains, Chains]
|
|
32
|
+
|
|
33
|
+
export type VisualChain = Chain & {
|
|
34
|
+
chainId: Chains
|
|
35
|
+
logoURI: string
|
|
36
|
+
alt: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type ChainsMetadata = Record<Chains, VisualChain>
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"composite": true,
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"moduleDetection": "force",
|
|
13
|
+
"moduleResolution": "nodenext"
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*.ts", "src/**/*.js"]
|
|
16
|
+
}
|