diff --git a/.yarn/patches/@amplitude-plugin-autocapture-browser-npm-1.0.3-edb25bef55.patch b/.yarn/patches/@amplitude-plugin-autocapture-browser-npm-1.0.3-edb25bef55.patch new file mode 100644 index 000000000..145a91021 --- /dev/null +++ b/.yarn/patches/@amplitude-plugin-autocapture-browser-npm-1.0.3-edb25bef55.patch @@ -0,0 +1,26 @@ +diff --git a/lib/cjs/constants.js b/lib/cjs/constants.js +index 5772226ef895bff7a6f7fa291fc1138b10049dab..c65a57bf3535e6c6d4d894657da9b571d7f40d8a 100644 +--- a/lib/cjs/constants.js ++++ b/lib/cjs/constants.js +@@ -29,7 +29,7 @@ exports.AMPLITUDE_ORIGINS_MAP = { + EU: exports.AMPLITUDE_ORIGIN_EU, + STAGING: exports.AMPLITUDE_ORIGIN_STAGING, + }; +-exports.AMPLITUDE_VISUAL_TAGGING_SELECTOR_SCRIPT_URL = 'https://cdn.amplitude.com/libs/visual-tagging-selector-1.0.0-alpha.js.gz'; ++exports.AMPLITUDE_VISUAL_TAGGING_SELECTOR_SCRIPT_URL = ''; + // This is the class name used by the visual tagging selector to highlight the selected element. + // Should not use this class in the selector. + exports.AMPLITUDE_VISUAL_TAGGING_HIGHLIGHT_CLASS = 'amp-visual-tagging-selector-highlight'; +diff --git a/lib/esm/constants.js b/lib/esm/constants.js +index 17e665c05e62e488032673d312a69432297cb6fd..7d75951a29fad79ba49c83c076785294166dd0bf 100644 +--- a/lib/esm/constants.js ++++ b/lib/esm/constants.js +@@ -27,7 +27,7 @@ export var AMPLITUDE_ORIGINS_MAP = { + EU: AMPLITUDE_ORIGIN_EU, + STAGING: AMPLITUDE_ORIGIN_STAGING, + }; +-export var AMPLITUDE_VISUAL_TAGGING_SELECTOR_SCRIPT_URL = 'https://cdn.amplitude.com/libs/visual-tagging-selector-1.0.0-alpha.js.gz'; ++export var AMPLITUDE_VISUAL_TAGGING_SELECTOR_SCRIPT_URL = ''; + // This is the class name used by the visual tagging selector to highlight the selected element. + // Should not use this class in the selector. + export var AMPLITUDE_VISUAL_TAGGING_HIGHLIGHT_CLASS = 'amp-visual-tagging-selector-highlight'; diff --git a/package.json b/package.json index 72efb5069..4f44c10d0 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "resolutions": { "@ledgerhq/compressjs": "https://registry.yarnpkg.com/@favware/skip-dependency/-/skip-dependency-1.2.1.tgz", "@noble/hashes": "^1.4.0", - "fork-ts-checker-webpack-plugin": "^6.5.3" + "fork-ts-checker-webpack-plugin": "^6.5.3", + "@amplitude/plugin-autocapture-browser@^1.0.2": "patch:@amplitude/plugin-autocapture-browser@npm%3A1.0.3#./.yarn/patches/@amplitude-plugin-autocapture-browser-npm-1.0.3-edb25bef55.patch" } } diff --git a/packages/extension/package.json b/packages/extension/package.json index dceaf7b33..18064e666 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -1,5 +1,5 @@ { - "name": "@enkryptcom/extension-vite", + "name": "@enkryptcom/extension", "version": "2.0.0", "private": true, "type": "module", diff --git a/packages/extension/src/libs/market-data/ethvm.ts b/packages/extension/src/libs/market-data/ethvm.ts index c81c335ae..eef9eddf8 100644 --- a/packages/extension/src/libs/market-data/ethvm.ts +++ b/packages/extension/src/libs/market-data/ethvm.ts @@ -77,7 +77,7 @@ export const getMarketInfoByIDs = ( return ethvmPost( '{"operationName":null,"variables":{},"query":"{\\n getCoinGeckoTokenMarketDataByIds(coinGeckoTokenIds: [' + params + - ']) {\\n id\\n symbol\\n name\\n image\\n market_cap\\n market_cap_rank\\n high_24h\\n low_24h\\n price_change_24h\\n price_change_percentage_24h\\n sparkline_in_7d {\\n price\\n }\\n price_change_percentage_7d_in_currency\\n current_price\\n }\\n}\\n"}' + ']) {\\n id\\n symbol\\n name\\n image\\n market_cap\\n market_cap_rank\\n high_24h\\n low_24h\\n price_change_24h\\n price_change_percentage_24h\\n sparkline_in_24h {\\n price\\n }\\n price_change_percentage_24h_in_currency\\n current_price\\n }\\n}\\n"}' ).then((json) => { return json.data.getCoinGeckoTokenMarketDataByIds as CoinGeckoTokenMarket[]; }); diff --git a/packages/extension/src/libs/market-data/types.ts b/packages/extension/src/libs/market-data/types.ts index 7e379dde3..695c69aaa 100644 --- a/packages/extension/src/libs/market-data/types.ts +++ b/packages/extension/src/libs/market-data/types.ts @@ -1,41 +1,41 @@ export enum StorageKeys { - lastTimestamp = "last-time-stamp", - allTokens = "all-tokens", - fiatInfo = "fiat-info", + lastTimestamp = 'last-time-stamp', + allTokens = 'all-tokens', + fiatInfo = 'fiat-info', } export interface CoingeckPlatforms { - [key: string]: any; - ethereum?: string; - "ethereum-classic"?: string; - "binance-smart-chain"?: string; - gochain?: string; - "polygon-pos"?: string; - moonbeam?: string; - rootstock?: string; + [key: string]: any + ethereum?: string + 'ethereum-classic'?: string + 'binance-smart-chain'?: string + gochain?: string + 'polygon-pos'?: string + moonbeam?: string + rootstock?: string } export interface CoinGeckoToken { - id: string; - symbol: string; - name: string; - platforms: CoingeckPlatforms; + id: string + symbol: string + name: string + platforms: CoingeckPlatforms } export interface CoinGeckoTokenMarket { - id: string; - symbol: string; - name: string; - image: string; - current_price: null | number; - market_cap: number; - market_cap_rank: number; - high_24h: number; - low_24h: number; - price_change_24h: number; - price_change_percentage_24h: number; - sparkline_in_7d: { price: number[] }; - price_change_percentage_7d_in_currency: number; + id: string + symbol: string + name: string + image: string + current_price: null | number + market_cap: number + market_cap_rank: number + high_24h: number + low_24h: number + price_change_24h: number + price_change_percentage_24h: number + sparkline_in_24h: { price: number[] } + price_change_percentage_24h_in_currency: number } export interface FiatMarket { - fiat_currency: string; - exchange_rate: string; + fiat_currency: string + exchange_rate: string } diff --git a/packages/extension/src/libs/window-promise/promise.ts b/packages/extension/src/libs/window-promise/promise.ts index afd50ec00..a5a6bd63e 100644 --- a/packages/extension/src/libs/window-promise/promise.ts +++ b/packages/extension/src/libs/window-promise/promise.ts @@ -1,84 +1,101 @@ import { sendToBackgroundFromBackground, sendToNewWindowFromBackground, -} from "@/libs/messenger/extension"; -import { ProviderName } from "@/types/provider"; -import Browser from "webextension-polyfill"; -import { InternalMethods, InternalOnMessageResponse } from "@/types/messenger"; -import { getCustomError, getError } from "../error"; -import getUiPath from "../utils/get-ui-path"; -import UIRoutes from "@/ui/provider-pages/enkrypt/routes/names"; -import { ErrorCodes } from "@/providers/ethereum/types"; +} from '@/libs/messenger/extension' +import { ProviderName } from '@/types/provider' +import Browser from 'webextension-polyfill' +import { InternalMethods, InternalOnMessageResponse } from '@/types/messenger' +import { getCustomError, getError } from '../error' +import getUiPath from '../utils/get-ui-path' +import UIRoutes from '@/ui/provider-pages/enkrypt/routes/names' +import { ErrorCodes } from '@/providers/ethereum/types' -const UNLOCK_PATH = getUiPath(UIRoutes.unlock.path, ProviderName.enkrypt); +const UNLOCK_PATH = getUiPath(UIRoutes.unlock.path, ProviderName.enkrypt) class WindowPromise { private async getRawResponse( url: string, msg: string, - tabId: number + tabInfo: { tabId: number }, ): Promise { - return new Promise((resolve) => { + return new Promise(resolve => { Browser.tabs.onUpdated.addListener(function listener(_tabId, info, tab) { - if (info.status === "complete" && _tabId === tabId && tab.url === url) { + if ( + info.status === 'complete' && + _tabId === tabInfo.tabId && + tab.url === url + ) { resolve( sendToNewWindowFromBackground( { provider: ProviderName.enkrypt, message: msg, }, - tabId - ) - ); - Browser.tabs.onUpdated.removeListener(listener); + tabInfo.tabId, + ), + ) + Browser.tabs.onUpdated.removeListener(listener) } - }); - Browser.tabs.update(tabId, { url }); - }); + }) + Browser.tabs.update(tabInfo.tabId, { url }) + }) } private removeTab(tabId: number) { - Browser.tabs.get(tabId).then((info) => { - Browser.windows.remove(info.windowId!); - }); + Browser.tabs.get(tabId).then(info => { + Browser.windows.remove(info.windowId!) + }) } async getResponse( url: string, msg: string, - unlockKeyring = false + unlockKeyring = false, ): Promise { + const loadingURL = '/index.html#/enkrypt/loading' + const tabInfo = { + tabId: -1, + } const windowInfo = await Browser.windows.create({ - url: "/index.html#/enkrypt/loading", - type: "popup", + url: loadingURL, + type: 'popup', focused: true, height: 600, width: 460, - }); - const tabId: number | undefined = windowInfo.tabs?.length - ? windowInfo.tabs[0].id - : 0; - if (typeof tabId === "undefined") { + }) + const validTabs = await Browser.tabs.query({ + windowId: windowInfo.id, + }) + tabInfo.tabId = validTabs.length && validTabs[0].id ? validTabs[0].id : -1 + if (typeof tabInfo.tabId === 'undefined' || tabInfo.tabId === -1) { return Promise.resolve({ - error: getCustomError("unknown error, no tabId"), - }); + error: getCustomError('unknown error, no tabId'), + }) } const waitForWindow = async (): Promise => { - - while ((await Browser.tabs.get(tabId)).status !== "complete") {} - }; - await waitForWindow(); + // eslint-disable-next-line no-empty + while ((await Browser.tabs.get(tabInfo.tabId)).status !== 'complete') {} + } + await waitForWindow() const monitorTabs = (): Promise => { - return new Promise((resolve) => { - Browser.tabs.onRemoved.addListener(function tabListener(_tabId) { - if (_tabId === tabId) { - Browser.tabs.onRemoved.removeListener(tabListener); + return new Promise(resolve => { + const tabReplacedListener = (addedId: number, replacedId: number) => { + if (replacedId === tabInfo.tabId) { + tabInfo.tabId = addedId + } + } + const tabListener = (_tabId: number) => { + if (_tabId === tabInfo.tabId) { + Browser.tabs.onRemoved.removeListener(tabListener) + Browser.tabs.onReplaced.removeListener(tabReplacedListener) resolve({ error: getError(ErrorCodes.userRejected), - }); + }) } - }); - }); - }; + } + Browser.tabs.onRemoved.addListener(tabListener) + Browser.tabs.onReplaced.addListener(tabReplacedListener) + }) + } const executePromise = async (): Promise => { const isKeyRingLocked = await sendToBackgroundFromBackground({ provider: ProviderName.enkrypt, @@ -86,37 +103,37 @@ class WindowPromise { method: InternalMethods.isLocked, params: [], }), - }).then((res) => JSON.parse(res.result as string)); + }).then(res => JSON.parse(res.result as string)) if (unlockKeyring && isKeyRingLocked) { const unlockKeyring = await this.getRawResponse( Browser.runtime.getURL(UNLOCK_PATH), msg, - tabId - ); + tabInfo, + ) if (unlockKeyring.error) { - this.removeTab(tabId); - return unlockKeyring; + this.removeTab(tabInfo.tabId) + return unlockKeyring } else { return await this.getRawResponse( Browser.runtime.getURL(url), msg, - tabId - ).then((res) => { - this.removeTab(tabId); - return res; - }); + tabInfo, + ).then(res => { + this.removeTab(tabInfo.tabId) + return res + }) } } return await this.getRawResponse( Browser.runtime.getURL(url), msg, - tabId - ).then((res) => { - this.removeTab(tabId); - return res; - }); - }; - return Promise.race([monitorTabs(), executePromise()]); + tabInfo, + ).then(res => { + this.removeTab(tabInfo.tabId) + return res + }) + } + return Promise.race([monitorTabs(), executePromise()]) } } -export default WindowPromise; +export default WindowPromise diff --git a/packages/extension/src/manifest/manifest.base.ts b/packages/extension/src/manifest/manifest.base.ts index 06943d996..c24fde0c6 100644 --- a/packages/extension/src/manifest/manifest.base.ts +++ b/packages/extension/src/manifest/manifest.base.ts @@ -7,7 +7,7 @@ export default { version, name: 'Enkrypt: ETH, BTC and Solana Wallet', short_name: 'Enkrypt', - description: 'Everything in the blockchain made easy', + description: 'The best multichain crypto wallet', permissions: [ 'storage', 'unlimitedStorage', diff --git a/packages/extension/src/providers/bitcoin/types/bitcoin-network.ts b/packages/extension/src/providers/bitcoin/types/bitcoin-network.ts index 10268d9c6..cf9fc6db6 100644 --- a/packages/extension/src/providers/bitcoin/types/bitcoin-network.ts +++ b/packages/extension/src/providers/bitcoin/types/bitcoin-network.ts @@ -141,10 +141,10 @@ export class BitcoinNetwork extends BaseNetwork { contract: "", decimals: this.decimals, sparkline: marketData.length - ? new Sparkline(marketData[0]!.sparkline_in_7d.price, 25).dataValues + ? new Sparkline(marketData[0]!.sparkline_in_24h.price, 25).dataValues : "", priceChangePercentage: marketData.length - ? marketData[0]!.price_change_percentage_7d_in_currency + ? marketData[0]!.price_change_percentage_24h_in_currency : 0, }; return [nativeAsset]; diff --git a/packages/extension/src/providers/ethereum/libs/assets-handlers/assetinfo-mew.ts b/packages/extension/src/providers/ethereum/libs/assets-handlers/assetinfo-mew.ts index be356a4ed..def357a2e 100644 --- a/packages/extension/src/providers/ethereum/libs/assets-handlers/assetinfo-mew.ts +++ b/packages/extension/src/providers/ethereum/libs/assets-handlers/assetinfo-mew.ts @@ -263,8 +263,8 @@ export default ( low_24h: 0, price_change_24h: 0, price_change_percentage_24h: 0, - sparkline_in_7d: { price: [] }, - price_change_percentage_7d_in_currency: 0, + sparkline_in_24h: { price: [] }, + price_change_percentage_24h_in_currency: 0, }; } @@ -303,9 +303,10 @@ export default ( valuef: formatFiatValue(currentPrice.toString()).value, contract: address, decimals: tokenInfo[address].decimals, - sparkline: new Sparkline(market.sparkline_in_7d.price, 25).dataValues, + sparkline: new Sparkline(market.sparkline_in_24h.price, 25) + .dataValues, priceChangePercentage: - market.price_change_percentage_7d_in_currency || 0, + market.price_change_percentage_24h_in_currency || 0, }; if (address !== NATIVE_TOKEN_ADDRESS) assets.push(asset); else nativeAsset = asset; diff --git a/packages/extension/src/providers/ethereum/networks/skale/skale-base.ts b/packages/extension/src/providers/ethereum/networks/skale/skale-base.ts index 1bfc55528..e5eda1e1b 100644 --- a/packages/extension/src/providers/ethereum/networks/skale/skale-base.ts +++ b/packages/extension/src/providers/ethereum/networks/skale/skale-base.ts @@ -162,11 +162,13 @@ async function getPreconfiguredTokens( ).value, decimals: assetDecimals, sparkline: nativeAssetMarketData[index] - ? new Sparkline(nativeAssetMarketData[index]?.sparkline_in_7d.price, 25) - .dataValues + ? new Sparkline( + nativeAssetMarketData[index]?.sparkline_in_24h.price, + 25, + ).dataValues : '', priceChangePercentage: - nativeAssetMarketData[index]?.price_change_percentage_7d_in_currency ?? + nativeAssetMarketData[index]?.price_change_percentage_24h_in_currency ?? 0, contract: asset.address, } diff --git a/packages/extension/src/providers/ethereum/types/evm-network.ts b/packages/extension/src/providers/ethereum/types/evm-network.ts index 5b953a00a..697994ebc 100644 --- a/packages/extension/src/providers/ethereum/types/evm-network.ts +++ b/packages/extension/src/providers/ethereum/types/evm-network.ts @@ -156,10 +156,11 @@ export class EvmNetwork extends BaseNetwork { ).value, decimals: this.decimals, sparkline: nativeMarketData - ? new Sparkline(nativeMarketData.sparkline_in_7d.price, 25).dataValues + ? new Sparkline(nativeMarketData.sparkline_in_24h.price, 25) + .dataValues : "", priceChangePercentage: - nativeMarketData?.price_change_percentage_7d_in_currency ?? 0, + nativeMarketData?.price_change_percentage_24h_in_currency ?? 0, contract: NATIVE_TOKEN_ADDRESS, }; @@ -273,11 +274,11 @@ export class EvmNetwork extends BaseNetwork { marketInfo.current_price?.toString() ?? "0" ).value; asset.sparkline = new Sparkline( - marketInfo.sparkline_in_7d.price, + marketInfo.sparkline_in_24h.price, 25 ).dataValues; asset.priceChangePercentage = - marketInfo.price_change_percentage_7d_in_currency || 0; + marketInfo.price_change_percentage_24h_in_currency || 0; } return asset; diff --git a/packages/extension/src/providers/kadena/libs/api.ts b/packages/extension/src/providers/kadena/libs/api.ts index 5fc4e4111..d19ff8e40 100644 --- a/packages/extension/src/providers/kadena/libs/api.ts +++ b/packages/extension/src/providers/kadena/libs/api.ts @@ -1,6 +1,6 @@ -import { KadenaRawInfo } from "@/types/activity"; -import { ProviderAPIInterface } from "@/types/provider"; -import { KadenaNetworkOptions } from "../types/kadena-network"; +import { KadenaRawInfo } from '@/types/activity' +import { ProviderAPIInterface } from '@/types/provider' +import { KadenaNetworkOptions } from '../types/kadena-network' import { ICommand, IUnsignedCommand, @@ -9,129 +9,127 @@ import { createClient, Pact, ChainId, -} from "@kadena/client"; -import { toBase } from "@enkryptcom/utils"; -import DomainState from "@/libs/domain-state"; +} from '@kadena/client' +import { toBase } from '@enkryptcom/utils' +import DomainState from '@/libs/domain-state' /** Kadena API wrapper */ class API implements ProviderAPIInterface { - decimals: number; - node: string; - networkId: string; - chainId: string; - apiHost: string; - domainState: DomainState; - displayAddress: (address: string) => string; + decimals: number + node: string + networkId: string + chainId: string + apiHost: string + domainState: DomainState + displayAddress: (address: string) => string constructor(node: string, options: KadenaNetworkOptions) { - this.decimals = options.decimals; - this.node = node; - this.networkId = options.kadenaApiOptions.networkId; - this.chainId = options.kadenaApiOptions.chainId; - this.apiHost = `${node}/${this.networkId}/chain/${this.chainId}/pact`; - this.displayAddress = options.displayAddress; - this.domainState = new DomainState(); + this.decimals = options.decimals + this.node = node + this.networkId = options.kadenaApiOptions.networkId + this.chainId = options.kadenaApiOptions.chainId + this.apiHost = `${node}/${this.networkId}/chain/${this.chainId}/pact` + this.displayAddress = options.displayAddress + this.domainState = new DomainState() } public get api() { - return this; + return this } private getApiHost(chainId: string) { - return `${this.node}/${this.networkId}/chain/${chainId}/pact`; + return `${this.node}/${this.networkId}/chain/${chainId}/pact` } - async init(): Promise {} async getChainId(): Promise { - return this.domainState.getSelectedSubNetWork().then((id) => { - if (id) return id; - return "0"; - }); + return this.domainState.getSelectedSubNetWork().then(id => { + if (id) return id + return '0' + }) } async getTransactionStatus(requestKey: string): Promise { - const chainId = await this.getChainId(); - const networkId = this.networkId; - const { pollStatus } = createClient(this.getApiHost(chainId)); + const chainId = await this.getChainId() + const networkId = this.networkId + const { pollStatus } = createClient(this.getApiHost(chainId)) const responses = await pollStatus({ requestKey, networkId, chainId: chainId as ChainId, - }); - return responses[requestKey]; + }) + return responses[requestKey] } async getBalanceByChainId(address: string, chainId: string): Promise { const balance = await this.getBalanceAPI( this.displayAddress(address), - chainId - ); - - if (balance.result.status === "failure") { - const error = balance.result.error as { message: string | undefined }; - const message = error.message ?? "Unknown error retrieving balances"; + chainId, + ) + if (balance.result.status === 'failure') { + const error = balance.result.error as { message: string | undefined } + const message = error.message ?? 'Unknown error retrieving balances' // expected error when account does not exist on a chain (balance == 0) - if (message.includes("row not found")) { - return toBase("0", this.decimals); + if (message.includes('row not found')) { + return toBase('0', this.decimals) } - throw new Error(message); + throw new Error(message) } - - const balanceValue = parseFloat(balance.result.data.toString()).toFixed( - this.decimals - ); - - return toBase(balanceValue, this.decimals); + const balanceValue = parseFloat( + balance.result.data.decimal + ? balance.result.data.decimal + : balance.result.data.toString(), + ).toString() + return toBase(balanceValue, this.decimals) } async getBalance(address: string): Promise { - const chainId = await this.getChainId(); - return this.getBalanceByChainId(address, chainId); + const chainId = await this.getChainId() + return this.getBalanceByChainId(address, chainId) } async getBalanceAPI(account: string, chainId: string) { const transaction = Pact.builder - .execution(Pact.modules.coin["get-balance"](account)) + .execution(Pact.modules.coin['get-balance'](account)) .setMeta({ chainId: chainId as ChainId }) .setNetworkId(this.networkId) - .createTransaction(); + .createTransaction() - return this.dirtyRead(transaction); + return this.dirtyRead(transaction) } async sendLocalTransaction( - signedTranscation: ICommand + signedTranscation: ICommand, ): Promise { - const chainId = await this.getChainId(); - const client = createClient(this.getApiHost(chainId)); - return client.local(signedTranscation as ICommand); + const chainId = await this.getChainId() + const client = createClient(this.getApiHost(chainId)) + return client.local(signedTranscation as ICommand) } async sendTransaction( - signedTranscation: ICommand + signedTranscation: ICommand, ): Promise { - const chainId = await this.getChainId(); - const client = createClient(this.getApiHost(chainId)); - return client.submit(signedTranscation as ICommand); + const chainId = await this.getChainId() + const client = createClient(this.getApiHost(chainId)) + return client.submit(signedTranscation as ICommand) } async listen( - transactionDescriptor: ITransactionDescriptor + transactionDescriptor: ITransactionDescriptor, ): Promise { - const chainId = await this.getChainId(); - const client = createClient(this.getApiHost(chainId)); - return client.listen(transactionDescriptor); + const chainId = await this.getChainId() + const client = createClient(this.getApiHost(chainId)) + return client.listen(transactionDescriptor) } async dirtyRead( - signedTranscation: ICommand | IUnsignedCommand + signedTranscation: ICommand | IUnsignedCommand, ): Promise { - const chainId = await this.getChainId(); - const client = createClient(this.getApiHost(chainId)); - return client.dirtyRead(signedTranscation); + const chainId = await this.getChainId() + const client = createClient(this.getApiHost(chainId)) + return client.dirtyRead(signedTranscation) } } -export default API; +export default API diff --git a/packages/extension/src/providers/kadena/types/kadena-network.ts b/packages/extension/src/providers/kadena/types/kadena-network.ts index fc8107578..8deb67879 100644 --- a/packages/extension/src/providers/kadena/types/kadena-network.ts +++ b/packages/extension/src/providers/kadena/types/kadena-network.ts @@ -125,10 +125,10 @@ export class KadenaNetwork extends BaseNetwork { contract: "", decimals: this.decimals, sparkline: marketData.length - ? new Sparkline(marketData[0]!.sparkline_in_7d.price, 25).dataValues + ? new Sparkline(marketData[0]!.sparkline_in_24h.price, 25).dataValues : "", priceChangePercentage: marketData.length - ? marketData[0]!.price_change_percentage_7d_in_currency + ? marketData[0]!.price_change_percentage_24h_in_currency : 0, }; diff --git a/packages/extension/src/providers/polkadot/types/substrate-network.ts b/packages/extension/src/providers/polkadot/types/substrate-network.ts index 265c54ba5..710194c51 100644 --- a/packages/extension/src/providers/polkadot/types/substrate-network.ts +++ b/packages/extension/src/providers/polkadot/types/substrate-network.ts @@ -166,9 +166,9 @@ export class SubstrateNetwork extends BaseNetwork { name: st.name, symbol: st.symbol, priceChangePercentage: - market[idx]?.price_change_percentage_7d_in_currency || 0, + market[idx]?.price_change_percentage_24h_in_currency || 0, sparkline: market[idx] - ? new Sparkline(market[idx]?.sparkline_in_7d.price, 25).dataValues + ? new Sparkline(market[idx]?.sparkline_in_24h.price, 25).dataValues : "", value: market[idx]?.current_price?.toString() || "0", valuef: formatFloatingPointValue( diff --git a/packages/extension/src/providers/solana/types/sol-network.ts b/packages/extension/src/providers/solana/types/sol-network.ts index 6c4300f5f..748533fed 100644 --- a/packages/extension/src/providers/solana/types/sol-network.ts +++ b/packages/extension/src/providers/solana/types/sol-network.ts @@ -136,10 +136,10 @@ export class SolanaNetwork extends BaseNetwork { contract: "", decimals: this.decimals, sparkline: marketData.length - ? new Sparkline(marketData[0]!.sparkline_in_7d.price, 25).dataValues + ? new Sparkline(marketData[0]!.sparkline_in_24h.price, 25).dataValues : "", priceChangePercentage: marketData.length - ? marketData[0]!.price_change_percentage_7d_in_currency + ? marketData[0]!.price_change_percentage_24h_in_currency : 0, }; return [nativeAsset]; diff --git a/packages/extension/src/providers/solana/ui/libs/decode-tx.ts b/packages/extension/src/providers/solana/ui/libs/decode-tx.ts index 8c8c8f513..8c5e50e73 100644 --- a/packages/extension/src/providers/solana/ui/libs/decode-tx.ts +++ b/packages/extension/src/providers/solana/ui/libs/decode-tx.ts @@ -4,7 +4,12 @@ import { AccountLayout, TOKEN_PROGRAM_ID, } from "@solana/spl-token"; -import { VersionedTransaction, PublicKey } from "@solana/web3.js"; +import { + VersionedTransaction, + PublicKey, + Transaction, + TransactionVersion, +} from "@solana/web3.js"; import SolanaAPI from "@/providers/solana/libs/api"; import { SolanaNetwork } from "../../types/sol-network"; import { toBN } from "web3-utils"; @@ -43,116 +48,121 @@ const assetFetch = ( }; const decodeTransaction = async ( - tx: VersionedTransaction, + tx: VersionedTransaction | Transaction, from: PublicKey, - network: SolanaNetwork + network: SolanaNetwork, + version: TransactionVersion ) => { const solAPI = (await network.api()).api as SolanaAPI; const allBalances = await network.getAllTokenInfo(from.toBase58()); const marketData = new MarketData(); - return solAPI.web3 - .simulateTransaction(tx, { - accounts: { - addresses: tx.message.staticAccountKeys.map((k) => k.toBase58()), - encoding: "base64", - }, - }) - .then(async (result) => { - if (result.value.err) return null; - const nativeChange = { - contract: NATIVE_TOKEN_ADDRESS, - amount: BigInt(result.value.accounts![0]!.lamports), - }; - const balanceChanges = result.value - .accounts!.filter((a) => { - const data = Buffer.from(a!.data[0], "base64"); - return ( - a!.owner === TOKEN_PROGRAM_ID.toBase58() && - data.length === ACCOUNT_SIZE - ); + return ( + version !== "legacy" + ? solAPI.web3.simulateTransaction(tx as VersionedTransaction, { + accounts: { + addresses: ( + tx as VersionedTransaction + ).message.staticAccountKeys.map((k) => k.toBase58()), + encoding: "base64", + }, }) - .map((a) => { - const data = Buffer.from(a!.data[0], "base64"); - return AccountLayout.decode(data); - }) - .filter((val) => val.owner.toBase58() === from.toBase58()) - .map((val) => { + : solAPI.web3.simulateTransaction(tx as Transaction, undefined, true) + ).then(async (result) => { + if (result.value.err) return null; + const nativeChange = { + contract: NATIVE_TOKEN_ADDRESS, + amount: BigInt(result.value.accounts![0]!.lamports), + }; + const balanceChanges = result.value + .accounts!.filter((a) => { + const data = Buffer.from(a!.data[0], "base64"); + return ( + a!.owner === TOKEN_PROGRAM_ID.toBase58() && + data.length === ACCOUNT_SIZE + ); + }) + .map((a) => { + const data = Buffer.from(a!.data[0], "base64"); + return AccountLayout.decode(data); + }) + .filter((val) => val.owner.toBase58() === from.toBase58()) + .map((val) => { + return { + contract: val.mint.toBase58(), + amount: val.amount, + }; + }); + const getTokenInfoPromises = await Promise.all( + balanceChanges.map((val) => { + return solAPI.getTokenInfo(val.contract).then((info) => { return { - contract: val.mint.toBase58(), - amount: val.amount, + ...info, + ...val, }; }); - const getTokenInfoPromises = await Promise.all( - balanceChanges.map((val) => { - return solAPI.getTokenInfo(val.contract).then((info) => { - return { - ...info, - ...val, - }; - }); - }) - ); - getTokenInfoPromises.unshift({ - amount: nativeChange.amount, - cgId: network.coingeckoID, - contract: NATIVE_TOKEN_ADDRESS, - decimals: network.decimals, - icon: network.icon, - name: network.currencyNameLong, - symbol: network.currencyName, - }); - const retVal: DecodedTxResponseType[] = []; - for (const token of getTokenInfoPromises) { - const res: DecodedTxResponseType = { - change: 0, - contract: token.contract, - decimals: token.decimals, - icon: token.icon || "", - isNegative: false, - name: token.name, - symbol: token.symbol, - price: "0", - USDval: "0", - }; - const balInfo = allBalances.find((b) => b.contract === token.contract); - if (balInfo) { - const curBalance = toBN(balInfo.balance); - const newBalance = toBN(token.amount.toString()); - const diff = newBalance.sub(curBalance).toNumber(); - res.change = Math.abs(diff); - res.isNegative = diff < 0; - res.USDval = new BigNumber(balInfo.value) + }) + ); + getTokenInfoPromises.unshift({ + amount: nativeChange.amount, + cgId: network.coingeckoID, + contract: NATIVE_TOKEN_ADDRESS, + decimals: network.decimals, + icon: network.icon, + name: network.currencyNameLong, + symbol: network.currencyName, + }); + const retVal: DecodedTxResponseType[] = []; + for (const token of getTokenInfoPromises) { + const res: DecodedTxResponseType = { + change: 0, + contract: token.contract, + decimals: token.decimals, + icon: token.icon || "", + isNegative: false, + name: token.name, + symbol: token.symbol, + price: "0", + USDval: "0", + }; + const balInfo = allBalances.find((b) => b.contract === token.contract); + if (balInfo) { + const curBalance = toBN(balInfo.balance); + const newBalance = toBN(token.amount.toString()); + const diff = newBalance.sub(curBalance).toNumber(); + res.change = Math.abs(diff); + res.isNegative = diff < 0; + res.USDval = new BigNumber(balInfo.value) + .times(fromBase(res.change.toString(), res.decimals)) + .toString(); + res.price = balInfo.value; + } else { + res.change = toBN(token.amount.toString()).toNumber(); + res.isNegative = false; + if (token.cgId) { + const val = await marketData.getMarketData([token.cgId]); + res.USDval = new BigNumber(val[0]!.current_price ?? 0) .times(fromBase(res.change.toString(), res.decimals)) .toString(); - res.price = balInfo.value; - } else { - res.change = toBN(token.amount.toString()).toNumber(); - res.isNegative = false; - if (token.cgId) { - const val = await marketData.getMarketData([token.cgId]); - res.USDval = new BigNumber(val[0]!.current_price ?? 0) - .times(fromBase(res.change.toString(), res.decimals)) - .toString(); - res.price = (val[0]!.current_price ?? 0).toString(); - } + res.price = (val[0]!.current_price ?? 0).toString(); } - if (token.decimals === 0) { - const assetDetails = await assetFetch(network.node, "getAsset", { - id: token.contract, - }).catch(() => { - return null; - }); - if (assetDetails) { - res.icon = `https://img.mewapi.io/?image=${assetDetails.result.content.links.image}`; - res.name = assetDetails.result.content.metadata.name; - res.symbol = assetDetails.result.content.metadata.symbol; - } + } + if (token.decimals === 0) { + const assetDetails = await assetFetch(network.node, "getAsset", { + id: token.contract, + }).catch(() => { + return null; + }); + if (assetDetails) { + res.icon = `https://img.mewapi.io/?image=${assetDetails.result.content.links.image}`; + res.name = assetDetails.result.content.metadata.name; + res.symbol = assetDetails.result.content.metadata.symbol; } - retVal.push(res); } - retVal.sort((v) => (v.isNegative ? -1 * v.change : v.change)); - return retVal; - }); + retVal.push(res); + } + retVal.sort((v) => (v.isNegative ? -1 * v.change : v.change)); + return retVal; + }); }; export default decodeTransaction; diff --git a/packages/extension/src/providers/solana/ui/libs/get-priority-fees.ts b/packages/extension/src/providers/solana/ui/libs/get-priority-fees.ts index 4705b1c9a..64537138f 100644 --- a/packages/extension/src/providers/solana/ui/libs/get-priority-fees.ts +++ b/packages/extension/src/providers/solana/ui/libs/get-priority-fees.ts @@ -22,7 +22,6 @@ const getPrioritizationFees = async ( (await connection.getRecentPrioritizationFees( config )) as PrioritizationFeeObject[]; - if (prioritizationFeeObjects.length === 0) { return { low: 1000, @@ -58,9 +57,9 @@ const getPrioritizationFees = async ( : Math.floor((sortedFees[midIndex - 1] + sortedFees[midIndex]) / 2); } return { - low: averageFeeIncludingZeros, - medium: medianFee, - high: averageFeeExcludingZeros, + low: averageFeeIncludingZeros || 1000, + medium: medianFee || 1500, + high: averageFeeExcludingZeros || 2000, }; } catch (error) { return { diff --git a/packages/extension/src/providers/solana/ui/sol-verify-transaction.vue b/packages/extension/src/providers/solana/ui/sol-verify-transaction.vue index 6ad475e96..7d7fb4956 100644 --- a/packages/extension/src/providers/solana/ui/sol-verify-transaction.vue +++ b/packages/extension/src/providers/solana/ui/sol-verify-transaction.vue @@ -141,12 +141,19 @@ import { trackSendEvents } from '@/libs/metrics' import { SendEventType } from '@/libs/metrics/types' import { SolanaNetwork } from '../types/sol-network' import SolanaAPI from '@/providers/solana/libs/api' -import { PublicKey, SendOptions, VersionedTransaction } from '@solana/web3.js' +import { + PublicKey, + SendOptions, + VersionedTransaction, + Transaction as LegacyTransaction, + ComputeBudgetProgram, +} from '@solana/web3.js' import { SolSignTransactionRequest } from './types' import bs58 from 'bs58' import DecodeTransaction, { DecodedTxResponseType } from './libs/decode-tx' import { TransactionSigner } from './libs/signer' import { NATIVE_TOKEN_ADDRESS } from '@/providers/ethereum/libs/common' +import getPrioritizationFees from './libs/get-priority-fees' const isProcessing = ref(false) const providerVerifyTransactionScrollRef = ref() @@ -172,7 +179,8 @@ const Options = ref({ }) const selectedFee = ref(GasPriceTypes.ECONOMY) const solConnection = ref() -const Tx = ref() +const Tx = ref() +const TxType = ref<'legacy' | 0>(0) const accountPubkey = ref() const sendOptions = ref({}) const payloadMethod = ref<'sol_signTransaction' | 'sol_signAndSendTransaction'>( @@ -206,7 +214,34 @@ onBeforeMount(async () => { ) as SolSignTransactionRequest sendOptions.value = JSON.parse(Request.value.params![2]) as SendOptions Tx.value = VersionedTransaction.deserialize(hexToBuffer(txMessage.hex)) - solConnection.value.web3.getFeeForMessage(Tx.value.message).then(fee => { + TxType.value = Tx.value.version + if (TxType.value === 'legacy') { + Tx.value = LegacyTransaction.from(hexToBuffer(txMessage.hex)) + let isPriorityFeesSet = false + Tx.value.instructions.forEach(i => { + if (i.programId.toBase58() === ComputeBudgetProgram.programId.toBase58()) + isPriorityFeesSet = true + }) + const priorityFee = await getPrioritizationFees( + new PublicKey(network.value.displayAddress(account.value.address)), + solConnection.value!.web3, + ) + if (!isPriorityFeesSet && priorityFee) { + Tx.value.add( + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: priorityFee.high * 100, + }), + ) + } + Tx.value.recentBlockhash = ( + await solConnection.value.web3.getLatestBlockhash() + ).blockhash + } + const feeMessage = + TxType.value === 'legacy' + ? (Tx.value as LegacyTransaction).compileMessage() + : (Tx.value as VersionedTransaction).message + solConnection.value.web3.getFeeForMessage(feeMessage).then(fee => { const getConvertedVal = () => fromBase(fee.value!.toString(), network.value.decimals) marketdata @@ -223,10 +258,12 @@ onBeforeMount(async () => { } }) }) + DecodeTransaction( Tx.value, accountPubkey.value, network.value as SolanaNetwork, + TxType.value, ) .then(vals => { decodedTx.value = vals @@ -240,7 +277,18 @@ const approve = async () => { trackSendEvents(SendEventType.SendAPIApprove, { network: network.value.name, }) - const msgToSign = Tx.value!.message.serialize() + const latestBlockHash = (await solConnection.value!.web3.getLatestBlockhash()) + .blockhash + if (TxType.value === 'legacy') { + ;(Tx.value as LegacyTransaction).recentBlockhash = latestBlockHash + } else { + ;(Tx.value as VersionedTransaction).message.recentBlockhash = + latestBlockHash + } + const msgToSign = + TxType.value !== 'legacy' + ? (Tx.value! as VersionedTransaction).message.serialize() + : (Tx.value! as LegacyTransaction).serializeMessage() const feePayer = new PublicKey( network.value.displayAddress(account.value.address), ) @@ -253,6 +301,10 @@ const approve = async () => { Tx.value!.addSignature(feePayer, res) const toData = decodedTx.value && decodedTx.value.length ? decodedTx.value[0] : null + const TransactionHash = + TxType.value !== 'legacy' + ? (Tx.value as VersionedTransaction).signatures[0] + : (Tx.value as LegacyTransaction).signatures[0].signature const txActivity: Activity = { from: accountPubkey.value!.toBase58(), to: toData ? toData.contract : accountPubkey.value!.toBase58(), @@ -269,7 +321,7 @@ const approve = async () => { }, type: ActivityType.transaction, value: toData ? toData.change.toString() : '0', - transactionHash: bs58.encode(Tx.value!.signatures[0]), + transactionHash: bs58.encode(TransactionHash!), } txActivity.to = txActivity.to === NATIVE_TOKEN_ADDRESS @@ -303,7 +355,7 @@ const approve = async () => { .sendRawTransaction(Tx.value!.serialize(), sendOptions.value) .then(sig => { Resolve.value({ - result: JSON.stringify(sig), + result: JSON.stringify(bufferToHex(bs58.decode(sig))), }) }) } diff --git a/packages/extension/src/ui/action/views/network-assets/components/custom-evm-token.vue b/packages/extension/src/ui/action/views/network-assets/components/custom-evm-token.vue index 80a338409..1b284b544 100644 --- a/packages/extension/src/ui/action/views/network-assets/components/custom-evm-token.vue +++ b/packages/extension/src/ui/action/views/network-assets/components/custom-evm-token.vue @@ -222,14 +222,14 @@ const addToken = async () => { fromBase(accountBalance.value ?? '0', tokenInfo.value.decimals), ).value const balanceUSD = market.value - ? new BigNumber(balancef).times(market.value.current_price).toNumber() + ? new BigNumber(balancef).times(market.value.current_price!).toNumber() : 0 const balanceUSDf = market.value - ? new BigNumber(balancef).times(market.value.current_price).toString() + ? new BigNumber(balancef).times(market.value.current_price!).toString() : '0' - const value = market.value?.current_price.toString() ?? '0' + const value = market.value?.current_price!.toString() ?? '0' const sparkline = market.value - ? new Sparkline(market.value?.sparkline_in_7d.price, 25).dataValues + ? new Sparkline(market.value?.sparkline_in_24h.price, 25).dataValues : '' const priceChangePercentage = market.value?.price_change_percentage_24h ?? 0 diff --git a/packages/extension/src/ui/action/views/swap/libs/solana-gasvals.ts b/packages/extension/src/ui/action/views/swap/libs/solana-gasvals.ts index b11d2be5a..0eb9a705b 100644 --- a/packages/extension/src/ui/action/views/swap/libs/solana-gasvals.ts +++ b/packages/extension/src/ui/action/views/swap/libs/solana-gasvals.ts @@ -1,219 +1,192 @@ -import { GasFeeType, GasPriceTypes } from "@/providers/common/types"; -import SolanaAPI from "@/providers/solana/libs/api"; -import { SolanaNetwork } from "@/providers/solana/types/sol-network"; -import { fromBase } from "@enkryptcom/utils"; +import { GasFeeType, GasPriceTypes } from '@/providers/common/types' +import SolanaAPI from '@/providers/solana/libs/api' +import { SolanaNetwork } from '@/providers/solana/types/sol-network' +import { fromBase } from '@enkryptcom/utils' import { VersionedTransaction as SolanaVersionedTransaction, Transaction as SolanaLegacyTransaction, VersionedMessage, Message, -} from "@solana/web3.js"; -import BigNumber from "bignumber.js"; -import { toBN } from "web3-utils"; +} from '@solana/web3.js' +import BigNumber from 'bignumber.js' +import { toBN } from 'web3-utils' + +type TaggedLegacyTransaction = { + kind: 'legacy' + instance: SolanaLegacyTransaction + hasThirdPartySignatures: boolean +} + +type TaggedVersionedTransaction = { + kind: 'versioned' + instance: SolanaVersionedTransaction + hasThirdPartySignatures: boolean +} + +type TaggedTransaction = TaggedLegacyTransaction | TaggedVersionedTransaction + +function getTxBlockHash(tx: TaggedTransaction): undefined | string { + let recentBlockHash: undefined | string + switch (tx.kind) { + case 'legacy': + recentBlockHash = tx.instance.recentBlockhash + break + case 'versioned': + recentBlockHash = tx.instance.message.recentBlockhash + break + default: + tx satisfies never + throw new Error(`Unexpected Solana transaction kind ${(tx as any).kind}`) + } + return recentBlockHash +} + +function setTxBlockHash(tx: TaggedTransaction, blockHash: string): void { + switch (tx.kind) { + case 'legacy': + tx.instance.recentBlockhash = blockHash + break + case 'versioned': + tx.instance.message.recentBlockhash = blockHash + break + default: + tx satisfies never + throw new Error(`Unexpected Solana transaction kind ${(tx as any).kind}`) + } +} + +function getTxMessage(tx: TaggedTransaction): Message | VersionedMessage { + let msg: Message | VersionedMessage + switch (tx.kind) { + case 'legacy': + msg = tx.instance.compileMessage() + break + case 'versioned': + msg = tx.instance.message + break + default: + throw new Error(`Unexpected Solana transaction kind ${(tx as any).kind}`) + } + return msg +} /** * Mutably updates transactions with the latest block hash * (not nice but convenient) */ export const getSolanaTransactionFees = async ( - txs: ( - | { - kind: "legacy"; - instance: SolanaLegacyTransaction; - hasThirdPartySignatures: boolean; - } - | { - kind: "versioned"; - instance: SolanaVersionedTransaction; - hasThirdPartySignatures: boolean; - } - )[], + txs: (TaggedLegacyTransaction | TaggedVersionedTransaction)[], network: SolanaNetwork, price: number, - additionalFee: ReturnType + additionalFee: ReturnType, ): Promise> => { - let feesumlamp = additionalFee; - const conn = ((await network.api()) as SolanaAPI).web3; - let latestBlockHash = await conn.getLatestBlockhash(); + let feesumlamp = additionalFee + const conn = ((await network.api()) as SolanaAPI).web3 + let latestBlockHash = await conn.getLatestBlockhash() for (let txi = 0, len = txs.length; txi < len; txi++) { - const tx = txs[txi]; + const tx = txs[txi] /** For logging / debugging */ - let txkind: string; + let txkind: string switch (tx.kind) { - case "legacy": - txkind = "legacy"; - break; - case "versioned": - txkind = `versioned (${tx.instance.version})`; - break; + case 'legacy': + txkind = 'legacy' + break + case 'versioned': + txkind = `versioned (${tx.instance.version})` + break case undefined: - txkind = "legacy"; - break; + txkind = 'legacy' + break default: - txkind = `unknown (${(tx as SolanaVersionedTransaction).version})`; - break; + txkind = `unknown (${(tx as SolanaVersionedTransaction).version})` + break } // Use the latest block hash in-case it's fallen too far behind // (can't change block hash if it's already signed) if (!tx.hasThirdPartySignatures) { - switch (tx.kind) { - case "legacy": - tx.instance.recentBlockhash = latestBlockHash.blockhash; - break; - case "versioned": - tx.instance.message.recentBlockhash = latestBlockHash.blockhash; - break; - default: - tx satisfies never; - throw new Error( - `Unexpected Solana transaction kind ${(tx as any).kind}` - ); - } + setTxBlockHash(tx, latestBlockHash.blockhash) } // Not sure why but getFeeForMessage sometimes returns null, so we will retry // with small backoff in-case it helps - const backoff = [0, 500, 1_500]; + const backoff = [0, 500, 1_500] /** 0 indexed attempt, used to index backoff ms from `backoff` */ - let attempt = 0; - let fee: number; - + let attempt = 0 + let fee: number + while (true) { if (attempt >= backoff.length) { - let recentBlockHash: undefined | string; - switch (tx.kind) { - case "legacy": - recentBlockHash = tx.instance.recentBlockhash; - break; - case "versioned": - recentBlockHash = tx.instance.message.recentBlockhash; - break; - default: - tx satisfies never; - throw new Error( - `Unexpected Solana transaction kind ${(tx as any).kind}` - ); - } + const blockHash = getTxBlockHash(tx) throw new Error( `Failed to get fee for Solana transaction ${txi + 1}` + ` after ${backoff.length} attempts.` + ` Transaction block hash` + - ` ${recentBlockHash} possibly expired.` + - ` txkind=${txkind}` - ); + ` ${blockHash} possibly expired.` + + ` txkind=${txkind}`, + ) } if (backoff[attempt] > 0) { // wait before retrying - await new Promise((res) => setTimeout(res, backoff[attempt])); + await new Promise(res => setTimeout(res, backoff[attempt])) } // Update the block hash in-case it caused 0 fees to be returned if (attempt > 0) { if (!tx.hasThirdPartySignatures) { - let recentBlockHash: undefined | string; - switch (tx.kind) { - case "legacy": - recentBlockHash = tx.instance.recentBlockhash; - break; - case "versioned": - recentBlockHash = tx.instance.message.recentBlockhash; - break; - default: - tx satisfies never; - throw new Error( - `Unexpected Solana transaction kind ${(tx as any).kind}` - ); - } + const blockHash = getTxBlockHash(tx) console.warn( `Cannot update block hash for signed transaction` + ` ${txi + 1}, retrying getFeeForMessage using the same` + - ` block hash ${recentBlockHash}` + - ` txkind=${txkind}` - ); + ` block hash ${blockHash}` + + ` txkind=${txkind}`, + ) } else { - latestBlockHash = await conn.getLatestBlockhash(); - switch (tx.kind) { - case "legacy": - tx.instance.recentBlockhash = latestBlockHash.blockhash; - break; - case "versioned": - tx.instance.message.recentBlockhash = latestBlockHash.blockhash; - break; - default: - tx satisfies never; - throw new Error( - `Unexpected Solana transaction kind ${(tx as any).kind}` - ); - } + latestBlockHash = await conn.getLatestBlockhash() + setTxBlockHash(tx, latestBlockHash.blockhash) } } /** Base fee + priority fee (Don't know why this returns null sometimes) */ - let msg: Message | VersionedMessage; - switch (tx.kind) { - case "legacy": - msg = tx.instance.compileMessage(); - break; - case "versioned": - msg = tx.instance.message; - break; - default: - throw new Error( - `Unexpected Solana transaction kind ${(tx as any).kind}` - ); - } - const feeResult = await conn.getFeeForMessage(msg); + const msg = getTxMessage(tx) + const feeResult = await conn.getFeeForMessage(msg) if (feeResult.value == null) { - let recentBlockHash: undefined | string; - switch (tx.kind) { - case "legacy": - recentBlockHash = tx.instance.recentBlockhash; - break; - case "versioned": - recentBlockHash = tx.instance.message.recentBlockhash; - break; - default: - tx satisfies never; - throw new Error( - `Unexpected Solana transaction kind ${(tx as any).kind}` - ); - } + const recentBlockHash = getTxBlockHash(tx) console.warn( `Failed to get fee for Solana transaction ${txi + 1}/${len}.` + ` Transaction block hash ${recentBlockHash}` + ` possibly expired. Attempt ${attempt + 1}/${backoff.length}.` + - ` txkind=${txkind}` - ); + ` txkind=${txkind}`, + ) } else { // Success - fee = feeResult.value; + fee = feeResult.value if (attempt > 0) { console.debug( `Successfully got fee for Solana transaction` + ` ${txi + 1}/${len} after ${attempt + 1} attempts.` + ` fee=${fee}` + - ` txkind=${txkind}` - ); + ` txkind=${txkind}`, + ) } - break; + break } - attempt += 1; + attempt += 1 } - feesumlamp = feesumlamp.add(toBN(fee)); + feesumlamp = feesumlamp.add(toBN(fee)) } // Convert from lamports to SOL - const feesumsol = fromBase(feesumlamp.toString(), network.decimals); + const feesumsol = fromBase(feesumlamp.toString(), network.decimals) return { [GasPriceTypes.REGULAR]: { nativeValue: feesumsol, fiatValue: new BigNumber(feesumsol).times(price).toString(), - nativeSymbol: "SOL", - fiatSymbol: "USD", + nativeSymbol: 'SOL', + fiatSymbol: 'USD', }, - }; -}; + } +} diff --git a/packages/swap/src/configs.ts b/packages/swap/src/configs.ts index 3dd83f545..560e283f6 100644 --- a/packages/swap/src/configs.ts +++ b/packages/swap/src/configs.ts @@ -57,19 +57,11 @@ const FEE_CONFIGS: Partial< // each kind of asset you want to receive fees for [ProviderName.jupiter]: { [WalletIdentifier.enkrypt]: { - // TODO: THIS IS NICK'S TESTING REFERRAL ADDRESS, NEEDS TO CHANGE BEFORE RELEASE - // referrer: "78ZKhPea9sVW3jLsjvQov9jUooGe3qGnwauA1QoJFCq1", - // referrer: "41gBjMVCQ4FD2GAsKdjVPEGLQc7K6AxoY8C7PJ7HDp6y", - referrer: "EcrQ8iRSczuUq8YPBQKx1mWRuH8xxi3t9uwfenb6o9aW", - // Rounded because Jupiter API only accepts bps integers + referrer: "D5qKNm99Fbh7FAVEQp5vTgRkw7NfdtSREW2rhNPFqq5x", fee: 0.01, }, [WalletIdentifier.mew]: { - // TODO: THIS IS NICK'S TESTING REFERRAL ADDRESS, NEEDS TO CHANGE BEFORE RELEASE - // referrer: "78ZKhPea9sVW3jLsjvQov9jUooGe3qGnwauA1QoJFCq1", - // referrer: "41gBjMVCQ4FD2GAsKdjVPEGLQc7K6AxoY8C7PJ7HDp6y", - referrer: "EcrQ8iRSczuUq8YPBQKx1mWRuH8xxi3t9uwfenb6o9aW", - // Rounded because Jupiter API only accepts bps integers + referrer: "AUX4AgB6rwsXudMJ3U3rPFCUajhxKbwdG149i5xeVyFq", fee: 0.03, }, }, @@ -101,7 +93,7 @@ const TOKEN_LISTS: { /** * ```sh - * curl -sL https://raw.githubusercontent.com/enkryptcom/dynamic-data/main/swaplists/changelly.json | jq '.' -C | less -R + * curl -sL https://raw.githubusercontent.com/enkryptcom/dynamic-data/main/swaplists/changelly.json | jq . -C | less -R * ``` */ const CHANGELLY_LIST = @@ -109,7 +101,7 @@ const CHANGELLY_LIST = /** * ```sh - * curl -sL https://raw.githubusercontent.com/enkryptcom/dynamic-data/main/swaplists/top-tokens.json | jq '.' -C | less -R + * curl -sL https://raw.githubusercontent.com/enkryptcom/dynamic-data/main/swaplists/top-tokens.json | jq . -C | less -R * ``` */ const TOP_TOKEN_INFO_LIST = diff --git a/packages/swap/src/index.ts b/packages/swap/src/index.ts index 9bee1de02..0365a909d 100644 --- a/packages/swap/src/index.ts +++ b/packages/swap/src/index.ts @@ -118,8 +118,7 @@ class Swap extends EventEmitter { new Jupiter(this.api as Web3Solana, this.network), // TODO: re-enable Rango on Solana when issues with it are fixed // new Rango(this.api as Web3Solana, this.network), - // TODO: re-enable Changelly on Solana when issues with it are fixed - // new Changelly(this.api, this.network), + new Changelly(this.api, this.network), ]; break; default: diff --git a/packages/swap/src/providers/changelly/index.ts b/packages/swap/src/providers/changelly/index.ts index bb76c6aee..f21eb63fd 100644 --- a/packages/swap/src/providers/changelly/index.ts +++ b/packages/swap/src/providers/changelly/index.ts @@ -10,6 +10,8 @@ import { TransactionMessage, Connection, TransactionInstruction, + ComputeBudgetInstruction, + ComputeBudgetProgram, } from "@solana/web3.js"; import { ASSOCIATED_TOKEN_PROGRAM_ID, @@ -44,7 +46,7 @@ import { } from "../../configs"; import { getTransfer } from "../../utils/approvals"; -import supportedNetworks from "./supported"; +import supportedNetworks, { supportedNetworksSet } from "./supported"; import { ChangellyApiCreateFixedRateTransactionParams, ChangellyApiCreateFixedRateTransactionResult, @@ -133,6 +135,7 @@ class Changelly extends ProviderClass { async init(): Promise { debug("init", "Initialising..."); + if (!Changelly.isSupported(this.network)) { debug( "init", @@ -151,15 +154,16 @@ class Changelly extends ProviderClass { }); /** List of changelly blockchain names */ - const supportedChangellyNames = Object.values(supportedNetworks).map( - (s) => s.changellyName + const supportedChangellyNames = new Set( + Object.values(supportedNetworks).map((s) => s.changellyName) ); this.changellyList.forEach((cur) => { // Must be a supported token - if (!supportedChangellyNames.includes(cur.blockchain)) { + if (!supportedChangellyNames.has(cur.blockchain)) { return; } + if ( cur.enabledFrom && cur.fixRateEnabled && @@ -184,6 +188,7 @@ class Changelly extends ProviderClass { }, }; } + if (cur.token) this.setTicker( cur.token, @@ -211,9 +216,7 @@ class Changelly extends ProviderClass { } static isSupported(network: SupportedNetworkName) { - return Object.keys(supportedNetworks).includes( - network as unknown as string - ); + return supportedNetworksSet.has(network); } /** @@ -473,7 +476,17 @@ class Changelly extends ProviderClass { else if (quoteRequestAmount.gt(minMax.maximumFrom)) quoteRequestAmount = minMax.maximumFrom; - if (quoteRequestAmount.toString() === "0") return null; + if (quoteRequestAmount.toString() === "0") { + debug( + "getQuote", + `No swap: Quote request amount is zero` + + ` fromToken=${options.fromToken.symbol}` + + ` toToken=${options.toToken.symbol}` + + ` minimumFrom=${minMax.minimumFrom.toString()}` + + ` maximumFrom=${minMax.maximumFrom.toString()}` + ); + return null; + } debug("getQuote", `Requesting changelly swap...`); @@ -561,15 +574,24 @@ class Changelly extends ProviderClass { ); const original = firstChangellyFixRateQuote.amountTo; // eslint-disable-next-line no-use-before-define - const [success, fixed] = trimDecimals( + const [success, fixed] = fixBaseAndTrimDecimals( original, options.toToken.decimals ); if (!success) throw err; - const rounded = ( - BigInt(toBase(fixed, options.toToken.decimals)) - BigInt(1) - ).toString(); + const rounded = (BigInt(fixed) - BigInt(1)).toString(); toTokenAmountBase = rounded; + + debug( + "getQuote", + `Fixed amountTo` + + ` firstChangellyFixRateQuote.amountTo=${firstChangellyFixRateQuote.amountTo}` + + ` toTokenAmountBase=${toTokenAmountBase}` + + ` options.toToken.decimals=${options.toToken.decimals}` + + ` options.toToken.symbol=${options.toToken.symbol}` + + ` options.toToken.name=${options.toToken.name}` + + ` options.toToken.address=${options.toToken.address}` + ); } // `toBase` fails sometimes because Changelly returns more decimals than the token has @@ -589,15 +611,24 @@ class Changelly extends ProviderClass { ); const original = firstChangellyFixRateQuote.networkFee; // eslint-disable-next-line no-use-before-define - const [success, fixed] = trimDecimals( + const [success, fixed] = fixBaseAndTrimDecimals( original, options.toToken.decimals ); if (!success) throw err; - const rounded = ( - BigInt(toBase(fixed, options.toToken.decimals)) + BigInt(1) - ).toString(); + const rounded = (BigInt(fixed) + BigInt(1)).toString(); networkFeeBase = rounded; + + debug( + "getQuote", + `Fixed networkFee` + + ` firstChangellyFixRateQuote.networkFee=${firstChangellyFixRateQuote.networkFee}` + + ` networkFeeBase=${networkFeeBase}` + + ` options.toToken.decimals=${options.toToken.decimals}` + + ` options.toToken.symbol=${options.toToken.symbol}` + + ` options.toToken.name=${options.toToken.name}` + + ` options.toToken.address=${options.toToken.address}` + ); } const providerQuoteResponse: ProviderQuoteResponse = { @@ -751,9 +782,7 @@ class Changelly extends ProviderClass { break; } case NetworkType.Solana: { - // TODO: finish implementing support for Solana debug("getSwap", `Changelly is not supported on Solana at this time`); - if (true as any) return null; const conn = this.web3 as Connection; @@ -762,6 +791,8 @@ class Changelly extends ProviderClass { // Create a transaction to transfer this much of that token to that thing let versionedTx: VersionedTransaction; if (quote.options.fromToken.address === NATIVE_TOKEN_ADDRESS) { + // Swapping from native SOL + debug( "getSwap", `Preparing Solana Changelly SOL swap transaction` + @@ -785,52 +816,62 @@ class Changelly extends ProviderClass { }).compileToV0Message() ); } else { - const wallet = new PublicKey(quote.options.fromAddress); + // Swapping from a token on SOL + + // We need to send our src tokens to the payin address + // for Changelly to begin the cross-chain swap + // But first we'll need to create the src mint ATA for the payin address + // so it can receive the tokens const mint = new PublicKey(quote.options.fromToken.address); const tokenProgramId = await getTokenProgramOfMint(conn, mint); - const walletMintAta = getSPLAssociatedTokenAccountPubkey( + const wallet = new PublicKey(quote.options.fromAddress); + const walletAta = getSPLAssociatedTokenAccountPubkey( wallet, mint, tokenProgramId ); - // TODO: is payin address an ATA or Wallet address? (must be wallet, right?) - const payinAta = new PublicKey(changellyFixedRateTx.payinAddress); + const payin = new PublicKey(changellyFixedRateTx.payinAddress); + const payinAta = getSPLAssociatedTokenAccountPubkey( + payin, + mint, + tokenProgramId + ); const amount = BigInt(quote.options.amount.toString()); debug( "getSwap", - // eslint-disable-next-line prefer-template `Preparing Solana Changelly SPL token swap transaction` + ` srcMint=${mint.toBase58()}` + + ` srcTokenProgramId=${tokenProgramId.toBase58()}` + ` wallet=${wallet.toBase58()}` + - ` walletSrcMintAta=${tokenProgramId.toBase58()}` + - ` dstMintAta=${payinAta.toBase58()}` + - ` tokenProgramId=${tokenProgramId.toBase58()}` + + ` walletAta=${tokenProgramId.toBase58()}` + + ` payin=${payin.toBase58()}` + + ` payinAta=${payinAta.toBase58()}` + ` latestBlockHash=${latestBlockHash.blockhash}` + ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + - ` payinAddress=${changellyFixedRateTx.payinAddress}` + ` amount=${amount}` ); // If the ATA account doesn't exist we need create it - const ataExists = await solAccountExists(conn, payinAta); + const payinAtaExists = await solAccountExists(conn, payinAta); + + // We probably need to set some priority fees? IDK... const instructions: TransactionInstruction[] = []; - if (ataExists) { + if (payinAtaExists) { debug( "getSwap", `Payin ATA already exists. No need to create it.` ); } else { debug("getSwap", `Payin ATA does not exist. Need to create it.`); - // TODO: finish implementing const extraRentFee = await conn.getMinimumBalanceForRentExemption( SPL_TOKEN_ATA_ACCOUNT_SIZE_BYTES ); const instruction = getCreateAssociatedTokenAccountIdempotentInstruction({ payerPubkey: wallet, - ataPubkey: payinAta, // TODO: we'd need to get the owner - ownerPubkey: new PublicKey("!! TODO !!"), + ataPubkey: payinAta, + ownerPubkey: payin, mintPubkey: mint, systemProgramId: SystemProgram.programId, tokenProgramId, @@ -843,9 +884,82 @@ class Changelly extends ProviderClass { ); } + /** Priority fee (units: micro lamports per compute unit) in recent transactions */ + const recentFees = await conn.getRecentPrioritizationFees(); + // Sort by fee amount ascending so we can get the median + recentFees.sort( + (a, b) => a.prioritizationFee - b.prioritizationFee + ); + const recentFeeCount = recentFees.length; + let recentFeeCountWithoutZeroes = 0; + let recentFeeMin = 0; + let recentFeeMax = 0; + let recentFeeSum = 0; + let recentFeeMedian = 0; + const recentFeeMediani = Math.floor(recentFeeCount / 2); + // Use the minimum of the mean average and median average as our priority fee + for ( + let recentFeei = 0; + recentFeei < recentFeeCount; + recentFeei++ + ) { + const { prioritizationFee } = recentFees[recentFeei]; + recentFeeSum += prioritizationFee; + if (recentFeei === recentFeeMediani) + recentFeeMedian = prioritizationFee; + if (prioritizationFee > 0) recentFeeCountWithoutZeroes++; + if (prioritizationFee < recentFeeMin) + recentFeeMin = prioritizationFee; + if (prioritizationFee > recentFeeMax) + recentFeeMax = prioritizationFee; + } + const recentFeeMean = recentFeeSum / recentFeeCountWithoutZeroes; + const recentFeeMinAvg = Math.min(recentFeeMean, recentFeeMedian); + + // TODO: Do we want to set the compute budget? What should we set it to? + // Set compute budget + // instructions.unshift( + // ComputeBudgetProgram.setComputeUnitLimit() + // ) + + // Set priority fee + if (recentFeeMinAvg <= 0) { + debug( + "getSwap", + `No recent fees, not setting priority fee` + + ` recentFeeCount=${recentFeeCount}` + + ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + + ` recentFeeSum=${recentFeeSum}` + + ` recentFeeMin=${recentFeeMin}` + + ` recentFeeMax=${recentFeeMax}` + + ` recentFeeMean=${recentFeeMean}` + + ` recentFeeMedian=${recentFeeMedian}` + + ` recentFeeMinAvg=${recentFeeMinAvg}` + ); + } else { + debug( + "getSwap", + `Setting priority fee` + + ` priority_fee=${recentFeeMinAvg} micro_lamports/compute_unit` + + ` recentFeeCount=${recentFeeCount}` + + ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + + ` recentFeeSum=${recentFeeSum}` + + ` recentFeeMin=${recentFeeMin}` + + ` recentFeeMax=${recentFeeMax}` + + ` recentFeeMean=${recentFeeMean}` + + ` recentFeeMedian=${recentFeeMedian}` + + ` recentFeeMinAvg=${recentFeeMinAvg}` + ); + instructions.unshift( + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: recentFeeMinAvg, + }) + ); + } + instructions.push( createSPLTransferInstruction( - /** source */ walletMintAta, + /** source */ walletAta, /** destination */ payinAta, /** owner */ wallet, /** amount */ amount, @@ -873,7 +987,9 @@ class Changelly extends ProviderClass { }; break; } + default: { + // Not EVM, not SOlana transaction = { from: quote.options.fromAddress, to: changellyFixedRateTx.payinAddress, @@ -902,15 +1018,24 @@ class Changelly extends ProviderClass { ); const original = changellyFixedRateTx.amountExpectedTo; // eslint-disable-next-line no-use-before-define - const [success, fixed] = trimDecimals( + const [success, fixed] = fixBaseAndTrimDecimals( original, quote.options.toToken.decimals ); if (!success) throw err; - const rounded = ( - BigInt(toBase(fixed, quote.options.toToken.decimals)) - BigInt(1) - ).toString(); + const rounded = (BigInt(fixed) - BigInt(1)).toString(); baseToAmount = rounded; + + debug( + "getQuote", + `Fixed amountExpectedTo` + + ` changellyFixedRateTx.amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + + ` baseToAmount=${baseToAmount}` + + ` quote.options.toToken.decimals=${quote.options.toToken.decimals}` + + ` quote.options.toToken.symbol=${quote.options.toToken.symbol}` + + ` quote.options.toToken.name=${quote.options.toToken.name}` + + ` quote.options.toToken.address=${quote.options.toToken.address}` + ); } const retResponse: ProviderSwapResponse = { @@ -974,7 +1099,7 @@ class Changelly extends ProviderClass { } } -function trimDecimals( +function fixBaseAndTrimDecimals( value: string, decimals: number ): [success: boolean, fixed: string] { diff --git a/packages/swap/src/providers/changelly/supported.ts b/packages/swap/src/providers/changelly/supported.ts index e4260486c..73b2a4f4c 100644 --- a/packages/swap/src/providers/changelly/supported.ts +++ b/packages/swap/src/providers/changelly/supported.ts @@ -1,6 +1,7 @@ // import { isValidSolanaAddress } from "../../utils/solana"; import { isPolkadotAddress, isEVMAddress } from "../../utils/common"; import { SupportedNetworkName } from "../../types"; +import { isValidSolanaAddress } from "../../utils/solana"; /** * Blockchain names: @@ -13,7 +14,7 @@ import { SupportedNetworkName } from "../../types"; * ```` */ const supportedNetworks: { - [key in SupportedNetworkName]?: { + readonly [key in SupportedNetworkName]?: { changellyName: string; isAddress?: (addr: string) => Promise; }; @@ -61,17 +62,19 @@ const supportedNetworks: { [SupportedNetworkName.Dogecoin]: { changellyName: "doge", }, - // TODO: Re-enable Solana when all issues with Changelly and Solana on Enkrypt are fixed - // @2024-10-01 - // [SupportedNetworkName.Solana]: { - // changellyName: "solana", - // async isAddress(address: string) { - // return isValidSolanaAddress(address); - // }, - // }, + [SupportedNetworkName.Solana]: { + changellyName: "solana", + async isAddress(address: string) { + return isValidSolanaAddress(address); + }, + }, [SupportedNetworkName.Rootstock]: { changellyName: "rootstock", }, }; +export const supportedNetworksSet = new Set( + Object.keys(supportedNetworks) +) as unknown as Set; + export default supportedNetworks; diff --git a/packages/swap/src/providers/changelly/types.ts b/packages/swap/src/providers/changelly/types.ts index 77bfe2383..2c9577e2b 100644 --- a/packages/swap/src/providers/changelly/types.ts +++ b/packages/swap/src/providers/changelly/types.ts @@ -108,7 +108,8 @@ export type ChangellyApiValidateAddressResult = { * * @example * ```sh - * curl -sL https://partners.mewapi.io/changelly-v2 -X POST -H Accept:application/json -H Content-Type:application/json --data '{"id":"1","jsonrpc":"2.0","method":"getFixRate","params":{"from":"sol","to":"btc"}}' | jq '.' -C | less -R + * # SOL to BTC + * curl -sL https://partners.mewapi.io/changelly-v2 -X POST -H Accept:application/json -H Content-Type:application/json --data '{"id":"1","jsonrpc":"2.0","method":"getFixRate","params":{"from":"sol","to":"btc"}}' | jq . -C | less -R * # { * # "jsonrpc": "2.0", * # "result": [ @@ -129,6 +130,9 @@ export type ChangellyApiValidateAddressResult = { * # ], * # "id": "1" * # } + * + * # DAI to USDC + * curl https://partners.mewapi.io/changelly-v2 -sL -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":"1","method":"getFixRate","params":{"from":"dai","to":"usdc"}}' | jq . -C | less -R * ```` */ export type ChangellyApiGetFixRateParams = { diff --git a/packages/swap/tests/changelly.test.ts b/packages/swap/tests/changelly.test.ts index 9ccaba78c..ae3bfe796 100644 --- a/packages/swap/tests/changelly.test.ts +++ b/packages/swap/tests/changelly.test.ts @@ -10,9 +10,9 @@ import { WalletIdentifier, } from "../src/types"; import { - fromToken, + fromTokenUSDT, toToken, - amount, + amountUSDT, fromAddress, toAddress, nodeURL as ethNodeURL, @@ -29,9 +29,9 @@ describe("Changelly Provider", () => { await init; const quote = await changelly.getQuote( { - amount, + amount: amountUSDT, fromAddress, - fromToken, + fromToken: fromTokenUSDT, toToken, toAddress, }, @@ -42,7 +42,7 @@ describe("Changelly Provider", () => { expect(quote?.quote.meta.walletIdentifier).to.be.eq( WalletIdentifier.enkrypt ); - expect(quote?.fromTokenAmount.gte(amount)).to.be.eq(true); + expect(quote?.fromTokenAmount.gte(amountUSDT)).to.be.eq(true); expect(quote?.toTokenAmount.gtn(0)).to.be.eq(true); const swap = await changelly.getSwap(quote!.quote); @@ -87,12 +87,10 @@ describe("Changelly Provider", () => { expect(Object.values(fromTokens).length).to.be.eq(1); expect(fromTokens[NATIVE_TOKEN_ADDRESS].name).to.be.eq("Polkadot"); }); - // TODO: switch this test to assert that Solana DOES initialise - // once we enable Changelly on Solana - it("it NOT should initialize other networks: Solana", async () => { + it("it should initialize other networks: Solana", async () => { const changelly2 = new Changelly(solConn, SupportedNetworkName.Solana); await changelly2.init(); const fromTokens = changelly2.getFromTokens(); - expect(Object.values(fromTokens).length).to.be.eq(0); + expect(Object.values(fromTokens).length).to.be.gte(1); }); }); diff --git a/packages/swap/tests/fixtures/mainnet/configs.ts b/packages/swap/tests/fixtures/mainnet/configs.ts index 6c6d3d8b2..469ce18c2 100644 --- a/packages/swap/tests/fixtures/mainnet/configs.ts +++ b/packages/swap/tests/fixtures/mainnet/configs.ts @@ -2,7 +2,8 @@ import { NetworkNames } from "@enkryptcom/types"; import { isAddress, toBN } from "web3-utils"; import { NetworkType, TokenType, TokenTypeTo } from "../../../src/types"; -const amount = toBN("100000000000000000000"); +const amount = toBN("100000000000000000000"); // DAI, $100, 18 decimals +const amountUSDT = toBN("100000000"); // USDT, $100, 6 decimals const fromAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; const toAddress = "0x255d4D554325568A2e628A1E93120EbA1157C07e"; @@ -32,6 +33,18 @@ const fromToken: TokenType = { type: NetworkType.EVM, }; +const fromTokenUSDT: TokenType = { + address: "0xdac17f958d2ee523a2206206994597c13d831ec7", + decimals: 6, + logoURI: + "https://assets.coingecko.com/coins/images/325/standard/Tether.png?1696501661", + name: "Tether", + symbol: "USDT", + rank: 18, + cgId: "tether", + type: NetworkType.EVM, +}; + const fromTokenWBTC: TokenType = { address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", decimals: 8, @@ -77,10 +90,12 @@ const toToken: TokenTypeTo = { export { fromToken, + fromTokenUSDT, toToken, toTokenWETH, fromTokenWBTC, amount, + amountUSDT, fromAddress, toAddress, nodeURL, diff --git a/packages/swap/tests/swap.test.ts b/packages/swap/tests/swap.test.ts index ac4c723c2..f790d7364 100644 --- a/packages/swap/tests/swap.test.ts +++ b/packages/swap/tests/swap.test.ts @@ -8,9 +8,11 @@ import { WalletIdentifier, } from "../src/types"; import { - fromToken, + // fromToken, + fromTokenUSDT, toToken, - amount, + // amount, + amountUSDT, fromAddress, toAddress, nodeURL, @@ -55,9 +57,9 @@ describe("Swap", () => { it("it should get quote and swap for different destination", async () => { await enkryptSwap.initPromise; const quotes = await enkryptSwap.getQuotes({ - amount, + amount: amountUSDT, fromAddress, - fromToken, + fromToken: fromTokenUSDT, toToken, toAddress, }); @@ -81,7 +83,9 @@ describe("Swap", () => { expect(oneInceQuote!.provider).to.be.eq(ProviderName.oneInch); expect(paraswapQuote!.provider).to.be.eq(ProviderName.paraswap); const swapOneInch = await enkryptSwap.getSwap(oneInceQuote!.quote); - expect(swapOneInch?.fromTokenAmount.toString()).to.be.eq(amount.toString()); + expect(swapOneInch?.fromTokenAmount.toString()).to.be.eq( + amountUSDT.toString() + ); expect(swapOneInch?.transactions.length).to.be.eq(2); const swapChangelly = await enkryptSwap.getSwap(changellyQuote!.quote); if (swapChangelly) expect(swapChangelly?.transactions.length).to.be.eq(1); @@ -90,9 +94,9 @@ describe("Swap", () => { it("it should get quote and swap for same destination", async () => { await enkryptSwap.initPromise; const quotes = await enkryptSwap.getQuotes({ - amount, + amount: amountUSDT, fromAddress, - fromToken, + fromToken: fromTokenUSDT, toToken, toAddress: fromAddress, }); diff --git a/yarn.lock b/yarn.lock index c60ab0c72..a0a015e7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -93,7 +93,7 @@ __metadata: languageName: node linkType: hard -"@amplitude/plugin-autocapture-browser@npm:^1.0.2": +"@amplitude/plugin-autocapture-browser@npm:1.0.3": version: 1.0.3 resolution: "@amplitude/plugin-autocapture-browser@npm:1.0.3" dependencies: @@ -105,6 +105,18 @@ __metadata: languageName: node linkType: hard +"@amplitude/plugin-autocapture-browser@patch:@amplitude/plugin-autocapture-browser@npm%3A1.0.3#./.yarn/patches/@amplitude-plugin-autocapture-browser-npm-1.0.3-edb25bef55.patch::locator=enkrypt%40workspace%3A.": + version: 1.0.3 + resolution: "@amplitude/plugin-autocapture-browser@patch:@amplitude/plugin-autocapture-browser@npm%3A1.0.3#./.yarn/patches/@amplitude-plugin-autocapture-browser-npm-1.0.3-edb25bef55.patch::version=1.0.3&hash=c16bd2&locator=enkrypt%40workspace%3A." + dependencies: + "@amplitude/analytics-client-common": ">=1 <3" + "@amplitude/analytics-types": ^2.8.2 + rxjs: ^7.8.1 + tslib: ^2.4.1 + checksum: 2548ff0f76b2565bc5a34feb154788a945e625a07ab9c5c0561dc3b296e4dbbc32120b4cd9306ef2cce8b342c5c5cb0e8bc3e236ab95e4476cf0e05b7323a092 + languageName: node + linkType: hard + "@amplitude/plugin-page-view-tracking-browser@npm:^2.3.4": version: 2.3.4 resolution: "@amplitude/plugin-page-view-tracking-browser@npm:2.3.4" @@ -1562,9 +1574,9 @@ __metadata: languageName: unknown linkType: soft -"@enkryptcom/extension-vite@workspace:packages/extension": +"@enkryptcom/extension@workspace:packages/extension": version: 0.0.0-use.local - resolution: "@enkryptcom/extension-vite@workspace:packages/extension" + resolution: "@enkryptcom/extension@workspace:packages/extension" dependencies: "@amplitude/analytics-browser": ^2.11.8 "@crxjs/vite-plugin": ^2.0.0-beta.25