Skip to content

Commit edf30ef

Browse files
Merge pull request #19 from namada-net/feat/notes-amounts-estimations
feat: notes amounts estimations
2 parents b8d1477 + 974c870 commit edf30ef

File tree

4 files changed

+126
-3
lines changed

4 files changed

+126
-3
lines changed

crate/src/sdk/mod.rs

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use namada_sdk::ibc::core::host::types::identifiers::{ChannelId, PortId};
3030
use namada_sdk::io::{Client, NamadaIo};
3131
use namada_sdk::key::{common, ed25519, RefTo, SigScheme};
3232
use namada_sdk::masp::shielded_wallet::ShieldedApi;
33-
use namada_sdk::masp::ShieldedContext;
33+
use namada_sdk::masp::{Conversions, ShieldedContext, SpentNotesTracker};
3434
use namada_sdk::masp_primitives::sapling::ViewingKey;
3535
use namada_sdk::masp_primitives::transaction::components::amount::I128Sum;
3636
use namada_sdk::masp_primitives::transaction::TxId;
@@ -691,9 +691,21 @@ impl Sdk {
691691
}
692692
};
693693

694+
let masp_section = tx
695+
.sections
696+
.iter()
697+
.find_map(|section| section.masp_tx())
698+
.ok_or_err_msg("Could not find masp_tx section")?;
699+
700+
self.namada
701+
.shielded_mut()
702+
.await
703+
.pre_cache_transaction(&masp_section)
704+
.await
705+
.map_err(|e| JsError::new(&e.to_string()))?;
706+
694707
self.serialize_tx_result(tx, wrapper_tx_msg, signing_data, Some(masp_signing_data))
695708
}
696-
697709
pub async fn build_unshielding_transfer(
698710
&self,
699711
unshielding_transfer_msg: &[u8],
@@ -1165,6 +1177,95 @@ impl Sdk {
11651177
to_js_result(reward)
11661178
}
11671179

1180+
pub async fn query_notes_to_spend(
1181+
&self,
1182+
owner: String,
1183+
chain_id: String,
1184+
) -> Result<JsValue, JsError> {
1185+
let xvk = ExtendedViewingKey::from_str(&owner)?;
1186+
let viewing_key = ExtendedFullViewingKey::from(xvk).fvk.vk;
1187+
1188+
let mut shielded: ShieldedContext<masp::JSShieldedUtils> = ShieldedContext::default();
1189+
shielded.utils.chain_id = chain_id.clone();
1190+
// TODO: pass handler
1191+
shielded.try_load(async |_| {}).await;
1192+
1193+
let res = shielded
1194+
.exchange_notes(
1195+
&self.namada,
1196+
&mut SpentNotesTracker::new(),
1197+
&viewing_key,
1198+
&mut Conversions::new(),
1199+
)
1200+
.await
1201+
.map_err(|e| JsError::new(&e.to_string()))?;
1202+
1203+
let values = res.values().collect::<Vec<_>>();
1204+
1205+
let mut final_res: HashMap<Address, Vec<(Amount, Option<Amount>)>> = HashMap::new();
1206+
1207+
for (_, _, _, value) in values.iter() {
1208+
let mut res: HashMap<Address, Amount> = HashMap::new();
1209+
1210+
for ((digit, address), val) in value.components() {
1211+
if res.contains_key(address) {
1212+
let existing = res
1213+
.get(address)
1214+
.ok_or(JsError::new("Expected to find map entry"))?;
1215+
let new_amount =
1216+
existing
1217+
.checked_add(Amount::from_masp_denominated_i128(*val, *digit).ok_or(
1218+
JsError::new("Cannot create amount from masp denominated"),
1219+
)?)
1220+
.ok_or(JsError::new("Overflow when summing amounts"))?;
1221+
1222+
res.insert(address.clone(), new_amount);
1223+
} else {
1224+
res.insert(
1225+
address.clone(),
1226+
Amount::from_masp_denominated_i128(*val, *digit).unwrap(),
1227+
);
1228+
}
1229+
}
1230+
1231+
let note = res
1232+
.get_index(0)
1233+
.ok_or(JsError::new("Expected to find at least one component"))?;
1234+
let conv = res.get_index(1);
1235+
1236+
let note_addr = note.0.clone();
1237+
if final_res.contains_key(&note_addr) {
1238+
let mut existing = final_res
1239+
.get(&note_addr)
1240+
.ok_or(JsError::new("Expected to find an entry"))?
1241+
.clone();
1242+
existing.push((*note.1, None));
1243+
1244+
final_res.insert(note_addr, existing);
1245+
} else {
1246+
final_res.insert(note_addr, vec![(*note.1, None)]);
1247+
}
1248+
1249+
if let Some(conv) = conv {
1250+
let conv_addr = conv.0.clone();
1251+
if final_res.contains_key(&conv_addr) {
1252+
let mut existing = final_res
1253+
.get(&conv_addr)
1254+
.ok_or(JsError::new("Expected to find an entry"))?
1255+
.clone();
1256+
existing.push((*note.1, Some(*conv.1)));
1257+
1258+
final_res.insert(conv_addr, existing);
1259+
} else {
1260+
final_res.insert(conv_addr, vec![(*note.1, Some(*conv.1))]);
1261+
}
1262+
}
1263+
}
1264+
1265+
to_js_result(final_res)
1266+
}
1267+
1268+
11681269
pub fn masp_address(&self) -> String {
11691270
MASP.to_string()
11701271
}

packages/lib/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export {
5858
ProofGenerationKey,
5959
PseudoExtendedKey,
6060
} from "./masp";
61-
export type { Masp } from "./masp";
61+
export type { Masp, NotesAndConversions } from "./masp";
6262
export { PhraseSize } from "./mnemonic";
6363
export type { Mnemonic } from "./mnemonic";
6464
export type { Signing } from "./signing";

packages/lib/src/masp/masp.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Sdk as SdkWasm } from "@namada/wasm";
2+
import { NotesAndConversions } from "./types";
23

34
/**
45
* Class representing utilities related to MASP
@@ -103,4 +104,19 @@ export class Masp {
103104
clearShieldedContext(chainId: string): Promise<void> {
104105
return SdkWasm.clear_shielded_context(chainId);
105106
}
107+
108+
/**
109+
* Returns all the notes and conversions connected to the given viewing key
110+
*
111+
* @async
112+
* @param viewingKey - Viewing key to get notes and conversions for
113+
* @param chainId - Chain ID to load correct shielded context
114+
* @returns Promise resolving to a map of addresses to arrays of notes and conversions tuple
115+
*/
116+
async getNotesAndConversions(
117+
viewingKey: string,
118+
chainId: string,
119+
): Promise<NotesAndConversions> {
120+
return await this.sdk.query_notes_to_spend(viewingKey, chainId);
121+
}
106122
}

packages/lib/src/masp/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@ export {
33
ProofGenerationKey,
44
PseudoExtendedKey,
55
} from "@namada/wasm";
6+
7+
type Address = string;
8+
type NoteVal = string;
9+
type AmountVal = string;
10+
type NoteAndConv = [NoteVal, AmountVal?];
11+
export type NotesAndConversions = Record<Address, NoteAndConv[]>;

0 commit comments

Comments
 (0)