diff --git a/apps/namadillo/src/App/Masp/MaspShield.tsx b/apps/namadillo/src/App/Masp/MaspShield.tsx index d02f40f5b5..6bb6e050f6 100644 --- a/apps/namadillo/src/App/Masp/MaspShield.tsx +++ b/apps/namadillo/src/App/Masp/MaspShield.tsx @@ -145,9 +145,9 @@ export const MaspShield: React.FC = () => { displayAmount, rpc: txResponse.msg.payload.chain.rpcUrl, chainId: txResponse.msg.payload.chain.chainId, - hash: txResponse.encodedTx.txs[0]?.hash, - feePaid: txResponse.encodedTx.wrapperTxProps.feeAmount, - resultTxHash: txResponse.encodedTx.txs[0]?.innerTxHashes[0], + hash: txResponse.encodedTxData.txs[0]?.hash, + feePaid: txResponse.encodedTxData.wrapperTxProps.feeAmount, + resultTxHash: txResponse.encodedTxData.txs[0]?.innerTxHashes[0], status: "success", createdAt: new Date(), updatedAt: new Date(), diff --git a/apps/namadillo/src/App/Masp/MaspUnshield.tsx b/apps/namadillo/src/App/Masp/MaspUnshield.tsx index bcc248e160..e7a3a8392c 100644 --- a/apps/namadillo/src/App/Masp/MaspUnshield.tsx +++ b/apps/namadillo/src/App/Masp/MaspUnshield.tsx @@ -150,9 +150,9 @@ export const MaspUnshield: React.FC = () => { displayAmount, rpc: txResponse.msg.payload.chain.rpcUrl, chainId: txResponse.msg.payload.chain.chainId, - hash: txResponse.encodedTx.txs[0]?.hash, - feePaid: txResponse.encodedTx.wrapperTxProps.feeAmount, - resultTxHash: txResponse.encodedTx.txs[0]?.innerTxHashes[0], + hash: txResponse.encodedTxData.txs[0]?.hash, + feePaid: txResponse.encodedTxData.wrapperTxProps.feeAmount, + resultTxHash: txResponse.encodedTxData.txs[0]?.innerTxHashes[0], status: "success", createdAt: new Date(), updatedAt: new Date(), diff --git a/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx b/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx index 408cf46670..6dd496fb64 100644 --- a/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx +++ b/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx @@ -12,6 +12,11 @@ import { allDefaultAccountsAtom } from "atoms/accounts"; import { namadaTransparentAssetsAtom } from "atoms/balance/atoms"; import { chainParametersAtom } from "atoms/chain/atoms"; import { applicationFeaturesAtom, rpcUrlAtom } from "atoms/settings"; +import { + shieldedTxAtom, + shieldTxAtom, + unshieldTxAtom, +} from "atoms/shield/atoms"; import { createShieldedTransferAtom, createShieldingTransferAtom, @@ -55,6 +60,12 @@ export const NamadaTransfer: React.FC = () => { const { data: availableAssetsData, isLoading: isLoadingAssets } = useAtomValue(namadaTransparentAssetsAtom); + // TODO: remove this block once transfer logic is unified + const { mutateAsync: performUnshieldTransfer } = useAtomValue(unshieldTxAtom); + const { mutateAsync: performShieldingTransfer } = useAtomValue(shieldTxAtom); + const { mutateAsync: performShieldedTransfer } = useAtomValue(shieldedTxAtom); + // end of block + const { transactions: myTransactions, findByHash, @@ -76,11 +87,12 @@ export const NamadaTransfer: React.FC = () => { }, [availableAssetsData]); const chainId = chainParameters.data?.chainId; - const sourceAddress = defaultAccounts.data?.find((account) => + const account = defaultAccounts.data?.find((account) => shielded ? account.type === AccountType.ShieldedKeys : account.type !== AccountType.ShieldedKeys - )?.address; + ); + const sourceAddress = account?.address; const selectedAssetAddress = searchParams.get(params.asset) || undefined; const selectedAsset = selectedAssetAddress ? availableAssets?.[selectedAssetAddress] : undefined; @@ -207,7 +219,25 @@ export const NamadaTransfer: React.FC = () => { chainId, }); - const txResponse = await performTransfer({ memo }); + // TODO: once transfer logic is unified, this block should be replaced by a single + // call to the performTransfer function + const shieldedTransferParams = { + sourceAddress: account!.pseudoExtendedKey || sourceAddress, + destinationAddress: target, + tokenAddress: selectedAsset.originalAddress, + amount: txAmount, + gasConfig, + }; + const txResponse = + txKind === "ShieldedToTransparent" ? + await performUnshieldTransfer(shieldedTransferParams) + : txKind === "TransparentToShielded" ? + await performShieldingTransfer(shieldedTransferParams) + : txKind === "ShieldedToShielded" ? + await performShieldedTransfer(shieldedTransferParams) + : await performTransfer({ memo }); + // end of block + if (txResponse) { const txList = createTransferDataFromNamada( txKind, diff --git a/apps/namadillo/src/App/WorkerTest.tsx b/apps/namadillo/src/App/WorkerTest.tsx index 7a26ea3a00..bda2cd16b6 100644 --- a/apps/namadillo/src/App/WorkerTest.tsx +++ b/apps/namadillo/src/App/WorkerTest.tsx @@ -164,7 +164,6 @@ export function WorkerTest(): JSX.Element { unshieldingProps: [shieldingMsgValue], chain: chain!, vks: vks!, - indexerUrl, }, }; @@ -227,7 +226,7 @@ export function WorkerTest(): JSX.Element { gasPrice: BigNumber(0), gasToken: "tnam1", }, - shieldingProps: [shieldingMsgValue], + props: [shieldingMsgValue], chain: chain!, vks: vks!, }, diff --git a/apps/namadillo/src/atoms/shield/atoms.ts b/apps/namadillo/src/atoms/shield/atoms.ts index 093523f468..299622741f 100644 --- a/apps/namadillo/src/atoms/shield/atoms.ts +++ b/apps/namadillo/src/atoms/shield/atoms.ts @@ -4,7 +4,9 @@ import { indexerUrlAtom, rpcUrlAtom } from "atoms/settings"; import { NamadaKeychain } from "hooks/useNamadaKeychain"; import { atomWithMutation } from "jotai-tanstack-query"; import { + ShieldedTransferParams, ShieldTransferParams, + submitShieldedTx, submitShieldTx, submitUnshieldTx, UnshieldTransferParams, @@ -28,7 +30,6 @@ export const unshieldTxAtom = atomWithMutation((get) => { const rpcUrl = get(rpcUrlAtom); const { data: account } = get(defaultAccountAtom); const { data: chain } = get(chainAtom); - const indexerUrl = get(indexerUrlAtom); return { mutationKey: ["unshield-tx"], @@ -43,7 +44,31 @@ export const unshieldTxAtom = atomWithMutation((get) => { rpcUrl, account!, chain!, - indexerUrl, + params, + disposableSigner + ); + }, + }; +}); + +export const shieldedTxAtom = atomWithMutation((get) => { + const rpcUrl = get(rpcUrlAtom); + const { data: account } = get(defaultAccountAtom); + const { data: chain } = get(chainAtom); + + return { + mutationKey: ["shielded-tx"], + mutationFn: async (params: ShieldedTransferParams) => { + const namada = await new NamadaKeychain().get(); + const disposableSigner = await namada?.genDisposableKeypair(); + if (!disposableSigner) { + throw new Error("No signer available"); + } + + return submitShieldedTx( + rpcUrl, + account!, + chain!, params, disposableSigner ); diff --git a/apps/namadillo/src/atoms/shield/services.ts b/apps/namadillo/src/atoms/shield/services.ts index 049cda1fec..8d82d6aa74 100644 --- a/apps/namadillo/src/atoms/shield/services.ts +++ b/apps/namadillo/src/atoms/shield/services.ts @@ -1,6 +1,7 @@ import { Account, GenDisposableSignerResponse, + ShieldedTransferMsgValue, ShieldingTransferMsgValue, UnshieldingTransferMsgValue, } from "@namada/types"; @@ -8,7 +9,7 @@ import BigNumber from "bignumber.js"; import * as Comlink from "comlink"; import { EncodedTxData, signTx } from "lib/query"; import { Address, ChainSettings, GasConfig } from "types"; -import { Shield, Unshield } from "workers/MaspTxMessages"; +import { Shield, ShieldedTransfer, Unshield } from "workers/MaspTxMessages"; import { Worker as MaspTxWorkerApi, registerTransferHandlers as maspTxRegisterTransferHandlers, @@ -31,6 +32,14 @@ export type UnshieldTransferParams = { gasConfig: GasConfig; }; +export type ShieldedTransferParams = { + sourceAddress: Address; + destinationAddress: Address; + tokenAddress: Address; + amount: BigNumber; + gasConfig: GasConfig; +}; + export const submitShieldTx = async ( rpcUrl: string, account: Account, @@ -39,7 +48,7 @@ export const submitShieldTx = async ( params: ShieldTransferParams ): Promise<{ msg: Shield; - encodedTx: EncodedTxData; + encodedTxData: EncodedTxData; }> => { const { sourceAddress: source, @@ -87,19 +96,18 @@ export const submitShieldTx = async ( worker.terminate(); - return { msg, encodedTx }; + return { msg, encodedTxData: encodedTx }; }; export const submitUnshieldTx = async ( rpcUrl: string, account: Account, chain: ChainSettings, - indexerUrl: string, params: UnshieldTransferParams, disposableSigner: GenDisposableSignerResponse ): Promise<{ msg: Unshield; - encodedTx: EncodedTxData; + encodedTxData: EncodedTxData; }> => { const { sourceAddress: source, @@ -133,24 +141,85 @@ export const submitUnshieldTx = async ( gasConfig, unshieldingProps: [unshieldingMsgValue], chain, - indexerUrl, vks: [], }, }; - const { payload: encodedTx } = await unshieldWorker.unshield(msg); + const { payload: encodedTxData } = await unshieldWorker.unshield(msg); - const signedTxs = await signTx(encodedTx, disposableSigner.address); + const signedTxs = await signTx(encodedTxData, disposableSigner.address); await unshieldWorker.broadcast({ type: "broadcast", payload: { - encodedTx, + encodedTx: encodedTxData, + signedTxs, + }, + }); + + worker.terminate(); + + return { msg, encodedTxData }; +}; + +export const submitShieldedTx = async ( + rpcUrl: string, + account: Account, + chain: ChainSettings, + params: ShieldedTransferParams, + disposableSigner: GenDisposableSignerResponse +): Promise<{ + msg: ShieldedTransfer; + encodedTxData: EncodedTxData; +}> => { + const { + sourceAddress: source, + destinationAddress: target, + tokenAddress: token, + amount, + gasConfig, + } = params; + + maspTxRegisterTransferHandlers(); + const worker = new MaspTxWorker(); + const workerApi = Comlink.wrap(worker); + await workerApi.init({ + type: "init", + payload: { rpcUrl, token, maspIndexerUrl: "" }, + }); + + const shieldedTransferMsgValue = new ShieldedTransferMsgValue({ + gasSpendingKey: source, + data: [{ source, target, token, amount }], + }); + + const msg: ShieldedTransfer = { + type: "shielded-transfer", + payload: { + account: { + ...account, + publicKey: disposableSigner.publicKey, + }, + gasConfig, + props: [shieldedTransferMsgValue], + chain, + vks: [], + }, + }; + + const { payload: encodedTxData } = await workerApi.shieldedTransfer(msg); + + const signedTxs = await signTx(encodedTxData, disposableSigner.address); + + await workerApi.broadcast({ + type: "broadcast", + payload: { + encodedTx: encodedTxData, signedTxs, }, }); worker.terminate(); - return { msg, encodedTx }; + return { msg, encodedTxData }; }; diff --git a/apps/namadillo/src/lib/transactions.ts b/apps/namadillo/src/lib/transactions.ts index 5b2cdd674d..ff2b59a0ab 100644 --- a/apps/namadillo/src/lib/transactions.ts +++ b/apps/namadillo/src/lib/transactions.ts @@ -24,7 +24,7 @@ import { TransferTransactionData, } from "types"; import { toDisplayAmount } from "utils"; -import { TransactionPair } from "./query"; +import { EncodedTxData, TransactionPair } from "./query"; export const getEventAttribute = ( tx: DeliverTxResponse, @@ -167,7 +167,13 @@ export const createTransferDataFromNamada = ( | TransactionPair | TransactionPair | TransactionPair - | TransactionPair, + | TransactionPair + // TODO: remove this block after transfer logic unification + | { encodedTxData: EncodedTxData } + | { encodedTxData: EncodedTxData } + | { encodedTxData: EncodedTxData }, + // end of block + memo?: string ): TransferTransactionData[] => { if (!txResponse?.encodedTxData?.txs?.length) { diff --git a/apps/namadillo/src/workers/MaspTxMessages.ts b/apps/namadillo/src/workers/MaspTxMessages.ts index efa3883d8e..41426c4edb 100644 --- a/apps/namadillo/src/workers/MaspTxMessages.ts +++ b/apps/namadillo/src/workers/MaspTxMessages.ts @@ -37,7 +37,6 @@ type UnshieldPayload = { gasConfig: GasConfig; unshieldingProps: UnshieldingTransferMsgValue[]; chain: ChainSettings; - indexerUrl: string; vks: DatedViewingKey[]; }; export type Unshield = WebWorkerMessage<"unshield", UnshieldPayload>; @@ -49,7 +48,7 @@ export type UnshieldDone = WebWorkerMessage< type ShieldedTransferPayload = { account: Account; gasConfig: GasConfig; - shieldingProps: ShieldedTransferMsgValue[]; + props: ShieldedTransferMsgValue[]; chain: ChainSettings; vks: DatedViewingKey[]; }; diff --git a/apps/namadillo/src/workers/MaspTxWorker.ts b/apps/namadillo/src/workers/MaspTxWorker.ts index e2e280d369..cbbbf5d433 100644 --- a/apps/namadillo/src/workers/MaspTxWorker.ts +++ b/apps/namadillo/src/workers/MaspTxWorker.ts @@ -125,7 +125,7 @@ async function shieldedTransfer( sdk: Sdk, payload: ShieldedTransfer["payload"] ): Promise> { - const { account, gasConfig, chain, shieldingProps, vks } = payload; + const { account, gasConfig, chain, props, vks } = payload; await sdk.rpc.shieldedSync(vks); await sdk.masp.loadMaspParams(""); @@ -135,7 +135,7 @@ async function shieldedTransfer( account, gasConfig, chain, - shieldingProps, + props, sdk.tx.buildShieldedTransfer, true );