Sub-Adapters 6
Preview and test each sub adapter.
Arbitrum One Bridge (arbitrum-one)
Avalanche Bridge - Ethereum (avalanche-ethereum)
Avalanche Bridge - Bitcoin (avalanche-bitcoin)
Gnosis Chain Bridge (gnosis-chain)
Gnosis Chain Bridge - BNB Chain (gnosis-omni-bsc)
Optimism Bridge (optimism)
Adapter Code
Check the entire code written for the Adapter.
Source code
Showing TS source.
1export const name = 'Simple Bridges';
2export const version = '0.2.5';
3export const license = 'MIT';
4export const description = 'Fetches data for chain-to-chain bridges that can be easily queried.';
5
6interface Chain {
7 chain: string
8 ignore?: string[]
9 countManually?: boolean
10 addresses?: string[]
11}
12
13interface SubBridge {
14 id: string
15 icon?: string
16 bridgeId: string
17 subtitle?: string
18 chainA: Chain
19 chainB: Chain
20 iconType?: string
21 metadata?: Partial<BridgeMetadata>
22}
23
24interface BridgeMetadata {
25 [name: string]: any
26 id: string
27 icon?: string
28 iconType?: string
29 name: string
30 category: string
31 audits?: {
32 name: string
33 date: string
34 url: string
35 }[]
36}
37
38const bridges: BridgeMetadata[] = [
39 {
40 id: 'arbitrum-one',
41 icon: 'QmeRunQGxv3haLoMfgwD2VjKwScf7gDQiA1DCYd1HNBCG6',
42 name: 'Arbitrum One Bridge',
43 website: 'https://bridge.arbitrum.io/',
44 category: 'native',
45 description: 'The Arbitrum Bridge is the native bridge of the Arbitrum rollup, which allows any ERC-20 '
46 + 'asset to be bridged from Ethereum mainnet into Arbitrum One.\n'
47 + 'The bridge inherits the security of Ethereum, although the Arbitrum maintains upgrade keys which can '
48 + 'change the bridge code.',
49 },
50 {
51 id: 'avalanche',
52 name: 'Avalanche Bridge',
53 website: 'https://bridge.avax.network/',
54 category: 'multisig-hardware',
55 icon: 'QmcWreeBT5HuurXsc5LbdDphmXUY3T4YrfnXvqt6y2no68',
56 requiredSigners: 6,
57 totalSigners: 8,
58 description: 'The Avalanche Bridge allows assets to be bridged from the Ethereum and Bitcoin blockchains '
59 + 'to the Avalanche C-Chain.\n'
60 + 'The bridge is secured by an Intel SGX secure enclave, which validates the deposit/withdrawal events '
61 + 'on the various chains.',
62 },
63 {
64 id: 'gnosis-chain',
65 icon: 'QmPFzLaw3G3SDvsHoebWGn52H1n8dpfEmmvSznkKDkPuC4',
66 name: 'Gnosis Chain Bridge',
67 category: 'multisig',
68 requiredSigners: 4,
69 totalSigners: 6,
70 description: 'The Gnosis Chain\'s native bridge bridges Dai from Ethereum into xDai, the native '
71 + 'asset of Gnosis Chain. Additionally, the Gnosis Chain OmniBridge allows any ERC-20 asset to be '
72 + 'bridged from Ethereum.\n'
73 + 'Bridge validators observe events on both chains and relay messages to the '
74 + 'bridge contract. Bridge validators are selected by the 6 members of the Bridge Governance Board.',
75 },
76 {
77 id: 'optimism',
78 icon: 'QmegSBGwcyoYU9yrgGS2U13DAexE9VYdetTn9rqa88yjZK',
79 name: 'Optimism Bridge',
80 category: 'native',
81 description: 'The Optimism Bridge is the native bridge of the Optimism rollup, which allows any ERC-20 '
82 + 'asset to be bridged from Ethereum mainnet into Optimism.\n'
83 + 'The bridge inherits the security of Ethereum, although the Optimism maintains upgrade keys which can '
84 + 'change the bridge code.',
85 },
86]
87
88const subBridges: SubBridge[] = [
89 {
90 id: 'arbitrum-one',
91 bridgeId: 'arbitrum-one',
92 chainA: {
93 chain: 'ethereum',
94 addresses: [
95 '0x8315177aB297bA92A06054cE80a67Ed4DBd7ed3a',
96 '0xa3A7B6F88361F48403514059F1F16C8E78d60EeC',
97 '0xcEe284F754E854890e311e3280b767F80797180d',
98 ],
99 },
100 chainB: {
101 chain: 'arbitrum-one',
102 },
103 },
104 {
105 id: 'avalanche-ethereum',
106 bridgeId: 'avalanche',
107 chainA: {
108 chain: 'avalanche',
109 },
110 chainB: {
111 chain: 'ethereum',
112 addresses: [
113 '0x8eb8a3b98659cce290402893d0123abb75e3ab28',
114 ],
115 },
116 metadata: {
117 subtitle: 'Ethereum',
118 },
119 },
120 {
121 id: 'avalanche-bitcoin',
122 bridgeId: 'avalanche',
123 chainA: {
124 chain: 'avalanche',
125 },
126 chainB: {
127 chain: 'bitcoin',
128 addresses: [
129 'bc1q2f0tczgrukdxjrhhadpft2fehzpcrwrz549u90',
130 ],
131 },
132 metadata: {
133 subtitle: 'Bitcoin',
134 },
135 },
136 {
137 id: 'gnosis-chain',
138 bridgeId: 'gnosis-chain',
139 chainA: {
140 chain: 'ethereum',
141 addresses: [
142 '0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016', // xDai Bridge
143 '0x88ad09518695c6c3712AC10a214bE5109a655671', // OmniBridge
144 ],
145 },
146 chainB: {
147 chain: 'gnosis-chain',
148 },
149 },
150 {
151 id: 'gnosis-omni-bsc',
152 bridgeId: 'gnosis-chain',
153 chainA: {
154 chain: 'bsc',
155 addresses: [
156 '0xF0b456250DC9990662a6F25808cC74A6d1131Ea9',
157 ],
158 },
159 chainB: {
160 chain: 'gnosis-chain',
161 },
162 metadata: {
163 subtitle: 'BNB Chain',
164 },
165 },
166 {
167 id: 'optimism',
168 bridgeId: 'optimism',
169 chainA: {
170 chain: 'ethereum',
171 addresses: [
172 '0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65',
173 '0x5Fd79D46EBA7F351fe49BFF9E87cdeA6c821eF9f',
174 '0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1',
175 ],
176 },
177 chainB: {
178 chain: 'optimism',
179 },
180 },
181];
182
183export async function setup(sdk: Context) {
184 const solanaTokens = {
185 'So11111111111111111111111111111111111111112': () => sdk.coinGecko.getCurrentPrice('solana'),
186 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v': async () => 1, // USDC
187 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB': async () => 1, // USDT
188 'NFTUkR4u7wKxy9QLaX2TGvd9oZSWoMo4jqSJqdMb7Nk': () => sdk.coinGecko.getCurrentPrice('blockasset'),
189 '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R': () => sdk.coinGecko.getCurrentPrice('raydium'),
190 }
191
192 const getSolanaPortfolio = async (chain: Chain) => {
193 const response = await sdk.http.post('https://api.mainnet-beta.solana.com', {
194 jsonrpc: '2.0',
195 id: '1',
196 method: 'getTokenAccountsByOwner',
197 params: [
198 (chain as any).address,
199 { programId: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
200 { encoding: "jsonParsed", commitment: "processed" },
201 ],
202 });
203
204 let total = 0
205
206 for (const token of response.result.value) {
207 const getPrice = solanaTokens[token.account.data.parsed.info.mint]
208 if (getPrice) {
209 const price = await getPrice()
210 const value = price * token.account.data.parsed.info.tokenAmount.uiAmount
211 total += value
212 }
213 }
214
215 return total
216 }
217
218 const portfolioCache: { [addresses: string]: Promise<any> } = {}
219 const getPortfolio = (addresses: string[]): Promise<any> => {
220 const key = addresses.join(',')
221 if (!portfolioCache[key]) {
222 const url = `https://cryptostats-api-proxy.vercel.app/api/v1/zapper/${key}`;
223 portfolioCache[key] = sdk.http.get(url)
224 .then(result => {
225 if (result.success) {
226 return result.value
227 }
228 throw new Error(`Request to '${url}' failed: ${result.message}`);
229 });
230 }
231 return portfolioCache[key];
232 }
233
234 const getZapperValue = async (chain: Chain) => {
235 // @ts-ignore
236 const addresses = chain.addresses || [chain.address]
237 const portfolio = await getPortfolio(addresses)
238 let value = 0
239
240 for (const token of portfolio) {
241 if (!chain.ignore || !chain.ignore.includes(token.address)) {
242 value += token.balanceUSD
243 }
244 }
245
246 return value
247 }
248
249 const getBitcoinValue = async (chain: Chain) => {
250 // @ts-ignore
251 const addresses: string[] = chain.addresses || [chain.address]
252
253 const balances = await Promise.all(addresses.map(async (address: string): Promise<number> => {
254 const data = await sdk.http.get(`https://blockstream.info/api/address/${address}`);
255 const unspent = (data.chain_stats.funded_txo_sum - data.chain_stats.spent_txo_sum) / 1e8;
256 return unspent;
257 }));
258
259 const total = balances.reduce((prev: number, val: number) => prev + val, 0);
260 const btcPrice = await sdk.coinGecko.getCurrentPrice('bitcoin');
261 return total * btcPrice;
262 }
263
264 const chainLoaders: { [chain: string]: (chain: Chain) => Promise<number> } = {
265 bitcoin: getBitcoinValue,
266 ethereum: getZapperValue,
267 bsc: getZapperValue,
268 matic: getZapperValue,
269 avalanche: getZapperValue,
270 fantom: getZapperValue,
271 solana: getSolanaPortfolio,
272 };
273
274 const getValue = (chain: Chain) => {
275 if (!chain.addresses) {
276 return Promise.resolve(0);
277 }
278 if (!chainLoaders[chain.chain]) {
279 throw new Error(`Chain ${chain.chain} not found`);
280 }
281 return chainLoaders[chain.chain](chain);
282 }
283
284 const metadataByBridge: { [id: string]: any } = {};
285
286 for (const bridge of bridges) {
287 const { id, icon, iconType, ...metadata } = bridge;
288 const bridgeMetadata: any = {
289 ...metadata,
290 icon: icon ? sdk.ipfs.getDataURILoader(icon, iconType || 'image/svg+xml') : null,
291 }
292 sdk.registerBundle(id, bridgeMetadata);
293 metadataByBridge[id] = bridgeMetadata;
294 }
295
296 for (const subbridge of subBridges) {
297 if (!metadataByBridge[subbridge.bridgeId]) {
298 throw new Error(`Missing bridgeId for ${subbridge.id}`)
299 }
300
301 sdk.register({
302 id: subbridge.id,
303 bundle: subbridge.bridgeId,
304 queries: {
305 currentValueBridgedAToB: () => getValue(subbridge.chainA),
306 currentValueBridgedBToA: () => getValue(subbridge.chainB),
307 },
308 metadata: {
309 ...metadataByBridge[subbridge.bridgeId],
310 ...subbridge.metadata,
311 icon: subbridge.icon
312 ? sdk.ipfs.getDataURILoader(subbridge.icon, subbridge.iconType || 'image/svg+xml')
313 : metadataByBridge[subbridge.bridgeId].icon,
314 chainA: subbridge.chainA.chain,
315 chainB: subbridge.chainB.chain,
316 },
317 });
318 }
319}
320
It's something off?
Report it to the discussion board on Discord, we will take care of it.