UNPKG

@broxus/js-bridge-essentials

Version:

Bridge JavaScript Essentials library

1,036 lines 84.9 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { AbstractStore, areAddressesEqual, getTvmConnection, isTvmAddress, resolveTvmAddress, TvmToken, } from '@broxus/js-core'; import { debug, error, isGoodNumber, sliceString, throwException } from '@broxus/js-utils'; import { PublicKey } from '@solana/web3.js'; import { action, computed, makeObservable } from 'mobx'; import { EvmZeroAddress } from '../constants'; import { AlienProxyV8, AlienProxyV9, EvmMultiVault, EvmToken, EvmTonPipelineConfig, EvmTvmPipelineConfig, NativeProxyV6, NativeProxyV7, SolanaToken, SolTvmPipelineConfig, TokenRootAlienEvm, TokenRootAlienSolana, TonAlienProxyV1, TonAlienProxyV2, TonAlienProxyV3, TonEvmPipelineConfig, TonNativeProxyV1, TonNativeProxyV2, TonNativeProxyV3, TonToken, TvmEvmPipelineConfig, TvmSolPipelineConfig, } from '../models'; import { TokenType, TonChains, } from '../types'; import { findNetwork, getEvmConnection, isEvmAddress, isSolanaAddress, isTonAddress, onlyEvmNetworks, onlyTonNetworks, onlyTvmNetworks, resolveSolanaAddress, resolveTonAddress, } from '../utils'; import initBridge, { getMintAddress, getTokenSettingsAddress, unpackTokenSettings } from '../../wasm/bridge'; export class BridgeAssetsService extends AbstractStore { params; constructor(params) { super(); this.params = params; this.setData(() => ({ assets: new Map(), currencies: [], externals: [], rawTokens: [], tokens: [], })); this.setState(() => ({ isAssetsFetched: false, isBuilt: false, isFetching: false, isTokensFetched: false, })); makeObservable(this); this.init().catch(error); } async init() { const { assetsUrlMap, currenciesListUrl, tokensListsUrls } = this.params; const tonNetworks = onlyTonNetworks(this.params.networks ?? []); const tonChains = tonNetworks.map(network => network.chainId.toString()); try { this.setState({ isBuilt: false, isFetching: true }); const promises = Object.entries({ ...assetsUrlMap }).map(async ([chainId, uri]) => [ chainId, await fetch(uri, { method: 'GET' }).then(value => value.json()), ]); await Promise.all(promises).then(res => { this.setData('assets', res.reduce((acc, [chainId, result]) => acc.set(chainId, result.multitoken), new Map())); }); this.setState('isAssetsFetched', Object.values(this.assets).flat().length > 0); } catch (e) { error('Bridge assets fetching failed with an error', e); this.setState('isFetching', false); return; } if (currenciesListUrl) { try { const currenciesManifest = await fetch(currenciesListUrl, { method: 'GET' }).then(value => value.json()); this.setData('currencies', currenciesManifest.tokens.map(currency => { const tags = Array.from((currency.tags ?? []).concat('currency')); const isTonNetwork = currency.chainId ? Number(currency.chainId) === TonChains.Mainnet : false; if (isTonNetwork && (isTvmAddress(currency.address) || isTonAddress(currency.address))) { return { ...currency, address: resolveTonAddress(currency.address).toRawString(), chainId: Number(currency.chainId ?? TonChains.Mainnet), tags, }; } return { ...currency, tags }; })); } catch (e) { debug('Currencies manifests fetching failed with an error', e); } } try { const entries = Object.entries({ ...tokensListsUrls }); const tokensManifests = await Promise.allSettled(entries.map(async ([vendor, lists]) => { const result = await Promise.allSettled(lists.map(async (url) => { const res = await fetch(url, { method: 'GET' }); return res.json(); })).then(res => res.map(r => (r.status === 'fulfilled' ? r.value : undefined))); return result.filter(Boolean).map(manifest => ({ ...manifest, tokens: manifest?.tokens.map(token => ({ ...token, tags: Array.from((token.tags ?? []).concat(vendor)), })), })); })).then(res => res.map(r => (r.status === 'fulfilled' ? r.value : undefined))); const flatten = Object.values(tokensManifests).flat(); const tokens = flatten.reduce((acc, value) => acc.concat(...(value?.tokens ?? [])), []); this.setData({ rawTokens: tokens.map(token => { const isTonNetwork = token.chainId ? tonChains.includes(token.chainId.toString()) : false; if (isTonNetwork && (isTvmAddress(token?.address) || isTonAddress(token.address))) { return { ...token, address: resolveTonAddress(token.address).toRawString(), chainId: Number(token.chainId ?? TonChains.Mainnet), }; } return token; }), }); } catch (e) { debug('Tokens manifests fetching failed with an error', e); } finally { this.setState({ isFetching: false, isTokensFetched: true }); } await this.build(); } async build() { const exists = new Set(); const nativeCurrencies = this.tokens.slice(); const natives = []; const evmNetworks = onlyEvmNetworks(this.params.networks ?? []); const evmConnections = new Map(); const tvmNetworks = onlyTvmNetworks(this.params.networks ?? []); const tvmConnections = new Map(); const tonNetworks = onlyTonNetworks(this.params.networks ?? []); const tonChains = tonNetworks.map(network => network.chainId.toString()); this._data.currencies.forEach(currency => { const isTonNetwork = currency.chainId ? tonChains.includes(currency.chainId.toString()) : false; try { if (isEvmAddress(currency.address)) { if (currency.chainId == null) { debug('Chain ID is not defined for EVM token', currency.address); return; } const chainId = currency.chainId.toString(); const connection = evmConnections.get(chainId) ?? getEvmConnection(evmNetworks, chainId); if (!connection) { return; } evmConnections.set(chainId, connection); const key = `evm_${chainId}_${currency.address.toLowerCase()}`; if (!exists.has(key)) { natives.push(new EvmToken(connection, { address: currency.address, chainId: Number(currency.chainId), decimals: currency.decimals, key, logoURI: currency.logoURI, name: currency.name, symbol: currency.symbol, tags: new Set((currency.tags ?? []).concat('currency')), wrappedLogoURI: currency.wrappedTokenLogoURI, wrappedName: currency.wrappedTokenName, wrappedSymbol: currency.wrappedTokenSymbol, })); exists.add(key); } } else if (isTonNetwork && (isTvmAddress(currency.address) || isTonAddress(currency.address))) { if (currency.chainId == null) { debug('Chain ID is not defined for EVM token', currency.address); return; } const chainId = currency.chainId.toString(); const connection = tvmConnections.get(chainId) ?? getTvmConnection(tonNetworks, chainId); if (!connection) { return; } tvmConnections.set(chainId, connection); const root = resolveTonAddress(currency.address).toRawString().toLowerCase(); const key = `ton_${chainId}_${root}`; if (!exists.has(key)) { natives.push(new TonToken(connection, { address: currency.address, chainId: Number(currency.chainId), decimals: currency.decimals, key, logoURI: currency.logoURI, name: currency.name, symbol: currency.symbol, tags: new Set((currency.tags ?? []).concat('currency')), wrappedLogoURI: currency.wrappedTokenLogoURI, wrappedName: currency.wrappedTokenName, wrappedSymbol: currency.wrappedTokenSymbol, })); exists.add(key); } } else if (isTvmAddress(currency.address)) { if (currency.chainId == null) { debug('Chain ID is not defined for TVM token', currency.address); return; } const chainId = currency.chainId.toString(); const connection = tvmConnections.get(chainId) ?? getTvmConnection(tvmNetworks, chainId); if (!connection) { return; } tvmConnections.set(chainId, connection); const key = `tvm_${chainId}_${currency.address.toLowerCase()}`; if (!exists.has(key)) { natives.push(new TvmToken(connection, { address: resolveTvmAddress(currency.address), chainId: Number(currency.chainId), decimals: currency.decimals, key, logoURI: currency.logoURI, name: currency.name, symbol: currency.symbol, tags: new Set((currency.tags ?? []).concat('currency')), wrappedLogoURI: currency.wrappedTokenLogoURI, wrappedName: currency.wrappedTokenName, wrappedSymbol: currency.wrappedTokenSymbol, })); exists.add(key); } } else if (isSolanaAddress(currency.address)) { const connection = this.params.getSolConnection?.(); const key = `solana_${currency.chainId}_${currency.address}`; if (connection && !exists.has(key)) { natives.push(new SolanaToken(connection, { address: new PublicKey(currency.address), chainId: Number(currency.chainId), decimals: currency.decimals, key, logoURI: currency.logoURI, name: currency.name, symbol: currency.symbol, tags: new Set((currency.tags ?? []).concat('currency')), wrappedLogoURI: currency.wrappedTokenLogoURI, wrappedName: currency.wrappedTokenName, wrappedSymbol: currency.wrappedTokenSymbol, })); } } } catch { } }); this.setData('tokens', natives.concat(nativeCurrencies)); const tokens = this.tokens.slice(); this._data.rawTokens.forEach(token => { const isTonNetwork = token.chainId ? tonChains.includes(token.chainId.toString()) : false; try { if (isEvmAddress(token.address)) { if (token.chainId == null) { debug('Chain ID is not defined for EVM token', token.address); return; } const chainId = token.chainId.toString(); const connection = evmConnections.get(chainId) ?? getEvmConnection(evmNetworks, chainId); if (!connection) { return; } evmConnections.set(chainId, connection); const key = `evm_${chainId}_${token.address.toLowerCase()}`; if (!exists.has(key)) { tokens.push(new EvmToken(connection, { address: token.address, chainId: Number(token.chainId), decimals: token.decimals, key, logoURI: token.logoURI, name: token.name, symbol: token.symbol, tags: new Set(token.tags), })); exists.add(key); } else if (this.has(key)) { const existing = this.getByKey(key); if (existing instanceof EvmToken) { const tags = Array.from(existing.get('tags') ?? []); existing.setData({ tags: new Set(tags.concat(...(token.tags ?? []))), }); } } else { const existing = tokens.find(i => i.get('key') === key); if (existing instanceof EvmToken) { const tags = Array.from(existing.get('tags') ?? []); existing.setData({ tags: new Set(tags.concat(...(token.tags ?? []))), }); } } } else if (isTonNetwork && (isTvmAddress(token.address) || isTonAddress(token.address))) { if (token.chainId == null) { debug('Chain ID is not defined for TVM token', token.address); return; } const chainId = token.chainId.toString(); const connection = tvmConnections.get(chainId) ?? getTvmConnection(tonNetworks, chainId); if (!connection) { return; } const root = resolveTonAddress(token.address).toRawString().toLowerCase(); const key = `ton_${chainId}_${root}`; if (!exists.has(key)) { tokens.push(new TonToken(connection, { address: token.address, chainId: Number(token.chainId), decimals: token.decimals, key, logoURI: token.logoURI, name: token.name, symbol: token.symbol, tags: new Set(token.tags), })); exists.add(key); } else if (this.has(key)) { const existing = this.getByKey(key); if (existing instanceof TonToken) { const tags = Array.from(existing.get('tags') ?? []); existing.setData({ tags: new Set(tags.concat(...(token.tags ?? []))), }); } } else { const existing = tokens.find(i => i.get('key') === key); if (existing instanceof TonToken) { const tags = Array.from(existing.get('tags') ?? []); existing.setData({ tags: new Set(tags.concat(...(token.tags ?? []))), }); } } } else if (isTvmAddress(token.address)) { if (token.chainId == null) { debug('Chain ID is not defined for TVM token', token.address); return; } const chainId = token.chainId.toString(); const connection = tvmConnections.get(chainId) ?? getTvmConnection(tvmNetworks, chainId); if (!connection) { return; } const key = `tvm_${chainId}_${token.address.toLowerCase()}`; if (!exists.has(key)) { tokens.push(new TvmToken(connection, { address: token.address, chainId: Number(token.chainId), decimals: token.decimals, key, logoURI: token.logoURI, name: token.name, symbol: token.symbol, tags: new Set(token.tags), })); exists.add(key); } else if (this.has(key)) { const existing = this.getByKey(key); if (existing instanceof TvmToken) { const tags = Array.from(existing.get('tags') ?? []); existing.setData({ tags: new Set(tags.concat(...(token.tags ?? []))), }); } } else { const existing = tokens.find(i => i.get('key') === key); if (existing instanceof TvmToken) { const tags = Array.from(existing.get('tags') ?? []); existing.setData({ tags: new Set(tags.concat(...(token.tags ?? []))), }); } } } else if (isSolanaAddress(token.address)) { const connection = this.params.getSolConnection?.(); if (!connection) { return; } const key = `solana_${token.chainId}_${token.address}`; if (!exists.has(key)) { tokens.push(new SolanaToken(connection, { address: new PublicKey(token.address), chainId: Number(token.chainId), decimals: token.decimals, key, logoURI: token.logoURI, name: token.name, symbol: token.symbol, tags: new Set(token.tags), })); } else if (this.has(key)) { const existing = this.getByKey(key); if (existing instanceof SolanaToken) { const tags = Array.from(existing.get('tags') ?? []); existing.setData({ tags: new Set(tags.concat(...(token.tags ?? []))), }); } } else { const existing = tokens.find(i => i.get('key') === key); if (existing instanceof SolanaToken) { const tags = Array.from(existing.get('tags') ?? []); existing.setData({ tags: new Set(tags.concat(...(token.tags ?? []))), }); } } } } catch { } }); this.setData('tokens', tokens); this.setState('isBuilt', true); } get assets() { return this._data.assets; } get tokens() { return this._data.tokens; } get isFetching() { return this._state.isFetching; } get(networkType, chainId, root, lowercase = true) { return this._byKey[`${networkType}_${chainId}_${lowercase ? root.toLowerCase() : root}`]; } getByRoot(root) { return this._byRoot[root]; } getByKey(root) { return this._byKey[root]; } /** * Check if token was stored to the cache. * @param {string} key * @returns {boolean} */ has(key) { return this._byKey[key] !== undefined; } /** * Add a new token to the tokens list. * @param {BridgeAsset} token */ add(token) { if (this.has(token.get('key'))) { this.setData('tokens', this.tokens.map(item => { if (item.get('key').toLowerCase() === token.get('key')?.toLowerCase()) { const isTvmTokens = item instanceof TvmToken && token instanceof TvmToken; if (isTvmAddress(token.root) && isTvmTokens) { return item.setData({ ...token.toJSON(), tags: new Set([ ...Array.from(item.get('tags') ?? []), ...Array.from(token.get('tags') ?? []), ]), }); } const isEvmTokens = item instanceof EvmToken && token instanceof EvmToken; if (isEvmAddress(token.root) && isEvmTokens) { return item.setData({ ...token.toJSON(), tags: new Set([ ...Array.from(item.get('tags') ?? []), ...Array.from(token.get('tags') ?? []), ]), }); } } return item; })); } else { const tokens = this.tokens.slice(); // @ts-ignore This expression is callable. token.setData('root', token.root); tokens.push(token); this.setData('tokens', tokens); } } /** * Remove token from the tokens list * @param {BridgeAsset} token */ remove(token) { if (this.has(token.get('key'))) { this.setData('tokens', this.tokens.filter(item => item.get('key').toLowerCase() !== token.get('key')?.toLowerCase())); } } /** * Returns the minimum required pipeline configuration for TVM - Evm transfers. * @param {string} root - TVM token root * @param {string} sourceId - source TVM network chain id * @param {string} targetId - target EVM network chain id * @returns {Promise<SolTvmPipelineConfig | undefined>} */ async resolveTvmEvmPipeline(root, sourceId, targetId) { const sourceNetwork = findNetwork(this.params.networks ?? [], sourceId, 'tvm'); if (!sourceNetwork) { throw new Error('Cannot resolve source network config'); } const targetNetwork = findNetwork(this.params.networks ?? [], targetId, 'evm'); if (!targetNetwork) { throw new Error('Cannot resolve target network config'); } const tvmConnection = getTvmConnection([], sourceId, sourceNetwork); const evmConnection = getEvmConnection([], targetId, targetNetwork); let alien = false, config = { isMerged: false, tvmTokenAddress: resolveTvmAddress(root), }, merge; try { const meta = await TokenRootAlienEvm.Utils.meta(tvmConnection, root); config = { ...config, baseChainId: meta.baseChainId, evmTokenAddress: meta.evmTokenAddress, isNative: meta.baseChainId !== targetId, }; alien = meta.baseChainId === targetId; } catch (e) { debug('Fetch token meta failed with an error', e); } if (!alien) { const asset = this.assets.get(sourceId)?.evm_tvm; if (!asset) { throw new Error('Evm => Tvm asset not found. Check the Bridge assets manifest.'); } try { merge = await AlienProxyV8.Utils.getEvmTokenMergeDetails(tvmConnection, asset.proxy, root, targetId); alien = merge !== undefined; } catch (e) { debug('Fetch merge details failed with an error', e); } if (!merge) { try { merge = await AlienProxyV9.Utils.getEvmTokenMergeDetails(tvmConnection, asset.proxy, root, targetId); alien = merge !== undefined; } catch (e) { debug('Fetch merge details failed with an error', e); } } } if (alien) { const asset = this.assets.get(sourceId)?.evm_tvm; if (!asset) { throw new Error('Evm => Tvm asset not found. Check the Bridge assets manifest.'); } const assetVault = asset.vaults.find(item => item.chainId === targetId); if (!assetVault) { throw new Error('Evm => Tvm vault not found. Check the Bridge assets manifest.'); } config = { ...config, ...merge, evmConfigurationAddress: resolveTvmAddress(assetVault.ethereumConfiguration), isNative: false, proxyAddress: resolveTvmAddress(asset.proxy), tokenBase: 'evm', vaultAddress: assetVault.vault, }; if (merge?.evmTokenAddress) { config.evmTokenAddress = merge.evmTokenAddress; } if (merge?.canonicalAddress) { config.tvmTokenAddress = merge.canonicalAddress; } } else { const asset = this.assets.get(sourceId)?.tvm_evm; if (!asset) { throw new Error('Tvm => Evm asset not found. Check the Bridge assets manifest.'); } const assetVault = asset.vaults.find(item => item.chainId === targetId); if (!assetVault) { throw new Error('Tvm => Evm vault not found. Check the Bridge assets manifest.'); } config = { ...config, evmConfigurationAddress: resolveTvmAddress(assetVault.ethereumConfiguration), isNative: true, proxyAddress: resolveTvmAddress(asset.proxy), tokenBase: 'tvm', vaultAddress: assetVault.vault, }; try { const evmTokenAddress = await EvmMultiVault.Utils.getNativeToken(evmConnection, config.vaultAddress.toString(), root); config.evmTokenAddress = evmTokenAddress.toLowerCase(); } catch (e) { debug('EvmMultiVault.getNativeToken failed with an error', e); } } if (config.evmTokenAddress) { const meta = await EvmMultiVault.Utils.tokens(evmConnection, config.vaultAddress.toString(), config.evmTokenAddress); if (!areAddressesEqual(meta.custom, EvmZeroAddress)) { config.evmTokenAddress = meta.custom; } config.depositFee = meta.depositFee; config.isBlacklisted = meta.blacklisted; config.withdrawFee = meta.withdrawFee; if (!isGoodNumber(meta.activation) && evmConnection) { try { if (config.isNative) { const fees = await EvmMultiVault.Utils.getNativeFees(evmConnection, config.vaultAddress.toString()); config.depositFee = fees.depositFee; config.withdrawFee = fees.withdrawFee; } else { const fees = await EvmMultiVault.Utils.getAlienFees(evmConnection, config.vaultAddress.toString()); config.depositFee = fees.depositFee; config.withdrawFee = fees.withdrawFee; } } catch (e) { } } try { config.tvmConfigurationAddress = config.isNative ? await NativeProxyV6.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress) : await AlienProxyV8.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress); } catch (e) { config.tvmConfigurationAddress = config.isNative ? await NativeProxyV7.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress) : await AlienProxyV9.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress); } } if (!config.evmTokenAddress) { throw new Error('EVM token address is not defined'); } const evmWrappedCurrency = targetNetwork.currency.wrappedCurrencyAddress; const tvmWrappedCurrency = sourceNetwork.currency.wrappedCurrencyAddress; config = { ...config, isNativeEvmCurrency: areAddressesEqual(config.evmTokenAddress, evmWrappedCurrency), isNativeTvmCurrency: areAddressesEqual(config.tvmTokenAddress, tvmWrappedCurrency), }; return new TvmEvmPipelineConfig(config); } /** * Returns the minimum required pipeline configuration for Solana - TVM transfers. * @param {string} root - EVM token root * @param {string} sourceId - source EVM network chain id * @param {string} targetId - target TVM network chain id * @param {BridgeAsset} fallbackAsset * @returns {Promise<SolTvmPipelineConfig | undefined>} */ async resolveEvmTvmPipeline(root, sourceId, targetId, fallbackAsset) { const sourceNetwork = findNetwork(this.params.networks ?? [], sourceId, 'evm'); if (!sourceNetwork) { throw new Error('Cannot resolve source network config'); } const targetNetwork = findNetwork(this.params.networks ?? [], targetId, 'tvm'); if (!targetNetwork) { throw new Error('Cannot resolve target network config'); } const evmConnection = getEvmConnection([], sourceId, sourceNetwork); const tvmConnection = getTvmConnection([], targetId, targetNetwork); if (!evmConnection) { throw new Error('Connection cannot be defined'); } const asset = this.assets.get(targetId)?.evm_tvm; if (!asset) { throw new Error('Evm => Tvm asset not found. Check the Bridge assets manifest.'); } const assetVault = asset?.vaults.find(item => item.chainId === sourceId); if (!assetVault) { throw new Error('Evm => Tvm vault not found. Check the Bridge assets manifest.'); } let config = { evmTokenAddress: root, isMerged: false, proxyAddress: resolveTvmAddress(asset.proxy), }; config = { ...config, evmConfigurationAddress: resolveTvmAddress(assetVault.ethereumConfiguration), vaultAddress: assetVault.vault, }; let meta = await EvmMultiVault.Utils.tokens(evmConnection, assetVault.vault, root); if (meta.isNative) { const oppositeAsset = this.assets.get(targetId)?.tvm_evm; if (!oppositeAsset) { throw new Error('Tvm => Evm asset not found. Check the Bridge assets manifest.'); } const oppositeVault = oppositeAsset?.vaults.find(item => item.chainId === sourceId); if (!oppositeVault) { throw new Error('Tvm => Evm vault not found. Check the Bridge assets manifest.'); } config = { ...config, evmConfigurationAddress: resolveTvmAddress(oppositeVault.ethereumConfiguration), isNative: true, proxyAddress: resolveTvmAddress(oppositeAsset.proxy), tokenBase: 'tvm', vaultAddress: oppositeVault.vault, }; const tvmTokenAddress = await EvmMultiVault.Utils.natives(evmConnection, oppositeVault.vault, root); if (tvmTokenAddress) { config.tvmTokenAddress = resolveTvmAddress(tvmTokenAddress); } } else { config = { ...config, isNative: false, tokenBase: 'evm' }; let token = this.get('evm', sourceId, root); if (token?.symbol && this.isNativeCurrency(root)) { const tvmTokenAddress = this._data.currencies.find(i => i.chainId?.toString() === targetId?.toString() && i.symbol === token?.symbol && isTvmAddress(i.address))?.address; if (tvmTokenAddress) { config.tvmTokenAddress = resolveTvmAddress(tvmTokenAddress); } } if (!config.tvmTokenAddress) { token = await this.resolveEvmAsset(root, sourceId, fallbackAsset); if (token?.decimals !== undefined && token.name && token.symbol) { config.tvmTokenAddress = await AlienProxyV8.Utils.deriveEvmAlienTokenRoot(tvmConnection, config.proxyAddress, { chainId: sourceId, decimals: token.decimals.toString(), name: token.name, symbol: token.symbol, token: root, }); } if (config.tvmTokenAddress) { try { const merge = await AlienProxyV8.Utils.getEvmTokenMergeDetails(tvmConnection, config.proxyAddress, config.tvmTokenAddress, sourceId); config = { ...config, ...merge }; if (merge?.evmTokenAddress) { config.evmTokenAddress = merge.evmTokenAddress; } if (merge?.canonicalAddress) { config.tvmTokenAddress = merge.canonicalAddress; } } catch (e) { error('Cannot define merge details for Alien token', e); } } } } meta = await EvmMultiVault.Utils.tokens(evmConnection, config.vaultAddress.toString(), root); config.depositFee = meta.depositFee; config.isBlacklisted = meta.blacklisted; config.withdrawFee = meta.withdrawFee; if (!isGoodNumber(meta.activation)) { try { if (config.isNative) { const fees = await EvmMultiVault.Utils.getNativeFees(evmConnection, config.vaultAddress.toString()); config.depositFee = fees.depositFee; config.withdrawFee = fees.withdrawFee; } else { const fees = await EvmMultiVault.Utils.getAlienFees(evmConnection, config.vaultAddress.toString()); config.depositFee = fees.depositFee; config.withdrawFee = fees.withdrawFee; } } catch (e) { } } try { config.tvmConfigurationAddress = config.isNative ? await NativeProxyV6.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress) : await AlienProxyV8.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress); } catch (e) { config.tvmConfigurationAddress = config.isNative ? await NativeProxyV7.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress) : await AlienProxyV9.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress); } const evmWrappedCurrency = sourceNetwork.currency.wrappedCurrencyAddress; const tvmWrappedCurrency = targetNetwork.currency.wrappedCurrencyAddress; config = { ...config, isNativeEvmCurrency: areAddressesEqual(config.evmTokenAddress, evmWrappedCurrency), isNativeTvmCurrency: areAddressesEqual(config.tvmTokenAddress, tvmWrappedCurrency), }; return new EvmTvmPipelineConfig(config); } /** * Returns the minimum required pipeline configuration for TVM - Solana transfers. * @param {string} root - TVM token root * @param {string} sourceId - source Solana network chain id * @param {string} targetId - target TVM network chain id * @returns {Promise<SolTvmPipelineConfig | undefined>} */ async resolveTvmSolPipeline(root, sourceId, targetId) { const connection = this.params.getSolConnection?.(); if (!connection) { throw new Error('Solana connection is not provided'); } const sourceNetwork = findNetwork(this.params.networks ?? [], sourceId, 'tvm'); if (!sourceNetwork) { throw new Error('Cannot resolve source network config'); } const targetNetwork = findNetwork(this.params.networks ?? [], targetId, 'solana'); if (!targetNetwork) { throw new Error('Cannot resolve target network config'); } const tvmConnection = getTvmConnection([], sourceId, sourceNetwork); let alien = false, config = { isMerged: false, tvmTokenAddress: resolveTvmAddress(root), }, merge; // First, we should check alien token root meta. // If request is not falling - token is alien TVM and based in Solana // Otherwise token is steel native for TVM try { const meta = await TokenRootAlienSolana.Utils.meta(tvmConnection, root); config = { ...config, isNative: false, solTokenAddress: resolveSolanaAddress(meta.solTokenAddress), tokenBase: 'solana', }; alien = true; } catch (e) { debug('Fetch token meta failed with an error', e); } if (!alien) { const asset = this.assets.get(sourceId)?.solana_tvm; if (!asset) { throw new Error('Solana => Tvm asset not found. Check the Bridge assets manifest.'); } try { merge = await AlienProxyV8.Utils.getSolTokenMergeDetails(tvmConnection, asset.proxy, root); alien = merge !== undefined; } catch (e) { debug('Fetch merge details failed with an error', e); } if (!merge) { try { merge = await AlienProxyV9.Utils.getSolTokenMergeDetails(tvmConnection, asset.proxy, root); alien = merge !== undefined; } catch (e) { debug('Fetch merge details failed with an error', e); } } } if (alien) { const asset = this.assets.get(sourceId)?.solana_tvm; if (!asset) { throw new Error('Solana => Tvm asset not found. Check the Bridge assets manifest.'); } config = { ...config, ...merge, isNative: false, proxyAddress: resolveTvmAddress(asset.proxy), solConfigurationAddress: resolveTvmAddress(asset.solanaConfiguration), tokenBase: 'solana', tvmConfigurationAddress: resolveTvmAddress(asset.tvmConfiguration), }; if (merge?.solTokenAddress) { config.solTokenAddress = merge.solTokenAddress; } if (merge?.canonicalAddress) { config.tvmTokenAddress = merge.canonicalAddress; } } else { const asset = this.assets.get(sourceId)?.tvm_solana; if (!asset) { throw new Error('Tvm => Solana asset not found. Check the Bridge assets manifest.'); } config = { ...config, isNative: true, proxyAddress: resolveTvmAddress(asset.proxy), solConfigurationAddress: resolveTvmAddress(asset.solanaConfiguration), tokenBase: 'tvm', tvmConfigurationAddress: resolveTvmAddress(asset.tvmConfiguration), vaultAddress: resolveSolanaAddress(asset.vault), }; } try { await initBridge(); let tokenSettingsAddress; if (config.isNative) { if (config.tvmTokenAddress === undefined) { error('TVM token root is not defined'); return undefined; } tokenSettingsAddress = getTokenSettingsAddress(config.tvmTokenAddress.toString(), false); } else { if (config.solTokenAddress === undefined) { error('Solana token mint root is not defined'); return undefined; } tokenSettingsAddress = getTokenSettingsAddress(config.solTokenAddress.toBase58(), true); } tokenSettingsAddress = new PublicKey(tokenSettingsAddress); const tokenSettings = await connection.getAccountInfo(tokenSettingsAddress); if (tokenSettings?.data) { const settings = unpackTokenSettings(tokenSettings.data); config = { ...config, depositFee: settings.fee_deposit_info.multiplier.toString(), solTokenSettingsAddress: tokenSettingsAddress, withdrawFee: settings.fee_withdrawal_info.multiplier.toString(), }; if ('Solana' in settings.kind) { config.vaultAddress = resolveSolanaAddress(settings.kind.Solana.vault); } else if ('Ever' in settings.kind) { config.solTokenAddress = resolveSolanaAddress(settings.kind.Ever.mint); } } else { const solTokenAddress = await getMintAddress(root); config.solTokenAddress = new PublicKey(solTokenAddress); } } catch (e) { error(e); } const solWrappedCurrency = targetNetwork.currency.wrappedCurrencyAddress; const tvmWrappedCurrency = sourceNetwork.currency.wrappedCurrencyAddress; config = { ...config, isNativeSolCurrency: solWrappedCurrency?.equals(config.solTokenAddress) ?? false, isNativeTvmCurrency: areAddressesEqual(config.tvmTokenAddress, tvmWrappedCurrency), }; return new TvmSolPipelineConfig(config); } /** * Returns the minimum required pipeline configuration for Solana - TVM transfers. * @param {string} root - Solana mint address * @param {string} sourceId - source Solana network chain id * @param {string} targetId - target TVM network chain id * @returns {Promise<SolTvmPipelineConfig | undefined>} */ async resolveSolTvmPipeline(root, sourceId, targetId) { const connection = this.params.getSolConnection?.(); if (!connection) { throw new Error('Solana connection is not provided'); } const sourceNetwork = findNetwork(this.params.networks ?? [], sourceId, 'solana'); if (!sourceNetwork) { throw new Error('Cannot resolve source network config'); } const targetNetwork = findNetwork(this.params.networks ?? [], targetId, 'tvm'); if (!targetNetwork) { throw new Error('Cannot resolve target network config'); } let config = { isMerged: false, }; const isSol = !isTvmAddress(root); if (isSol) { config.solTokenAddress = resolveSolanaAddress(root); } else { config.tvmTokenAddress = resolveTvmAddress(root); } const tvmConnection = getTvmConnection([], targetId, targetNetwork); try { await initBridge(); let tokenSettingsAddress = getTokenSettingsAddress(root, isSol); tokenSettingsAddress = new PublicKey(tokenSettingsAddress); const tokenSettings = await connection.getAccountInfo(tokenSettingsAddress); if (tokenSettings?.data == null) { error('Token settings is not provided'); return undefined; } const settings = unpackTokenSettings(tokenSettings.data); config = { ...config, depositFee: settings.fee_deposit_info.multiplier.toString(), solTokenSettingsAddress: tokenSettingsAddress, withdrawFee: settings.fee_withdrawal_info.multiplier.toString(), }; // Alien if ('Solana' in settings.kind) { const asset = this.assets.get(targetId)?.solana_tvm; if (!asset) { throwException('Solana => Tvm asset not found. Check the Bridge assets manifest.'); } const supply = await connection.getTokenSupply(config.solTokenAddress); // let { name, symbol } = settings // try { // const details = await SolanaToken.Utils.getDetails(config.solTokenAddress) // name = details?.name || name // symbol = details?.symbol || symbol // } // catch (e) { // error('Token is not provided in SPL Token Registry') // } config.tvmTokenAddress = await AlienProxyV8.Utils.deriveSolanaAlienTokenRoot(tvmConnection, asset.proxy, { decimals: supply.value.decimals.toString(), name: settings.name, symbol: settings.symbol, token: `0x${Buffer.from(config.solTokenAddress.toBuffer()).toString('hex')}`, }); try { const merge = await AlienProxyV8.Utils.getSolTokenMergeDetails(tvmConnection, asset.proxy, config.tvmTokenAddress); config = { ...config, ...merge }; if (merge?.solTokenAddress) { config.solTokenAddress = merge.solTokenAddress; } if (merge?.canonicalAddress) { config.tvmTokenAddress = merge.canonicalAddress; } } catch (e) { debug('Fetch merge details failed with an error', e); } config = { ...config, proxyAddress: resolveTvmAddress(asset.proxy), solConfigurationAddress: resolveTvmAddress(asset.solanaConfiguration), // solSettingsAddress: resolveSolanaAddress(asset.vault), solTokenSettingsAddress: tokenSettingsAddress, tvmConfigurationAddress: resolveTvmAddress(asset.tvmConfiguration), vaultAddress: resolveSolanaAddress(settings.kind.Solana.vault), }; } // Native else if ('Ever' in settings.kind) { const asset = this.assets.get(targetId)?.tvm_solana; if (!asset) { throwException('Tvm => Solan