From 6c0f5f6daa27d5c8cd0ef80fdc6918d9af9aef7e Mon Sep 17 00:00:00 2001 From: ltminhthu Date: Sat, 8 Jun 2024 15:09:52 +0700 Subject: [PATCH 1/8] delete files to refactor --- apps/wallet/src/utils/transaction/send.ts | 196 ------------------ apps/wallet/src/utils/transaction/send.web.ts | 126 ----------- apps/wallet/src/utils/transaction/solana.ts | 34 --- .../src/utils/transaction/solana.web.ts | 25 --- .../src/utils/transaction/swap/index.ts | 2 - .../src/utils/transaction/swap/jupiter.ts | 112 ---------- .../src/utils/transaction/swap/types.ts | 27 --- 7 files changed, 522 deletions(-) delete mode 100644 apps/wallet/src/utils/transaction/send.ts delete mode 100644 apps/wallet/src/utils/transaction/send.web.ts delete mode 100644 apps/wallet/src/utils/transaction/solana.ts delete mode 100644 apps/wallet/src/utils/transaction/solana.web.ts delete mode 100644 apps/wallet/src/utils/transaction/swap/index.ts delete mode 100644 apps/wallet/src/utils/transaction/swap/jupiter.ts delete mode 100644 apps/wallet/src/utils/transaction/swap/types.ts diff --git a/apps/wallet/src/utils/transaction/send.ts b/apps/wallet/src/utils/transaction/send.ts deleted file mode 100644 index 7f33b3712..000000000 --- a/apps/wallet/src/utils/transaction/send.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { PublicKey } from '@solana/web3.js'; -import { Networks, RequestType, ResponseCode } from '@walless/core'; -import type { ResponsePayload } from '@walless/messaging'; -import { aptos, solana, sui, utils } from '@walless/network'; -import { engine } from 'engine'; -import type { AptosContext, SolanaContext, SuiContext } from 'engine/runners'; -import { environment } from 'utils/config'; -import { solMint } from 'utils/constants'; -import { storage } from 'utils/storage'; - -import { - constructSolanaSendNftTransaction, - constructSolanaSendTokenTransaction, - constructSuiSendTokenTransaction, -} from './construct'; -import { getGasilonConfig } from './gasilon'; -import type { - SolanaSendNftTransaction, - SolanaSendTokenTransaction, - SolanaSendTransaction, - SuiSendTokenTransaction, -} from './types'; - -export const createAndSendSolanaTransaction = async ( - initTransaction: SolanaSendTokenTransaction | SolanaSendNftTransaction, - passcode: string, -) => { - const { type } = initTransaction; - let transaction; - if (type === 'token') { - transaction = await constructSolanaSendTokenTransaction( - initTransaction as SolanaSendTokenTransaction, - ); - } else { - transaction = await constructSolanaSendNftTransaction( - initTransaction as SolanaSendNftTransaction, - ); - } - if (!transaction) throw Error('failed to construct transaction'); - transaction = await solana.withSetComputeUnitPrice(transaction); - - const res = {} as ResponsePayload; - let privateKey; - try { - privateKey = await utils.getPrivateKey(storage, Networks.solana, passcode); - } catch { - res.responseCode = ResponseCode.WRONG_PASSCODE; - return res; - } - - const { tokenForFee } = initTransaction as SolanaSendTransaction; - const isGasilon = tokenForFee && tokenForFee.mint !== solMint; - if (isGasilon) { - const { sender, tokenForFee, fee } = initTransaction; - const config = await getGasilonConfig(); - if (!config) throw Error('Gasilon is not available'); - transaction = await solana.withGasilon(transaction, { - sender: new PublicKey(sender), - feeAmount: Math.round(fee * 10 ** tokenForFee.decimals), - feeMint: new PublicKey(tokenForFee.mint), - feePayer: new PublicKey(config.feePayer), - }); - - const gasilonEndpoint = environment.GASILON_ENDPOINT; - res.signatureString = await solana.signAndSendGasilonTransaction( - gasilonEndpoint, - transaction, - privateKey, - ); - } else { - const { connection } = engine.getContext(Networks.solana); - res.signatureString = await solana.signAndSendTransaction( - connection, - // TODO: support legacy transaction - transaction as never, - privateKey, - ); - } - res.responseCode = ResponseCode.SUCCESS; - - return res; -}; - -export const sendAptosTransaction = async ( - transaction: aptos.AptosCoinPayload | aptos.AptosTokenPayload, - passcode: string, -) => { - const res = {} as ResponsePayload; - - let privateKey; - try { - privateKey = await utils.getPrivateKey(storage, Networks.aptos, passcode); - } catch { - res.responseCode = ResponseCode.WRONG_PASSCODE; - return res; - } - - const isCoinTransaction = !('creator' in transaction); - - const { provider } = engine.getContext(Networks.aptos); - if (isCoinTransaction) { - res.signatureString = await aptos.handleTransferCoin( - provider, - privateKey, - transaction, - ); - } else { - res.signatureString = await aptos.handleTransferToken( - provider, - privateKey, - transaction, - ); - } - - res.responseCode = ResponseCode.SUCCESS; - - return res; -}; -export const handleAptosOnChainAction = async ({ - passcode, - type, - payload, -}: { - passcode: string; - type: RequestType; - payload: unknown; -}) => { - const res = {} as ResponsePayload; - - let privateKey; - try { - privateKey = await utils.getPrivateKey(storage, Networks.aptos, passcode); - } catch { - res.responseCode = ResponseCode.WRONG_PASSCODE; - return res; - } - - try { - const { provider } = engine.getContext(Networks.aptos); - switch (type) { - case RequestType.UPDATE_DIRECT_TRANSFER_ON_APTOS: - res.signatureString = await aptos.handleUpdateDirectTransfer( - provider, - privateKey, - payload as aptos.AptosDirectTransferPayload, - ); - break; - - case RequestType.CLAIM_TOKEN_ON_APTOS: - res.signatureString = await aptos.handleClaimToken( - provider, - privateKey, - payload as aptos.AptosClaimTokenPayload, - ); - break; - - default: - break; - } - - res.responseCode = ResponseCode.SUCCESS; - } catch { - res.responseCode = ResponseCode.ERROR; - } - - return res; -}; - -export const createAndSendSuiTransaction = async ( - initTransaction: SuiSendTokenTransaction, - passcode: string, -) => { - const res = {} as ResponsePayload; - let privateKey: Uint8Array; - try { - privateKey = await utils.getPrivateKey(storage, Networks.sui, passcode); - } catch { - res.responseCode = ResponseCode.WRONG_PASSCODE; - return res; - } - - const { client } = engine.getContext(Networks.sui); - const transactionBlock = await constructSuiSendTokenTransaction( - client, - initTransaction, - ); - const response = await sui.signAndExecuteTransaction( - client, - transactionBlock.serialize(), - privateKey, - ); - res.signatureString = response.transaction?.txSignatures; - res.responseCode = ResponseCode.SUCCESS; - - return res; -}; diff --git a/apps/wallet/src/utils/transaction/send.web.ts b/apps/wallet/src/utils/transaction/send.web.ts deleted file mode 100644 index 6282f9622..000000000 --- a/apps/wallet/src/utils/transaction/send.web.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { PublicKey } from '@solana/web3.js'; -import { Networks, RequestType, ResponseCode } from '@walless/core'; -import type { ResponsePayload } from '@walless/messaging'; -import { solana, sui, utils } from '@walless/network'; -import { sendRequest } from 'bridge'; -import { encode } from 'bs58'; -import { engine } from 'engine'; -import type { SolanaContext, SuiContext } from 'engine/runners'; -import { solMint } from 'utils/constants'; -import { storage } from 'utils/storage'; - -import { - constructSolanaSendNftTransaction, - constructSolanaSendTokenTransaction, - constructSuiSendTokenTransaction, -} from './construct'; -import { getGasilonConfig } from './gasilon'; -import type { - SolanaSendNftTransaction, - SolanaSendTokenTransaction, - SolanaSendTransaction, - SuiSendTokenTransaction, -} from './types'; - -export const createAndSendSolanaTransaction = async ( - initTransaction: SolanaSendTokenTransaction | SolanaSendNftTransaction, - passcode: string, -) => { - const { type } = initTransaction; - let transaction; - if (type === 'token') { - transaction = await constructSolanaSendTokenTransaction( - initTransaction as SolanaSendTokenTransaction, - ); - } else { - transaction = await constructSolanaSendNftTransaction( - initTransaction as SolanaSendNftTransaction, - ); - } - if (!transaction) throw Error('failed to construct transaction'); - - const { tokenForFee } = initTransaction as SolanaSendTransaction; - const isGasilon = tokenForFee && tokenForFee.mint !== solMint; - if (isGasilon) { - const { sender, tokenForFee, fee } = initTransaction; - const config = await getGasilonConfig(); - if (!config) throw Error('Gasilon is not available'); - transaction = await solana.withGasilon(transaction, { - sender: new PublicKey(sender), - feeAmount: Math.round(fee * 10 ** tokenForFee.decimals), - feeMint: new PublicKey(tokenForFee.mint), - feePayer: new PublicKey(config.feePayer), - }); - - const { connection } = engine.getContext(Networks.solana); - transaction = await solana.withSetComputeUnitPrice(transaction, connection); - - return await sendRequest({ - type: RequestType.SIGN_GASILON_TRANSACTION_ON_SOLANA, - transaction: encode( - transaction.serialize({ - requireAllSignatures: false, - }), - ), - passcode, - }); - } else { - const { connection } = engine.getContext(Networks.solana); - transaction = await solana.withSetComputeUnitPrice(transaction, connection); - return await sendRequest( - { - type: RequestType.SIGN_SEND_TRANSACTION_ON_SOLANA, - transaction: encode(transaction.serialize()), - passcode, - }, - 1000 * 60 * 5, - ); - } -}; - -export const handleAptosOnChainAction = async ({ - passcode, - payload, - type, -}: { - passcode: string; - type: RequestType; - payload: unknown; -}) => { - const res = await sendRequest({ - type, - transaction: JSON.stringify(payload), - passcode, - }); - - return res; -}; - -export const createAndSendSuiTransaction = async ( - initTransaction: SuiSendTokenTransaction, - passcode: string, -) => { - const res = {} as ResponsePayload; - let privateKey: Uint8Array; - try { - privateKey = await utils.getPrivateKey(storage, Networks.sui, passcode); - } catch { - res.responseCode = ResponseCode.WRONG_PASSCODE; - return res; - } - - const { client } = engine.getContext(Networks.sui); - const transactionBlock = await constructSuiSendTokenTransaction( - client, - initTransaction, - ); - const response = await sui.signAndExecuteTransaction( - client, - transactionBlock.serialize(), - privateKey, - ); - res.signatureString = response.transaction?.txSignatures; - res.responseCode = ResponseCode.SUCCESS; - - return res; -}; diff --git a/apps/wallet/src/utils/transaction/solana.ts b/apps/wallet/src/utils/transaction/solana.ts deleted file mode 100644 index 6a879a4c7..000000000 --- a/apps/wallet/src/utils/transaction/solana.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { VersionedTransaction } from '@solana/web3.js'; -import { Networks, ResponseCode } from '@walless/core'; -import type { ResponsePayload } from '@walless/messaging'; -import { solana, utils } from '@walless/network'; -import { engine } from 'engine'; -import type { SolanaContext } from 'engine/runners'; -import { storage } from 'utils/storage'; - -export const signAndSendTransaction = async ( - transaction: VersionedTransaction, - passcode: string, - options?: solana.SignAndSendOptions, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - timeout?: number, -): Promise => { - const res = {} as ResponsePayload; - let privateKey; - try { - privateKey = await utils.getPrivateKey(storage, Networks.solana, passcode); - } catch { - res.responseCode = ResponseCode.WRONG_PASSCODE; - return res; - } - - const { connection } = engine.getContext(Networks.solana); - res.signatureString = await solana.signAndSendTransaction( - connection, - transaction, - privateKey, - options, - ); - - return res; -}; diff --git a/apps/wallet/src/utils/transaction/solana.web.ts b/apps/wallet/src/utils/transaction/solana.web.ts deleted file mode 100644 index 38349f11d..000000000 --- a/apps/wallet/src/utils/transaction/solana.web.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { VersionedTransaction } from '@solana/web3.js'; -import { RequestType } from '@walless/core'; -import type { ResponsePayload } from '@walless/messaging'; -import type { solana } from '@walless/network'; -import { sendRequest } from 'bridge'; -import { encode } from 'bs58'; - -export const signAndSendTransaction = async ( - transaction: VersionedTransaction, - passcode: string, - options?: solana.SignAndSendOptions, - timeout?: number, -): Promise => { - const res = await sendRequest( - { - type: RequestType.SIGN_SEND_TRANSACTION_ON_SOLANA, - transaction: encode(transaction.serialize()), - passcode, - options, - }, - timeout, - ); - - return res as ResponsePayload; -}; diff --git a/apps/wallet/src/utils/transaction/swap/index.ts b/apps/wallet/src/utils/transaction/swap/index.ts deleted file mode 100644 index 08437d326..000000000 --- a/apps/wallet/src/utils/transaction/swap/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './jupiter'; -export * from './types'; diff --git a/apps/wallet/src/utils/transaction/swap/jupiter.ts b/apps/wallet/src/utils/transaction/swap/jupiter.ts deleted file mode 100644 index d9b456c44..000000000 --- a/apps/wallet/src/utils/transaction/swap/jupiter.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { VersionedTransaction } from '@solana/web3.js'; -import type { SolanaToken } from '@walless/core'; -import { logger, Networks } from '@walless/core'; -import type { TokenDocument } from '@walless/store'; -import { engine } from 'engine'; -import type { SolanaContext } from 'engine/runners'; -import { environment } from 'utils/config'; -import { solMint } from 'utils/constants'; - -import type { SwapQuote } from './types'; - -export const getAliasedMint = (token: TokenDocument) => { - if (environment.NETWORK_CLUSTER === 'devnet') { - if (token.mint === 'Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr') { - return 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; - } - } - - if (token.mint === solMint) { - return 'So11111111111111111111111111111111111111112'; - } - - return token.mint; -}; - -type GetSwapParams = { - fromMint: string; - toMint: string; - amount: number; - slippageBps?: number; -}; - -export const getSwapQuote = async ({ - fromMint, - toMint, - amount, - slippageBps = 50, -}: GetSwapParams): Promise => { - const res = await fetch( - `${environment.JUPITER_API_ENDPOINT}/quote?inputMint=${fromMint}&outputMint=${toMint}&amount=${amount}&slippageBps=${slippageBps}`, - ); - - const data = await res.json(); - if (res.ok) { - return data as SwapQuote; - } - - logger.error('fetch swap quote error:', data); -}; - -type SwapParams = { - fromMint: string; - toMint: string; - amount: number; - slippageBps?: number; - userPublicKey: string; - wrapAndUnwrapSol: boolean; -}; -export const constructSwapTransaction = async ({ - fromMint, - toMint, - amount, - slippageBps, - userPublicKey, - wrapAndUnwrapSol, -}: SwapParams): Promise => { - const quoteResponse = await getSwapQuote({ - fromMint, - toMint, - amount, - slippageBps, - }); - if (!quoteResponse) { - throw Error('Can not fetch swap quote, try again!'); - } - - const res = await fetch(`${environment.JUPITER_API_ENDPOINT}/swap`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - quoteResponse, - userPublicKey, - wrapAndUnwrapSol, - // ref: https://station.jup.ag/docs/apis/swap-api#setting-priority-fee-for-your-transaction - dynamicComputeUnitLimit: true, // allow dynamic compute limit instead of max 1,400,000 - prioritizationFeeLamports: 'auto', // or custom lamports: 1000 - }), - }); - if (!res.ok) { - throw Error('Can not construct swap transaction, try again!'); - } - - const { swapTransaction } = await res.json(); - const swapTransactionBuf = Buffer.from(swapTransaction, 'base64'); - const transaction = VersionedTransaction.deserialize(swapTransactionBuf); - const { connection } = engine.getContext(Networks.solana); - try { - // refetch latest blockhash with finalized, to avoid blockhash not found - const { blockhash } = await connection.getLatestBlockhash('finalized'); - transaction.message.recentBlockhash = blockhash; - } catch (error) { - logger.error( - 'failed to refetch latest blockhash for swap transaction', - error, - ); - throw Error('Can not construct swap transaction, try again!'); - } - - return transaction; -}; diff --git a/apps/wallet/src/utils/transaction/swap/types.ts b/apps/wallet/src/utils/transaction/swap/types.ts deleted file mode 100644 index 83a5df4bc..000000000 --- a/apps/wallet/src/utils/transaction/swap/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -export type SwapQuote = { - inputMint: string; - inAmount: string; - outputMint: string; - outAmount: string; - swapMode: 'ExactIn' | 'ExactOut'; - slippageBps: number; - platformFee: number | null; - priceImpactPct: string; - routePlan: { - swapInfo: SwapQuoteInfo; - percent: number; - }[]; - contextSlot: number; - timeTaken: number; -}; - -export type SwapQuoteInfo = { - ammKey: string; - label: string; - inputMint: string; - outputMint: string; - inAmount: string; - outAmount: string; - feeAmount: string; - feeMint: string; -}; From 7920e9e7d1fc3688ec488336dd0725f6aaddbe9a Mon Sep 17 00:00:00 2001 From: ltminhthu Date: Sat, 8 Jun 2024 15:18:44 +0700 Subject: [PATCH 2/8] add trasnfer transaction on tezos (not including fee) --- .../src/utils/transaction/tezos/index.ts | 1 + .../src/utils/transaction/tezos/send/fee.ts | 13 ++++ .../src/utils/transaction/tezos/send/index.ts | 46 ++++++++++++++ .../utils/transaction/tezos/send/internal.ts | 62 +++++++++++++++++++ packages/core/utils/assets.ts | 4 +- 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 apps/wallet/src/utils/transaction/tezos/index.ts create mode 100644 apps/wallet/src/utils/transaction/tezos/send/fee.ts create mode 100644 apps/wallet/src/utils/transaction/tezos/send/index.ts create mode 100644 apps/wallet/src/utils/transaction/tezos/send/internal.ts diff --git a/apps/wallet/src/utils/transaction/tezos/index.ts b/apps/wallet/src/utils/transaction/tezos/index.ts new file mode 100644 index 000000000..d1ab99c82 --- /dev/null +++ b/apps/wallet/src/utils/transaction/tezos/index.ts @@ -0,0 +1 @@ +export * from './send'; diff --git a/apps/wallet/src/utils/transaction/tezos/send/fee.ts b/apps/wallet/src/utils/transaction/tezos/send/fee.ts new file mode 100644 index 000000000..56f28acd0 --- /dev/null +++ b/apps/wallet/src/utils/transaction/tezos/send/fee.ts @@ -0,0 +1,13 @@ +import type { TezosToolkit } from '@taquito/taquito'; +import type { TezosSendTokenTransaction } from 'utils/transaction/types'; + +import { getTransferParams } from './internal'; +export const getTezosTransactionFee = async ( + payload: TezosSendTokenTransaction, + connection: TezosToolkit, +) => { + const transferParams = await getTransferParams(payload, connection); + const estmtn = await connection.estimate.transfer(transferParams); + + return estmtn.suggestedFeeMutez; +}; diff --git a/apps/wallet/src/utils/transaction/tezos/send/index.ts b/apps/wallet/src/utils/transaction/tezos/send/index.ts new file mode 100644 index 000000000..4d71becc2 --- /dev/null +++ b/apps/wallet/src/utils/transaction/tezos/send/index.ts @@ -0,0 +1,46 @@ +import { importKey } from '@taquito/signer'; +import { Networks, ResponseCode } from '@walless/core'; +import type { ResponsePayload } from '@walless/messaging'; +import { utils } from '@walless/network'; +import { encode } from 'bs58'; +import { engine } from 'engine'; +import type { TezosContext } from 'engine/runners'; +import { storage } from 'utils/storage'; +import type { TezosSendTokenTransaction } from 'utils/transaction/types'; + +import { getTransferParams } from './internal'; + +export const createAndSendTezosTransaction = async ( + payload: TezosSendTokenTransaction, + passcode: string, +) => { + const { connection } = engine.getContext(Networks.tezos); + + const res = {} as ResponsePayload; + + let privateKey; + try { + const decodedPrivateKey = await utils.getPrivateKey( + storage, + Networks.tezos, + passcode, + ); + privateKey = encode(decodedPrivateKey); + } catch { + res.responseCode = ResponseCode.WRONG_PASSCODE; + return res; + } + + await importKey(connection, privateKey); + + const transferParams = await getTransferParams(payload, connection); + + const tx = await connection.wallet.transfer(transferParams).send(); + + res.signatureString = tx.opHash; + res.isSuccessful = (await tx.confirmation())?.completed; + + res.responseCode = ResponseCode.SUCCESS; + + return res; +}; diff --git a/apps/wallet/src/utils/transaction/tezos/send/internal.ts b/apps/wallet/src/utils/transaction/tezos/send/internal.ts new file mode 100644 index 000000000..6bdd168f2 --- /dev/null +++ b/apps/wallet/src/utils/transaction/tezos/send/internal.ts @@ -0,0 +1,62 @@ +import type { TezosToolkit } from '@taquito/taquito'; +import { OpKind } from '@taquito/taquito'; +import { TezosTokenTypes } from '@walless/core'; +import { XTZ } from 'engine/runners/tezos/token'; +import type { TezosSendTokenTransaction } from 'utils/transaction/types'; + +export const getTransferParams = async ( + { + sender, + receiver, + amount, + token, + }: Omit, + connection: TezosToolkit, +) => { + if (token.contract.address === XTZ.contract.address) { + return { + amount: amount, + to: receiver, + }; + } + + const contract = await connection.contract.at(token.contract.address); + + if (token.tokenType === TezosTokenTypes.FA2) { + return { + kind: OpKind.TRANSACTION, + to: contract.address, + amount: 0, + parameter: { + entrypoint: 'transfer', + value: [ + { + prim: 'Pair', + args: [ + { string: sender }, + [ + { + prim: 'Pair', + args: [ + { string: receiver }, + { + prim: 'Pair', + args: [ + { int: token.tokenId }, + { int: (amount * 10 ** token.decimals).toFixed() }, + ], + }, + ], + }, + ], + ], + }, + ], + }, + }; + } + + return contract.methods + .transfer(sender, receiver, amount * 10 ** token.decimals) + .toTransferParams(); +}; diff --git a/packages/core/utils/assets.ts b/packages/core/utils/assets.ts index 4f6f14c8e..32f8a5bcf 100644 --- a/packages/core/utils/assets.ts +++ b/packages/core/utils/assets.ts @@ -83,8 +83,8 @@ export type SuiToken = Token & { // There are 2 standards of tokens in Tezos, they are FA1.2 and FA2 export enum TezosTokenTypes { - FA12 = 'FA1.2', - FA2 = 'FA2', + FA12 = 'fa1.2', + FA2 = 'fa2', } export type TezosContract = { From d6f2494d635a7a43ad760b8f3efbaacfcfc7c08b Mon Sep 17 00:00:00 2001 From: ltminhthu Date: Sat, 8 Jun 2024 15:28:47 +0700 Subject: [PATCH 3/8] refactor: move sui's transaction to transaction/sui --- .../wallet/src/utils/transaction/sui/index.ts | 1 + .../utils/transaction/sui/send/construct.ts | 19 +++++++++ .../src/utils/transaction/sui/send/fee.ts | 25 +++++++++++ .../src/utils/transaction/sui/send/index.ts | 41 +++++++++++++++++++ .../utils/transaction/sui/send/index.web.ts | 41 +++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 apps/wallet/src/utils/transaction/sui/index.ts create mode 100644 apps/wallet/src/utils/transaction/sui/send/construct.ts create mode 100644 apps/wallet/src/utils/transaction/sui/send/fee.ts create mode 100644 apps/wallet/src/utils/transaction/sui/send/index.ts create mode 100644 apps/wallet/src/utils/transaction/sui/send/index.web.ts diff --git a/apps/wallet/src/utils/transaction/sui/index.ts b/apps/wallet/src/utils/transaction/sui/index.ts new file mode 100644 index 000000000..d1ab99c82 --- /dev/null +++ b/apps/wallet/src/utils/transaction/sui/index.ts @@ -0,0 +1 @@ +export * from './send'; diff --git a/apps/wallet/src/utils/transaction/sui/send/construct.ts b/apps/wallet/src/utils/transaction/sui/send/construct.ts new file mode 100644 index 000000000..ea53a899b --- /dev/null +++ b/apps/wallet/src/utils/transaction/sui/send/construct.ts @@ -0,0 +1,19 @@ +import type { SuiClient } from '@mysten/sui.js/dist/cjs/client'; +import { sui } from '@walless/network'; +import type { SuiSendTokenTransaction } from 'utils/transaction/types'; + +export const constructSuiSendTokenTransaction = async ( + client: SuiClient, + initTransaction: SuiSendTokenTransaction, +) => { + const { tokenForFee, amount, receiver, sender, token } = initTransaction; + return sui.constructSuiTransactionBlock( + client, + token.coinType, + token.decimals, + tokenForFee.coinType, + amount, + receiver, + sender, + ); +}; diff --git a/apps/wallet/src/utils/transaction/sui/send/fee.ts b/apps/wallet/src/utils/transaction/sui/send/fee.ts new file mode 100644 index 000000000..f3f802845 --- /dev/null +++ b/apps/wallet/src/utils/transaction/sui/send/fee.ts @@ -0,0 +1,25 @@ +import type { GasCostSummary } from '@mysten/sui.js/client'; +import { SUI_DECIMALS } from '@mysten/sui.js/dist/cjs/utils'; +import type { TransactionBlock } from '@mysten/sui.js/transactions'; +import { Networks } from '@walless/core'; +import { engine } from 'engine'; +import type { SuiContext } from 'engine/runners'; + +const calculateSuiGas = (gas: GasCostSummary): number => { + const gasBigInt = + BigInt(gas.computationCost) + + BigInt(gas.storageCost) - + BigInt(gas.storageRebate); + return Number(gasBigInt) / 10 ** SUI_DECIMALS; +}; + +export const getSuiTransactionFee = async (txb: TransactionBlock) => { + const { client } = engine.getContext(Networks.sui); + const builtTxb = await txb.build({ client }).catch(console.log); + if (!builtTxb) throw new Error('unable to build txb'); + const dryrunResult = await client.dryRunTransactionBlock({ + transactionBlock: builtTxb, + }); + + return calculateSuiGas(dryrunResult?.effects.gasUsed); +}; diff --git a/apps/wallet/src/utils/transaction/sui/send/index.ts b/apps/wallet/src/utils/transaction/sui/send/index.ts new file mode 100644 index 000000000..9dd3fd4d1 --- /dev/null +++ b/apps/wallet/src/utils/transaction/sui/send/index.ts @@ -0,0 +1,41 @@ +import { Networks, ResponseCode } from '@walless/core'; +import type { ResponsePayload } from '@walless/messaging'; +import { sui, utils } from '@walless/network'; +import { engine } from 'engine'; +import type { SuiContext } from 'engine/runners'; +import { storage } from 'utils/storage'; +import type { SuiSendTokenTransaction } from 'utils/transaction/types'; + +import { constructSuiSendTokenTransaction } from './construct'; + +export const createAndSendSuiTransaction = async ( + initTransaction: SuiSendTokenTransaction, + passcode: string, +) => { + const res = {} as ResponsePayload; + let privateKey: Uint8Array; + try { + privateKey = await utils.getPrivateKey(storage, Networks.sui, passcode); + } catch { + res.responseCode = ResponseCode.WRONG_PASSCODE; + return res; + } + + const { client } = engine.getContext(Networks.sui); + const transactionBlock = await constructSuiSendTokenTransaction( + client, + initTransaction, + ); + const response = await sui.signAndExecuteTransaction( + client, + transactionBlock.serialize(), + privateKey, + ); + res.signatureString = response.transaction?.txSignatures; + res.responseCode = ResponseCode.SUCCESS; + + return res; +}; + +export * from './construct'; +export * from './fee'; diff --git a/apps/wallet/src/utils/transaction/sui/send/index.web.ts b/apps/wallet/src/utils/transaction/sui/send/index.web.ts new file mode 100644 index 000000000..9dd3fd4d1 --- /dev/null +++ b/apps/wallet/src/utils/transaction/sui/send/index.web.ts @@ -0,0 +1,41 @@ +import { Networks, ResponseCode } from '@walless/core'; +import type { ResponsePayload } from '@walless/messaging'; +import { sui, utils } from '@walless/network'; +import { engine } from 'engine'; +import type { SuiContext } from 'engine/runners'; +import { storage } from 'utils/storage'; +import type { SuiSendTokenTransaction } from 'utils/transaction/types'; + +import { constructSuiSendTokenTransaction } from './construct'; + +export const createAndSendSuiTransaction = async ( + initTransaction: SuiSendTokenTransaction, + passcode: string, +) => { + const res = {} as ResponsePayload; + let privateKey: Uint8Array; + try { + privateKey = await utils.getPrivateKey(storage, Networks.sui, passcode); + } catch { + res.responseCode = ResponseCode.WRONG_PASSCODE; + return res; + } + + const { client } = engine.getContext(Networks.sui); + const transactionBlock = await constructSuiSendTokenTransaction( + client, + initTransaction, + ); + const response = await sui.signAndExecuteTransaction( + client, + transactionBlock.serialize(), + privateKey, + ); + res.signatureString = response.transaction?.txSignatures; + res.responseCode = ResponseCode.SUCCESS; + + return res; +}; + +export * from './construct'; +export * from './fee'; From 359914747bafbf2a1620a0b5061a21e65d3b867f Mon Sep 17 00:00:00 2001 From: ltminhthu Date: Sat, 8 Jun 2024 15:29:43 +0700 Subject: [PATCH 4/8] refactor: move solana's transaction to transaction/solana (include send and swap) --- .../src/utils/transaction/solana/index.ts | 2 + .../transaction/solana/send/construct.ts | 65 ++++++++ .../src/utils/transaction/solana/send/fee.ts | 147 ++++++++++++++++++ .../utils/transaction/solana/send/index.ts | 111 +++++++++++++ .../transaction/solana/send/index.web.ts | 99 ++++++++++++ .../utils/transaction/solana/swap/index.ts | 2 + .../utils/transaction/solana/swap/jupiter.ts | 112 +++++++++++++ .../utils/transaction/solana/swap/types.ts | 27 ++++ 8 files changed, 565 insertions(+) create mode 100644 apps/wallet/src/utils/transaction/solana/index.ts create mode 100644 apps/wallet/src/utils/transaction/solana/send/construct.ts create mode 100644 apps/wallet/src/utils/transaction/solana/send/fee.ts create mode 100644 apps/wallet/src/utils/transaction/solana/send/index.ts create mode 100644 apps/wallet/src/utils/transaction/solana/send/index.web.ts create mode 100644 apps/wallet/src/utils/transaction/solana/swap/index.ts create mode 100644 apps/wallet/src/utils/transaction/solana/swap/jupiter.ts create mode 100644 apps/wallet/src/utils/transaction/solana/swap/types.ts diff --git a/apps/wallet/src/utils/transaction/solana/index.ts b/apps/wallet/src/utils/transaction/solana/index.ts new file mode 100644 index 000000000..e852f5234 --- /dev/null +++ b/apps/wallet/src/utils/transaction/solana/index.ts @@ -0,0 +1,2 @@ +export * from './send'; +export * from './swap'; diff --git a/apps/wallet/src/utils/transaction/solana/send/construct.ts b/apps/wallet/src/utils/transaction/solana/send/construct.ts new file mode 100644 index 000000000..eae05e0ea --- /dev/null +++ b/apps/wallet/src/utils/transaction/solana/send/construct.ts @@ -0,0 +1,65 @@ +import type { VersionedTransaction } from '@solana/web3.js'; +import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; +import { Networks } from '@walless/core'; +import { solana } from '@walless/network'; +import { engine } from 'engine'; +import type { SolanaContext } from 'engine/runners'; +import { solMint } from 'utils/constants'; +import type { + SolanaSendNftTransaction, + SolanaSendTokenTransaction, +} from 'utils/transaction/types'; + +export const constructSolanaSendTokenTransaction = async ( + transaction: Omit< + SolanaSendTokenTransaction, + 'type' | 'network' | 'fee' | 'tokenForFee' + >, +): Promise => { + const { + sender: senderStr, + receiver: receiverStr, + token, + amount, + } = transaction; + + const sender = new PublicKey(senderStr); + const receiver = new PublicKey(receiverStr); + + const { connection } = engine.getContext(Networks.solana); + + const isNativeTransaction = token.mint == solMint; + if (isNativeTransaction) { + return await solana.constructSendSOLTransaction(connection, { + sender, + receiver, + amount: amount * LAMPORTS_PER_SOL, + }); + } else { + return await solana.constructSendSPLTokenTransaction(connection, { + sender, + receiver, + amount: Math.round(amount * 10 ** token.decimals), + mint: new PublicKey(token.mint), + }); + } +}; + +export const constructSolanaSendNftTransaction = async ( + initTransaction: Omit< + SolanaSendNftTransaction, + 'type' | 'network' | 'fee' | 'tokenForFee' + >, +): Promise => { + const { connection } = engine.getContext(Networks.solana); + const { sender, receiver, amount, nft } = initTransaction; + + const transaction = await solana.constructSendNftTransaction(connection, { + sender: new PublicKey(sender), + receiver: new PublicKey(receiver), + amount: amount, + mint: new PublicKey(nft.mint), + }); + + return transaction; +}; diff --git a/apps/wallet/src/utils/transaction/solana/send/fee.ts b/apps/wallet/src/utils/transaction/solana/send/fee.ts new file mode 100644 index 000000000..5c876a1d6 --- /dev/null +++ b/apps/wallet/src/utils/transaction/solana/send/fee.ts @@ -0,0 +1,147 @@ +import { PublicKey } from '@metaplex-foundation/js'; +import { ACCOUNT_SIZE, getAssociatedTokenAddressSync } from '@solana/spl-token'; +import type { VersionedTransaction } from '@solana/web3.js'; +import { LAMPORTS_PER_SOL } from '@solana/web3.js'; +import type { SolanaCollectible, SolanaToken } from '@walless/core'; +import { logger, Networks } from '@walless/core'; +import { solana } from '@walless/network'; +import { withSetComputeUnitPrice } from '@walless/network/src/solana'; +import type { NftDocument, TokenDocument } from '@walless/store'; +import base58 from 'bs58'; +import { engine } from 'engine'; +import type { SolanaContext } from 'engine/runners'; +import { environment } from 'utils/config'; +import { solMint } from 'utils/constants'; +import { getGasilonConfig } from 'utils/transaction/gasilon'; +import type { + SolanaSendNftTransaction, + SolanaSendTokenTransaction, +} from 'utils/transaction/types'; + +import { + constructSolanaSendNftTransaction, + constructSolanaSendTokenTransaction, +} from './construct'; + +export type GetSolanaFeeConfig = { + type: 'token' | 'nft'; + sender: string; + receiver: string; + nft?: NftDocument; + token?: TokenDocument; + tokenForFee: TokenDocument; +}; + +export const getSolanaTransactionFee = async ( + initTransaction: GetSolanaFeeConfig, +): Promise => { + const { type, sender, receiver, token, nft } = initTransaction; + + let transaction; + if (type === 'token') { + if (!token) throw Error("require token when type is 'token'"); + transaction = await constructSolanaSendTokenTransaction({ + sender, + receiver, + token, + amount: 0, + }); + } else { + if (!nft) throw Error("require nft when type is 'nft'"); + transaction = await constructSolanaSendNftTransaction({ + sender, + receiver, + nft, + amount: 0, + }); + } + + const { connection } = engine.getContext(Networks.solana); + + if (!transaction) return 0; + transaction = await withSetComputeUnitPrice(transaction, connection); + + const message = (transaction as VersionedTransaction).message; + const transactionFeePromise = connection + .getFeeForMessage(message) + .then((res) => res.value || 0); + + const rentFeePromise = (async () => { + const { receiver } = initTransaction; + let mint; + if (initTransaction.type === 'token') { + const { token } = initTransaction as SolanaSendTokenTransaction; + if (token.mint === solMint) return 0; + mint = token.mint; + } else { + const { nft } = initTransaction as SolanaSendNftTransaction; + mint = nft.mint; + } + + const receiverATAddress = getAssociatedTokenAddressSync( + new PublicKey(mint), + new PublicKey(receiver), + ); + + const receiverATAccount = + await connection.getAccountInfo(receiverATAddress); + + if (!receiverATAccount) { + return await connection.getMinimumBalanceForRentExemption(ACCOUNT_SIZE); + } + + return 0; + })(); + + const [txFee, rentFee] = await Promise.all([ + transactionFeePromise, + rentFeePromise, + ]); + + return (txFee + rentFee) / LAMPORTS_PER_SOL; +}; + +export type GetGasilonFeeConfig = { + sender: string; + receiver: string; + mint: string; + feeMint: string; +}; + +export const getGasilonFee = async ({ + sender, + receiver, + mint, + feeMint, +}: GetGasilonFeeConfig): Promise => { + const config = await getGasilonConfig(); + if (!config) return 0; + + const { connection } = engine.getContext(Networks.solana); + const transaction = await solana.constructGasilonTransaction(connection, { + sender: new PublicKey(sender), + receiver: new PublicKey(receiver), + feePayer: new PublicKey(config.feePayer), + amount: 0, // mock amount for get gee + fee: 0, // mock amount for get gee + mint: new PublicKey(mint), + feeMint: new PublicKey(feeMint), + }); + + try { + const res = await fetch(`${environment.GASILON_ENDPOINT}/solana/getFee`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + transaction: base58.encode(transaction.serialize()), + }), + }); + + const data = await res.json(); + + return data.totalByFeeToken; + } catch (error) { + logger.error('failed to get gasilon transaction fee', error); + return 0; + } +}; diff --git a/apps/wallet/src/utils/transaction/solana/send/index.ts b/apps/wallet/src/utils/transaction/solana/send/index.ts new file mode 100644 index 000000000..9d9413d05 --- /dev/null +++ b/apps/wallet/src/utils/transaction/solana/send/index.ts @@ -0,0 +1,111 @@ +import { PublicKey } from '@metaplex-foundation/js'; +import type { VersionedTransaction } from '@solana/web3.js'; +import { Networks, ResponseCode } from '@walless/core'; +import type { ResponsePayload } from '@walless/messaging'; +import { solana, utils } from '@walless/network'; +import { engine } from 'engine'; +import type { SolanaContext } from 'engine/runners'; +import { environment } from 'utils/config'; +import { solMint } from 'utils/constants'; +import { storage } from 'utils/storage'; +import { getGasilonConfig } from 'utils/transaction/gasilon'; +import type { + SolanaSendNftTransaction, + SolanaSendTokenTransaction, + SolanaSendTransaction, +} from 'utils/transaction/types'; + +import { + constructSolanaSendNftTransaction, + constructSolanaSendTokenTransaction, +} from './construct'; + +export const createAndSendSolanaTransaction = async ( + initTransaction: SolanaSendTokenTransaction | SolanaSendNftTransaction, + passcode: string, +) => { + const { type } = initTransaction; + let transaction; + if (type === 'token') { + transaction = await constructSolanaSendTokenTransaction( + initTransaction as SolanaSendTokenTransaction, + ); + } else { + transaction = await constructSolanaSendNftTransaction( + initTransaction as SolanaSendNftTransaction, + ); + } + if (!transaction) throw Error('failed to construct transaction'); + transaction = await solana.withSetComputeUnitPrice(transaction); + + const res = {} as ResponsePayload; + let privateKey; + try { + privateKey = await utils.getPrivateKey(storage, Networks.solana, passcode); + } catch { + res.responseCode = ResponseCode.WRONG_PASSCODE; + return res; + } + + const { tokenForFee } = initTransaction as SolanaSendTransaction; + const isGasilon = tokenForFee && tokenForFee.mint !== solMint; + if (isGasilon) { + const { sender, tokenForFee, fee } = initTransaction; + const config = await getGasilonConfig(); + if (!config) throw Error('Gasilon is not available'); + transaction = await solana.withGasilon(transaction, { + sender: new PublicKey(sender), + feeAmount: Math.round(fee * 10 ** tokenForFee.decimals), + feeMint: new PublicKey(tokenForFee.mint), + feePayer: new PublicKey(config.feePayer), + }); + + const gasilonEndpoint = environment.GASILON_ENDPOINT; + res.signatureString = await solana.signAndSendGasilonTransaction( + gasilonEndpoint, + transaction, + privateKey, + ); + } else { + const { connection } = engine.getContext(Networks.solana); + res.signatureString = await solana.signAndSendTransaction( + connection, + // TODO: support legacy transaction + transaction as never, + privateKey, + ); + } + res.responseCode = ResponseCode.SUCCESS; + + return res; +}; + +export const signAndSendTransaction = async ( + transaction: VersionedTransaction, + passcode: string, + options?: solana.SignAndSendOptions, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + timeout?: number, +): Promise => { + const res = {} as ResponsePayload; + let privateKey; + try { + privateKey = await utils.getPrivateKey(storage, Networks.solana, passcode); + } catch { + res.responseCode = ResponseCode.WRONG_PASSCODE; + return res; + } + + const { connection } = engine.getContext(Networks.solana); + res.signatureString = await solana.signAndSendTransaction( + connection, + transaction, + privateKey, + options, + ); + + return res; +}; + +export * from './construct'; +export * from './fee'; diff --git a/apps/wallet/src/utils/transaction/solana/send/index.web.ts b/apps/wallet/src/utils/transaction/solana/send/index.web.ts new file mode 100644 index 000000000..b679ae22f --- /dev/null +++ b/apps/wallet/src/utils/transaction/solana/send/index.web.ts @@ -0,0 +1,99 @@ +import { PublicKey } from '@metaplex-foundation/js'; +import type { VersionedTransaction } from '@solana/web3.js'; +import { Networks, RequestType } from '@walless/core'; +import type { ResponsePayload } from '@walless/messaging'; +import { solana } from '@walless/network'; +import { sendRequest } from 'bridge'; +import { encode } from 'bs58'; +import { engine } from 'engine'; +import type { SolanaContext } from 'engine/runners'; +import { solMint } from 'utils/constants'; +import { getGasilonConfig } from 'utils/transaction/gasilon'; +import type { + SolanaSendNftTransaction, + SolanaSendTokenTransaction, + SolanaSendTransaction, +} from 'utils/transaction/types'; + +import { + constructSolanaSendNftTransaction, + constructSolanaSendTokenTransaction, +} from './construct'; + +export const createAndSendSolanaTransaction = async ( + initTransaction: SolanaSendTokenTransaction | SolanaSendNftTransaction, + passcode: string, +) => { + const { type } = initTransaction; + let transaction; + if (type === 'token') { + transaction = await constructSolanaSendTokenTransaction( + initTransaction as SolanaSendTokenTransaction, + ); + } else { + transaction = await constructSolanaSendNftTransaction( + initTransaction as SolanaSendNftTransaction, + ); + } + if (!transaction) throw Error('failed to construct transaction'); + + const { tokenForFee } = initTransaction as SolanaSendTransaction; + const isGasilon = tokenForFee && tokenForFee.mint !== solMint; + if (isGasilon) { + const { sender, tokenForFee, fee } = initTransaction; + const config = await getGasilonConfig(); + if (!config) throw Error('Gasilon is not available'); + transaction = await solana.withGasilon(transaction, { + sender: new PublicKey(sender), + feeAmount: Math.round(fee * 10 ** tokenForFee.decimals), + feeMint: new PublicKey(tokenForFee.mint), + feePayer: new PublicKey(config.feePayer), + }); + + const { connection } = engine.getContext(Networks.solana); + transaction = await solana.withSetComputeUnitPrice(transaction, connection); + + return await sendRequest({ + type: RequestType.SIGN_GASILON_TRANSACTION_ON_SOLANA, + transaction: encode( + transaction.serialize({ + requireAllSignatures: false, + }), + ), + passcode, + }); + } else { + const { connection } = engine.getContext(Networks.solana); + transaction = await solana.withSetComputeUnitPrice(transaction, connection); + return await sendRequest( + { + type: RequestType.SIGN_SEND_TRANSACTION_ON_SOLANA, + transaction: encode(transaction.serialize()), + passcode, + }, + 1000 * 60 * 5, + ); + } +}; + +export const signAndSendTransaction = async ( + transaction: VersionedTransaction, + passcode: string, + options?: solana.SignAndSendOptions, + timeout?: number, +): Promise => { + const res = await sendRequest( + { + type: RequestType.SIGN_SEND_TRANSACTION_ON_SOLANA, + transaction: encode(transaction.serialize()), + passcode, + options, + }, + timeout, + ); + + return res as ResponsePayload; +}; + +export * from './construct'; +export * from './fee'; diff --git a/apps/wallet/src/utils/transaction/solana/swap/index.ts b/apps/wallet/src/utils/transaction/solana/swap/index.ts new file mode 100644 index 000000000..08437d326 --- /dev/null +++ b/apps/wallet/src/utils/transaction/solana/swap/index.ts @@ -0,0 +1,2 @@ +export * from './jupiter'; +export * from './types'; diff --git a/apps/wallet/src/utils/transaction/solana/swap/jupiter.ts b/apps/wallet/src/utils/transaction/solana/swap/jupiter.ts new file mode 100644 index 000000000..d9b456c44 --- /dev/null +++ b/apps/wallet/src/utils/transaction/solana/swap/jupiter.ts @@ -0,0 +1,112 @@ +import { VersionedTransaction } from '@solana/web3.js'; +import type { SolanaToken } from '@walless/core'; +import { logger, Networks } from '@walless/core'; +import type { TokenDocument } from '@walless/store'; +import { engine } from 'engine'; +import type { SolanaContext } from 'engine/runners'; +import { environment } from 'utils/config'; +import { solMint } from 'utils/constants'; + +import type { SwapQuote } from './types'; + +export const getAliasedMint = (token: TokenDocument) => { + if (environment.NETWORK_CLUSTER === 'devnet') { + if (token.mint === 'Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr') { + return 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; + } + } + + if (token.mint === solMint) { + return 'So11111111111111111111111111111111111111112'; + } + + return token.mint; +}; + +type GetSwapParams = { + fromMint: string; + toMint: string; + amount: number; + slippageBps?: number; +}; + +export const getSwapQuote = async ({ + fromMint, + toMint, + amount, + slippageBps = 50, +}: GetSwapParams): Promise => { + const res = await fetch( + `${environment.JUPITER_API_ENDPOINT}/quote?inputMint=${fromMint}&outputMint=${toMint}&amount=${amount}&slippageBps=${slippageBps}`, + ); + + const data = await res.json(); + if (res.ok) { + return data as SwapQuote; + } + + logger.error('fetch swap quote error:', data); +}; + +type SwapParams = { + fromMint: string; + toMint: string; + amount: number; + slippageBps?: number; + userPublicKey: string; + wrapAndUnwrapSol: boolean; +}; +export const constructSwapTransaction = async ({ + fromMint, + toMint, + amount, + slippageBps, + userPublicKey, + wrapAndUnwrapSol, +}: SwapParams): Promise => { + const quoteResponse = await getSwapQuote({ + fromMint, + toMint, + amount, + slippageBps, + }); + if (!quoteResponse) { + throw Error('Can not fetch swap quote, try again!'); + } + + const res = await fetch(`${environment.JUPITER_API_ENDPOINT}/swap`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + quoteResponse, + userPublicKey, + wrapAndUnwrapSol, + // ref: https://station.jup.ag/docs/apis/swap-api#setting-priority-fee-for-your-transaction + dynamicComputeUnitLimit: true, // allow dynamic compute limit instead of max 1,400,000 + prioritizationFeeLamports: 'auto', // or custom lamports: 1000 + }), + }); + if (!res.ok) { + throw Error('Can not construct swap transaction, try again!'); + } + + const { swapTransaction } = await res.json(); + const swapTransactionBuf = Buffer.from(swapTransaction, 'base64'); + const transaction = VersionedTransaction.deserialize(swapTransactionBuf); + const { connection } = engine.getContext(Networks.solana); + try { + // refetch latest blockhash with finalized, to avoid blockhash not found + const { blockhash } = await connection.getLatestBlockhash('finalized'); + transaction.message.recentBlockhash = blockhash; + } catch (error) { + logger.error( + 'failed to refetch latest blockhash for swap transaction', + error, + ); + throw Error('Can not construct swap transaction, try again!'); + } + + return transaction; +}; diff --git a/apps/wallet/src/utils/transaction/solana/swap/types.ts b/apps/wallet/src/utils/transaction/solana/swap/types.ts new file mode 100644 index 000000000..83a5df4bc --- /dev/null +++ b/apps/wallet/src/utils/transaction/solana/swap/types.ts @@ -0,0 +1,27 @@ +export type SwapQuote = { + inputMint: string; + inAmount: string; + outputMint: string; + outAmount: string; + swapMode: 'ExactIn' | 'ExactOut'; + slippageBps: number; + platformFee: number | null; + priceImpactPct: string; + routePlan: { + swapInfo: SwapQuoteInfo; + percent: number; + }[]; + contextSlot: number; + timeTaken: number; +}; + +export type SwapQuoteInfo = { + ammKey: string; + label: string; + inputMint: string; + outputMint: string; + inAmount: string; + outAmount: string; + feeAmount: string; + feeMint: string; +}; From 198a4edf31a95b62bccc2e43bf0939be8ed2571a Mon Sep 17 00:00:00 2001 From: ltminhthu Date: Sat, 8 Jun 2024 15:30:34 +0700 Subject: [PATCH 5/8] refactor: move aptos's transaction to transaction/aptos --- .../src/utils/transaction/aptos/index.ts | 1 + .../src/utils/transaction/aptos/send/index.ts | 91 +++++++++++++++++++ .../utils/transaction/aptos/send/index.web.ts | 20 ++++ 3 files changed, 112 insertions(+) create mode 100644 apps/wallet/src/utils/transaction/aptos/index.ts create mode 100644 apps/wallet/src/utils/transaction/aptos/send/index.ts create mode 100644 apps/wallet/src/utils/transaction/aptos/send/index.web.ts diff --git a/apps/wallet/src/utils/transaction/aptos/index.ts b/apps/wallet/src/utils/transaction/aptos/index.ts new file mode 100644 index 000000000..d1ab99c82 --- /dev/null +++ b/apps/wallet/src/utils/transaction/aptos/index.ts @@ -0,0 +1 @@ +export * from './send'; diff --git a/apps/wallet/src/utils/transaction/aptos/send/index.ts b/apps/wallet/src/utils/transaction/aptos/send/index.ts new file mode 100644 index 000000000..d03ea6908 --- /dev/null +++ b/apps/wallet/src/utils/transaction/aptos/send/index.ts @@ -0,0 +1,91 @@ +import { Networks, RequestType, ResponseCode } from '@walless/core'; +import type { ResponsePayload } from '@walless/messaging'; +import { aptos, utils } from '@walless/network'; +import { engine } from 'engine'; +import type { AptosContext } from 'engine/runners'; +import { storage } from 'utils/storage'; + +export const sendAptosTransaction = async ( + transaction: aptos.AptosCoinPayload | aptos.AptosTokenPayload, + passcode: string, +) => { + const res = {} as ResponsePayload; + + let privateKey; + try { + privateKey = await utils.getPrivateKey(storage, Networks.aptos, passcode); + } catch { + res.responseCode = ResponseCode.WRONG_PASSCODE; + return res; + } + + const isCoinTransaction = !('creator' in transaction); + + const { provider } = engine.getContext(Networks.aptos); + if (isCoinTransaction) { + res.signatureString = await aptos.handleTransferCoin( + provider, + privateKey, + transaction, + ); + } else { + res.signatureString = await aptos.handleTransferToken( + provider, + privateKey, + transaction, + ); + } + + res.responseCode = ResponseCode.SUCCESS; + + return res; +}; +export const handleAptosOnChainAction = async ({ + passcode, + type, + payload, +}: { + passcode: string; + type: RequestType; + payload: unknown; +}) => { + const res = {} as ResponsePayload; + + let privateKey; + try { + privateKey = await utils.getPrivateKey(storage, Networks.aptos, passcode); + } catch { + res.responseCode = ResponseCode.WRONG_PASSCODE; + return res; + } + + try { + const { provider } = engine.getContext(Networks.aptos); + switch (type) { + case RequestType.UPDATE_DIRECT_TRANSFER_ON_APTOS: + res.signatureString = await aptos.handleUpdateDirectTransfer( + provider, + privateKey, + payload as aptos.AptosDirectTransferPayload, + ); + break; + + case RequestType.CLAIM_TOKEN_ON_APTOS: + res.signatureString = await aptos.handleClaimToken( + provider, + privateKey, + payload as aptos.AptosClaimTokenPayload, + ); + break; + + default: + break; + } + + res.responseCode = ResponseCode.SUCCESS; + } catch { + res.responseCode = ResponseCode.ERROR; + } + + return res; +}; diff --git a/apps/wallet/src/utils/transaction/aptos/send/index.web.ts b/apps/wallet/src/utils/transaction/aptos/send/index.web.ts new file mode 100644 index 000000000..d615587ca --- /dev/null +++ b/apps/wallet/src/utils/transaction/aptos/send/index.web.ts @@ -0,0 +1,20 @@ +import type { RequestType } from '@walless/core'; +import { sendRequest } from 'bridge'; + +export const handleAptosOnChainAction = async ({ + passcode, + payload, + type, +}: { + passcode: string; + type: RequestType; + payload: unknown; +}) => { + const res = await sendRequest({ + type, + transaction: JSON.stringify(payload), + passcode, + }); + + return res; +}; From 167dc6a194a5d7ff6098a0393860cb23ddfb3c25 Mon Sep 17 00:00:00 2001 From: ltminhthu Date: Sat, 8 Jun 2024 15:34:24 +0700 Subject: [PATCH 6/8] refactor send transaction after adding tezos --- .../Send/InputTransaction/TokensTab.tsx | 4 + apps/wallet/src/features/Send/internal.ts | 68 ++++++- .../wallet/src/utils/transaction/construct.ts | 84 --------- apps/wallet/src/utils/transaction/fee.ts | 174 ++---------------- apps/wallet/src/utils/transaction/index.ts | 7 +- apps/wallet/src/utils/transaction/types.ts | 3 + 6 files changed, 86 insertions(+), 254 deletions(-) delete mode 100644 apps/wallet/src/utils/transaction/construct.ts diff --git a/apps/wallet/src/features/Send/InputTransaction/TokensTab.tsx b/apps/wallet/src/features/Send/InputTransaction/TokensTab.tsx index c96cc2e63..8f20ec6cd 100644 --- a/apps/wallet/src/features/Send/InputTransaction/TokensTab.tsx +++ b/apps/wallet/src/features/Send/InputTransaction/TokensTab.tsx @@ -6,6 +6,7 @@ import { Button, Select, Text, View } from '@walless/gui'; import type { TokenDocument } from '@walless/store'; import CheckedInput from 'components/CheckedInput'; import { NavButton } from 'components/NavButton'; +import { keyState } from 'state/keys'; import { useTokens } from 'utils/hooks'; import { checkValidAddress } from 'utils/transaction'; @@ -89,6 +90,9 @@ export const TokensTab: FC = ({ onContinue }) => { useEffect(() => { if (type === 'token' && token) { txActions.update({ network: token.network }); + const publicKeys = Array.from(keyState.map.values()); + const key = publicKeys.find((k) => k.network === network); + if (key) txActions.update({ sender: key._id }); } }, [type, token]); diff --git a/apps/wallet/src/features/Send/internal.ts b/apps/wallet/src/features/Send/internal.ts index a29d4501d..d8c62814b 100644 --- a/apps/wallet/src/features/Send/internal.ts +++ b/apps/wallet/src/features/Send/internal.ts @@ -12,27 +12,33 @@ import type { TokenDocument, } from '@walless/store'; import { engine } from 'engine'; -import type { SolanaContext, SuiContext } from 'engine/runners'; +import type { SolanaContext, SuiContext, TezosContext } from 'engine/runners'; import { throttle } from 'lodash'; import { showError } from 'modals/Error'; import { ModalId } from 'modals/types'; import { solMint } from 'utils/constants'; import { useSnapshot } from 'utils/hooks'; import { - createAndSendSolanaTransaction, createAndSendSuiTransaction, - getGasilonFee, - getSolanaTransactionFee, + createAndSendTezosTransaction, getSuiTransactionFee, hasEnoughBalanceToMakeTx, } from 'utils/transaction'; -import { constructSuiSendTokenTransaction } from 'utils/transaction/construct'; +import { createAndSendSolanaTransaction } from 'utils/transaction'; +import { constructSuiSendTokenTransaction } from 'utils/transaction'; +import { + getGasilonFee, + getSolanaTransactionFee, +} from 'utils/transaction/solana/send/fee'; import type { SolanaSendTransaction, SuiSendTransaction, + TezosSendTokenTransaction, } from 'utils/transaction/types'; import { proxy } from 'valtio'; +import { getTezosTransactionFee } from './../../utils/transaction/tezos/send/fee'; + const initialContext: TransactionContext = { type: 'token', amount: '', @@ -143,6 +149,18 @@ export const txActions = { break; } + case Networks.tezos: { + txActions.update({ feeLoading: true }); + const genericTransaction = await createGenericTransaction(); + const { connection } = engine.getContext(Networks.tezos); + const fee = await getTezosTransactionFee( + genericTransaction as TezosSendTokenTransaction, + connection, + ); + txActions.update({ feeAmount: fee, feeLoading: false }); + break; + } + default: { txActions.update({ feeAmount: 0 }); break; @@ -203,6 +221,27 @@ export const txActions = { }); onComplete?.(); } + } else if (txContext.tx.network === Networks.tezos) { + const payload = { + sender: genericTransaction?.sender, + receiver: genericTransaction?.receiver, + amount: genericTransaction?.amount, + token: genericTransaction?.token, + } as TezosSendTokenTransaction; + + const res = await createAndSendTezosTransaction(payload, passcode); + + if (res.responseCode === ResponseCode.WRONG_PASSCODE) { + showError({ errorText: 'Passcode is NOT matched' }); + } else if (res.responseCode === ResponseCode.SUCCESS) { + const signature = res.signatureString; + txActions.update({ signature }); + txActions.update({ time: new Date() }); + txActions.update({ + status: res.isSuccessful ? 'success' : 'failed', + }); + onComplete?.(); + } } return true; @@ -268,17 +307,32 @@ const createGenericTransaction = async () => { } else { throw new Error('Transfer Sui NFT not supported'); } + } else if (network === Networks.tezos) { + const { token } = txContext.tx as TokenTransactionContext; + transaction = { + sender, + receiver, + token, + amount: parseInt(amount) || 0, + network, + type, + } as TezosSendTokenTransaction; } else { throw new Error('Unsupported network'); } const hasEnoughBalance = await hasEnoughBalanceToMakeTx(transaction); + console.log('transaction', transaction); + if (!hasEnoughBalance) { + console.log('100'); showError({ errorText: 'Insufficient balance to send' }); return; } + console.log('transaction', transaction); + return transaction; }; @@ -340,3 +394,7 @@ export type SuiTransactionContext = SuiTokenTransactionContext & SuiCollectibleTransactionContext & { signature?: string; }; + +export type TezosTransactionContext = TokenTransactionContext & { + signature?: string; +}; diff --git a/apps/wallet/src/utils/transaction/construct.ts b/apps/wallet/src/utils/transaction/construct.ts deleted file mode 100644 index 238ad2583..000000000 --- a/apps/wallet/src/utils/transaction/construct.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type { SuiClient } from '@mysten/sui.js/client'; -import type { VersionedTransaction } from '@solana/web3.js'; -import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; -import { Networks } from '@walless/core'; -import { solana, sui } from '@walless/network'; -import { engine } from 'engine'; -import type { SolanaContext } from 'engine/runners'; -import { solMint } from 'utils/constants'; - -import type { - SolanaSendNftTransaction, - SolanaSendTokenTransaction, - SuiSendTokenTransaction, -} from './types'; - -export const constructSolanaSendTokenTransaction = async ( - transaction: Omit< - SolanaSendTokenTransaction, - 'type' | 'network' | 'fee' | 'tokenForFee' - >, -): Promise => { - const { - sender: senderStr, - receiver: receiverStr, - token, - amount, - } = transaction; - - const sender = new PublicKey(senderStr); - const receiver = new PublicKey(receiverStr); - - const { connection } = engine.getContext(Networks.solana); - - const isNativeTransaction = token.mint == solMint; - if (isNativeTransaction) { - return await solana.constructSendSOLTransaction(connection, { - sender, - receiver, - amount: amount * LAMPORTS_PER_SOL, - }); - } else { - return await solana.constructSendSPLTokenTransaction(connection, { - sender, - receiver, - amount: Math.round(amount * 10 ** token.decimals), - mint: new PublicKey(token.mint), - }); - } -}; - -export const constructSolanaSendNftTransaction = async ( - initTransaction: Omit< - SolanaSendNftTransaction, - 'type' | 'network' | 'fee' | 'tokenForFee' - >, -): Promise => { - const { connection } = engine.getContext(Networks.solana); - const { sender, receiver, amount, nft } = initTransaction; - - const transaction = await solana.constructSendNftTransaction(connection, { - sender: new PublicKey(sender), - receiver: new PublicKey(receiver), - amount: amount, - mint: new PublicKey(nft.mint), - }); - - return transaction; -}; - -export const constructSuiSendTokenTransaction = async ( - client: SuiClient, - initTransaction: SuiSendTokenTransaction, -) => { - const { tokenForFee, amount, receiver, sender, token } = initTransaction; - return sui.constructSuiTransactionBlock( - client, - token.coinType, - token.decimals, - tokenForFee.coinType, - amount, - receiver, - sender, - ); -}; diff --git a/apps/wallet/src/utils/transaction/fee.ts b/apps/wallet/src/utils/transaction/fee.ts index 45b52f045..3c57bffdb 100644 --- a/apps/wallet/src/utils/transaction/fee.ts +++ b/apps/wallet/src/utils/transaction/fee.ts @@ -1,32 +1,17 @@ -import type { GasCostSummary } from '@mysten/sui.js/client'; -import type { TransactionBlock } from '@mysten/sui.js/transactions'; -import { SUI_DECIMALS } from '@mysten/sui.js/utils'; -import { ACCOUNT_SIZE, getAssociatedTokenAddressSync } from '@solana/spl-token'; -import type { VersionedTransaction } from '@solana/web3.js'; -import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; -import type { SolanaCollectible, SolanaToken } from '@walless/core'; -import { logger, Networks } from '@walless/core'; -import { solana } from '@walless/network'; -import { withSetComputeUnitPrice } from '@walless/network/src/solana'; -import type { NftDocument, TokenDocument } from '@walless/store'; -import base58 from 'bs58'; +import { Networks } from '@walless/core'; import { engine } from 'engine'; -import type { SolanaContext, SuiContext } from 'engine/runners'; -import { environment } from 'utils/config'; +import type { SuiContext } from 'engine/runners'; import { solMint } from 'utils/constants'; -import { - constructSolanaSendNftTransaction, - constructSolanaSendTokenTransaction, - constructSuiSendTokenTransaction, -} from './construct'; -import { getGasilonConfig } from './gasilon'; +import { getGasilonFee, getSolanaTransactionFee } from './solana'; +import { constructSuiSendTokenTransaction, getSuiTransactionFee } from './sui'; import type { SendNftTransaction, SendTokenTransaction, SolanaSendNftTransaction, SolanaSendTokenTransaction, SuiSendTransaction, + TezosSendTokenTransaction, } from './types'; export const hasEnoughBalanceToMakeTx = async ( @@ -35,7 +20,8 @@ export const hasEnoughBalanceToMakeTx = async ( | SendNftTransaction | SolanaSendTokenTransaction | SolanaSendNftTransaction - | SuiSendTransaction, + | SuiSendTransaction + | TezosSendTokenTransaction, ) => { const { network } = initTransaction; @@ -85,150 +71,12 @@ export const hasEnoughBalanceToMakeTx = async ( return false; } } + case Networks.tezos: { + const tezosTransaction = initTransaction as TezosSendTokenTransaction; + return tezosTransaction.token.balance > tezosTransaction.amount; + } default: { return false; } } }; - -export type GetSolanaFeeConfig = { - type: 'token' | 'nft'; - sender: string; - receiver: string; - nft?: NftDocument; - token?: TokenDocument; - tokenForFee: TokenDocument; -}; - -export const getSolanaTransactionFee = async ( - initTransaction: GetSolanaFeeConfig, -): Promise => { - const { type, sender, receiver, token, nft } = initTransaction; - - let transaction; - if (type === 'token') { - if (!token) throw Error("require token when type is 'token'"); - transaction = await constructSolanaSendTokenTransaction({ - sender, - receiver, - token, - amount: 0, - }); - } else { - if (!nft) throw Error("require nft when type is 'nft'"); - transaction = await constructSolanaSendNftTransaction({ - sender, - receiver, - nft, - amount: 0, - }); - } - - const { connection } = engine.getContext(Networks.solana); - - if (!transaction) return 0; - transaction = await withSetComputeUnitPrice(transaction, connection); - - const message = (transaction as VersionedTransaction).message; - const transactionFeePromise = connection - .getFeeForMessage(message) - .then((res) => res.value || 0); - - const rentFeePromise = (async () => { - const { receiver } = initTransaction; - let mint; - if (initTransaction.type === 'token') { - const { token } = initTransaction as SolanaSendTokenTransaction; - if (token.mint === solMint) return 0; - mint = token.mint; - } else { - const { nft } = initTransaction as SolanaSendNftTransaction; - mint = nft.mint; - } - - const receiverATAddress = getAssociatedTokenAddressSync( - new PublicKey(mint), - new PublicKey(receiver), - ); - - const receiverATAccount = - await connection.getAccountInfo(receiverATAddress); - - if (!receiverATAccount) { - return await connection.getMinimumBalanceForRentExemption(ACCOUNT_SIZE); - } - - return 0; - })(); - - const [txFee, rentFee] = await Promise.all([ - transactionFeePromise, - rentFeePromise, - ]); - - return (txFee + rentFee) / LAMPORTS_PER_SOL; -}; - -export type GetGasilonFeeConfig = { - sender: string; - receiver: string; - mint: string; - feeMint: string; -}; - -export const getGasilonFee = async ({ - sender, - receiver, - mint, - feeMint, -}: GetGasilonFeeConfig): Promise => { - const config = await getGasilonConfig(); - if (!config) return 0; - - const { connection } = engine.getContext(Networks.solana); - const transaction = await solana.constructGasilonTransaction(connection, { - sender: new PublicKey(sender), - receiver: new PublicKey(receiver), - feePayer: new PublicKey(config.feePayer), - amount: 0, // mock amount for get gee - fee: 0, // mock amount for get gee - mint: new PublicKey(mint), - feeMint: new PublicKey(feeMint), - }); - - try { - const res = await fetch(`${environment.GASILON_ENDPOINT}/solana/getFee`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - transaction: base58.encode(transaction.serialize()), - }), - }); - - const data = await res.json(); - - return data.totalByFeeToken; - } catch (error) { - logger.error('failed to get gasilon transaction fee', error); - return 0; - } -}; - -export const getSuiTransactionFee = async (txb: TransactionBlock) => { - const { client } = engine.getContext(Networks.sui); - const builtTxb = await txb.build({ client }).catch(console.log); - if (!builtTxb) throw new Error('unable to build txb'); - const dryrunResult = await client.dryRunTransactionBlock({ - transactionBlock: builtTxb, - }); - - return calculateSuiGas(dryrunResult?.effects.gasUsed); -}; - -const calculateSuiGas = (gas: GasCostSummary): number => { - const gasBigInt = - BigInt(gas.computationCost) + - BigInt(gas.storageCost) - - BigInt(gas.storageRebate); - return Number(gasBigInt) / 10 ** SUI_DECIMALS; -}; diff --git a/apps/wallet/src/utils/transaction/index.ts b/apps/wallet/src/utils/transaction/index.ts index d3a9c85a3..207da1fde 100644 --- a/apps/wallet/src/utils/transaction/index.ts +++ b/apps/wallet/src/utils/transaction/index.ts @@ -1,5 +1,8 @@ +export * from './aptos'; export * from './common'; export * from './fee'; export * from './gasilon'; -export * from './send'; -export * from './swap'; +export * from './solana'; +export * from './sui'; +export * from './tezos'; +export * from './types'; diff --git a/apps/wallet/src/utils/transaction/types.ts b/apps/wallet/src/utils/transaction/types.ts index c586c76e3..247e7e815 100644 --- a/apps/wallet/src/utils/transaction/types.ts +++ b/apps/wallet/src/utils/transaction/types.ts @@ -5,6 +5,7 @@ import type { SolanaCollectible, SolanaToken, SuiToken, + TezosToken, Token, } from '@walless/core'; import type { NftDocument, TokenDocument } from '@walless/store'; @@ -54,3 +55,5 @@ export type SuiSendTokenTransaction = SendTokenTransaction & }; export type SuiSendTransaction = SuiSendTokenTransaction; + +export type TezosSendTokenTransaction = SendTokenTransaction; From 6f7c74355ce93aedcf4a5e5fe3e9a496cc84917e Mon Sep 17 00:00:00 2001 From: ltminhthu Date: Sat, 8 Jun 2024 15:34:39 +0700 Subject: [PATCH 7/8] update import of swapping on solana --- apps/wallet/src/features/Swap/ConfirmPasscode/index.tsx | 3 ++- apps/wallet/src/features/Swap/InputSwap/ToToken.tsx | 2 +- apps/wallet/src/features/Swap/context.ts | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/wallet/src/features/Swap/ConfirmPasscode/index.tsx b/apps/wallet/src/features/Swap/ConfirmPasscode/index.tsx index a6a4382ba..482b2504e 100644 --- a/apps/wallet/src/features/Swap/ConfirmPasscode/index.tsx +++ b/apps/wallet/src/features/Swap/ConfirmPasscode/index.tsx @@ -11,8 +11,9 @@ import type { SolanaContext } from 'engine/runners'; import { showError } from 'modals/Error'; import assets from 'utils/assets'; import { nativeModules } from 'utils/native'; -import { constructSwapTransaction, getAliasedMint } from 'utils/transaction'; import { signAndSendTransaction } from 'utils/transaction/solana'; +import { getAliasedMint } from 'utils/transaction/solana/swap'; +import { constructSwapTransaction } from 'utils/transaction/solana/swap'; import { swapActions, swapContext } from '../context'; diff --git a/apps/wallet/src/features/Swap/InputSwap/ToToken.tsx b/apps/wallet/src/features/Swap/InputSwap/ToToken.tsx index c77e3e646..78098a2fc 100644 --- a/apps/wallet/src/features/Swap/InputSwap/ToToken.tsx +++ b/apps/wallet/src/features/Swap/InputSwap/ToToken.tsx @@ -7,7 +7,7 @@ import { useDebouncedCallback } from 'use-debounce'; import { parseWithDecimals } from 'utils/format'; import type { JupiterToken } from 'utils/hooks'; import { useSnapshot } from 'utils/hooks'; -import { getAliasedMint, getSwapQuote } from 'utils/transaction'; +import { getAliasedMint, getSwapQuote } from 'utils/transaction/solana/swap'; import type { SwapContext } from '../context'; import { swapActions, swapContext } from '../context'; diff --git a/apps/wallet/src/features/Swap/context.ts b/apps/wallet/src/features/Swap/context.ts index 08daab7a7..5a233aaf9 100644 --- a/apps/wallet/src/features/Swap/context.ts +++ b/apps/wallet/src/features/Swap/context.ts @@ -4,8 +4,9 @@ import { AnimateDirections, BindDirections, modalActions } from '@walless/gui'; import type { TokenDocument } from '@walless/store'; import { ModalId } from 'modals/types'; import type { JupiterToken } from 'utils/hooks'; -import type { SwapQuote } from 'utils/transaction'; -import { checkValidAddress, getAliasedMint } from 'utils/transaction'; +import { checkValidAddress } from 'utils/transaction'; +import type { SwapQuote } from 'utils/transaction/solana/swap'; +import { getAliasedMint } from 'utils/transaction/solana/swap'; import { proxy } from 'valtio'; import SelectFromToken from './Select/SelectFromToken'; From 7ae963543d7ac34ac3af814ed4af6137ec6a3cf1 Mon Sep 17 00:00:00 2001 From: ltminhthu Date: Tue, 11 Jun 2024 12:59:39 +0700 Subject: [PATCH 8/8] disable getting fee because of private key required --- apps/wallet/src/features/Send/internal.ts | 30 ++++++++----------- .../src/utils/transaction/sui/send/fee.ts | 2 +- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/apps/wallet/src/features/Send/internal.ts b/apps/wallet/src/features/Send/internal.ts index d8c62814b..3d480404e 100644 --- a/apps/wallet/src/features/Send/internal.ts +++ b/apps/wallet/src/features/Send/internal.ts @@ -12,7 +12,7 @@ import type { TokenDocument, } from '@walless/store'; import { engine } from 'engine'; -import type { SolanaContext, SuiContext, TezosContext } from 'engine/runners'; +import type { SolanaContext, SuiContext } from 'engine/runners'; import { throttle } from 'lodash'; import { showError } from 'modals/Error'; import { ModalId } from 'modals/types'; @@ -37,8 +37,6 @@ import type { } from 'utils/transaction/types'; import { proxy } from 'valtio'; -import { getTezosTransactionFee } from './../../utils/transaction/tezos/send/fee'; - const initialContext: TransactionContext = { type: 'token', amount: '', @@ -149,17 +147,17 @@ export const txActions = { break; } - case Networks.tezos: { - txActions.update({ feeLoading: true }); - const genericTransaction = await createGenericTransaction(); - const { connection } = engine.getContext(Networks.tezos); - const fee = await getTezosTransactionFee( - genericTransaction as TezosSendTokenTransaction, - connection, - ); - txActions.update({ feeAmount: fee, feeLoading: false }); - break; - } + // case Networks.tezos: { + // txActions.update({ feeLoading: true }); + // const genericTransaction = await createGenericTransaction(); + // const { connection } = engine.getContext(Networks.tezos); + // const fee = await getTezosTransactionFee( + // genericTransaction as TezosSendTokenTransaction, + // connection, + // ); + // txActions.update({ feeAmount: fee, feeLoading: false }); + // break; + // } default: { txActions.update({ feeAmount: 0 }); @@ -323,16 +321,12 @@ const createGenericTransaction = async () => { const hasEnoughBalance = await hasEnoughBalanceToMakeTx(transaction); - console.log('transaction', transaction); - if (!hasEnoughBalance) { console.log('100'); showError({ errorText: 'Insufficient balance to send' }); return; } - console.log('transaction', transaction); - return transaction; }; diff --git a/apps/wallet/src/utils/transaction/sui/send/fee.ts b/apps/wallet/src/utils/transaction/sui/send/fee.ts index f3f802845..a9364c143 100644 --- a/apps/wallet/src/utils/transaction/sui/send/fee.ts +++ b/apps/wallet/src/utils/transaction/sui/send/fee.ts @@ -1,6 +1,6 @@ import type { GasCostSummary } from '@mysten/sui.js/client'; -import { SUI_DECIMALS } from '@mysten/sui.js/dist/cjs/utils'; import type { TransactionBlock } from '@mysten/sui.js/transactions'; +import { SUI_DECIMALS } from '@mysten/sui.js/utils'; import { Networks } from '@walless/core'; import { engine } from 'engine'; import type { SuiContext } from 'engine/runners';