From d5975d9cf9aaf12f08107c9f402bc7439fbcc161 Mon Sep 17 00:00:00 2001 From: Mateusz Jasiuk Date: Tue, 5 Aug 2025 15:37:30 +0200 Subject: [PATCH 1/3] feat: display warning when unshielding using ledger device --- apps/namadillo/src/App/Masp/MaspUnshield.tsx | 62 ++++++++++++++- apps/namadillo/src/atoms/balance/atoms.ts | 21 +++++ apps/namadillo/src/atoms/balance/services.ts | 9 +++ packages/sdk/src/rpc/rpc.ts | 16 ++++ packages/shared/lib/Cargo.lock | 28 ------- packages/shared/lib/Cargo.toml | 4 +- packages/shared/lib/src/query.rs | 80 ++++++++++++++++++-- 7 files changed, 183 insertions(+), 37 deletions(-) diff --git a/apps/namadillo/src/App/Masp/MaspUnshield.tsx b/apps/namadillo/src/App/Masp/MaspUnshield.tsx index 819ee4362c..cd86edd6dc 100644 --- a/apps/namadillo/src/App/Masp/MaspUnshield.tsx +++ b/apps/namadillo/src/App/Masp/MaspUnshield.tsx @@ -1,4 +1,4 @@ -import { Panel } from "@namada/components"; +import { Alert, Panel } from "@namada/components"; import { AccountType } from "@namada/types"; import { MaspSyncCover } from "App/Common/MaspSyncCover"; import { NamadaTransferTopHeader } from "App/NamadaTransfer/NamadaTransferTopHeader"; @@ -10,6 +10,7 @@ import { import { allDefaultAccountsAtom } from "atoms/accounts"; import { lastCompletedShieldedSyncAtom, + maspNotesAtom, namadaShieldedAssetsAtom, } from "atoms/balance/atoms"; import { chainParametersAtom } from "atoms/chain/atoms"; @@ -25,7 +26,8 @@ import { wallets } from "integrations"; import invariant from "invariant"; import { useAtom, useAtomValue } from "jotai"; import { createTransferDataFromNamada } from "lib/transactions"; -import { useState } from "react"; +import { useEffect, useState } from "react"; +import { toDisplayAmount } from "utils"; export const MaspUnshield: React.FC = () => { const [displayAmount, setDisplayAmount] = useState(); @@ -54,6 +56,9 @@ export const MaspUnshield: React.FC = () => { const account = defaultAccounts.data?.find( (account) => account.type === AccountType.ShieldedKeys ); + const isLedgerAccount = defaultAccounts.data?.some( + (account) => account.type === AccountType.Ledger + ); const sourceAddress = account?.address; const destinationAddress = defaultAccounts.data?.find( (account) => account.type !== AccountType.ShieldedKeys @@ -66,6 +71,10 @@ export const MaspUnshield: React.FC = () => { const selectedAsset = selectedAssetAddress ? availableAssets?.[selectedAssetAddress] : undefined; + const notesAtom = useAtomValue(maspNotesAtom); + const [notes, setNotes] = useState([] as [string, BigNumber][]); + const [availableToSpend, setAvailableToSpend] = useState(); + const { execute: performTransfer, isPending: isPerformingTransfer, @@ -140,6 +149,41 @@ export const MaspUnshield: React.FC = () => { } } }; + + useEffect(() => { + if ( + !isLedgerAccount || + !selectedAsset || + !notesAtom.isSuccess || + !feeProps + ) { + setNotes([]); + return; + } + + const www = notesAtom.data + .filter(([token]) => token === selectedAsset.asset.address) + .map( + ([token, balance]) => + [token, toDisplayAmount(selectedAsset.asset, BigNumber(balance))] as [ + string, + BigNumber, + ] + ) + .sort((a, b) => b[1].minus(a[1]).toNumber()); + + const kappa = www.slice(0, 4).reduce((acc, [_, amount]) => { + return acc.plus(amount); + }, BigNumber(0)); + const gas = feeProps.gasConfig.gasLimit.times( + feeProps.gasConfig.gasPriceInMinDenom + ); + + setNotes(www); + setAvailableToSpend(kappa.minus(gas)); + // TODO: Check if not called to often + }, [selectedAsset, notesAtom.data, account, feeProps]); + // We stop the ledger status check when the transfer is in progress setLedgerStatusStop(isPerformingTransfer); @@ -151,6 +195,20 @@ export const MaspUnshield: React.FC = () => { isDestinationShielded={false} /> + {notes.length > 4 && ( + +

+ Due to ledger BS we have to limit the amount that you can unshield + at this time to {availableToSpend?.toString()} +
+ After tx is successful, you will be able to unshield more +

+
+ )} { return get(shieldedSyncProgress) === 1; }); +export const maspNotesAtom = atomWithQuery((get) => { + const viewingKeysQuery = get(viewingKeysAtom); + const chainTokensQuery = get(chainTokensAtom); + const chainParametersQuery = get(chainParametersAtom); + + const [viewingKey] = viewingKeysQuery.data ?? []; + const chainTokens = chainTokensQuery.data?.map((t) => t.address); + const chainId = chainParametersQuery.data?.chainId; + + return { + queryKey: ["masp-notes", viewingKey, chainTokens, chainId], + ...queryDependentFn(async () => { + if (!viewingKey || !chainTokens || !chainId) { + return []; + } + return await fetchNotes(viewingKey, chainTokens, chainId); + }, [viewingKeysQuery, chainTokensQuery, chainParametersQuery]), + }; +}); + export const shieldedBalanceAtom = atomWithQuery((get) => { const enablePolling = get(shouldUpdateBalanceAtom); const viewingKeysQuery = get(viewingKeysAtom); diff --git a/apps/namadillo/src/atoms/balance/services.ts b/apps/namadillo/src/atoms/balance/services.ts index 583cc622c2..8e94ea72b5 100644 --- a/apps/namadillo/src/atoms/balance/services.ts +++ b/apps/namadillo/src/atoms/balance/services.ts @@ -101,6 +101,15 @@ export const fetchShieldedBalance = async ( return await sdk.rpc.queryBalance(viewingKey.key, addresses, chainId); }; +export const fetchNotes = async ( + viewingKey: DatedViewingKey, + addresses: string[], + chainId: string +): Promise => { + const sdk = await getSdkInstance(); + return await sdk.rpc.queryMASPNotes(viewingKey.key, addresses, chainId); +}; + export const fetchShieldedRewards = async ( viewingKey: DatedViewingKey, chainId: string, diff --git a/packages/sdk/src/rpc/rpc.ts b/packages/sdk/src/rpc/rpc.ts index 7796442433..016c957652 100644 --- a/packages/sdk/src/rpc/rpc.ts +++ b/packages/sdk/src/rpc/rpc.ts @@ -55,6 +55,22 @@ export class Rpc { return await this.query.query_balance(owner, tokens, chainId); } + /** + * Query MASP notes + * @async + * @param owner - Owner address + * @param tokens - Array of token addresses + * @param chainId - Chain id needed to load specific context + * @returns [[tokenAddress, amount]] + */ + async queryMASPNotes( + owner: string, + tokens: string[], + chainId: string + ): Promise { + return await this.query.query_notes_to_spend(owner, tokens, chainId); + } + /** * Query native token from chain * @async diff --git a/packages/shared/lib/Cargo.lock b/packages/shared/lib/Cargo.lock index 678b9dca4f..5ebcae827f 100644 --- a/packages/shared/lib/Cargo.lock +++ b/packages/shared/lib/Cargo.lock @@ -3878,7 +3878,6 @@ dependencies = [ [[package]] name = "namada_account" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "namada_core", @@ -3890,7 +3889,6 @@ dependencies = [ [[package]] name = "namada_controller" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "namada_core", "smooth-operator", @@ -3900,7 +3898,6 @@ dependencies = [ [[package]] name = "namada_core" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "bech32 0.11.0", "borsh", @@ -3949,7 +3946,6 @@ dependencies = [ [[package]] name = "namada_ethereum_bridge" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "ethers", @@ -3977,7 +3973,6 @@ dependencies = [ [[package]] name = "namada_events" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "namada_core", @@ -3991,7 +3986,6 @@ dependencies = [ [[package]] name = "namada_gas" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "namada_core", @@ -4004,7 +3998,6 @@ dependencies = [ [[package]] name = "namada_governance" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "itertools 0.14.0", @@ -4027,7 +4020,6 @@ dependencies = [ [[package]] name = "namada_ibc" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "data-encoding", @@ -4062,7 +4054,6 @@ dependencies = [ [[package]] name = "namada_io" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "async-trait", "kdam", @@ -4075,7 +4066,6 @@ dependencies = [ [[package]] name = "namada_macros" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "data-encoding", "proc-macro2", @@ -4087,7 +4077,6 @@ dependencies = [ [[package]] name = "namada_merkle_tree" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "eyre", @@ -4102,7 +4091,6 @@ dependencies = [ [[package]] name = "namada_parameters" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "namada_core", "namada_macros", @@ -4117,7 +4105,6 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "itertools 0.14.0", @@ -4141,7 +4128,6 @@ dependencies = [ [[package]] name = "namada_replay_protection" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "namada_core", ] @@ -4149,7 +4135,6 @@ dependencies = [ [[package]] name = "namada_sdk" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "async-trait", "bech32 0.11.0", @@ -4219,7 +4204,6 @@ dependencies = [ [[package]] name = "namada_shielded_token" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "async-trait", "borsh", @@ -4260,7 +4244,6 @@ dependencies = [ [[package]] name = "namada_state" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "clru", @@ -4283,7 +4266,6 @@ dependencies = [ [[package]] name = "namada_storage" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "itertools 0.14.0", @@ -4302,7 +4284,6 @@ dependencies = [ [[package]] name = "namada_systems" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "namada_core", "namada_events", @@ -4312,7 +4293,6 @@ dependencies = [ [[package]] name = "namada_token" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "namada_core", @@ -4330,7 +4310,6 @@ dependencies = [ [[package]] name = "namada_trans_token" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "konst", "namada_core", @@ -4347,7 +4326,6 @@ dependencies = [ [[package]] name = "namada_tx" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "ark-bls12-381", "bitflags 2.9.0", @@ -4376,7 +4354,6 @@ dependencies = [ [[package]] name = "namada_tx_env" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "namada_core", "namada_events", @@ -4386,7 +4363,6 @@ dependencies = [ [[package]] name = "namada_vm" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "clru", @@ -4409,7 +4385,6 @@ dependencies = [ [[package]] name = "namada_vote_ext" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "borsh", "namada_core", @@ -4421,7 +4396,6 @@ dependencies = [ [[package]] name = "namada_vp" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "namada_core", "namada_events", @@ -4437,7 +4411,6 @@ dependencies = [ [[package]] name = "namada_vp_env" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "derivative", "masp_primitives", @@ -4452,7 +4425,6 @@ dependencies = [ [[package]] name = "namada_wallet" version = "0.251.1" -source = "git+https://github.com/namada-net/namada?tag=libs-v0.251.1#9785a72dc9215c8891b614040a8cf6e09ae31bb6" dependencies = [ "bimap", "borsh", diff --git a/packages/shared/lib/Cargo.toml b/packages/shared/lib/Cargo.toml index 462fe52b29..b61918ca81 100644 --- a/packages/shared/lib/Cargo.toml +++ b/packages/shared/lib/Cargo.toml @@ -18,7 +18,7 @@ nodejs = [] web = [] [build-dependencies] -namada_tx = { git = "https://github.com/namada-net/namada", tag="libs-v0.251.1" } +namada_tx = { path="/home/mj/Projects/heliax/namada/crates/tx" } [dependencies] async-trait = {version = "0.1.51"} @@ -27,7 +27,7 @@ chrono = "0.4.22" getrandom = { version = "0.3.0", features = [] } gloo-utils = { version = "0.1.5", features = ["serde"] } js-sys = "0.3.60" -namada_sdk = { git = "https://github.com/namada-net/namada", tag="libs-v0.251.1", default-features = false } +namada_sdk = { path="/home/mj/Projects/heliax/namada/crates/sdk" , default-features = false } rand = {version = "0.8.5"} rayon = { version = "1.8", optional = true } rexie = "0.5" diff --git a/packages/shared/lib/src/query.rs b/packages/shared/lib/src/query.rs index 485baeeb4f..e41201c662 100644 --- a/packages/shared/lib/src/query.rs +++ b/packages/shared/lib/src/query.rs @@ -18,7 +18,7 @@ use namada_sdk::masp::{IndexerMaspClient, LedgerMaspClient, LinearBackoffSleepMa use namada_sdk::masp::{ShieldedContext, ShieldedSyncConfig}; use namada_sdk::masp_primitives::asset_type::AssetType; use namada_sdk::masp_primitives::sapling::ViewingKey; -use namada_sdk::masp_primitives::transaction::components::ValueSum; +use namada_sdk::masp_primitives::transaction::components::{I128Sum, ValueSum}; use namada_sdk::masp_primitives::zip32::ExtendedFullViewingKey; use namada_sdk::parameters::storage; use namada_sdk::proof_of_stake::Epoch; @@ -29,7 +29,7 @@ use namada_sdk::rpc::{ query_proposal_votes, query_storage_value, }; use namada_sdk::state::Key; -use namada_sdk::token; +use namada_sdk::token::{self, Change}; use namada_sdk::tx::{ TX_BOND_WASM, TX_CLAIM_REWARDS_WASM, TX_IBC_WASM, TX_REDELEGATE_WASM, TX_REVEAL_PK, TX_TRANSFER_WASM, TX_UNBOND_WASM, TX_VOTE_PROPOSAL, TX_WITHDRAW_WASM, @@ -235,11 +235,11 @@ impl Query { } fn get_decoded_balance( - decoded_balance: (ValueSum, ValueSum), + decoded_balance: ValueSum, ) -> Vec<(Address, token::Amount)> { let mut result = Vec::new(); - for (token_addr, amount) in decoded_balance.0.components() { + for (token_addr, amount) in decoded_balance.components() { let amount = token::Amount::from_change(*amount); result.push((token_addr.clone(), amount)); } @@ -395,6 +395,76 @@ impl Query { Ok(()) } + pub async fn query_notes_to_spend( + &self, + owner: String, + tokens: Box<[JsValue]>, + chain_id: String, + ) -> Result { + let tokens: Vec
= tokens + .iter() + .map(|address| { + let address_str = address.as_string().unwrap(); + Address::from_str(&address_str).unwrap() + }) + .collect(); + let xvk = ExtendedViewingKey::from_str(&owner)?; + let viewing_key = ExtendedFullViewingKey::from(xvk).fvk.vk; + + // We are recreating shielded context to avoid multiple mutable borrows + let mut shielded: ShieldedContext = ShieldedContext::default(); + shielded.utils.chain_id = chain_id.clone(); + shielded.try_load(async |_| {}).await; + shielded + .precompute_asset_types(&self.client, tokens.iter().collect()) + .await + .map_err(|e| JsError::new(&format!("{:?}", e)))?; + let _ = shielded.save().await; + + let epoch = query_masp_epoch(&self.client).await?; + + let mut notes = vec![]; + // Retrieve the notes that can be spent by this key + if let Some(avail_notes) = shielded.pos_map.get(&viewing_key) { + for note_idx in avail_notes { + // Spent notes cannot contribute a new transaction's pool + if shielded.spents.contains(note_idx) { + continue; + } + // Get note associated with this ID + let note = shielded.note_map.get(note_idx).unwrap(); + // Finally add value to multi-asset accumulator + notes.push( + I128Sum::from_nonnegative(note.asset_type, i128::from(note.value)).unwrap(), + ); + } + } + let mut dupa = vec![]; + + for note in notes.iter() { + for (asset_type, val) in note.components() { + let decoded = shielded.decode_asset_type(&self.client, *asset_type).await; + match decoded { + Some(pre_asset_type) if pre_asset_type.epoch.is_none_or(|e| e <= epoch) => { + let decoded_change = + Change::from_masp_denominated(*val, pre_asset_type.position) + .expect("expected this to fit"); + let www = ValueSum::from_pair(pre_asset_type.token, decoded_change); + + let www = Self::get_decoded_balance(www); + + dupa.push(www); + } + _ => {} + } + } + } + + let flat = dupa.into_iter().flatten().collect::>(); + + to_js_result(flat) + } + /// Queries shielded balance for a given extended viewing key /// /// # Arguments @@ -431,7 +501,7 @@ impl Query { .decode_combine_sum_to_epoch(&self.client, balance, epoch) .await; - Self::get_decoded_balance(decoded_balance) + Self::get_decoded_balance(decoded_balance.0) } None => vec![], }; From a00fa82d32fa518308b1da76e5ff06c8901798dd Mon Sep 17 00:00:00 2001 From: Mateusz Jasiuk Date: Wed, 6 Aug 2025 13:29:50 +0200 Subject: [PATCH 2/3] WIP WIP --- apps/namadillo/src/App/Masp/MaspUnshield.tsx | 20 +++++--- packages/shared/lib/src/query.rs | 48 ++++++++++++++++++-- packages/shared/lib/src/sdk/tx.rs | 11 ++++- 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/apps/namadillo/src/App/Masp/MaspUnshield.tsx b/apps/namadillo/src/App/Masp/MaspUnshield.tsx index cd86edd6dc..bd0c66cb7f 100644 --- a/apps/namadillo/src/App/Masp/MaspUnshield.tsx +++ b/apps/namadillo/src/App/Masp/MaspUnshield.tsx @@ -72,7 +72,7 @@ export const MaspUnshield: React.FC = () => { selectedAssetAddress ? availableAssets?.[selectedAssetAddress] : undefined; const notesAtom = useAtomValue(maspNotesAtom); - const [notes, setNotes] = useState([] as [string, BigNumber][]); + const [notes, setNotes] = useState<[string, BigNumber][] | null>(); const [availableToSpend, setAvailableToSpend] = useState(); const { @@ -152,14 +152,22 @@ export const MaspUnshield: React.FC = () => { useEffect(() => { if ( - !isLedgerAccount || + // !isLedgerAccount || !selectedAsset || !notesAtom.isSuccess || !feeProps ) { - setNotes([]); + // notes !== null && setNotes(null); return; } + const notes = notesAtom.data.filter( + ([token]) => token === selectedAsset.asset.address + ); + // const conversions = notesAtom.data[1].filter( + // ([token]) => token === selectedAsset.asset.address + // ); + console.log("notes", notes); + // console.log("conversions", conversions); const www = notesAtom.data .filter(([token]) => token === selectedAsset.asset.address) @@ -179,8 +187,8 @@ export const MaspUnshield: React.FC = () => { feeProps.gasConfig.gasPriceInMinDenom ); - setNotes(www); - setAvailableToSpend(kappa.minus(gas)); + // setNotes(www); + // setAvailableToSpend(kappa.minus(gas)); // TODO: Check if not called to often }, [selectedAsset, notesAtom.data, account, feeProps]); @@ -195,7 +203,7 @@ export const MaspUnshield: React.FC = () => { isDestinationShielded={false} /> - {notes.length > 4 && ( + {notes && notes.length > 4 && ( { let decoded_change = Change::from_masp_denominated(*val, pre_asset_type.position) .expect("expected this to fit"); - let www = ValueSum::from_pair(pre_asset_type.token, decoded_change); + let www = ValueSum::from_pair(pre_asset_type.token.clone(), decoded_change); let www = Self::get_decoded_balance(www); - - dupa.push(www); + let zzz = www + .into_iter() + .map(|(token, amount)| (token, amount, conv.is_some())) + .collect::>(); + + if conv.is_some() { + web_sys::console::log_1( + &format!("Adding conversion for entry {:?} ", zzz) + .into(), + ); + for (_, val) in + I128Sum::from(conv.unwrap().0.clone()).components() + { + + let decoded_change = + Change::from_masp_denominated(*val, pre_asset_type.position) + .expect("expected this to fit"); + let www = ValueSum::from_pair( + pre_asset_type.token.clone(), + decoded_change, + ); + + let www = Self::get_decoded_balance(www); + web_sys::console::log_1(&format!("Conversion {:?}", www).into()); + } + } + + dupa.push(zzz); } _ => {} } diff --git a/packages/shared/lib/src/sdk/tx.rs b/packages/shared/lib/src/sdk/tx.rs index 0d8a4b441b..b31a671673 100644 --- a/packages/shared/lib/src/sdk/tx.rs +++ b/packages/shared/lib/src/sdk/tx.rs @@ -5,7 +5,8 @@ use gloo_utils::format::JsValueSerdeExt; use namada_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; use namada_sdk::collections::HashSet; use namada_sdk::masp_primitives::transaction::components::sapling::builder::StoredBuildParams; -use namada_sdk::masp_primitives::transaction::components::sapling::fees::{InputView, OutputView}; +use namada_sdk::masp_primitives::transaction::components::sapling::fees::{ConvertView, InputView, OutputView}; +use namada_sdk::masp_primitives::transaction::components::I128Sum; use namada_sdk::masp_primitives::zip32::ExtendedFullViewingKey; use namada_sdk::signing::SigningTxData; use namada_sdk::token::{Amount, DenominatedAmount, Transfer}; @@ -410,6 +411,14 @@ fn get_masp_details( }) .collect::>(); + let conversions = masp_builder.builder.sapling_converts().iter().map(|conv| { + I128Sum::from(conv.conversion().clone()) + }).collect::>(); + web_sys::console::log_1(&JsValue::from_str(&format!( + "Masp conversions: {:?}", + conversions + ))); + let outputs = masp_builder .builder .sapling_outputs() From 2bfc589b5678e5d1756aeaeb5de3ef3e64fcc4da Mon Sep 17 00:00:00 2001 From: Mateusz Jasiuk Date: Fri, 8 Aug 2025 11:43:05 +0200 Subject: [PATCH 3/3] feat: wup wip wip --- packages/shared/lib/src/query.rs | 141 +++++++++++++++++------------- packages/shared/lib/src/sdk/tx.rs | 1 + 2 files changed, 79 insertions(+), 63 deletions(-) diff --git a/packages/shared/lib/src/query.rs b/packages/shared/lib/src/query.rs index d210ec6d26..b55ec13c27 100644 --- a/packages/shared/lib/src/query.rs +++ b/packages/shared/lib/src/query.rs @@ -425,84 +425,99 @@ impl Query { let epoch = query_masp_epoch(&self.client).await?; - let mut notes = vec![]; - let mut conv = Conversions::new(); - // Retrieve the notes that can be spent by this key - if let Some(avail_notes) = shielded.pos_map.get(&viewing_key) { - for note_idx in avail_notes { - // Spent notes cannot contribute a new transaction's pool - if shielded.spents.contains(note_idx) { - continue; - } - // Get note associated with this ID - let note = shielded.note_map.get(note_idx).unwrap(); - // Finally add value to multi-asset accumulator - notes.push( - I128Sum::from_nonnegative(note.asset_type, i128::from(note.value)).unwrap(), - ); - } - } + // let mut pos: BTreeMap> = BTreeMap::new(); + // let mut burn: BTreeMap = BTreeMap::new(); + let mut res: Vec<(Address, String)> = vec![]; - for n in ¬es { - for (asset_type, _) in n.components() { - shielded - .query_allowed_conversion(&self.client, *asset_type, &mut conv) - .await - .map_err(|e| JsError::new(&format!("{:?}", e)))?; - } - } + if let Some(balance) = shielded + .compute_shielded_balance(&viewing_key) + .await + .unwrap() + { + let exchanged_amount = shielded + .compute_exchanged_amount( + &self.client, + &WebIo, + balance.clone(), + &mut Conversions::new(), + ) + .await + .unwrap() + .0; - let mut dupa = vec![]; + let conversions = exchanged_amount - balance.clone(); - for note in notes.iter() { - for (asset_type, val) in note.components() { + for (asset_type, val) in conversions.components() { let decoded = shielded.decode_asset_type(&self.client, *asset_type).await; - let conv = conv.get(asset_type); + match decoded { Some(pre_asset_type) if pre_asset_type.epoch.is_none_or(|e| e <= epoch) => { let decoded_change = Change::from_masp_denominated(*val, pre_asset_type.position) .expect("expected this to fit"); - let www = ValueSum::from_pair(pre_asset_type.token.clone(), decoded_change); - - let www = Self::get_decoded_balance(www); - let zzz = www - .into_iter() - .map(|(token, amount)| (token, amount, conv.is_some())) - .collect::>(); - - if conv.is_some() { - web_sys::console::log_1( - &format!("Adding conversion for entry {:?} ", zzz) - .into(), - ); - for (_, val) in - I128Sum::from(conv.unwrap().0.clone()).components() - { - - let decoded_change = - Change::from_masp_denominated(*val, pre_asset_type.position) - .expect("expected this to fit"); - let www = ValueSum::from_pair( - pre_asset_type.token.clone(), - decoded_change, - ); - - let www = Self::get_decoded_balance(www); - web_sys::console::log_1(&format!("Conversion {:?}", www).into()); - } - } - - dupa.push(zzz); + + res.push(( + pre_asset_type.token.clone(), + decoded_change.to_string(), + )); } _ => {} } } - } - let flat = dupa.into_iter().flatten().collect::>(); + // for (asset_type, val) in conversions.components() { + // let decoded = shielded.decode_asset_type(&self.client, *asset_type).await; + // // web_sys::console::info_1(&format!("Decoded {:?}", decoded).into()); + + // match decoded { + // Some(pre_asset_type) if pre_asset_type.epoch.is_none_or(|e| e <= epoch) => { + // let decoded_change = + // Change::from_masp_denominated(*val, pre_asset_type.position) + // .expect("expected this to fit"); + // // web_sys::console::info_1( + // // &format!("Decoded change {:?}", decoded_change).into(), + // // ); + + // if decoded_change < Change::zero() { + // burn.insert(pre_asset_type.token.clone(), decoded_change); + // } else { + // let www = pos.get_mut(&pre_asset_type.token.clone()); + // if let Some(www) = www { + // www.push(decoded_change); + // } else { + // pos.insert(pre_asset_type.token.clone(), vec![decoded_change]); + // } + // } + // } + // _ => {} + // } + // } + } + + + // for (token, amounts) in pos { + // let burns = burn.get(&token); + + // if let Some(burn) = burns { + // for amount in amounts { + // if amount.checked_add(*burn).unwrap() != I256::zero() { + // res.push((token.clone(), token::Amount::from_change(amount))); + // } + // } + // } else { + + // for amount in amounts { + // res.push(( + // token.clone(), + // token::Amount::from_change(amount), + // )); + // } + // } + // } + + web_sys::console::info_1(&format!("Result {:?}", res).into()); - to_js_result(flat) + to_js_result(res) } /// Queries shielded balance for a given extended viewing key diff --git a/packages/shared/lib/src/sdk/tx.rs b/packages/shared/lib/src/sdk/tx.rs index b31a671673..5b65a36fc7 100644 --- a/packages/shared/lib/src/sdk/tx.rs +++ b/packages/shared/lib/src/sdk/tx.rs @@ -414,6 +414,7 @@ fn get_masp_details( let conversions = masp_builder.builder.sapling_converts().iter().map(|conv| { I128Sum::from(conv.conversion().clone()) }).collect::>(); + web_sys::console::log_1(&JsValue::from_str(&format!( "Masp conversions: {:?}", conversions