diff --git a/apps/extension/.gitignore b/apps/extension/.gitignore index a581c5a12e..adfe1df541 100644 --- a/apps/extension/.gitignore +++ b/apps/extension/.gitignore @@ -1,4 +1,6 @@ # dependencies +.yalc +yalc.lock .env /node_modules /build diff --git a/apps/extension/package.json b/apps/extension/package.json index df96444831..c32d3c142d 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -35,7 +35,8 @@ "@dao-xyz/borsh": "^5.1.5", "@ledgerhq/hw-transport": "^6.31.4", "@ledgerhq/hw-transport-webusb": "^6.29.4", - "@namada/sdk": "^0.20.7", + "@namada/sdk": "file:.yalc/@namada/sdk", + "@namada/sdk-multicore": "file:.yalc/@namada/sdk-multicore", "@zondax/ledger-namada": "^2.0.0", "bignumber.js": "^9.1.1", "buffer": "^6.0.3", diff --git a/apps/namadillo/.gitignore b/apps/namadillo/.gitignore index 812f78443a..dd408a19c1 100644 --- a/apps/namadillo/.gitignore +++ b/apps/namadillo/.gitignore @@ -1,4 +1,6 @@ # dependencies +yalc.lock +/.yalc /node_modules /.pnp .pnp.js diff --git a/apps/namadillo/package.json b/apps/namadillo/package.json index f4b9d619aa..daaa22faad 100644 --- a/apps/namadillo/package.json +++ b/apps/namadillo/package.json @@ -12,7 +12,7 @@ "@keplr-wallet/types": "^0.12.136", "@namada/chain-registry": "^1.3.0", "@namada/indexer-client": "3.3.2", - "@namada/sdk-multicore": "^0.20.7", + "@namada/sdk-multicore": "file:.yalc/@namada/sdk-multicore", "@tailwindcss/container-queries": "^0.1.1", "@tanstack/query-core": "^5.40.0", "@tanstack/react-query": "^5.40.0", diff --git a/apps/namadillo/src/App/Common/GasFeeModal.tsx b/apps/namadillo/src/App/Common/GasFeeModal.tsx index be2ec89c3b..04b66c9979 100644 --- a/apps/namadillo/src/App/Common/GasFeeModal.tsx +++ b/apps/namadillo/src/App/Common/GasFeeModal.tsx @@ -13,6 +13,7 @@ import BigNumber from "bignumber.js"; import clsx from "clsx"; import { TransactionFeeProps } from "hooks/useTransactionFee"; import { useAtomValue } from "jotai"; +import { useCallback, useState } from "react"; import { IoClose } from "react-icons/io5"; import { twMerge } from "tailwind-merge"; import { Asset, GasConfig, NamadaAsset } from "types"; @@ -110,9 +111,12 @@ export const GasFeeModal = ({ onChangeGasToken, } = feeProps; + const [gasLimit, setGasLimit] = useState(gasConfig.gasLimit); + const [gasToken, setGasToken] = useState(gasConfig.gasToken); + const sortByNativeToken = useSortByNativeToken(); const buildGasOption = useBuildGasOption({ - gasConfig, + gasConfig: { ...gasConfig, gasLimit, gasToken }, gasPriceTable, chainAssetsMap, }); @@ -143,8 +147,19 @@ export const GasFeeModal = ({ const isLoading = isShielded && !shieldedAmount.data; + // We only commit the changes when the modal is closed, this prevents some atoms from triggering too many times + const onCloseCallback = useCallback(() => { + if (typeof gasLimit !== "undefined") { + onChangeGasLimit(gasLimit); + } + if (typeof gasToken !== "undefined") { + onChangeGasToken(gasToken); + } + onClose(); + }, [gasLimit, gasToken]); + return ( - +
@@ -188,7 +203,7 @@ export const GasFeeModal = ({ "cursor-auto bg-yellow text-black" : "cursor-pointer bg-neutral-800 hover:bg-neutral-700" )} - onClick={() => onChangeGasLimit(BigNumber(item.amount))} + onClick={() => setGasLimit(BigNumber(item.amount))} >
{item.label}
{totalInDollars && ( @@ -240,7 +255,7 @@ export const GasFeeModal = ({ labelProps={{ className: "group-hover/item:text-current", }} - onChange={(e) => onChangeGasToken(e.target.value)} + onChange={(e) => setGasToken(e.target.value)} options={ gasPriceTable ?.filter(filterAvailableTokensOnly) @@ -322,8 +337,8 @@ export const GasFeeModal = ({ e.target.value && onChangeGasLimit(e.target.value)} + value={gasLimit} + onChange={(e) => e.target.value && setGasLimit(e.target.value)} />
@@ -335,7 +350,7 @@ export const GasFeeModal = ({ backgroundHoverColor="yellow" textColor="white" textHoverColor="black" - onClick={onClose} + onClick={onCloseCallback} > Close diff --git a/apps/namadillo/src/App/Common/TransactionFeeButton.tsx b/apps/namadillo/src/App/Common/TransactionFeeButton.tsx index 7f2a133aff..1935957309 100644 --- a/apps/namadillo/src/App/Common/TransactionFeeButton.tsx +++ b/apps/namadillo/src/App/Common/TransactionFeeButton.tsx @@ -1,5 +1,6 @@ import { namadaRegistryChainAssetsMapAtom } from "atoms/integrations"; import BigNumber from "bignumber.js"; +import clsx from "clsx"; import { TransactionFeeProps } from "hooks/useTransactionFee"; import { useAtomValue } from "jotai"; import { useMemo, useState } from "react"; @@ -13,10 +14,12 @@ export const TransactionFeeButton = ({ feeProps, className, isShieldedTransfer = false, + disabled = false, }: { feeProps: TransactionFeeProps; className?: string; isShieldedTransfer?: boolean; + disabled?: boolean; }): JSX.Element => { const [modalOpen, setModalOpen] = useState(false); @@ -42,9 +45,14 @@ export const TransactionFeeButton = ({ displayAmount={gasDisplayAmount?.totalDisplayAmount || BigNumber(0)} symbol={gasDisplayAmount?.asset.symbol || ""} /> -
+
Fee Options:
+ {maxAmount && ( +
+ Max: +
+ )} {isInsufficientBalance && (
Insufficient balance to cover the fee
diff --git a/apps/namadillo/src/App/Transfer/TransferDestination.tsx b/apps/namadillo/src/App/Transfer/TransferDestination.tsx index 94c1b57590..bb184ed269 100644 --- a/apps/namadillo/src/App/Transfer/TransferDestination.tsx +++ b/apps/namadillo/src/App/Transfer/TransferDestination.tsx @@ -36,6 +36,7 @@ type TransferDestinationProps = { address?: string; memo?: string; onChangeMemo?: (address: string) => void; + disabled?: boolean; }; export const TransferDestination = ({ @@ -60,6 +61,7 @@ export const TransferDestination = ({ onChangeMemo, openChainSelector, openProviderSelector, + disabled = false, }: TransferDestinationProps): JSX.Element => { return (
diff --git a/apps/namadillo/src/App/Transfer/TransferModule.tsx b/apps/namadillo/src/App/Transfer/TransferModule.tsx index 2d24ca9079..381b747188 100644 --- a/apps/namadillo/src/App/Transfer/TransferModule.tsx +++ b/apps/namadillo/src/App/Transfer/TransferModule.tsx @@ -61,6 +61,7 @@ export type TransferSourceProps = TransferModuleConfig & { isLoadingAssets?: boolean; selectedAssetAddress?: Address; availableAmount?: BigNumber; + maxAmount?: BigNumber; onChangeSelectedAsset?: (address: Address | undefined) => void; amount?: BigNumber; onChangeAmount?: (amount: BigNumber | undefined) => void; @@ -104,6 +105,7 @@ export type TransferModuleProps = { isSyncingMasp?: boolean; buttonTextErrors?: Partial>; onComplete?: () => void; + disabled?: boolean; } & ( | { ibcTransfer?: undefined; ibcOptions?: undefined } | { ibcTransfer: "deposit" | "withdraw"; ibcOptions: IbcOptions } @@ -167,6 +169,7 @@ export const TransferModule = ({ buttonTextErrors = {}, isSyncingMasp = false, isShieldedTx = false, + disabled = false, }: TransferModuleProps): JSX.Element => { const navigate = useNavigate(); const location = useLocation(); @@ -259,7 +262,7 @@ export const TransferModule = ({ return "NoAmount"; } else if ( !availableAmountMinusFees || - source.amount.gt(availableAmountMinusFees) + source.amount.gt(source.maxAmount || availableAmountMinusFees) ) { return "NotEnoughBalance"; } else if (!destination.wallet && !destination.customAddress) { @@ -492,6 +495,7 @@ export const TransferModule = ({ isLoadingAssets={source.isLoadingAssets} chain={parseChainInfo(source.chain, source.isShieldedAddress)} availableAmount={source.availableAmount} + maxAmount={source.maxAmount} availableAmountMinusFees={availableAmountMinusFees} amount={source.amount} openProviderSelector={onChangeWallet(source)} @@ -509,6 +513,7 @@ export const TransferModule = ({ isShieldedAddress={source.isShieldedAddress} onChangeShielded={source.onChangeShielded} isSubmitting={isSubmitting} + disabled={disabled} /> {ibcTransfer && requiresIbcChannels && ( void; amount?: BigNumber; availableAmount?: BigNumber; + maxAmount?: BigNumber; availableAmountMinusFees?: BigNumber; onChangeAmount?: (amount: BigNumber | undefined) => void; isShieldedAddress?: boolean; onChangeShielded?: (isShielded: boolean) => void; + disabled?: boolean; }; const amountMaxDecimalPlaces = (asset?: Asset): number | undefined => { @@ -53,6 +55,7 @@ export const TransferSource = ({ openChainSelector, openAssetSelector, availableAmount, + maxAmount, availableAmountMinusFees, amount, onChangeAmount, @@ -60,6 +63,7 @@ export const TransferSource = ({ isSyncingMasp, onChangeShielded, isSubmitting, + disabled, }: TransferSourceProps): JSX.Element => { return (
@@ -139,7 +143,7 @@ export const TransferSource = ({ onChangeAmount?.(e.target.value)} placeholder="Amount" @@ -161,10 +165,12 @@ export const TransferSource = ({
- onChangeAmount && onChangeAmount(availableAmountMinusFees) + onChangeAmount && + onChangeAmount(maxAmount || availableAmountMinusFees) } />
diff --git a/apps/namadillo/src/atoms/balance/atoms.ts b/apps/namadillo/src/atoms/balance/atoms.ts index 68763685d8..c02e87d3aa 100644 --- a/apps/namadillo/src/atoms/balance/atoms.ts +++ b/apps/namadillo/src/atoms/balance/atoms.ts @@ -1,9 +1,11 @@ import { DefaultApi } from "@namada/indexer-client"; import { Account, AccountType, DatedViewingKey } from "@namada/types"; +import { isShieldedAddress, isTransparentAddress } from "App/Transfer/common"; import { accountsAtom, allDefaultAccountsAtom, defaultAccountAtom, + isLedgerAccountAtom, transparentBalanceAtom, } from "atoms/accounts/atoms"; import { indexerApiAtom } from "atoms/api"; @@ -26,17 +28,19 @@ import * as O from "fp-ts/Option"; import invariant from "invariant"; import { atom, getDefaultStore } from "jotai"; import { atomWithQuery } from "jotai-tanstack-query"; -import { atomWithStorage } from "jotai/utils"; +import { atomFamily, atomWithStorage } from "jotai/utils"; import { Address, TokenBalance } from "types"; -import { namadaAsset, toDisplayAmount } from "utils"; +import { isNamadaAsset, namadaAsset, toDisplayAmount } from "utils"; import { mapNamadaAddressesToAssets, mapNamadaAssetsToTokenBalances, } from "./functions"; import { + estimateMaxMaspTxAmountByNotesWorker, fetchShieldedBalance, fetchShieldedRewards, fetchShieldedRewardsPerToken, + getNotesAndConversions, shieldedSync, } from "./services"; @@ -202,6 +206,168 @@ export const shieldedBalanceAtom = atomWithQuery((get) => { }; }); +export const getNotesAndConversionsAtom = atomWithQuery((get) => { + const viewingKeysQuery = get(viewingKeysAtom); + const chainParametersQuery = get(chainParametersAtom); + + const [viewingKey] = viewingKeysQuery.data ?? []; + const chainId = chainParametersQuery.data?.chainId; + + return { + queryKey: ["get-notes-and-conversions", viewingKey, chainId], + ...queryDependentFn(async () => { + if (!viewingKey || !chainId) { + return {}; + } + + const result = await getNotesAndConversions(viewingKey.key, chainId); + return result; + }, [viewingKeysQuery, chainParametersQuery]), + }; +}); + +// TODO: move +const reduceNotes = ( + x: number, + sortedNotes: [string, string?][] +): BigNumber => { + const amount = sortedNotes.slice(0, x).reduce((acc, [note, conv]) => { + const val = conv ? BigNumber(conv) : BigNumber(note); + return acc.plus(val); + }, BigNumber(0)); + + return BigNumber(amount); +}; + +export const estimateMaxMaspTxAmountAtom = atomFamily( + (props: { + token?: string; + feeToken?: string; + feeAmount?: BigNumber; + source?: string; + target?: string; + }) => + atomWithQuery((get) => { + const chainParametersQuery = get(chainParametersAtom); + const chainId = chainParametersQuery.data?.chainId; + const notesAndConversionsQuery = get(getNotesAndConversionsAtom); + const notesAndConversions = notesAndConversionsQuery.data; + const assetsMapQuery = get(namadaRegistryChainAssetsMapAtom); + const assetsMap = assetsMapQuery.data; + const isTransparent = + props.target ? isTransparentAddress(props.target) : undefined; + + return { + queryKey: [ + "estimate-max-masp-tx-amount", + chainId, + props.token, + props.feeToken, + props.feeAmount?.toString(), + isTransparent, + ], + ...queryDependentFn(async () => { + const { token, feeToken, feeAmount, source, target } = props; + if ( + !source || + !target || + !token || + !feeToken || + !feeAmount || + !chainId || + !notesAndConversions?.[token] || + !assetsMap + ) { + return BigNumber(0); + } + + const sortedNotes = notesAndConversions[token].sort( + ([noteA, convA], [noteB, convB]) => { + const valA = convA ? BigNumber(convA) : BigNumber(noteA); + const valB = convB ? BigNumber(convB) : BigNumber(noteB); + + return valB.comparedTo(valA); + } + ); + + const isNAM = isNamadaAsset(assetsMap[token]); + const asset = assetsMap[token]; + + const promises = [5, 4, 3, 2, 1].map(async (x) => { + const rawAmount = reduceNotes(x, sortedNotes); + const one = + isNAM ? toDisplayAmount(asset, BigNumber(1)) : BigNumber(1); + const amount = + isNAM ? toDisplayAmount(asset, rawAmount) : rawAmount; + const amountWithFee = + token === feeToken ? amount.minus(feeAmount) : amount; + const displayAmountWithFee = + isNAM ? amountWithFee : toDisplayAmount(asset, amountWithFee); + + return estimateMaxMaspTxAmountByNotesWorker({ + source, + target, + token, + feeToken, + // We do -1 to assume the fees - for consistency + amount: amountWithFee.minus(one), + feeAmount, + chainId, + }).then((res) => [displayAmountWithFee, res] as const); + }); + + try { + const res = await Promise.all(promises); + const amount = + res.find(([, success]) => success)?.[0] ?? BigNumber(0); + return amount; + } catch (e) { + console.error("Failed to estimate max masp tx amount", e); + } + + // TODO: proper error handling + return BigNumber(0); + }, [chainParametersQuery, notesAndConversionsQuery, assetsMapQuery]), + }; + }), + (a, b) => JSON.stringify(a) === JSON.stringify(b) +); + +const ledgerEstimationStopAtom = atom(false); +// TODO: rename +export const estimateMaxMaspTxAmountLedgerAtom = atomFamily( + (props: { + token?: string; + feeToken?: string; + feeAmount?: BigNumber; + source?: string; + target?: string; + }) => { + const baseAtom = estimateMaxMaspTxAmountAtom(props); + return atom( + (get) => { + const isLedger = get(isLedgerAccountAtom); + const stop = get(ledgerEstimationStopAtom); + + const { target } = props; + if ( + stop || + !isLedger || + !target || + !(isTransparentAddress(target) || isShieldedAddress(target)) + ) { + return null; + } + return get(baseAtom); + }, + (_, set, stop: boolean) => { + set(ledgerEstimationStopAtom, stop); + } + ); + }, + (a, b) => JSON.stringify(a) === JSON.stringify(b) +); + export const namadaShieldedAssetsAtom = atomWithQuery((get) => { const storageShieldedBalance = get(storageShieldedBalanceAtom); const viewingKeysQuery = get(viewingKeysAtom); diff --git a/apps/namadillo/src/atoms/balance/services.ts b/apps/namadillo/src/atoms/balance/services.ts index 24f903dfbf..8ca6d98bff 100644 --- a/apps/namadillo/src/atoms/balance/services.ts +++ b/apps/namadillo/src/atoms/balance/services.ts @@ -3,6 +3,8 @@ import * as Comlink from "comlink"; import { Balance, DatedViewingKey, + MaxMaspTxAmountProps, + NotesAndConversions, ProgressBarNames, SdkEvents, } from "@namada/sdk-multicore"; @@ -17,6 +19,7 @@ import { import ShieldedSyncWorker from "workers/ShieldedSyncWorker?worker"; // TODO: move to @namada/types? import BigNumber from "bignumber.js"; +import { EstimateMaxMaspTxAmountByNotes } from "workers/MaspTxMessages"; import { Worker as MaspTxWorkerApi, registerTransferHandlers, @@ -105,6 +108,79 @@ export const fetchShieldedBalance = async ( return await sdk.rpc.queryBalance(viewingKey.key, addresses, chainId); }; +export const getNotesAndConversions = async ( + viewingKey: string, + chainId: string +): Promise => { + const sdk = await getSdkInstance(); + const worker = new MaspTxWorker(); + const workerLink = Comlink.wrap(worker); + await workerLink.init({ + type: "init", + payload: { + // TODO: + rpcUrl: "https://namada-rpc.emberstake.xyz", + token: sdk.nativeToken, + maspIndexerUrl: "", + }, + }); + + const res = await workerLink.notesAndConversions({ + type: "notes-and-conversions", + payload: { viewingKey, chainId }, + }); + worker.terminate(); + + return res.payload; +}; + +export const estimateMaxMaspTxAmountByNotes = async ( + props: MaxMaspTxAmountProps, + chainId: string +): Promise => { + const sdk = await getSdkInstance(); + return await sdk.masp.estimateMaxMaspTxAmount(props, chainId); +}; + +export const estimateMaxMaspTxAmountByNotesWorker = async ( + //TODO: do not reuse worker type here + props: EstimateMaxMaspTxAmountByNotes["payload"] +): Promise => { + registerTransferHandlers(); + const sdk = await getSdkInstance(); + const worker = new MaspTxWorker(); + const workerLink = Comlink.wrap(worker); + await workerLink.init({ + type: "init", + payload: { + // TODO: + rpcUrl: "https://namada-rpc.emberstake.xyz", + token: sdk.nativeToken, + maspIndexerUrl: "", + }, + }); + + const res = await workerLink.estimateMaxMaspTxAmountByNotes({ + type: "estimate-max-masp-tx-amount-by-notes", + payload: props, + }); + worker.terminate(); + + return res.payload; +}; + +// const reduceNotes = ( +// x: number, +// sortedNotes: [string, string?][] +// ): BigNumber => { +// const amount = sortedNotes.slice(0, x).reduce((acc, [note, conv]) => { +// const val = conv ? BigNumber(conv) : BigNumber(note); +// return acc.plus(val); +// }, BigNumber(0)); + +// return BigNumber(amount); +// }; + export const fetchShieldedRewards = async ( viewingKey: DatedViewingKey, chainId: string, diff --git a/apps/namadillo/src/hooks/useMaxMaspAmountForHWWallet.tsx b/apps/namadillo/src/hooks/useMaxMaspAmountForHWWallet.tsx new file mode 100644 index 0000000000..b7d55c4821 --- /dev/null +++ b/apps/namadillo/src/hooks/useMaxMaspAmountForHWWallet.tsx @@ -0,0 +1,68 @@ +import { estimateMaxMaspTxAmountLedgerAtom } from "atoms/balance"; +import { namadaRegistryChainAssetsMapAtom } from "atoms/integrations"; +import BigNumber from "bignumber.js"; +import { useAtomValue } from "jotai"; +import { useMemo } from "react"; +import { Asset, GasConfig } from "types"; +import { toDisplayAmount } from "utils"; +import { getDisplayGasFee } from "utils/gas"; + +type MaxMaspAmountForHWWalletResponse = { + amount: BigNumber; + displayWarning: boolean; + calculating: boolean; +}; + +type MaxMaspAmountForHWWalletParams = { + asset?: Asset; + amount?: BigNumber; + gasConfig: GasConfig; +}; + +export const useMaxMaspAmountForHWWallet = ({ + asset, + amount, + gasConfig, +}: MaxMaspAmountForHWWalletParams): MaxMaspAmountForHWWalletResponse | null => { + const maxMaspTxAmountQuery = useAtomValue( + estimateMaxMaspTxAmountLedgerAtom({ + token: asset?.address, + feeToken: gasConfig.gasToken, + }) + ); + const chainAssetsMap = useAtomValue(namadaRegistryChainAssetsMapAtom); + + const res = useMemo(() => { + if (!maxMaspTxAmountQuery) { + return null; + } + const { data } = maxMaspTxAmountQuery; + if (!data || !asset || !amount) { + return [BigNumber(0), false] as const; + } + const displayGas = getDisplayGasFee(gasConfig, chainAssetsMap.data || {}); + + const max = toDisplayAmount(asset, data); + const displayWarning = max.lt(amount); + const maxWithFee = max.minus(displayGas.totalDisplayAmount); + + return [maxWithFee, displayWarning] as const; + }, [ + maxMaspTxAmountQuery?.data?.toString(), + gasConfig.gasLimit.toString(), + amount?.toString(), + asset?.address, + ]); + + if (!res || !maxMaspTxAmountQuery) { + return null; + } + + const [maxAmount, displayWarning] = res; + return { + amount: maxAmount, + displayWarning, + calculating: + maxMaspTxAmountQuery.isPending || maxMaspTxAmountQuery.isFetching, + }; +}; diff --git a/apps/namadillo/src/workers/MaspTxMessages.ts b/apps/namadillo/src/workers/MaspTxMessages.ts index 3825c58d5b..d9e333a9b5 100644 --- a/apps/namadillo/src/workers/MaspTxMessages.ts +++ b/apps/namadillo/src/workers/MaspTxMessages.ts @@ -1,5 +1,6 @@ import { IbcTransferProps, + NotesAndConversions as NotesAndConversionsResponse, SdkWasmOptions, ShieldedTransferProps, ShieldingTransferProps, @@ -117,6 +118,37 @@ export type ShieldedRewardsPerTokenDone = WebWorkerMessage< Record >; +type EstimateMaxMaspTxAmountByNotesPayload = { + source: string; + target: string; + token: string; + feeToken: string; + amount: BigNumber; + feeAmount: BigNumber; + chainId: string; +}; +export type EstimateMaxMaspTxAmountByNotes = WebWorkerMessage< + "estimate-max-masp-tx-amount-by-notes", + EstimateMaxMaspTxAmountByNotesPayload +>; +export type EstimateMaxMaspTxAmountByNotesDone = WebWorkerMessage< + "estimate-max-masp-tx-amount-by-notes-done", + boolean +>; + +type NotesAndConversionsPayload = { + viewingKey: string; + chainId: string; +}; +export type NotesAndConversions = WebWorkerMessage< + "notes-and-conversions", + NotesAndConversionsPayload +>; +export type NotesAndConversionsDone = WebWorkerMessage< + "notes-and-conversions-done", + NotesAndConversionsResponse +>; + type BroadcastPayload = TransactionPair; export type Broadcast = WebWorkerMessage<"broadcast", BroadcastPayload>; diff --git a/apps/namadillo/src/workers/MaspTxWorker.ts b/apps/namadillo/src/workers/MaspTxWorker.ts index 86b73c8503..a5cd7b25c5 100644 --- a/apps/namadillo/src/workers/MaspTxWorker.ts +++ b/apps/namadillo/src/workers/MaspTxWorker.ts @@ -1,5 +1,6 @@ import { IbcTransferProps, + NotesAndConversions as NotesAndConversionsResponse, Sdk, ShieldedTransferProps, ShieldingTransferProps, @@ -14,12 +15,16 @@ import { namadaAsset, toDisplayAmount } from "utils"; import { Broadcast, BroadcastDone, + EstimateMaxMaspTxAmountByNotes, + EstimateMaxMaspTxAmountByNotesDone, GenerateIbcShieldingMemo, GenerateIbcShieldingMemoDone, IbcTransfer, IbcTransferDone, Init, InitDone, + NotesAndConversions, + NotesAndConversionsDone, Shield, ShieldDone, ShieldedRewards, @@ -121,6 +126,32 @@ export class Worker { }; } + async estimateMaxMaspTxAmountByNotes( + m: EstimateMaxMaspTxAmountByNotes + ): Promise { + if (!this.sdk) { + throw new Error("SDK is not initialized"); + } + + return { + type: "estimate-max-masp-tx-amount-by-notes-done", + payload: await estimateMaxMaspTxAmountByNotes(this.sdk, m.payload), + }; + } + + async notesAndConversions( + m: NotesAndConversions + ): Promise { + if (!this.sdk) { + throw new Error("SDK is not initialized"); + } + + return { + type: "notes-and-conversions-done", + payload: await notesAndConversions(this.sdk, m.payload), + }; + } + async broadcast(m: Broadcast): Promise { if (!this.sdk) { throw new Error("SDK is not initialized"); @@ -273,6 +304,29 @@ async function shieldedRewards( return await sdk.rpc.shieldedRewards(viewingKey, chainId); } +async function estimateMaxMaspTxAmountByNotes( + sdk: Sdk, + payload: EstimateMaxMaspTxAmountByNotes["payload"] +): Promise { + const { chainId, ...rest } = payload; + const props = { + ...rest, + amount: payload.amount.toString(), + feeAmount: payload.feeAmount.toString(), + }; + + return await sdk.masp.estimateMaxMaspTxAmount(props, payload.chainId); +} + +async function notesAndConversions( + sdk: Sdk, + payload: NotesAndConversions["payload"] +): Promise { + const { chainId, viewingKey } = payload; + + return await sdk.masp.getNotesAndConversions(viewingKey, chainId); +} + // TODO: We will probably move this to the separate worker async function broadcast( sdk: Sdk, @@ -311,6 +365,12 @@ export const registerTransferHandlers = (): void => { ); registerBNTransferHandler("shielded-rewards"); registerBNTransferHandler("shielded-rewards-done"); + registerBNTransferHandler( + "estimate-max-masp-tx-amount-by-notes" + ); + registerBNTransferHandler( + "estimate-max-masp-tx-amount-by-notes-done" + ); registerBNTransferHandler("broadcast"); }; diff --git a/yarn.lock b/yarn.lock index bbaeeb51d4..2d26a0ae3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3578,7 +3578,8 @@ __metadata: "@dao-xyz/borsh": "npm:^5.1.5" "@ledgerhq/hw-transport": "npm:^6.31.4" "@ledgerhq/hw-transport-webusb": "npm:^6.29.4" - "@namada/sdk": "npm:^0.20.7" + "@namada/sdk": "file:.yalc/@namada/sdk" + "@namada/sdk-multicore": "file:.yalc/@namada/sdk-multicore" "@namada/sdk-node": "npm:^0.20.7" "@svgr/webpack": "npm:^6.3.1" "@types/chrome": "npm:^0.0.237" @@ -3765,7 +3766,7 @@ __metadata: "@keplr-wallet/types": "npm:^0.12.136" "@namada/chain-registry": "npm:^1.3.0" "@namada/indexer-client": "npm:3.3.2" - "@namada/sdk-multicore": "npm:^0.20.7" + "@namada/sdk-multicore": "file:.yalc/@namada/sdk-multicore" "@namada/sdk-node": "npm:^0.20.7" "@namada/vite-esbuild-plugin": "npm:^1.0.1" "@playwright/test": "npm:^1.24.1" @@ -3852,9 +3853,26 @@ __metadata: languageName: unknown linkType: soft -"@namada/sdk-multicore@npm:^0.20.7": - version: 0.20.7 - resolution: "@namada/sdk-multicore@npm:0.20.7" +"@namada/sdk-multicore@file:.yalc/@namada/sdk-multicore::locator=%40namada%2Fextension%40workspace%3Aapps%2Fextension": + version: 0.21.0 + resolution: "@namada/sdk-multicore@file:.yalc/@namada/sdk-multicore#.yalc/@namada/sdk-multicore::hash=312175&locator=%40namada%2Fextension%40workspace%3Aapps%2Fextension" + dependencies: + "@cosmjs/encoding": "npm:^0.29.0" + "@dao-xyz/borsh": "npm:^5.1.5" + "@ledgerhq/hw-transport": "npm:^6.31.4" + "@ledgerhq/hw-transport-webusb": "npm:^6.29.4" + "@zondax/ledger-namada": "npm:^2.0.0" + bignumber.js: "npm:^9.1.1" + buffer: "npm:^6.0.3" + semver: "npm:^7.7.2" + slip44: "npm:^3.0.18" + checksum: 10c0/7ab68c923ace15a9bebc10e370db4a850b00c0d7e03b5c16286511920b079fc19119d865c4e06e3bd2b2788f2fda659a0941b54cec4c0382349b7025c8dc3ffd + languageName: node + linkType: hard + +"@namada/sdk-multicore@file:.yalc/@namada/sdk-multicore::locator=%40namada%2Fnamadillo%40workspace%3Aapps%2Fnamadillo": + version: 0.21.0 + resolution: "@namada/sdk-multicore@file:.yalc/@namada/sdk-multicore#.yalc/@namada/sdk-multicore::hash=0ce3a6&locator=%40namada%2Fnamadillo%40workspace%3Aapps%2Fnamadillo" dependencies: "@cosmjs/encoding": "npm:^0.29.0" "@dao-xyz/borsh": "npm:^5.1.5" @@ -3865,7 +3883,7 @@ __metadata: buffer: "npm:^6.0.3" semver: "npm:^7.7.2" slip44: "npm:^3.0.18" - checksum: 10c0/954de17370d879af0781454f3f13239c3e2557592f353051fbcd7236d18ed0d084bd3d23c67995701cc5bb07abd15b39568d366c8d632a72efe8c70dd60956f9 + checksum: 10c0/ae0b2ade50800cb510fc89c5255e19f8e34f2d58fc3a00cc73cc093d67046d3aca470e25370411462858ebefadb09698049175bd099028721d961241c5d4467a languageName: node linkType: hard @@ -3886,9 +3904,9 @@ __metadata: languageName: node linkType: hard -"@namada/sdk@npm:^0.20.7": - version: 0.20.7 - resolution: "@namada/sdk@npm:0.20.7" +"@namada/sdk@file:.yalc/@namada/sdk::locator=%40namada%2Fextension%40workspace%3Aapps%2Fextension": + version: 0.21.0 + resolution: "@namada/sdk@file:.yalc/@namada/sdk#.yalc/@namada/sdk::hash=2f1d91&locator=%40namada%2Fextension%40workspace%3Aapps%2Fextension" dependencies: "@cosmjs/encoding": "npm:^0.29.0" "@dao-xyz/borsh": "npm:^5.1.5" @@ -3899,7 +3917,7 @@ __metadata: buffer: "npm:^6.0.3" semver: "npm:^7.6.3" slip44: "npm:^3.0.18" - checksum: 10c0/0a4ff08bc4ca2c27069d5f6c52e2fc421d5647827a0a4d9386c24d436dbc1c254f31c5f7d5b0b6a650f3ce10730dab1dbf8fd24ba79df1bcb1fae10864ed5f37 + checksum: 10c0/0b7163cc3a91cf61823b73b2c31b5460effda2ec81f9fc9beba75f7b59c9b9bf9f715a9ec6adbcb53c3e78f10a6bfc55c951fa9e758002dc72aa6979dd377695 languageName: node linkType: hard