Adapter

Simple Bridges

Fetches data for chain-to-chain bridges that can be easily queried.

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.

Adapter Info

Version

0.2.5

License

MIT

IPFS CID

QmPXjVJAqZW4XWwHT486TfYbXXnPTbXi3HNbucbP15dK7P

CID (source)

QmPfwY7sbxcs595nC6jTtryD7P2AyqSxcvS3c7tFSu5267

Collections

Author

mihal.eth