@broxus/js-bridge-essentials
Version:
Bridge JavaScript Essentials library
1,016 lines (1,015 loc) • 89.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
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 __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BridgeAssetsService = void 0;
const js_core_1 = require("@broxus/js-core");
const js_utils_1 = require("@broxus/js-utils");
const web3_js_1 = require("@solana/web3.js");
const buffer_1 = require("buffer");
const mobx_1 = require("mobx");
const constants_1 = require("../constants");
const models_1 = require("../models");
const types_1 = require("../types");
const utils_1 = require("../utils");
// eslint-disable-next-line import/no-relative-packages
const bridge_1 = __importStar(require("../../wasm/bridge"));
class BridgeAssetsService extends js_core_1.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,
}));
(0, mobx_1.makeObservable)(this);
this.init().catch(js_utils_1.error);
}
async init() {
const { assetsUrlMap, currenciesListUrl, tokensListsUrls } = this.params;
const tonNetworks = (0, utils_1.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) {
(0, js_utils_1.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) === types_1.TonChains.Mainnet : false;
if (isTonNetwork && ((0, js_core_1.isTvmAddress)(currency.address) || (0, utils_1.isTonAddress)(currency.address))) {
return {
...currency,
address: (0, utils_1.resolveTonAddress)(currency.address).toRawString(),
chainId: Number(currency.chainId ?? types_1.TonChains.Mainnet),
tags,
};
}
return { ...currency, tags };
}));
}
catch (e) {
(0, js_utils_1.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 && ((0, js_core_1.isTvmAddress)(token?.address) || (0, utils_1.isTonAddress)(token.address))) {
return {
...token,
address: (0, utils_1.resolveTonAddress)(token.address).toRawString(),
chainId: Number(token.chainId ?? types_1.TonChains.Mainnet),
};
}
return token;
}),
});
}
catch (e) {
(0, js_utils_1.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 = (0, utils_1.onlyEvmNetworks)(this.params.networks ?? []);
const evmConnections = new Map();
const tvmNetworks = (0, utils_1.onlyTvmNetworks)(this.params.networks ?? []);
const tvmConnections = new Map();
const tonNetworks = (0, utils_1.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 ((0, utils_1.isEvmAddress)(currency.address)) {
if (currency.chainId == null) {
(0, js_utils_1.debug)('Chain ID is not defined for EVM token', currency.address);
return;
}
const chainId = currency.chainId.toString();
const connection = evmConnections.get(chainId) ?? (0, utils_1.getEvmConnection)(evmNetworks, chainId);
if (!connection) {
return;
}
evmConnections.set(chainId, connection);
const key = `evm_${chainId}_${currency.address.toLowerCase()}`;
if (!exists.has(key)) {
natives.push(new models_1.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 && ((0, js_core_1.isTvmAddress)(currency.address) || (0, utils_1.isTonAddress)(currency.address))) {
if (currency.chainId == null) {
(0, js_utils_1.debug)('Chain ID is not defined for EVM token', currency.address);
return;
}
const chainId = currency.chainId.toString();
const connection = tvmConnections.get(chainId) ?? (0, js_core_1.getTvmConnection)(tonNetworks, chainId);
if (!connection) {
return;
}
tvmConnections.set(chainId, connection);
const root = (0, utils_1.resolveTonAddress)(currency.address).toRawString().toLowerCase();
const key = `ton_${chainId}_${root}`;
if (!exists.has(key)) {
natives.push(new models_1.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 ((0, js_core_1.isTvmAddress)(currency.address)) {
if (currency.chainId == null) {
(0, js_utils_1.debug)('Chain ID is not defined for TVM token', currency.address);
return;
}
const chainId = currency.chainId.toString();
const connection = tvmConnections.get(chainId) ?? (0, js_core_1.getTvmConnection)(tvmNetworks, chainId);
if (!connection) {
return;
}
tvmConnections.set(chainId, connection);
const key = `tvm_${chainId}_${currency.address.toLowerCase()}`;
if (!exists.has(key)) {
natives.push(new js_core_1.TvmToken(connection, {
address: (0, js_core_1.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 ((0, utils_1.isSolanaAddress)(currency.address)) {
const connection = this.params.getSolConnection?.();
const key = `solana_${currency.chainId}_${currency.address}`;
if (connection && !exists.has(key)) {
natives.push(new models_1.SolanaToken(connection, {
address: new web3_js_1.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 ((0, utils_1.isEvmAddress)(token.address)) {
if (token.chainId == null) {
(0, js_utils_1.debug)('Chain ID is not defined for EVM token', token.address);
return;
}
const chainId = token.chainId.toString();
const connection = evmConnections.get(chainId) ?? (0, utils_1.getEvmConnection)(evmNetworks, chainId);
if (!connection) {
return;
}
evmConnections.set(chainId, connection);
const key = `evm_${chainId}_${token.address.toLowerCase()}`;
if (!exists.has(key)) {
tokens.push(new models_1.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 models_1.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 models_1.EvmToken) {
const tags = existing.get('tags')?.values().toArray() ?? [];
existing.setData({
tags: new Set(tags.concat(...(token.tags ?? []))),
});
}
}
}
else if (isTonNetwork && ((0, js_core_1.isTvmAddress)(token.address) || (0, utils_1.isTonAddress)(token.address))) {
if (token.chainId == null) {
(0, js_utils_1.debug)('Chain ID is not defined for TVM token', token.address);
return;
}
const chainId = token.chainId.toString();
const connection = tvmConnections.get(chainId) ?? (0, js_core_1.getTvmConnection)(tonNetworks, chainId);
if (!connection) {
return;
}
const root = (0, utils_1.resolveTonAddress)(token.address).toRawString().toLowerCase();
const key = `ton_${chainId}_${root}`;
if (!exists.has(key)) {
tokens.push(new models_1.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 models_1.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 models_1.TonToken) {
const tags = existing.get('tags')?.values().toArray() ?? [];
existing.setData({
tags: new Set(tags.concat(...(token.tags ?? []))),
});
}
}
}
else if ((0, js_core_1.isTvmAddress)(token.address)) {
if (token.chainId == null) {
(0, js_utils_1.debug)('Chain ID is not defined for TVM token', token.address);
return;
}
const chainId = token.chainId.toString();
const connection = tvmConnections.get(chainId) ?? (0, js_core_1.getTvmConnection)(tvmNetworks, chainId);
if (!connection) {
return;
}
const key = `tvm_${chainId}_${token.address.toLowerCase()}`;
if (!exists.has(key)) {
tokens.push(new js_core_1.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 js_core_1.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 js_core_1.TvmToken) {
const tags = existing.get('tags')?.values().toArray() ?? [];
existing.setData({
tags: new Set(tags.concat(...(token.tags ?? []))),
});
}
}
}
else if ((0, utils_1.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 models_1.SolanaToken(connection, {
address: new web3_js_1.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 models_1.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 models_1.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 js_core_1.TvmToken && token instanceof js_core_1.TvmToken;
if ((0, js_core_1.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 models_1.EvmToken && token instanceof models_1.EvmToken;
if ((0, utils_1.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 = (0, utils_1.findNetwork)(this.params.networks ?? [], sourceId, 'tvm');
if (!sourceNetwork) {
throw new Error('Cannot resolve source network config');
}
const targetNetwork = (0, utils_1.findNetwork)(this.params.networks ?? [], targetId, 'evm');
if (!targetNetwork) {
throw new Error('Cannot resolve target network config');
}
const tvmConnection = (0, js_core_1.getTvmConnection)([], sourceId, sourceNetwork);
const evmConnection = (0, utils_1.getEvmConnection)([], targetId, targetNetwork);
let alien = false, merge, config = {
isMerged: false,
tvmTokenAddress: (0, js_core_1.resolveTvmAddress)(root),
};
try {
const meta = await models_1.TokenRootAlienEvm.Utils.meta(tvmConnection, root);
config = {
...config,
baseChainId: meta.baseChainId,
evmTokenAddress: meta.evmTokenAddress,
isNative: meta.baseChainId !== targetId,
};
alien = meta.baseChainId === targetId;
}
catch (e) {
(0, js_utils_1.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 models_1.AlienProxyV8.Utils.getEvmTokenMergeDetails(tvmConnection, asset.proxy, root, targetId);
alien = merge !== undefined;
}
catch (e) {
(0, js_utils_1.debug)('Fetch merge details failed with an error', e);
}
if (!merge) {
try {
merge = await models_1.AlienProxyV9.Utils.getEvmTokenMergeDetails(tvmConnection, asset.proxy, root, targetId);
alien = merge !== undefined;
}
catch (e) {
(0, js_utils_1.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: (0, js_core_1.resolveTvmAddress)(assetVault.ethereumConfiguration),
isNative: false,
proxyAddress: (0, js_core_1.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: (0, js_core_1.resolveTvmAddress)(assetVault.ethereumConfiguration),
isNative: true,
proxyAddress: (0, js_core_1.resolveTvmAddress)(asset.proxy),
tokenBase: 'tvm',
vaultAddress: assetVault.vault,
};
try {
const evmTokenAddress = await models_1.EvmMultiVault.Utils.getNativeToken(evmConnection, config.vaultAddress.toString(), root);
config.evmTokenAddress = evmTokenAddress.toLowerCase();
}
catch (e) {
(0, js_utils_1.debug)('EvmMultiVault.getNativeToken failed with an error', e);
}
}
if (config.evmTokenAddress) {
const meta = await models_1.EvmMultiVault.Utils.tokens(evmConnection, config.vaultAddress.toString(), config.evmTokenAddress);
if (!(0, js_core_1.isAddressesEquals)(meta.custom, constants_1.EvmZeroAddress)) {
config.evmTokenAddress = meta.custom;
}
config.depositFee = meta.depositFee;
config.isBlacklisted = meta.blacklisted;
config.withdrawFee = meta.withdrawFee;
if (!(0, js_utils_1.isGoodNumber)(meta.activation) && evmConnection) {
try {
if (config.isNative) {
const fees = await models_1.EvmMultiVault.Utils.getNativeFees(evmConnection, config.vaultAddress.toString());
config.depositFee = fees.depositFee;
config.withdrawFee = fees.withdrawFee;
}
else {
const fees = await models_1.EvmMultiVault.Utils.getAlienFees(evmConnection, config.vaultAddress.toString());
config.depositFee = fees.depositFee;
config.withdrawFee = fees.withdrawFee;
}
}
catch (e) { }
}
try {
config.tvmConfigurationAddress = config.isNative
? await models_1.NativeProxyV6.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress)
: await models_1.AlienProxyV8.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress);
}
catch (e) {
config.tvmConfigurationAddress = config.isNative
? await models_1.NativeProxyV7.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress)
: await models_1.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: (0, js_core_1.isAddressesEquals)(config.evmTokenAddress, evmWrappedCurrency),
isNativeTvmCurrency: (0, js_core_1.isAddressesEquals)(config.tvmTokenAddress, tvmWrappedCurrency),
};
return new models_1.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 = (0, utils_1.findNetwork)(this.params.networks ?? [], sourceId, 'evm');
if (!sourceNetwork) {
throw new Error('Cannot resolve source network config');
}
const targetNetwork = (0, utils_1.findNetwork)(this.params.networks ?? [], targetId, 'tvm');
if (!targetNetwork) {
throw new Error('Cannot resolve target network config');
}
const evmConnection = (0, utils_1.getEvmConnection)([], sourceId, sourceNetwork);
const tvmConnection = (0, js_core_1.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: (0, js_core_1.resolveTvmAddress)(asset.proxy),
};
config = {
...config,
evmConfigurationAddress: (0, js_core_1.resolveTvmAddress)(assetVault.ethereumConfiguration),
vaultAddress: assetVault.vault,
};
let meta = await models_1.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: (0, js_core_1.resolveTvmAddress)(oppositeVault.ethereumConfiguration),
isNative: true,
proxyAddress: (0, js_core_1.resolveTvmAddress)(oppositeAsset.proxy),
tokenBase: 'tvm',
vaultAddress: oppositeVault.vault,
};
const tvmTokenAddress = await models_1.EvmMultiVault.Utils.natives(evmConnection, oppositeVault.vault, root);
if (tvmTokenAddress) {
config.tvmTokenAddress = (0, js_core_1.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
&& (0, js_core_1.isTvmAddress)(i.address))?.address;
if (tvmTokenAddress) {
config.tvmTokenAddress = (0, js_core_1.resolveTvmAddress)(tvmTokenAddress);
}
}
if (!config.tvmTokenAddress) {
token = await this.resolveEvmAsset(root, sourceId, fallbackAsset);
if (token?.decimals !== undefined && token.name && token.symbol) {
config.tvmTokenAddress = await models_1.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 models_1.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) {
(0, js_utils_1.error)('Cannot define merge details for Alien token', e);
}
}
}
}
meta = await models_1.EvmMultiVault.Utils.tokens(evmConnection, config.vaultAddress.toString(), root);
config.depositFee = meta.depositFee;
config.isBlacklisted = meta.blacklisted;
config.withdrawFee = meta.withdrawFee;
if (!(0, js_utils_1.isGoodNumber)(meta.activation)) {
try {
if (config.isNative) {
const fees = await models_1.EvmMultiVault.Utils.getNativeFees(evmConnection, config.vaultAddress.toString());
config.depositFee = fees.depositFee;
config.withdrawFee = fees.withdrawFee;
}
else {
const fees = await models_1.EvmMultiVault.Utils.getAlienFees(evmConnection, config.vaultAddress.toString());
config.depositFee = fees.depositFee;
config.withdrawFee = fees.withdrawFee;
}
}
catch (e) { }
}
try {
config.tvmConfigurationAddress = config.isNative
? await models_1.NativeProxyV6.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress)
: await models_1.AlienProxyV8.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress);
}
catch (e) {
config.tvmConfigurationAddress = config.isNative
? await models_1.NativeProxyV7.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress)
: await models_1.AlienProxyV9.Utils.getTvmEvmConfiguration(tvmConnection, config.proxyAddress);
}
const evmWrappedCurrency = sourceNetwork.currency.wrappedCurrencyAddress;
const tvmWrappedCurrency = targetNetwork.currency.wrappedCurrencyAddress;
config = {
...config,
isNativeEvmCurrency: (0, js_core_1.isAddressesEquals)(config.evmTokenAddress, evmWrappedCurrency),
isNativeTvmCurrency: (0, js_core_1.isAddressesEquals)(config.tvmTokenAddress, tvmWrappedCurrency),
};
return new models_1.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 = (0, utils_1.findNetwork)(this.params.networks ?? [], sourceId, 'tvm');
if (!sourceNetwork) {
throw new Error('Cannot resolve source network config');
}
const targetNetwork = (0, utils_1.findNetwork)(this.params.networks ?? [], targetId, 'solana');
if (!targetNetwork) {
throw new Error('Cannot resolve target network config');
}
const tvmConnection = (0, js_core_1.getTvmConnection)([], sourceId, sourceNetwork);
let alien = false, merge, config = {
isMerged: false,
tvmTokenAddress: (0, js_core_1.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 models_1.TokenRootAlienSolana.Utils.meta(tvmConnection, root);
config = {
...config,
isNative: false,
solTokenAddress: (0, utils_1.resolveSolanaAddress)(meta.solTokenAddress),
tokenBase: 'solana',
};
alien = true;
}
catch (e) {
(0, js_utils_1.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 models_1.AlienProxyV8.Utils.getSolTokenMergeDetails(tvmConnection, asset.proxy, root);
alien = merge !== undefined;
}
catch (e) {
(0, js_utils_1.debug)('Fetch merge details failed with an error', e);
}
if (!merge) {
try {
merge = await models_1.AlienProxyV9.Utils.getSolTokenMergeDetails(tvmConnection, asset.proxy, root);
alien = merge !== undefined;
}
catch (e) {
(0, js_utils_1.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: (0, js_core_1.resolveTvmAddress)(asset.proxy),
solConfigurationAddress: (0, js_core_1.resolveTvmAddress)(asset.solanaConfiguration),
tokenBase: 'solana',
tvmConfigurationAddress: (0, js_core_1.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: (0, js_core_1.resolveTvmAddress)(asset.proxy),
solConfigurationAddress: (0, js_core_1.resolveTvmAddress)(asset.solanaConfiguration),
tokenBase: 'tvm',
tvmConfigurationAddress: (0, js_core_1.resolveTvmAddress)(asset.tvmConfiguration),
vaultAddress: (0, utils_1.resolveSolanaAddress)(asset.vault),
};
}
try {
await (0, bridge_1.default)();
let tokenSettingsAddress;
if (config.isNative) {
if (config.tvmTokenAddress === undefined) {
(0, js_utils_1.error)('TVM token root is not defined');
return undefined;
}
tokenSettingsAddress = (0, bridge_1.getTokenSettingsAddress)(config.tvmTokenAddress.toString(), false);
}
else {
if (config.solTokenAddress === undefined) {
(0, js_utils_1.error)('Solana token mint root is not defined');
return undefined;
}
tokenSettingsAddress = (0, bridge_1.getTokenSettingsAddress)(config.solTokenAddress.toBase58(), true);
}
tokenSettingsAddress = new web3_js_1.PublicKey(tokenSettingsAddress);
const tokenSettings = await connection.getAccountInfo(tokenSettingsAddress);
if (tokenSettings?.data) {
const settings = (0, bridge_1.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 = (0, utils_1.resolveSolanaAddress)(settings.kind.Solana.vault);
}
else if ('Ever' in settings.kind) {
config.solTokenAddress = (0, utils_1.resolveSolanaAddress)(settings.kind.Ever.mint);
}
}
else {
const solTokenAddress = await (0, bridge_1.getMintAddress)(root);
config.solTokenAddress = new web3_js_1.PublicKey(solTokenAddress);
}
}
catch (e) {
(0, js_utils_1.error)(e);
}
const solWrappedCurrency = targetNetwork.currency.wrappedCurrencyAddress;
const tvmWrappedCurrency = sourceNetwork.currency.wrappedCurrencyAddress;
config = {
...config,
isNativeSolCurrency: solWrappedCurrency?.equals(config.solTokenAddress) ?? false,
isNativeTvmCurrency: (0, js_core_1.isAddressesEquals)(config.tvmTokenAddress, tvmWrappedCurrency),
};
return new models_1.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 = (0, utils_1.findNetwork)(this.params.networks ?? [], sourceId, 'solana');
if (!sourceNetwork) {
throw new Error('Cannot resolve source network config');
}
const targetNetwork = (0, utils_1.findNetwork)(this.params.networks ?? [], targetId, 'tvm');
if (!targetNetwork) {
throw new Error('Cannot resolve target network config');
}
const tvmConnection = (0, js_core_1.getTvmConnection)([], targetId, targetNetwork);
let config = {
isMerged: false,
};
const isSol = !(0, js_core_1.isTvmAddress)(root);
if (isSol) {
config.solTokenAddress = (0, utils_1.resolveSolanaAddress)(root);
}
else {
config.tvmTokenAddress = (0, js_core_1.resolveTvmAddress)(root);
}
try {
await (0, bridge_1.default)();
let tokenSettingsAddress = (0, bridge_1.getTokenSettingsAddress)(root, isSol);
tokenSettingsAddress = new web3_js_1.PublicKey(tokenSettingsAddress);
const tokenSettings = await connection.getAccountInfo(tokenSettingsAddress);
if (tokenSettings?.data == null) {
(0, js_utils_1.error)('Token settings is not provided');