diff --git a/types/api/azure/index.ts b/types/api/azure/index.ts index 3866405..108e360 100644 --- a/types/api/azure/index.ts +++ b/types/api/azure/index.ts @@ -1,7 +1,7 @@ export * from "./secretswap_pairs"; export * from "./rewards"; export * from "./secretswap_pools"; -export * from "./secret_tokens_legacy"; +// export * from "./secret_tokens_legacy"; export * from "./tokens"; // Note: Types below are untested and may be incorrect diff --git a/utils/dummyData/usages2test.ts b/utils/dummyData/usages2test.ts index 5909a9b..b4b2f22 100644 --- a/utils/dummyData/usages2test.ts +++ b/utils/dummyData/usages2test.ts @@ -1,34 +1,34 @@ // Define a type for Usage which can be one of these three strings export type Usage = "BRIDGE" | "LPSTAKING" | "SWAP"; -// This utility type checks if any element is present more than once in a tuple -export type HasDuplicates = T extends readonly [ - infer X, - ...infer Rest -] - ? X extends Rest[number] - ? true - : HasDuplicates - : false; +// // This utility type checks if any element is present more than once in a tuple +// export type HasDuplicates = T extends readonly [ +// infer X, +// ...infer Rest +// ] +// ? X extends Rest[number] +// ? true +// : HasDuplicates +// : false; -// Define a type that is either the array or never if there are duplicates -export type UniqueArray = - HasDuplicates extends true ? never : T; +// // Define a type that is either the array or never if there are duplicates +// export type UniqueArray = +// HasDuplicates extends true ? never : T; // Usage of UniqueArray type -export type Usages = UniqueArray<[Usage, Usage, Usage]>; +// export type Usages = UniqueArray<[Usage, Usage, Usage]>; // Example usages -const validUsages: Usages = ["BRIDGE", "LPSTAKING", "SWAP"] as const; // This will be of type Usages if unique +// const validUsages: Usages = ["BRIDGE", "LPSTAKING", "SWAP"] as const; // This will be of type Usages if unique // const invalidUsages: Usages = ["BRIDGE", "BRIDGE", "SWAP"] as const; // This will raise a type error as it should be never // Helper function to construct a UniqueArray, ensuring type checking -function createUniqueArray( - ...elements: UniqueArray -): UniqueArray { - return elements as UniqueArray; -} +// function createUniqueArray( +// ...elements: UniqueArray +// ): UniqueArray { +// return elements as UniqueArray; +// } // Proper usage -const uniqueUsages = createUniqueArray("BRIDGE", "LPSTAKING", "SWAP"); // OK +// const uniqueUsages = createUniqueArray("BRIDGE", "LPSTAKING", "SWAP"); // OK // const invalidUsages = createUniqueArray("BRIDGE", "BRIDGE", "SWAP"); // Error at compile time diff --git a/utils/secretjs/SecretSwapSite/SwapFunctions/getBestRoute.ts b/utils/secretjs/SecretSwapSite/SwapFunctions/getBestRoute.ts index 4637020..7148455 100644 --- a/utils/secretjs/SecretSwapSite/SwapFunctions/getBestRoute.ts +++ b/utils/secretjs/SecretSwapSite/SwapFunctions/getBestRoute.ts @@ -2,7 +2,7 @@ import BigNumber from "bignumber.js"; import { compute_offer_amount, compute_swap, -} from "../blockchain-bridge/scrt/swap"; +} from "../blockchain-bridge/scrt/swap/swap"; import { SwapPair } from "../types/SwapPair"; import { getOfferAndAskPools } from "./getOfferAndAskPools"; import { SwapTokenMap } from "../types/SwapToken"; @@ -52,7 +52,7 @@ export function getBestRoute({ const fromToken = route[i]; const toToken = route[i + 1]; const pair: SwapPair | undefined = pairs.get( - `${fromToken}${SwapPair.id_delimiter}${toToken}` + `${fromToken}${SwapPair.id_delimiter}${toToken}`, ); if (!pair) { break; @@ -63,7 +63,7 @@ export function getBestRoute({ toToken, pair, tokens, - balances + balances, ); const offer_amount = from; @@ -80,7 +80,7 @@ export function getBestRoute({ const { return_amount } = compute_swap( offer_pool, ask_pool, - offer_amount + offer_amount, ); if (return_amount.isNaN() || return_amount.isLessThanOrEqualTo(0)) { @@ -115,7 +115,7 @@ export function getBestRoute({ const fromToken = route[i - 1]; const toToken = route[i]; const pair: SwapPair | undefined = pairs.get( - `${fromToken}${SwapPair.id_delimiter}${toToken}` + `${fromToken}${SwapPair.id_delimiter}${toToken}`, ); if (!pair) { break; @@ -125,7 +125,7 @@ export function getBestRoute({ toToken, pair, tokens, - balances + balances, ); const ask_amount = to; @@ -143,7 +143,7 @@ export function getBestRoute({ const { offer_amount } = compute_offer_amount( offer_pool, ask_pool, - ask_amount + ask_amount, ); if (offer_amount.isNaN() || offer_amount.isLessThanOrEqualTo(0)) { @@ -160,7 +160,7 @@ export function getBestRoute({ } const fromWithGas = from.plus( - cachedGasFeesUnfilledCoin[route.length - 1] + cachedGasFeesUnfilledCoin[route.length - 1], ); allRoutesOutputs.push({ route, fromOutput: from, fromWithGas }); diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/SwapPair.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/SwapPair.ts new file mode 100644 index 0000000..9a7a0f6 --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/SwapPair.ts @@ -0,0 +1,104 @@ +import { Asset, NativeToken, Token } from './trade'; +import { getSymbolsFromPair, Pair } from '../../../blockchain-bridge/scrt/swap'; +import { SwapTokenMap } from './SwapToken'; +import { CosmWasmClient } from 'secretjs'; + +export class SwapPair { + pair_identifier: string; + asset_infos: Asset[]; + contract_addr: string; + liquidity_token: string; + static id_delimiter = '/'; + + constructor( + symbol0: string, + asset0: NativeToken | Token, + symbol1: string, + asset1: NativeToken | Token, + contract_addr: string, + liquidity_token: string, + pair_identifier: string, + ) { + this.asset_infos = []; + this.asset_infos.push(new Asset(symbol0, asset0)); + this.asset_infos.push(new Asset(symbol1, asset1)); + this.contract_addr = contract_addr; + this.liquidity_token = liquidity_token; + this.pair_identifier = pair_identifier; + } + + lpTokenSymbol(): string { + return `LP-${this.identifier()}`; + } + + identifier(): string { + return this.pair_identifier; + } + + assetIds(): string[] { + return this.pair_identifier.split(SwapPair.id_delimiter); + } + + isSymbolInPair(symbol: string): boolean { + return symbol.toUpperCase() === this.asset_infos[0].symbol || symbol.toUpperCase() === this.asset_infos[1].symbol; + } + + humanizedSymbol(): string { + return `${this.asset_infos[0].symbol}-${this.asset_infos[1].symbol}`; + } + + isIdInPair(id: string): boolean { + const pairIdentifiers = this.pair_identifier.split(SwapPair.id_delimiter); + + for (const pId of pairIdentifiers) { + if (pId.toLowerCase() === id) { + return true; + } + } + + return false; + } + + static fromPair(pair: Pair, tokenMap: SwapTokenMap) { + const identifiers = getSymbolsFromPair(pair); + + const symbol0 = tokenMap.get(identifiers[0]).symbol; + const symbol1 = tokenMap.get(identifiers[1]).symbol; + + const pair_identifier = pairIdFromTokenIds(identifiers[0], identifiers[1]); + + //const symbol0 = asset0.type === 'native_token' ? asset0.native_token.denom : asset0.token.contract_addr; + return new SwapPair( + symbol0, + pair.asset_infos[0], + symbol1, + pair.asset_infos[1], + pair.contract_addr, + pair.liquidity_token, + pair_identifier, + ); + } + + private static code_hash: string; + static getPairCodeHash(pair_address: string, secretjs: CosmWasmClient): Promise { + // TODO fix this if we ever have a factory with multiple pair_code_id + // For now this is the best way to avoid a lot of secretjs requests + return new Promise(async (accept, reject) => { + try { + if (!SwapPair.code_hash) { + SwapPair.code_hash = await secretjs.getCodeHashByContractAddr(pair_address); + } + accept(SwapPair.code_hash); + } catch (e) { + reject(e); + } + }); + } +} + +export const pairIdFromTokenIds = (id0: string, id1: string): string => { + return id0.localeCompare(id1) === -1 + ? `${id0}${SwapPair.id_delimiter}${id1}` + : `${id1}${SwapPair.id_delimiter}${id0}`; +}; +export type PairMap = Map; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/SwapToken.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/SwapToken.ts new file mode 100644 index 0000000..49942ca --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/SwapToken.ts @@ -0,0 +1,93 @@ +import { ITokenInfo } from '../../../stores/interfaces'; +import { Snip20TokenInfo, validateBech32Address } from '../../../blockchain-bridge'; +import { tokenImages } from '../../../components/Earn/EarnRow'; +import { sleep } from 'utils'; + +export type SwapTokenMap = Map; + +export type SwapToken = { + symbol: string; + logo?: string; + identifier?: string; + decimals?: number; + address?: string; + name?: string; + balance?: string; + price?: Number; +}; + +export const getPricesForJSONTokens = async () => { + for (let i = 0; i < 4; i++) { + if (globalThis.config['PRICE_DATA']['SEFI/USDT'].price) { + break + } + await sleep(1000) + } + return { + 'secret15l9cqgz5uezgydrglaak5ahfac69kmx2qpd6xt': globalThis.config['PRICE_DATA']['SEFI/USDT'].price, + 'uscrt': globalThis.config['PRICE_DATA']['SCRT/USD'].price, + 'secret1yxcexylwyxlq58umhgsjgstgcg2a0ytfy4d9lt': globalThis.config['PRICE_DATA']['BUTT/USD'].price, + 'secret14mzwd0ps5q277l20ly2q3aetqe3ev4m4260gf4': globalThis.config['PRICE_DATA']['ATOM/USD'].price, + 'secret1zwwealwm0pcl9cul4nt6f38dsy6vzplw8lp3qg': globalThis.config['PRICE_DATA']['OSMO/USD'].price, + 'secret1k8cge73c3nh32d4u0dsd5dgtmk63shtlrfscj5': globalThis.config['PRICE_DATA']['DVPN/USD'].price, + 'secret19ungtd2c7srftqdwgq0dspwvrw63dhu79qxv88': globalThis.config['PRICE_DATA']['XMR/USD'].price, + } +} + +export const SwapTokenFromSnip20Params = (address: string, token: Snip20TokenInfo) => { + + const customTokenInfo: SwapToken = { + symbol: token.symbol, + address: address, + decimals: token.decimals, + logo: '/static/unknown.png', + identifier: address, + name: token.name, + }; + + return customTokenInfo; +}; + +export const TokenMapfromITokenInfo = async (tokens: ITokenInfo[]): Promise => { + let swapTokens: SwapTokenMap = new Map(); + + const tokenPrices = await getPricesForJSONTokens() + + for (const t of tokens) { + const secretAddress = validateBech32Address(t.dst_address) + ? t.dst_address + : validateBech32Address(t.src_address) + ? t.src_address + : ''; + let symbol; + if (t.display_props.symbol === 'SCRT') { + symbol = 'SCRT'; + } else if (t.display_props.symbol.toLowerCase() === 'sscrt') { + symbol = 'sSCRT'; + } else if (t.display_props.symbol.toLowerCase() === 'sefi') { + symbol = 'SEFI'; + } else if (t.display_props.symbol.toLowerCase() === 'sienna') { + symbol = t.display_props.symbol.toUpperCase(); + } else if (t.display_props.symbol.toLowerCase() === 'alter') { + symbol = t.display_props.symbol.toUpperCase(); + } else if (t.display_props.symbol.toLowerCase() === 'shd') { + symbol = t.display_props.symbol.toUpperCase(); + } else { + symbol = 's' + t.display_props.symbol; + } + + const swapToken: SwapToken = { + identifier: secretAddress, + symbol: symbol, + logo: t.display_props.symbol.startsWith("lp-") ? t.display_props.image : tokenImages[t.display_props.symbol.toUpperCase()], + decimals: Number(t.decimals), + name: t.name, + address: secretAddress, + price: secretAddress === 'secret15l9cqgz5uezgydrglaak5ahfac69kmx2qpd6xt' ? tokenPrices[secretAddress] : (Number(t.price) ? Number(t.price) : 0), + }; + + swapTokens.set(swapToken.identifier, swapToken); + } + + return swapTokens; +}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/trade.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/trade.ts new file mode 100644 index 0000000..dd753d6 --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/TokenModalTypes/types/trade.ts @@ -0,0 +1,139 @@ +import { SwapToken } from './SwapToken'; + +export enum TradeType { + EXACT_INPUT, + EXACT_OUTPUT, +} + +export enum Rounding { + ROUND_DOWN, + ROUND_HALF_UP, + ROUND_UP, +} + +export interface TokenInfo { + symbol: string; + address?: string; + token_code_hash?: string; +} + +export interface Token { + type: 'token'; + token: { + contract_addr: string; + token_code_hash: string; + viewing_key: string; + }; +} + +export interface NativeToken { + type: 'native_token'; + native_token: { + denom: string; + }; +} + +export class Currency { + public readonly amount: string; + public readonly token: Asset; + + isEqualToIdentifier(info: string) { + return this.token.info.type === 'native_token' + ? this.token.info.native_token.denom === info + : this.token.info.token.contract_addr === info; + } + + constructor(token: Asset, amount: string) { + this.amount = amount; + this.token = token; + } +} + +export class Asset { + public info: Token | NativeToken; + public symbol: string; + + public isNative(): this is NativeToken { + return Asset._isNative(this.info); + } + + private static _isNative(info: any): info is NativeToken { + return 'native_token' in info; + } + + static fromSwapToken(token: SwapToken): Asset { + let tokenInfo: TokenInfo = { + symbol: token.symbol, + address: token?.address, + }; + + return Asset.fromTokenInfo(tokenInfo); + } + + static fromTokenInfo(token: TokenInfo): Asset { + if (token.address) { + return new Asset(token.symbol, { + type: 'token', + token: { + contract_addr: token.address, + token_code_hash: token.token_code_hash, + viewing_key: '', + }, + }); + } else { + return new Asset(token.symbol, { + type: 'native_token', + native_token: { denom: `u${token.symbol.toLowerCase()}` }, + }); + } + } + + constructor(symbol: string, info: Token | NativeToken) { + this.info = info; + this.symbol = symbol; + } +} + +export class Trade { + /** + * The route of the trade, i.e. which pairs the trade goes through. + */ + //public readonly route: Route + /** + * The type of the trade, either exact in or exact out. + */ + public readonly tradeType: TradeType; + /** + * The input amount for the trade assuming no slippage. + */ + public readonly inputAmount: Currency; + /** + * The output amount for the trade assuming no slippage. + */ + public readonly outputAmount: Currency; + /** + * The price expressed in terms of output amount/input amount. + */ + //public readonly executionPrice: number + /** + * The mid price after the trade executes assuming no slippage. + */ + public readonly price: number; + + //public readonly pair: string + + getExactAmount(): string { + return this.tradeType === TradeType.EXACT_OUTPUT ? this.outputAmount.amount : this.inputAmount.amount; + } + + getEstimatedAmount(): string { + return this.tradeType === TradeType.EXACT_OUTPUT ? this.inputAmount.amount : this.outputAmount.amount; + } + + constructor(inputAmount: Currency, outputAmount: Currency, tradeType: TradeType) { + this.inputAmount = inputAmount; + this.outputAmount = outputAmount; + this.tradeType = tradeType; + //this.executionPrice = Number(outputAmount.amount) / Number(inputAmount.amount); + } +} diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/index.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/index.ts index 8bcc1f6..0f37e14 100644 --- a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/index.ts +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/index.ts @@ -1,5 +1,5 @@ -export * from './bridge_rewards'; -export * from './utils'; -export * from './snip20'; -export * from './keplr'; -export * from './sefi'; \ No newline at end of file +// export * from './bridge_rewards'; +export * from "./utils"; +// export * from "./snip20"; +export * from "./keplr"; +// export * from "./sefi"; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/keplr.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/keplr.ts index 992a5ea..d05f8b4 100644 --- a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/keplr.ts +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/keplr.ts @@ -1,22 +1,28 @@ -import { sleep } from '../../utils'; +import { sleep } from "../../utils"; +import { Keplr } from "@keplr-wallet/types"; // todo: clean this shit up -import { ERROR_WRONG_VIEWING_KEY } from '../../pages/Swap/utils'; +// import { ERROR_WRONG_VIEWING_KEY } from '../../pages/Swap/utils'; + +export const ERROR_WRONG_VIEWING_KEY = "Viewing Key Error"; export const getViewingKey = async (params: { - keplr: any; + keplr: Keplr; chainId: string; address: string; currentBalance?: string; }) => { const { keplr, chainId, address, currentBalance } = params; - if (typeof currentBalance === 'string' && currentBalance.includes(ERROR_WRONG_VIEWING_KEY)) { + if ( + typeof currentBalance === "string" && + currentBalance.includes(ERROR_WRONG_VIEWING_KEY) + ) { // In case this tx was set_viewing_key in order to correct the wrong viewing key error // Allow Keplr time to locally save the new viewing key await sleep(1000); } - let viewingKey: string; + let viewingKey: string | undefined = undefined; let tries = 0; while (true) { diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/sefi.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/sefi.ts deleted file mode 100644 index 052b7cf..0000000 --- a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/sefi.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from 'secretjs'; -import { getScrtProof } from 'services'; -import { AsyncSender } from './asyncSender'; - -export const isClaimedSefiRewardsScrt = async (params: { - secretjs: CosmWasmClient; - index: number; -}): Promise => { - const { secretjs, index } = params; - console.log(index); - try { - const resp = await secretjs.queryContractSmart(globalThis.config.SCRT_DIST_TOKEN_ADDRESS, { - is_claimed: { index: index.toString() }, - }); - - return resp; - } catch (e) { - console.log(e); - throw Error('Address does not exist'); - } -}; - -export const ClaimAirdrop = async (params: { secretjs: AsyncSender; address: string }): Promise => { - const { secretjs, address } = params; - const res = await getScrtProof(address); - const proof = res.proof; - - const execMsg = { - index: proof.index.toString(), - address: address, - amount: parseInt(proof.amount, 16).toString(), - proof: proof.proof.map(p => p.substring(2)), // map to remove the '0x's - }; - - const result = await secretjs.asyncExecute(globalThis.config.SCRT_DIST_TOKEN_ADDRESS, { - claim: execMsg, - }); - - return result; -}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20.ts deleted file mode 100644 index 88a2be9..0000000 --- a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from 'secretjs'; -import { divDecimals, sleep, unlockToken } from '../../utils'; -import { StdFee } from 'secretjs/types/types'; -import { AsyncSender } from './asyncSender'; - -export const Snip20SwapHash = (params: { tx_id: string; address: string }): string => { - return `${params.tx_id}|${params.address}`; -}; - -export interface Snip20TokenInfo { - name: string; - symbol: string; - decimals: number; - total_supply?: string; -} - -export const GetSnip20Params = async (params: { - secretjs: CosmWasmClient; - address: string; -}): Promise => { - const { secretjs, address } = params; - - try { - const paramsResponse = await secretjs.queryContractSmart(address, { token_info: {} }); - - return { - name: paramsResponse.token_info.name, - symbol: paramsResponse.token_info.symbol, - decimals: paramsResponse.token_info.decimals, - total_supply: paramsResponse.token_info?.total_supply, - }; - } catch (e) { - throw Error('Failed to get info'); - } -}; - -export const Snip20GetBalance = async (params: { - secretjs: CosmWasmClient; - token: string; - address: string; - key: string; -}) => { - const { secretjs, address, token, key } = params; - - let balanceResponse; - for (let i = 0; i < 4; i++) { - try { - balanceResponse = await secretjs.queryContractSmart(token, { - balance: { - address: address, - key, - }, - }); - break; - } catch (e) { - if (e.message !== 'Failed to decrypt the following error message: rpc error: code = Unknown desc = contract: not found (HTTP 500).') { - console.error(e) - return unlockToken; - } - await sleep(1000) - } - } - - if (balanceResponse.viewing_key_error) { - return 'Fix Unlock'; - } - - if (Number(balanceResponse.balance.amount) === 0) { - return '0'; - } - return balanceResponse.balance.amount; -}; - -export const Snip20SendToBridge = async (params: { - secretjs: AsyncSender; - address: string; - amount: string; - msg: string; - recipient?: string; -}): Promise => { - const tx = await Snip20Send({ - recipient: params.recipient || globalThis.config.SCRT_SWAP_CONTRACT, - ...params, - }); - - const txIdKvp = tx.logs[0].events[1].attributes.find(kv => kv.key === 'tx_id'); - let tx_id: string; - if (txIdKvp && txIdKvp.value) { - tx_id = txIdKvp.value; - } else { - throw new Error('Failed to get tx_id'); - } - - return tx_id; -}; - -export const Snip20Send = async (params: { - secretjs: AsyncSender; - address: string; - amount: string; - msg: string; - recipient: string; - fee?: StdFee; -}): Promise => { - const { secretjs, address, amount, msg, recipient, fee } = params; - - return await secretjs.asyncExecute( - address, - { - send: { - amount, - recipient, - msg, - }, - }, - '', - [], - fee, - ); -}; - -export const GetContractCodeHash = async ({ - secretjs, - address, -}: { - secretjs: CosmWasmClient; - address: string; -}): Promise => { - return await secretjs.getCodeHashByContractAddr(address); -}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/GetContractCodeHash.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/GetContractCodeHash.ts new file mode 100644 index 0000000..566ceee --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/GetContractCodeHash.ts @@ -0,0 +1,14 @@ +import { SecretNetworkClient } from "secretjs"; + +export const GetContractCodeHash = async ({ + secretjs, + address, +}: { + secretjs: SecretNetworkClient; + address: string; +}): Promise => { + const response = await secretjs.query.compute.codeHashByContractAddress({ + contract_address: address, + }); + return response.code_hash; +}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/GetSnip20Params.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/GetSnip20Params.ts new file mode 100644 index 0000000..f5195d6 --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/GetSnip20Params.ts @@ -0,0 +1,32 @@ +import { SecretNetworkClient } from "secretjs"; +import { Snip20TokenInfo } from "./types/Snip20TokenInfo"; + +export const GetSnip20Params = async (params: { + secretjs: SecretNetworkClient; + address: string; + codeHash: string; +}): Promise => { + const { secretjs, address, codeHash } = params; + + try { + const response = await secretjs.query.compute.queryContract({ + contract_address: address, + code_hash: codeHash, + query: { + token_info: {}, + }, + }); + + const tokenInfo = response as { token_info: Snip20TokenInfo }; + + return { + name: tokenInfo.token_info.name, + symbol: tokenInfo.token_info.symbol, + decimals: tokenInfo.token_info.decimals, + total_supply: tokenInfo.token_info.total_supply, + }; + } catch (e) { + console.error("Failed to get token info", e); + throw Error("Failed to get info"); + } +}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20GetBalance.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20GetBalance.ts new file mode 100644 index 0000000..f727367 --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20GetBalance.ts @@ -0,0 +1,69 @@ +import { SecretNetworkClient } from "secretjs"; +import { sleep } from "../../../utils"; // Ensure correct path to import +import { GetContractCodeHash } from "./GetContractCodeHash"; // Ensure correct path to import +export const unlockToken = "Unlock"; + +interface BalanceQueryResponse { + balance?: { + amount: string; + }; + viewing_key_error?: string; +} + +export const Snip20GetBalance = async (params: { + secretjs: SecretNetworkClient; + token: string; + address: string; + key: string; +}): Promise => { + const { secretjs, address, token, key } = params; + + const codeHash = await GetContractCodeHash({ secretjs, address: token }); + if (!codeHash) { + throw new Error("Code hash not found for the specified address."); + } + + let balanceResponse: BalanceQueryResponse | undefined; + for (let i = 0; i < 4; i++) { + try { + const response = await secretjs.query.compute.queryContract({ + contract_address: token, + code_hash: codeHash, // Use the fetched code hash + query: { + balance: { + address, + key, + }, + }, + }); + balanceResponse = response as BalanceQueryResponse; + break; + } catch (err) { + const e = err as Error; + console.error(e); + if ( + e.message !== + "Failed to decrypt the following error message: rpc error: code = Unknown desc = contract: not found (HTTP 500)." + ) { + return unlockToken; + } + await sleep(1000); + } + } + + if (!balanceResponse) { + throw new Error("Failed to get balance response after several attempts."); + } + + if (balanceResponse.viewing_key_error) { + return "Fix Unlock"; + } + + if (balanceResponse.balance && Number(balanceResponse.balance.amount) === 0) { + return "0"; + } + + return balanceResponse.balance + ? balanceResponse.balance.amount + : "Unknown Balance"; +}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20Send.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20Send.ts new file mode 100644 index 0000000..43dbcaa --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20Send.ts @@ -0,0 +1,39 @@ +import { SecretNetworkClient, TxResponse } from "secretjs"; + +export const Snip20Send = async (params: { + secretjs: SecretNetworkClient; + address: string; + amount: string; + msg: string; + recipient: string; + fee?: { amount: [{ amount: string; denom: string }]; gas: string }; +}): Promise => { + const { secretjs, address, amount, msg, recipient, fee } = params; + + const executeMsg = { + send: { + amount, + recipient, + msg, + }, + }; + + const tx = await secretjs.tx.compute.executeContract( + { + sender: address, + contract_address: recipient, + code_hash: "", // Provide the correct code hash here + msg: executeMsg, + sent_funds: [], + }, + { + gasLimit: fee?.gas ? parseInt(fee.gas) : 200000, + gasPriceInFeeDenom: fee?.amount?.[0]?.amount + ? parseFloat(fee.amount[0].amount) + : 0.25, // Convert gas price to number + feeDenom: fee?.amount?.[0]?.denom || "uscrt", + }, + ); + + return tx; +}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20SendToBridge.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20SendToBridge.ts new file mode 100644 index 0000000..342b947 --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20SendToBridge.ts @@ -0,0 +1,56 @@ +import { SecretNetworkClient, TxResponse } from "secretjs"; +import { Snip20Send } from "./Snip20Send"; // Ensure correct path to import Snip20Send +import { GetContractCodeHash } from "./GetContractCodeHash"; // Ensure correct path to import +import useGlobalConfigStore from "@/store/useGlobalConfigStore"; + +export const Snip20SendToBridge = async (params: { + secretjs: SecretNetworkClient; + address: string; + amount: string; + msg: string; + recipient?: string; + fee?: { amount: [{ amount: string; denom: string }]; gas: string }; +}): Promise => { + const { SCRT_SWAP_CONTRACT } = useGlobalConfigStore().config; + const recipientAddress = params.recipient || SCRT_SWAP_CONTRACT; + + // Fetch the code hash of the recipient contract + const codeHash = await GetContractCodeHash({ + secretjs: params.secretjs, + address: recipientAddress, + }); + if (!codeHash) { + throw new Error("Code hash not found for the specified recipient address."); + } + + const tx: TxResponse = await Snip20Send({ + ...params, + recipient: recipientAddress, + }); + + // Parse the transaction logs to find the tx_id + let tx_id: string | undefined; + if (tx.arrayLog) { + const txIdKvp = tx.arrayLog.find((kv) => kv.key === "tx_id"); + if (txIdKvp) { + tx_id = txIdKvp.value; + } + } + + if (!tx_id && tx.jsonLog) { + for (const log of tx.jsonLog) { + const txIdKvp = log.events.find((event) => event.type === "wasm") + ?.attributes.find((kv) => kv.key === "tx_id"); + if (txIdKvp) { + tx_id = txIdKvp.value; + break; + } + } + } + + if (!tx_id) { + throw new Error("Failed to get tx_id"); + } + + return tx_id; +}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20SwapHash b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20SwapHash new file mode 100644 index 0000000..0a4750c --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/Snip20SwapHash @@ -0,0 +1,5 @@ +export const Snip20SwapHash = ( + params: { tx_id: string; address: string }, +): string => { + return `${params.tx_id}|${params.address}`; +}; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/index.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/index.ts new file mode 100644 index 0000000..73197a6 --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/index.ts @@ -0,0 +1,6 @@ +export * from "./GetContractCodeHash"; +export * from "./GetSnip20Params"; +export * from "./types/Snip20TokenInfo"; +export * from "./Snip20Send"; +export * from "./Snip20GetBalance"; +export * from "./Snip20SendToBridge"; diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/types/Snip20TokenInfo.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/types/Snip20TokenInfo.ts new file mode 100644 index 0000000..7ddb380 --- /dev/null +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/snip20/types/Snip20TokenInfo.ts @@ -0,0 +1,6 @@ +export interface Snip20TokenInfo { + name: string; + symbol: string; + decimals: number; + total_supply?: string; +} diff --git a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/swap.ts b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/swap/swap.ts similarity index 75% rename from utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/swap.ts rename to utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/swap/swap.ts index f79b453..df35643 100644 --- a/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/swap.ts +++ b/utils/secretjs/SecretSwapSite/blockchain-bridge/scrt/swap/swap.ts @@ -1,14 +1,25 @@ -import BigNumber from 'bignumber.js'; -import { storeTxResultLocally } from 'pages/Swap/utils'; -import { ExecuteResult, SigningCosmWasmClient, CosmWasmClient } from 'secretjs'; -import { Asset, Currency, NativeToken, Token, Trade, TradeType } from '../../pages/TokenModal/types/trade'; -import { GetContractCodeHash } from './snip20'; -import { extractValueFromLogs, getFeeForExecute, validateBech32Address } from './utils'; -import { AsyncSender } from './asyncSender'; -import { GAS_FOR_CREATE_PAIR } from '../../utils/gasPrices'; +import BigNumber from "bignumber.js"; +// import { storeTxResultLocally } from 'pages/Swap/utils'; +// import { ExecuteResult, SigningCosmWasmClient, CosmWasmClient } from 'secretjs'; +import { + Asset, + Currency, + NativeToken, + Token, + Trade, + TradeType, +} from "../TokenModalTypes/types/trade"; +import { GetContractCodeHash } from "../snip20/GetContractCodeHash"; +import { + // extractValueFromLogs, + getFeeForExecute, + validateBech32Address, +} from "../utils"; +import { AsyncSender } from "../asyncSender"; +import { GAS_FOR_CREATE_PAIR } from "../../../utils/gasPrices"; export const buildAssetInfo = (currency: Currency) => { - if (currency.token.info.type === 'native_token') { + if (currency.token.info.type === "native_token") { return { info: { native_token: currency.token.info.native_token }, amount: currency.amount, @@ -19,7 +30,7 @@ export const buildAssetInfo = (currency: Currency) => { token: { contract_addr: currency.token.info.token.contract_addr, token_code_hash: currency.token.info.token.token_code_hash, - viewing_key: '', + viewing_key: "", }, }, amount: currency.amount, @@ -67,7 +78,9 @@ export const ReverseSimulateResult = async (params: { }): Promise => { const { secretjs, trade, pair } = params; - console.log(`trade: ${pair}: ${JSON.stringify(buildAssetInfo(trade.outputAmount))}`); + console.log( + `trade: ${pair}: ${JSON.stringify(buildAssetInfo(trade.outputAmount))}`, + ); return await secretjs.queryContractSmart(pair, { reverse_simulation: { @@ -88,9 +101,9 @@ export const handleSimulation = async ( pair: string, swapDirection: TradeType, ): Promise => { - let returned_asset = '0'; - let commission_amount = '0'; - let spread_amount = '0'; + let returned_asset = "0"; + let commission_amount = "0"; + let spread_amount = "0"; switch (swapDirection) { case TradeType.EXACT_INPUT: if (isNaN(Number(trade.inputAmount))) { @@ -101,7 +114,7 @@ export const handleSimulation = async ( secretjs, trade, pair, - }).catch(err => { + }).catch((err) => { throw new Error(`Failed to run simulation: ${err}`); }); @@ -122,13 +135,14 @@ export const handleSimulation = async ( console.error(2); } - const resultReverse: ReverseSimulationResponse = await ReverseSimulateResult({ - secretjs, - trade, - pair, - }).catch(err => { - throw new Error(`Failed to run reverse simulation: ${err}`); - }); + const resultReverse: ReverseSimulationResponse = + await ReverseSimulateResult({ + secretjs, + trade, + pair, + }).catch((err) => { + throw new Error(`Failed to run reverse simulation: ${err}`); + }); returned_asset = resultReverse.offer_amount; commission_amount = resultReverse.commission_amount; spread_amount = resultReverse.spread_amount; @@ -162,10 +176,14 @@ export const compute_swap = ( // offer => ask // ask_amount = (ask_pool - cp / (offer_pool + offer_amount)) * (1 - commission_rate) const cp = offer_pool.multipliedBy(ask_pool); - let return_amount = ask_pool.minus(cp.multipliedBy(new BigNumber(1).dividedBy(offer_pool.plus(offer_amount)))); + let return_amount = ask_pool.minus( + cp.multipliedBy(new BigNumber(1).dividedBy(offer_pool.plus(offer_amount))), + ); // calculate spread & commission - const spread_amount = offer_amount.multipliedBy(ask_pool.dividedBy(offer_pool)).minus(return_amount); + const spread_amount = offer_amount.multipliedBy( + ask_pool.dividedBy(offer_pool), + ).minus(return_amount); const commission_amount = return_amount.multipliedBy(COMMISSION_RATE); // commission will be absorbed to pool @@ -191,18 +209,27 @@ export const compute_offer_amount = ( const offer_amount = cp .multipliedBy( - new BigNumber(1).dividedBy(ask_pool.minus(ask_amount.multipliedBy(reverse_decimal(one_minus_commission)))), + new BigNumber(1).dividedBy( + ask_pool.minus( + ask_amount.multipliedBy(reverse_decimal(one_minus_commission)), + ), + ), ) .minus(offer_pool); - const before_commission_deduction = ask_amount.multipliedBy(reverse_decimal(one_minus_commission)); + const before_commission_deduction = ask_amount.multipliedBy( + reverse_decimal(one_minus_commission), + ); let spread_amount = new BigNumber(0); try { - spread_amount = offer_amount.multipliedBy(ask_pool.dividedBy(offer_pool)).minus(before_commission_deduction); + spread_amount = offer_amount.multipliedBy(ask_pool.dividedBy(offer_pool)) + .minus(before_commission_deduction); } catch (e) {} - const commission_amount = before_commission_deduction.multipliedBy(COMMISSION_RATE); + const commission_amount = before_commission_deduction.multipliedBy( + COMMISSION_RATE, + ); return { offer_amount, spread_amount, commission_amount }; }; @@ -236,13 +263,16 @@ export const CreateNewPair = async ({ const asset_infos = []; for (const t of [tokenA, tokenB]) { // is a token - if ('token' in t.info) { + if ("token" in t.info) { if (!validateBech32Address(t.info.token.contract_addr)) { - throw new Error('Token address is not valid'); + throw new Error("Token address is not valid"); } const token = t.info.token; try { - token.token_code_hash = await GetContractCodeHash({ secretjs, address: token.contract_addr }); + token.token_code_hash = await GetContractCodeHash({ + secretjs, + address: token.contract_addr, + }); } catch (e) { throw `Error fetching code hash for ${t.symbol} ${t.info.token.contract_addr}: ${e.message}`; } @@ -259,7 +289,7 @@ export const CreateNewPair = async ({ { create_pair: { asset_infos }, }, - '', + "", [], getFeeForExecute(GAS_FOR_CREATE_PAIR), ); @@ -271,11 +301,16 @@ interface GetAllPairsResponse { pairs: Array; } -export const GetAllPairs = async (params: { secretjs: SigningCosmWasmClient }): Promise => { +export const GetAllPairs = async ( + params: { secretjs: SigningCosmWasmClient }, +): Promise => { const { secretjs } = params; - return await secretjs.queryContractSmart(globalThis.config.AMM_FACTORY_CONTRACT, { - pairs: { limit: 30 }, - }); + return await secretjs.queryContractSmart( + globalThis.config.AMM_FACTORY_CONTRACT, + { + pairs: { limit: 30 }, + }, + ); }; export type Pair = { @@ -292,12 +327,12 @@ export type AssetInfos = { export const getSymbolsFromPair = (pair: { asset_infos: any }): string[] => { const symbols = []; - if ('native_token' in pair.asset_infos[0]) { + if ("native_token" in pair.asset_infos[0]) { symbols.push(pair.asset_infos[0].native_token.denom); } else { symbols.push(pair.asset_infos[0].token.contract_addr); } - if ('native_token' in pair.asset_infos[1]) { + if ("native_token" in pair.asset_infos[1]) { symbols.push(pair.asset_infos[1].native_token.denom); } else { symbols.push(pair.asset_infos[1].token.contract_addr);