From 23551bce3f5e26f686e04c26352e5c7353b2c699 Mon Sep 17 00:00:00 2001 From: Mateusz Jasiuk Date: Mon, 17 Feb 2025 12:23:54 +0100 Subject: [PATCH 1/2] feat: wip pay fees for masp with transparent balance --- .../src/background/keyring/keyring.ts | 2 +- apps/namadillo/src/App/Common/GasFeeModal.tsx | 26 ++ apps/namadillo/src/App/WorkerTest.tsx | 241 ------------------ apps/namadillo/src/atoms/transfer/atoms.ts | 7 +- apps/namadillo/src/atoms/transfer/services.ts | 11 +- apps/namadillo/src/hooks/useTransaction.tsx | 6 +- apps/namadillo/src/hooks/useTransactionFee.ts | 7 + apps/namadillo/src/hooks/useTransfer.ts | 4 +- 8 files changed, 45 insertions(+), 259 deletions(-) delete mode 100644 apps/namadillo/src/App/WorkerTest.tsx diff --git a/apps/extension/src/background/keyring/keyring.ts b/apps/extension/src/background/keyring/keyring.ts index 9dc26aa2f..c27cfc475 100644 --- a/apps/extension/src/background/keyring/keyring.ts +++ b/apps/extension/src/background/keyring/keyring.ts @@ -796,7 +796,7 @@ export class KeyRing { const realAddress = disposableKey?.realAddress || signer; // If disposable key is provided, use it to map real address to spending key - const xsks = [await this.getSpendingKey(realAddress)]; + const xsks = [await this.getSpendingKey(disposableKey ? realAddress : signer)]; const { signing } = this.sdkService.getSdk(); diff --git a/apps/namadillo/src/App/Common/GasFeeModal.tsx b/apps/namadillo/src/App/Common/GasFeeModal.tsx index 3bbb5bf20..957142675 100644 --- a/apps/namadillo/src/App/Common/GasFeeModal.tsx +++ b/apps/namadillo/src/App/Common/GasFeeModal.tsx @@ -3,7 +3,9 @@ import { ActionButton, AmountInput, Modal, + Stack, StyledSelectBox, + ToggleButton, } from "@namada/components"; import { transparentBalanceAtom } from "atoms/accounts"; import { shieldedBalanceAtom } from "atoms/balance"; @@ -105,8 +107,10 @@ export const GasFeeModal = ({ gasConfig, gasEstimate, gasPriceTable, + gasSource, onChangeGasLimit, onChangeGasToken, + onChangeGasSource, } = feeProps; const sortByNativeToken = useSortByNativeToken(); @@ -207,6 +211,28 @@ export const GasFeeModal = ({ Balance Fee + +
Fee Token
+ + onChangeGasSource( + gasSource === "shielded" ? "transparent" : "shielded" + ) + } + containerProps={{ className: "gap-3 text-xs" }} + /> +
a.type === AccountType.ShieldedKeys && a.alias === account?.alias - ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window as any).shieldedSync = async (vk: string) => { - const worker = new ShieldedSyncWorker(); - const shieldedSyncWorker = Comlink.wrap(worker); - await shieldedSyncWorker.init({ - type: "init", - payload: { - rpcUrl, - token: token!, - }, - }); - - await shieldedSyncWorker.sync({ - type: "sync", - payload: { - vks: [{ key: vk, birthday: 0 }], - chainId: chain!.id, - }, - }); - - worker.terminate(); - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window as any).shield = async (target: string, amount: number) => { - registerTransferHandlers(); - const worker = new MaspTxWorker(); - const shieldWorker = Comlink.wrap(worker); - - await shieldWorker.init({ - type: "init", - payload: { - rpcUrl, - token: token!, - maspIndexerUrl, - }, - }); - - const shieldingMsgValue = new ShieldingTransferMsgValue({ - target, - data: [ - { - source: account?.address || "", - token: token!, - amount: BigNumber(amount), - }, - ], - }); - - const publicKeyRevealed = await isPublicKeyRevealed(account!.address); - const msg: Shield = { - type: "shield", - payload: { - publicKeyRevealed, - account: account!, - gasConfig: { - gasLimit: BigNumber(50000), - gasPriceInMinDenom: BigNumber(0), - gasToken: "tnam1", - }, - props: [shieldingMsgValue], - chain: chain!, - }, - }; - - const { payload: encodedTx } = await shieldWorker.shield(msg); - - const signedTxs = await signTx(encodedTx, account?.address || ""); - - await shieldWorker.broadcast({ - type: "broadcast", - payload: { - encodedTxData: encodedTx, - signedTxs, - }, - }); - - worker.terminate(); - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window as any).unshield = async (target: string, amount: number) => { - registerTransferHandlers(); - const worker = new MaspTxWorker(); - const shieldWorker = Comlink.wrap(worker); - - await shieldWorker.init({ - type: "init", - payload: { - rpcUrl, - token: token!, - maspIndexerUrl, - }, - }); - - const shieldingMsgValue = new UnshieldingTransferMsgValue({ - source: shieldedAccount!.pseudoExtendedKey!, - gasSpendingKey: shieldedAccount!.pseudoExtendedKey!, - data: [ - { - target, - token: token!, - amount: BigNumber(amount), - }, - ], - }); - - const disposableSigner = (await refetch()).data; - - const msg: Unshield = { - type: "unshield", - payload: { - account: { - ...account!, - publicKey: disposableSigner!.publicKey, - }, - gasConfig: { - gasLimit: BigNumber(100000), - gasPriceInMinDenom: BigNumber(0), - gasToken: "tnam1", - }, - props: [shieldingMsgValue], - chain: chain!, - }, - }; - - const { payload: encodedTx } = await shieldWorker.unshield(msg); - const signedTxs = await signTx(encodedTx, disposableSigner?.address || ""); - - await shieldWorker.broadcast({ - type: "broadcast", - payload: { - encodedTxData: encodedTx, - signedTxs, - }, - }); - - worker.terminate(); - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window as any).shielded = async (target: string, amount: number) => { - registerTransferHandlers(); - const worker = new MaspTxWorker(); - const shieldWorker = Comlink.wrap(worker); - - await shieldWorker.init({ - type: "init", - payload: { - rpcUrl, - token: token!, - maspIndexerUrl, - }, - }); - - const shieldingMsgValue = new ShieldedTransferMsgValue({ - gasSpendingKey: shieldedAccount!.pseudoExtendedKey!, - data: [ - { - source: shieldedAccount!.pseudoExtendedKey!, - target, - token: token!, - amount: BigNumber(amount), - }, - ], - }); - - const disposableSigner = (await refetch()).data; - - const msg: ShieldedTransfer = { - type: "shielded-transfer", - payload: { - account: { - ...account!, - publicKey: disposableSigner!.publicKey, - }, - gasConfig: { - gasLimit: BigNumber(50000), - gasPriceInMinDenom: BigNumber(0), - gasToken: "tnam1", - }, - props: [shieldingMsgValue], - chain: chain!, - }, - }; - - const { payload: encodedTx } = await shieldWorker.shieldedTransfer(msg); - const signedTxs = await signTx(encodedTx, disposableSigner?.address || ""); - - await shieldWorker.broadcast({ - type: "broadcast", - payload: { - encodedTxData: encodedTx, - signedTxs, - }, - }); - - worker.terminate(); - }; - - return
; -} diff --git a/apps/namadillo/src/atoms/transfer/atoms.ts b/apps/namadillo/src/atoms/transfer/atoms.ts index 4fe199da7..8dd557e2f 100644 --- a/apps/namadillo/src/atoms/transfer/atoms.ts +++ b/apps/namadillo/src/atoms/transfer/atoms.ts @@ -122,11 +122,6 @@ export const createUnshieldingTransferAtom = atomWithMutation((get) => { signer, memo, }: BuildTxAtomParams) => { - invariant( - signer, - "Disposable signer is required for unshielding transfers" - ); - await sync( allViewingKeys, chainId, @@ -191,7 +186,7 @@ const sync = async ( const { set } = getDefaultStore(); await shieldedSync({ rpcUrl, - maspIndexerUrl, + maspIndexerUrl: maspIndexerUrl || "", token: namTokenAddress, viewingKeys: allViewingKeys, chainId, diff --git a/apps/namadillo/src/atoms/transfer/services.ts b/apps/namadillo/src/atoms/transfer/services.ts index 09e6f62de..8c39d6949 100644 --- a/apps/namadillo/src/atoms/transfer/services.ts +++ b/apps/namadillo/src/atoms/transfer/services.ts @@ -199,15 +199,15 @@ export const createUnshieldingTransferTx = async ( props: UnshieldingTransferMsgValue[], gasConfig: GasConfig, rpcUrl: string, - disposableSigner: GenDisposableSignerResponse, + signer?: GenDisposableSignerResponse, memo?: string ): Promise | undefined> => { - const { publicKey: signerPublicKey } = disposableSigner; - const source = props[0]?.source; const destination = props[0]?.data[0]?.target; const token = props[0]?.data[0]?.token; const amount = props[0]?.data[0]?.amount; + const accountWithSigner = + signer ? { ...account, publicKey: signer.publicKey } : account; let bparams: BparamsMsgValue[] | undefined; @@ -231,10 +231,7 @@ export const createUnshieldingTransferTx = async ( const msg: Unshield = { type: "unshield", payload: { - account: { - ...account, - publicKey: signerPublicKey, - }, + account: accountWithSigner, gasConfig, props: [msgValue], chain, diff --git a/apps/namadillo/src/hooks/useTransaction.tsx b/apps/namadillo/src/hooks/useTransaction.tsx index 976acacfe..416fe96c0 100644 --- a/apps/namadillo/src/hooks/useTransaction.tsx +++ b/apps/namadillo/src/hooks/useTransaction.tsx @@ -42,7 +42,7 @@ export type UseTransactionPropsEvents = { export type UseTransactionProps = { params: T[]; createTxAtom: AtomType; - useDisposableSigner?: boolean; + canUseDisposableSigner?: boolean; eventType: TransactionEventsClasses; parsePendingTxNotification?: (tx: TransactionPair) => PartialNotification; parseErrorTxNotification?: () => PartialNotification; @@ -64,7 +64,7 @@ export type UseTransactionOutput = { export const useTransaction = ({ params, createTxAtom, - useDisposableSigner, + canUseDisposableSigner, eventType, parsePendingTxNotification, parseErrorTxNotification, @@ -121,6 +121,8 @@ export const useTransaction = ({ account?.address, "Extension not connected or no account is selected" ); + const useDisposableSigner = + canUseDisposableSigner && feeProps.gasSource === "shielded"; const txAdditionalParams = { ...additionalParams }; if (useDisposableSigner) { diff --git a/apps/namadillo/src/hooks/useTransactionFee.ts b/apps/namadillo/src/hooks/useTransactionFee.ts index 93ddd2ae2..eec8e180a 100644 --- a/apps/namadillo/src/hooks/useTransactionFee.ts +++ b/apps/namadillo/src/hooks/useTransactionFee.ts @@ -20,8 +20,10 @@ export type TransactionFeeProps = { isLoading: boolean; gasEstimate: GasEstimate | undefined; gasPriceTable: GasPriceTable | undefined; + gasSource: "shielded" | "transparent"; onChangeGasLimit: (value: BigNumber) => void; onChangeGasToken: (value: string) => void; + onChangeGasSource: (value: "shielded" | "transparent") => void; }; export const useTransactionFee = ( @@ -32,6 +34,9 @@ export const useTransactionFee = ( const [gasTokenValue, setGasTokenValue] = useState(); const userTransparentBalances = useAtomValue(transparentBalanceAtom); const userShieldedBalances = useAtomValue(shieldedBalanceAtom); + const [gasSource, setGasSource] = useState<"shielded" | "transparent">( + "shielded" + ); const isPublicKeyRevealed = useAtomValue(isPublicKeyRevealedAtom); const { data: nativeToken, isLoading: isLoadingNativeToken } = useAtomValue( @@ -149,7 +154,9 @@ export const useTransactionFee = ( isLoading, gasEstimate, gasPriceTable, + gasSource, onChangeGasLimit: setGasLimitValue, onChangeGasToken: setGasTokenValue, + onChangeGasSource: setGasSource, }; }; diff --git a/apps/namadillo/src/hooks/useTransfer.ts b/apps/namadillo/src/hooks/useTransfer.ts index 29ab3a27c..7a9127f5d 100644 --- a/apps/namadillo/src/hooks/useTransfer.ts +++ b/apps/namadillo/src/hooks/useTransfer.ts @@ -121,7 +121,7 @@ export const useTransfer = ({ ], }, ], - useDisposableSigner: true, + canUseDisposableSigner: true, ...commomProps, }); @@ -147,7 +147,7 @@ export const useTransfer = ({ data: [{ target, token, amount: baseDenomAmount }], }, ], - useDisposableSigner: true, + canUseDisposableSigner: true, ...commomProps, }); From 79a6270a4166a4a4e0dfac9ab69dff100d72ab9e Mon Sep 17 00:00:00 2001 From: Mateusz Jasiuk Date: Tue, 18 Feb 2025 15:21:21 +0100 Subject: [PATCH 2/2] feat: enable gas source switch only for unshielding and shielded transfers --- .../src/Approvals/ConfirmSignLedgerTx.tsx | 27 ++++++++--- apps/namadillo/src/App/Common/GasFeeModal.tsx | 48 ++++++++++++------- .../src/App/Common/TransactionFeeButton.tsx | 8 +--- apps/namadillo/src/App/Masp/MaspUnshield.tsx | 1 - .../src/App/NamadaTransfer/NamadaTransfer.tsx | 1 - .../src/App/Transfer/TransferDestination.tsx | 9 +--- .../src/App/Transfer/TransferModule.tsx | 3 -- apps/namadillo/src/hooks/useTransactionFee.ts | 17 +++++-- 8 files changed, 64 insertions(+), 50 deletions(-) diff --git a/apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx b/apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx index 81a346637..1a57a74e9 100644 --- a/apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx +++ b/apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx @@ -188,8 +188,6 @@ export const ConfirmSignLedgerTx: React.FC = ({ details }) => { setStepTwoDescription("Preparing transaction..."); try { - // TODO: we have to check if the signer is disposable or not - const accountDetails = await requester.sendMessage( Ports.Background, new QueryAccountDetailsMsg(signer) @@ -235,6 +233,11 @@ export const ConfirmSignLedgerTx: React.FC = ({ details }) => { .type ) ); + const disposableSigner = txDetails.reduce((acc, curr) => { + console.log("www", curr.wrapperFeePayer, accountDetails.address); + return acc || curr.wrapperFeePayer !== accountDetails.address; + }, false); + // For now we work under the assumption that we can't batch transfers from masp with other tx types const fromMasp = transferTypes.includes("Shielded") || @@ -257,7 +260,9 @@ export const ConfirmSignLedgerTx: React.FC = ({ details }) => { }); // Adds new signature to the collection await handleMaspSignTx(ledger, tx, zip32Path, maspSignatures); - } else { + } + + if (!disposableSigner) { const bip44Path = makeBip44Path(chains.namada.bip44.coinType, path); // Adds new signature to the collection await handleSignTx(ledger, tx, bip44Path, signatures); @@ -273,10 +278,18 @@ export const ConfirmSignLedgerTx: React.FC = ({ details }) => { Ports.Background, new ReplaceMaspSignaturesMsg(msgId, maspSignatures) ); - await requester.sendMessage( - Ports.Background, - new SubmitApprovedSignTxMsg(msgId, signer) - ); + + if (disposableSigner) { + await requester.sendMessage( + Ports.Background, + new SubmitApprovedSignTxMsg(msgId, signer) + ); + } else { + await requester.sendMessage( + Ports.Background, + new SubmitApprovedSignLedgerTxMsg(msgId, signatures) + ); + } } else { await requester.sendMessage( Ports.Background, diff --git a/apps/namadillo/src/App/Common/GasFeeModal.tsx b/apps/namadillo/src/App/Common/GasFeeModal.tsx index 957142675..dee46ebd6 100644 --- a/apps/namadillo/src/App/Common/GasFeeModal.tsx +++ b/apps/namadillo/src/App/Common/GasFeeModal.tsx @@ -5,6 +5,7 @@ import { Modal, Stack, StyledSelectBox, + Text, ToggleButton, } from "@namada/components"; import { transparentBalanceAtom } from "atoms/accounts"; @@ -97,7 +98,6 @@ const useBuildGasOption = ({ export const GasFeeModal = ({ feeProps, onClose, - isShielded = false, }: { feeProps: TransactionFeeProps; onClose: () => void; @@ -108,6 +108,7 @@ export const GasFeeModal = ({ gasEstimate, gasPriceTable, gasSource, + gasSourceSwitch, onChangeGasLimit, onChangeGasToken, onChangeGasSource, @@ -122,7 +123,7 @@ export const GasFeeModal = ({ const findUserBalanceByTokenAddress = (tokenAddres: string): BigNumber => { // TODO: we need to refactor userShieldedBalances to return Balance[] type instead const balances = - isShielded ? + gasSource === "shielded" ? shieldedAmount.data?.map((balance) => ({ minDenomAmount: balance.minDenomAmount, tokenAddress: balance.address, @@ -211,27 +212,38 @@ export const GasFeeModal = ({ Balance Fee + {gasSourceSwitch && gasSource !== "shielded" && ( + + Warning! Using fees from your transparent account will reveal data. +
+ To keep your data protected we recommend moving assets to the +
+ shield pool to pay fees. +
+ )}
Fee Token
- - onChangeGasSource( - gasSource === "shielded" ? "transparent" : "shielded" - ) - } - containerProps={{ className: "gap-3 text-xs" }} - /> + {gasSourceSwitch && ( + + onChangeGasSource( + gasSource === "shielded" ? "transparent" : "shielded" + ) + } + containerProps={{ className: "gap-3 text-xs" }} + /> + )}
{ const [modalOpen, setModalOpen] = useState(false); const chainAssetsMap = useAtomValue(chainAssetsMapAtom); @@ -54,11 +52,7 @@ export const TransactionFeeButton = ({ {modalOpen && ( - setModalOpen(false)} - isShielded={isShieldedTransfer} - /> + setModalOpen(false)} /> )} ); diff --git a/apps/namadillo/src/App/Masp/MaspUnshield.tsx b/apps/namadillo/src/App/Masp/MaspUnshield.tsx index 803cec80b..0608417dc 100644 --- a/apps/namadillo/src/App/Masp/MaspUnshield.tsx +++ b/apps/namadillo/src/App/Masp/MaspUnshield.tsx @@ -162,7 +162,6 @@ export const MaspUnshield: React.FC = () => { isShieldedAddress: false, }} feeProps={feeProps} - isShieldedTx={true} isSubmitting={isPerformingTransfer || isSuccess} errorMessage={generalErrorMessage} onSubmitTransfer={onSubmitTransfer} diff --git a/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx b/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx index 33233a536..5d4742f38 100644 --- a/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx +++ b/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx @@ -202,7 +202,6 @@ export const NamadaTransfer: React.FC = () => { feeProps={feeProps} currentStatus={currentStatus} currentStatusExplanation={currentStatusExplanation} - isShieldedTx={isSourceShielded} isSubmitting={ isPerformingTransfer || isTransferSuccessful || Boolean(completedAt) } diff --git a/apps/namadillo/src/App/Transfer/TransferDestination.tsx b/apps/namadillo/src/App/Transfer/TransferDestination.tsx index 54efd5f08..4764fb576 100644 --- a/apps/namadillo/src/App/Transfer/TransferDestination.tsx +++ b/apps/namadillo/src/App/Transfer/TransferDestination.tsx @@ -15,7 +15,6 @@ import { TokenAmountCard } from "./TokenAmountCard"; type TransferDestinationProps = { isShieldedAddress?: boolean; - isShieldedTx?: boolean; onChangeShielded?: (isShielded: boolean) => void; chain?: Chain; wallet?: WalletProvider; @@ -43,7 +42,6 @@ export const TransferDestination = ({ wallet, walletAddress, isShieldedAddress, - isShieldedTx = false, onChangeShielded, gasDisplayAmount, gasAsset, @@ -173,12 +171,7 @@ export const TransferDestination = ({ {!isSubmitting && (