@broxus/js-bridge-essentials
Version:
Bridge JavaScript Essentials library
1,035 lines (1,034 loc) • 85.1 kB
JavaScript
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, TvmToken, getTvmConnection, isAddressesEquals, isTvmAddress, resolveTvmAddress, } from '@broxus/js-core';
import { debug, error, isGoodNumber, sliceAddress, throwException } from '@broxus/js-utils';
import { PublicKey } from '@solana/web3.js';
import { Buffer } from 'buffer';
import { action, computed, makeObservable } from 'mobx';
import { EvmZeroAddress } from '../constants';
import { AlienProxyV8, AlienProxyV9, EvmMultiVault, EvmToken, EvmTonPipelineConfig, EvmTvmPipelineConfig, NativeProxyV6, NativeProxyV7, SolTvmPipelineConfig, SolanaToken, 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';
// eslint-disable-next-line import/no-relative-packages
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 = new Set((currency.tags ?? []).concat('currency')).values().toArray();
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: new Set((token.tags ?? []).concat(vendor)).values().toArray(),
})),
}));
})).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 = existing.get('tags')?.values().toArray() ?? [];
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 = existing.get('tags')?.values().toArray() ?? [];
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 = existing.get('tags')?.values().toArray() ?? [];
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 = existing.get('tags')?.values().toArray() ?? [];
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 = existing.get('tags')?.values().toArray() ?? [];
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 = existing.get('tags')?.values().toArray() ?? [];
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 = existing.get('tags')?.values().toArray() ?? [];
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 = existing.get('tags')?.values().toArray() ?? [];
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([
...(item.get('tags')?.values().toArray() ?? []),
...(token.get('tags')?.values().toArray() ?? []),
]),
});
}
const isEvmTokens = item instanceof EvmToken && token instanceof EvmToken;
if (isEvmAddress(token.root) && isEvmTokens) {
return item.setData({
...token.toJSON(),
tags: new Set([
...(item.get('tags')?.values().toArray() ?? []),
...(token.get('tags')?.values().toArray() ?? []),
]),
});
}
}
return item;
}));
}
else {
const tokens = this.tokens.slice();
// @ts-ignore
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, merge, config = {
isMerged: false,
tvmTokenAddress: resolveTvmAddress(root),
};
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 (!isAddressesEquals(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: isAddressesEquals(config.evmTokenAddress, evmWrappedCurrency),
isNativeTvmCurrency: isAddressesEquals(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: isAddressesEquals(config.evmTokenAddress, evmWrappedCurrency),
isNativeTvmCurrency: isAddressesEquals(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, merge, config = {
isMerged: false,
tvmTokenAddress: resolveTvmAddress(root),
};
// 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: isAddressesEquals(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');
}
const tvmConnection = getTvmConnection([], targetId, targetNetwork);
let config = {
isMerged: false,
};
const isSol = !isTvmAddress(root);
if (isSol) {
config.solTokenAddress = resolveSolanaAddress(root);
}
else {
config.tvmTokenAddress = resolveTvmAddress(root);
}
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