1use std::collections::{BTreeMap, BTreeSet};
2
3use alloc::format;
4use alloc::string::{String, ToString};
5use alloc::vec::Vec;
6use ibig::UBig;
7use rose_crypto::PrivateKey;
8use rose_grpc_proto::pb::common::v1 as pb_v1;
9use rose_grpc_proto::pb::common::v2 as pb;
10use rose_nockchain_types::{
11 builder::TxBuilder,
12 note::{Name, Note, NoteData, NoteDataEntry, Pkh, TimelockRange, Version},
13 tx::{LockPrimitive, LockRoot, NockchainTx, RawTx, Seed, SpendCondition},
14 Nicks,
15};
16use rose_nockchain_types::{Hax, LockTim, MissingUnlocks, Source, SpendBuilder};
17use rose_ztd::{cue, jam, Digest, Hashable as HashableTrait, NounDecode, NounEncode};
18use serde::{Deserialize, Serialize};
19use wasm_bindgen::prelude::*;
20
21use crate::memo::memo_from_js;
22
23#[wasm_bindgen(js_name = Digest)]
28#[derive(Clone, Serialize, Deserialize)]
29#[serde(transparent)]
30pub struct WasmDigest {
31 #[wasm_bindgen(skip)]
32 pub value: String,
33}
34
35#[wasm_bindgen(js_class = Digest)]
36impl WasmDigest {
37 #[wasm_bindgen(constructor)]
38 pub fn new(value: String) -> Self {
39 Self { value }
40 }
41
42 #[wasm_bindgen(getter)]
43 pub fn value(&self) -> String {
44 self.value.clone()
45 }
46
47 fn to_internal(&self) -> Result<Digest, &'static str> {
48 self.value.as_str().try_into()
49 }
50
51 fn from_internal(digest: &Digest) -> Self {
52 Self {
53 value: digest.to_string(),
54 }
55 }
56
57 #[wasm_bindgen(js_name = toProtobuf)]
58 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
59 let digest = self.to_internal().map_err(JsValue::from_str)?;
60 let pb = pb_v1::Hash::from(digest);
61 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
62 }
63
64 #[wasm_bindgen(js_name = fromProtobuf)]
65 pub fn from_protobuf(value: JsValue) -> Result<WasmDigest, JsValue> {
66 let pb: pb_v1::Hash = serde_wasm_bindgen::from_value(value)?;
67 let digest: Digest = pb
68 .try_into()
69 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
70 Ok(WasmDigest::from_internal(&digest))
71 }
72}
73
74#[wasm_bindgen(js_name = Version)]
75#[derive(Clone, Serialize, Deserialize)]
76pub struct WasmVersion {
77 version: u32,
78}
79
80#[wasm_bindgen(js_class = Version)]
81impl WasmVersion {
82 #[wasm_bindgen(constructor)]
83 pub fn new(version: u32) -> Self {
84 Self { version }
85 }
86
87 #[wasm_bindgen(js_name = V0)]
88 pub fn v0() -> Self {
89 Self { version: 0 }
90 }
91
92 #[wasm_bindgen(js_name = V1)]
93 pub fn v1() -> Self {
94 Self { version: 1 }
95 }
96
97 #[wasm_bindgen(js_name = V2)]
98 pub fn v2() -> Self {
99 Self { version: 2 }
100 }
101
102 fn to_internal(&self) -> Version {
103 self.version.into()
104 }
105
106 fn from_internal(version: &Version) -> Self {
107 Self {
108 version: version.clone().into(),
109 }
110 }
111}
112
113#[wasm_bindgen(js_name = Name)]
114#[derive(Clone, Serialize, Deserialize)]
115pub struct WasmName {
116 #[wasm_bindgen(skip)]
117 pub first: Digest,
118 #[wasm_bindgen(skip)]
119 pub last: Digest,
120}
121
122#[wasm_bindgen(js_class = Name)]
123impl WasmName {
124 #[wasm_bindgen(constructor)]
125 pub fn new(first: String, last: String) -> Result<Self, JsValue> {
126 let first = Digest::try_from(&*first)?;
127 let last = Digest::try_from(&*last)?;
128 Ok(Self { first, last })
129 }
130
131 #[wasm_bindgen(getter)]
132 pub fn first(&self) -> String {
133 self.first.to_string()
134 }
135
136 #[wasm_bindgen(getter)]
137 pub fn last(&self) -> String {
138 self.last.to_string()
139 }
140
141 fn to_internal(&self) -> Name {
142 Name::new(self.first, self.last)
143 }
144
145 #[allow(dead_code)]
146 fn from_internal(name: &Name) -> Self {
147 Self {
150 first: name.first,
151 last: name.last,
152 }
153 }
154
155 #[wasm_bindgen(js_name = toProtobuf)]
156 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
157 let name = self.to_internal();
158 let pb = pb_v1::Name::from(name);
159 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
160 }
161
162 #[wasm_bindgen(js_name = fromProtobuf)]
163 pub fn from_protobuf(value: JsValue) -> Result<WasmName, JsValue> {
164 let pb: pb_v1::Name = serde_wasm_bindgen::from_value(value)?;
165 let name: Name = pb
166 .try_into()
167 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
168 Ok(WasmName::from_internal(&name))
169 }
170}
171
172#[wasm_bindgen(js_name = TimelockRange)]
173#[derive(Clone, Serialize, Deserialize)]
174pub struct WasmTimelockRange {
175 #[wasm_bindgen(skip)]
176 pub min: Option<u64>,
177 #[wasm_bindgen(skip)]
178 pub max: Option<u64>,
179}
180
181#[wasm_bindgen(js_class = TimelockRange)]
182impl WasmTimelockRange {
183 #[wasm_bindgen(constructor)]
184 pub fn new(min: Option<u64>, max: Option<u64>) -> Self {
185 Self { min, max }
186 }
187
188 #[wasm_bindgen(getter)]
189 pub fn min(&self) -> Option<u64> {
190 self.min
191 }
192
193 #[wasm_bindgen(getter)]
194 pub fn max(&self) -> Option<u64> {
195 self.max
196 }
197
198 fn to_internal(&self) -> TimelockRange {
199 TimelockRange::new(self.min, self.max)
200 }
201
202 fn from_internal(internal: TimelockRange) -> WasmTimelockRange {
203 WasmTimelockRange {
204 min: internal.min,
205 max: internal.max,
206 }
207 }
208}
209
210#[wasm_bindgen(js_name = Source)]
211#[derive(Clone, Serialize, Deserialize)]
212pub struct WasmSource {
213 #[wasm_bindgen(skip)]
214 pub hash: WasmDigest,
215 #[wasm_bindgen(skip)]
216 pub is_coinbase: bool,
217}
218
219#[wasm_bindgen(js_class = Source)]
220impl WasmSource {
221 #[wasm_bindgen(getter, js_name = hash)]
222 pub fn hash(&self) -> WasmDigest {
223 self.hash.clone()
224 }
225
226 #[wasm_bindgen(getter, js_name = isCoinbase)]
227 pub fn is_coinbase(&self) -> bool {
228 self.is_coinbase
229 }
230
231 fn to_internal(&self) -> Result<Source, String> {
232 Ok(Source {
233 hash: self.hash.to_internal()?,
234 is_coinbase: self.is_coinbase,
235 })
236 }
237
238 fn from_internal(internal: &Source) -> Self {
239 Self {
240 hash: WasmDigest::from_internal(&internal.hash),
241 is_coinbase: internal.is_coinbase,
242 }
243 }
244}
245
246#[wasm_bindgen(js_name = NoteDataEntry)]
251#[derive(Clone, Serialize, Deserialize)]
252pub struct WasmNoteDataEntry {
253 #[wasm_bindgen(skip)]
254 pub key: String,
255 #[wasm_bindgen(skip)]
256 pub blob: Vec<u8>,
257}
258
259#[wasm_bindgen(js_class = NoteDataEntry)]
260impl WasmNoteDataEntry {
261 #[wasm_bindgen(constructor)]
262 pub fn new(key: String, blob: Vec<u8>) -> Self {
263 Self { key, blob }
264 }
265
266 #[wasm_bindgen(getter)]
267 pub fn key(&self) -> String {
268 self.key.clone()
269 }
270
271 #[wasm_bindgen(getter)]
272 pub fn blob(&self) -> Vec<u8> {
273 self.blob.clone()
274 }
275
276 fn to_internal(&self) -> Result<NoteDataEntry, String> {
277 let val = cue(&self.blob).ok_or_else(|| "Failed to deserialize noun".to_string())?;
278 Ok(NoteDataEntry {
279 key: self.key.clone(),
280 val,
281 })
282 }
283
284 fn from_internal(entry: &NoteDataEntry) -> Self {
285 Self {
286 key: entry.key.clone(),
287 blob: jam(entry.val.clone()),
288 }
289 }
290
291 #[wasm_bindgen(js_name = toProtobuf)]
292 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
293 let entry = self.to_internal().map_err(|e| JsValue::from_str(&e))?;
294 let pb = pb::NoteDataEntry::from(entry);
295 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
296 }
297
298 #[wasm_bindgen(js_name = fromProtobuf)]
299 pub fn from_protobuf(value: JsValue) -> Result<WasmNoteDataEntry, JsValue> {
300 let pb: pb::NoteDataEntry = serde_wasm_bindgen::from_value(value)?;
301 let entry: NoteDataEntry = pb
302 .try_into()
303 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
304 Ok(WasmNoteDataEntry::from_internal(&entry))
305 }
306}
307
308#[wasm_bindgen(js_name = NoteData)]
309#[derive(Clone, Serialize, Deserialize)]
310pub struct WasmNoteData {
311 #[wasm_bindgen(skip)]
312 pub entries: Vec<WasmNoteDataEntry>,
313}
314
315#[wasm_bindgen(js_class = NoteData)]
316impl WasmNoteData {
317 #[wasm_bindgen(constructor)]
318 pub fn new(entries: Vec<WasmNoteDataEntry>) -> Self {
319 Self { entries }
320 }
321
322 #[wasm_bindgen]
323 pub fn empty() -> Self {
324 Self {
325 entries: Vec::new(),
326 }
327 }
328
329 #[wasm_bindgen(js_name = fromPkh)]
330 pub fn from_pkh(pkh: WasmPkh) -> Result<Self, JsValue> {
331 let note_data = NoteData::from_pkh(pkh.to_internal()?);
332 Ok(Self::from_internal(¬e_data))
333 }
334
335 #[wasm_bindgen(getter)]
336 pub fn entries(&self) -> Vec<WasmNoteDataEntry> {
337 self.entries.clone()
338 }
339
340 fn to_internal(&self) -> Result<NoteData, String> {
341 let entries: Result<Vec<NoteDataEntry>, String> =
342 self.entries.iter().map(|e| e.to_internal()).collect();
343 Ok(NoteData { entries: entries? })
344 }
345
346 fn from_internal(note_data: &NoteData) -> Self {
347 Self {
348 entries: note_data
349 .entries
350 .iter()
351 .map(WasmNoteDataEntry::from_internal)
352 .collect(),
353 }
354 }
355
356 #[wasm_bindgen(js_name = toProtobuf)]
357 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
358 let data = self.to_internal().map_err(|e| JsValue::from_str(&e))?;
359 let pb = pb::NoteData::from(data);
360 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
361 }
362
363 #[wasm_bindgen(js_name = fromProtobuf)]
364 pub fn from_protobuf(value: JsValue) -> Result<WasmNoteData, JsValue> {
365 let pb: pb::NoteData = serde_wasm_bindgen::from_value(value)?;
366 let data: NoteData = pb
367 .try_into()
368 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
369 Ok(WasmNoteData::from_internal(&data))
370 }
371}
372
373#[wasm_bindgen(js_name = Note)]
374#[derive(Clone, Serialize, Deserialize)]
375pub struct WasmNote {
376 #[wasm_bindgen(skip)]
377 pub version: WasmVersion,
378 #[wasm_bindgen(skip)]
379 pub origin_page: u64,
380 #[wasm_bindgen(skip)]
381 pub name: WasmName,
382 #[wasm_bindgen(skip)]
383 pub note_data: WasmNoteData,
384 #[wasm_bindgen(skip)]
385 pub assets: Nicks,
386}
387
388#[wasm_bindgen(js_class = Note)]
389impl WasmNote {
390 #[wasm_bindgen(constructor)]
391 pub fn new(
392 version: WasmVersion,
393 origin_page: u64,
394 name: WasmName,
395 note_data: WasmNoteData,
396 assets: Nicks,
397 ) -> Self {
398 Self {
399 version,
400 origin_page,
401 name,
402 note_data,
403 assets,
404 }
405 }
406
407 #[wasm_bindgen(getter)]
408 pub fn version(&self) -> WasmVersion {
409 self.version.clone()
410 }
411
412 #[wasm_bindgen(getter, js_name = originPage)]
413 pub fn origin_page(&self) -> u64 {
414 self.origin_page
415 }
416
417 #[wasm_bindgen(getter)]
418 pub fn name(&self) -> WasmName {
419 self.name.clone()
420 }
421
422 #[wasm_bindgen(getter, js_name = noteData)]
423 pub fn note_data(&self) -> WasmNoteData {
424 self.note_data.clone()
425 }
426
427 #[wasm_bindgen(getter)]
428 pub fn assets(&self) -> Nicks {
429 self.assets
430 }
431
432 #[wasm_bindgen]
433 pub fn hash(&self) -> Result<WasmDigest, JsValue> {
434 let note = self
435 .to_internal()
436 .map_err(|e| JsValue::from_str(&e.to_string()))?;
437 Ok(WasmDigest::from_internal(¬e.hash()))
438 }
439
440 #[wasm_bindgen(js_name = fromProtobuf)]
443 pub fn from_protobuf(pb_note: JsValue) -> Result<WasmNote, JsValue> {
444 let pb: pb::Note = serde_wasm_bindgen::from_value(pb_note)?;
445 let note: Note = pb
446 .try_into()
447 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
448 Ok(WasmNote::from_internal(note))
449 }
450
451 #[wasm_bindgen(js_name = toProtobuf)]
452 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
453 let note = self
454 .to_internal()
455 .map_err(|e| JsValue::from_str(&e.to_string()))?;
456 let pb = pb::Note::from(note);
457 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
458 }
459
460 fn to_internal(&self) -> Result<Note, String> {
461 Ok(Note::new(
462 self.version.to_internal(),
463 self.origin_page,
464 self.name.to_internal(),
465 self.note_data.to_internal()?,
466 self.assets,
467 ))
468 }
469
470 fn from_internal(internal: Note) -> Self {
471 Self {
472 version: WasmVersion::from_internal(&internal.version),
473 origin_page: internal.origin_page,
474 name: WasmName::from_internal(&internal.name),
475 note_data: WasmNoteData::from_internal(&internal.note_data),
476 assets: internal.assets,
477 }
478 }
479}
480
481#[wasm_bindgen(js_name = Pkh)]
486#[derive(Clone, Serialize, Deserialize)]
487pub struct WasmPkh {
488 #[wasm_bindgen(skip)]
489 pub m: u64,
490 #[wasm_bindgen(skip)]
491 pub hashes: Vec<String>,
492}
493
494#[wasm_bindgen(js_class = Pkh)]
495impl WasmPkh {
496 #[wasm_bindgen(constructor)]
497 pub fn new(m: u64, hashes: Vec<String>) -> Self {
498 Self { m, hashes }
499 }
500
501 #[wasm_bindgen]
502 pub fn single(hash: String) -> Self {
503 Self {
504 m: 1,
505 hashes: alloc::vec![hash],
506 }
507 }
508
509 #[wasm_bindgen(getter)]
510 pub fn m(&self) -> u64 {
511 self.m
512 }
513
514 #[wasm_bindgen(getter)]
515 pub fn hashes(&self) -> Vec<String> {
516 self.hashes.clone()
517 }
518
519 fn to_internal(&self) -> Result<Pkh, String> {
520 let hashes: Result<Vec<Digest>, _> =
521 self.hashes.iter().map(|s| s.as_str().try_into()).collect();
522 Ok(Pkh::new(self.m, hashes?))
523 }
524
525 fn from_internal(internal: Pkh) -> Self {
526 Self::new(
527 internal.m,
528 internal.hashes.into_iter().map(|v| v.to_string()).collect(),
529 )
530 }
531
532 #[wasm_bindgen(js_name = toProtobuf)]
533 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
534 let pkh = self.to_internal().map_err(|e| JsValue::from_str(&e))?;
535 let pb = pb::PkhLock::from(pkh);
536 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
537 }
538
539 #[wasm_bindgen(js_name = fromProtobuf)]
540 pub fn from_protobuf(value: JsValue) -> Result<WasmPkh, JsValue> {
541 let pb: pb::PkhLock = serde_wasm_bindgen::from_value(value)?;
542 let pkh: Pkh = pb
543 .try_into()
544 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
545 Ok(WasmPkh::from_internal(pkh))
546 }
547}
548
549#[wasm_bindgen(js_name = LockTim)]
550#[derive(Clone, Serialize, Deserialize)]
551pub struct WasmLockTim {
552 #[wasm_bindgen(skip)]
553 pub rel: WasmTimelockRange,
554 #[wasm_bindgen(skip)]
555 pub abs: WasmTimelockRange,
556}
557
558#[wasm_bindgen(js_class = LockTim)]
559impl WasmLockTim {
560 #[wasm_bindgen(constructor)]
561 pub fn new(rel: WasmTimelockRange, abs: WasmTimelockRange) -> Self {
562 Self { rel, abs }
563 }
564
565 #[wasm_bindgen]
566 pub fn coinbase() -> Self {
567 let tim = LockTim::coinbase();
568 Self {
569 rel: WasmTimelockRange {
570 min: tim.rel.min,
571 max: tim.rel.max,
572 },
573 abs: WasmTimelockRange {
574 min: tim.abs.min,
575 max: tim.abs.max,
576 },
577 }
578 }
579
580 #[wasm_bindgen(getter)]
581 pub fn rel(&self) -> WasmTimelockRange {
582 self.rel.clone()
583 }
584
585 #[wasm_bindgen(getter)]
586 pub fn abs(&self) -> WasmTimelockRange {
587 self.abs.clone()
588 }
589
590 fn to_internal(&self) -> LockTim {
591 LockTim {
592 rel: self.rel.to_internal(),
593 abs: self.abs.to_internal(),
594 }
595 }
596
597 fn from_internal(internal: LockTim) -> WasmLockTim {
598 WasmLockTim {
599 rel: WasmTimelockRange::from_internal(internal.rel),
600 abs: WasmTimelockRange::from_internal(internal.abs),
601 }
602 }
603
604 #[wasm_bindgen(js_name = toProtobuf)]
605 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
606 let tim = self.to_internal();
607 let pb = pb::LockTim::from(tim);
608 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
609 }
610
611 #[wasm_bindgen(js_name = fromProtobuf)]
612 pub fn from_protobuf(value: JsValue) -> Result<WasmLockTim, JsValue> {
613 let pb: pb::LockTim = serde_wasm_bindgen::from_value(value)?;
614 let tim: LockTim = pb
615 .try_into()
616 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
617 Ok(WasmLockTim::from_internal(tim))
618 }
619}
620
621#[wasm_bindgen(js_name = Hax)]
622#[derive(Clone, Serialize, Deserialize)]
623pub struct WasmHax {
624 #[wasm_bindgen(skip)]
625 pub digests: Vec<WasmDigest>,
626}
627
628#[wasm_bindgen(js_class = Hax)]
629impl WasmHax {
630 #[wasm_bindgen(constructor)]
631 pub fn new(digests: Vec<WasmDigest>) -> Self {
632 Self { digests }
633 }
634
635 #[wasm_bindgen(getter)]
636 pub fn digests(&self) -> Vec<WasmDigest> {
637 self.digests.clone()
638 }
639
640 fn to_internal(&self) -> Result<Hax, String> {
641 Ok(Hax(self
642 .digests
643 .iter()
644 .map(WasmDigest::to_internal)
645 .collect::<Result<Vec<_>, _>>()?))
646 }
647
648 fn from_internal(internal: Hax) -> Self {
649 Self::new(internal.0.iter().map(WasmDigest::from_internal).collect())
650 }
651}
652
653#[wasm_bindgen(js_name = LockPrimitive)]
654#[derive(Clone, Serialize, Deserialize)]
655pub struct WasmLockPrimitive {
656 variant: String,
657 #[wasm_bindgen(skip)]
658 pub pkh_data: Option<WasmPkh>,
659 #[wasm_bindgen(skip)]
660 pub tim_data: Option<WasmLockTim>,
661 #[wasm_bindgen(skip)]
662 pub hax_data: Option<WasmHax>,
663}
664
665#[wasm_bindgen(js_class = LockPrimitive)]
666impl WasmLockPrimitive {
667 #[wasm_bindgen(js_name = newPkh)]
668 pub fn new_pkh(pkh: WasmPkh) -> WasmLockPrimitive {
669 Self {
670 variant: "pkh".to_string(),
671 pkh_data: Some(pkh),
672 tim_data: None,
673 hax_data: None,
674 }
675 }
676
677 #[wasm_bindgen(js_name = newTim)]
678 pub fn new_tim(tim: WasmLockTim) -> WasmLockPrimitive {
679 Self {
680 variant: "tim".to_string(),
681 pkh_data: None,
682 tim_data: Some(tim),
683 hax_data: None,
684 }
685 }
686
687 #[wasm_bindgen(js_name = newHax)]
688 pub fn new_hax(hax: WasmHax) -> Self {
689 Self {
690 variant: "hax".to_string(),
691 pkh_data: None,
692 tim_data: None,
693 hax_data: Some(hax),
694 }
695 }
696
697 #[wasm_bindgen(js_name = newBrn)]
698 pub fn new_brn() -> Self {
699 Self {
700 variant: "brn".to_string(),
701 pkh_data: None,
702 tim_data: None,
703 hax_data: None,
704 }
705 }
706
707 fn to_internal(&self) -> Result<LockPrimitive, String> {
708 match self.variant.as_str() {
709 "pkh" => {
710 if let Some(ref pkh) = self.pkh_data {
711 Ok(LockPrimitive::Pkh(pkh.to_internal()?))
712 } else {
713 Err("Missing pkh data".to_string())
714 }
715 }
716 "tim" => {
717 if let Some(ref tim) = self.tim_data {
718 Ok(LockPrimitive::Tim(tim.to_internal()))
719 } else {
720 Err("Missing tim data".to_string())
721 }
722 }
723 "hax" => {
724 if let Some(ref hax) = self.hax_data {
725 Ok(LockPrimitive::Hax(hax.to_internal()?))
726 } else {
727 Err("Missing hax data".to_string())
728 }
729 }
730 "brn" => Ok(LockPrimitive::Brn),
731 _ => Err("Invalid lock primitive variant".to_string()),
732 }
733 }
734
735 fn from_internal(internal: LockPrimitive) -> Self {
736 match internal {
737 LockPrimitive::Pkh(p) => Self::new_pkh(WasmPkh::from_internal(p)),
738 LockPrimitive::Tim(t) => Self::new_tim(WasmLockTim::from_internal(t)),
739 LockPrimitive::Hax(h) => Self::new_hax(WasmHax::from_internal(h)),
740 LockPrimitive::Brn => Self::new_brn(),
741 }
742 }
743
744 #[wasm_bindgen(js_name = toProtobuf)]
745 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
746 let prim = self.to_internal().map_err(|e| JsValue::from_str(&e))?;
747 let pb = pb::LockPrimitive::from(prim);
748 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
749 }
750
751 #[wasm_bindgen(js_name = fromProtobuf)]
752 pub fn from_protobuf(value: JsValue) -> Result<WasmLockPrimitive, JsValue> {
753 let pb: pb::LockPrimitive = serde_wasm_bindgen::from_value(value)?;
754 let prim: LockPrimitive = pb
755 .try_into()
756 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
757 Ok(WasmLockPrimitive::from_internal(prim))
758 }
759}
760
761#[wasm_bindgen(js_name = SpendCondition)]
762#[derive(Clone, Serialize, Deserialize)]
763pub struct WasmSpendCondition {
764 #[wasm_bindgen(skip)]
765 pub primitives: Vec<WasmLockPrimitive>,
766}
767
768#[wasm_bindgen(js_class = SpendCondition)]
769impl WasmSpendCondition {
770 #[wasm_bindgen(constructor)]
771 pub fn new(primitives: Vec<WasmLockPrimitive>) -> Self {
772 Self { primitives }
773 }
774
775 #[wasm_bindgen(js_name = newPkh)]
776 pub fn new_pkh(pkh: WasmPkh) -> WasmSpendCondition {
777 let primitive = WasmLockPrimitive::new_pkh(pkh);
778 Self {
779 primitives: alloc::vec![primitive],
780 }
781 }
782
783 #[wasm_bindgen]
784 pub fn hash(&self) -> Result<WasmDigest, JsValue> {
785 let condition = self
786 .to_internal()
787 .map_err(|e| JsValue::from_str(&e.to_string()))?;
788 Ok(WasmDigest::from_internal(&condition.hash()))
789 }
790
791 #[wasm_bindgen(js_name = firstName)]
792 pub fn first_name(&self) -> Result<WasmDigest, JsValue> {
793 let condition = self
794 .to_internal()
795 .map_err(|e| JsValue::from_str(&e.to_string()))?;
796 Ok(WasmDigest::from_internal(&condition.first_name()))
797 }
798
799 fn to_internal(&self) -> Result<SpendCondition, String> {
800 let mut primitives = Vec::new();
801 for prim in &self.primitives {
802 primitives.push(prim.to_internal()?);
803 }
804 Ok(SpendCondition(primitives))
805 }
806
807 fn from_internal(internal: SpendCondition) -> Self {
808 Self::new(
809 internal
810 .0
811 .into_iter()
812 .map(WasmLockPrimitive::from_internal)
813 .collect(),
814 )
815 }
816
817 #[wasm_bindgen(js_name = toProtobuf)]
818 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
819 let cond = self.to_internal().map_err(|e| JsValue::from_str(&e))?;
820 let pb = pb::SpendCondition::from(cond);
821 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
822 }
823
824 #[wasm_bindgen(js_name = fromProtobuf)]
825 pub fn from_protobuf(value: JsValue) -> Result<WasmSpendCondition, JsValue> {
826 let pb: pb::SpendCondition = serde_wasm_bindgen::from_value(value)?;
827 let cond: SpendCondition = pb
828 .try_into()
829 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
830 Ok(WasmSpendCondition::from_internal(cond))
831 }
832}
833
834#[wasm_bindgen(js_name = LockRoot)]
835#[derive(Clone, Debug)]
836pub struct WasmLockRoot {
837 #[wasm_bindgen(skip)]
838 pub internal: LockRoot,
839}
840
841#[wasm_bindgen(js_class = LockRoot)]
842impl WasmLockRoot {
843 #[wasm_bindgen(js_name = fromHash)]
844 pub fn from_hash(hash: WasmDigest) -> Result<Self, JsValue> {
845 Ok(Self {
846 internal: LockRoot::Hash(hash.to_internal()?),
847 })
848 }
849
850 #[wasm_bindgen(js_name = fromSpendCondition)]
851 pub fn from_spend_condition(cond: WasmSpendCondition) -> Result<Self, JsValue> {
852 Ok(Self {
853 internal: LockRoot::Lock(cond.to_internal()?),
854 })
855 }
856
857 #[wasm_bindgen(getter, js_name = hash)]
858 pub fn hash(&self) -> WasmDigest {
859 WasmDigest::from_internal(&self.internal.hash())
860 }
861
862 #[wasm_bindgen(getter, js_name = lock)]
863 pub fn lock(&self) -> Option<WasmSpendCondition> {
864 match &self.internal {
865 LockRoot::Lock(cond) => Some(WasmSpendCondition::from_internal(cond.clone())),
866 _ => None,
867 }
868 }
869
870 fn to_internal(&self) -> LockRoot {
871 self.internal.clone()
872 }
873
874 fn from_internal(internal: LockRoot) -> Self {
875 Self { internal }
876 }
877}
878
879#[wasm_bindgen(js_name = Seed)]
880pub struct WasmSeed {
881 #[wasm_bindgen(skip)]
882 pub output_source: Option<WasmSource>,
883 #[wasm_bindgen(skip)]
884 pub lock_root: WasmLockRoot,
885 #[wasm_bindgen(skip)]
886 pub gift: Nicks,
887 #[wasm_bindgen(skip)]
888 pub note_data: WasmNoteData,
889 #[wasm_bindgen(skip)]
890 pub parent_hash: WasmDigest,
891}
892
893#[wasm_bindgen(js_class = Seed)]
894impl WasmSeed {
895 #[wasm_bindgen(constructor)]
896 pub fn new(
897 output_source: Option<WasmSource>,
898 lock_root: WasmLockRoot,
899 gift: Nicks,
900 note_data: WasmNoteData,
901 parent_hash: WasmDigest,
902 ) -> Self {
903 Self {
904 output_source,
905 lock_root,
906 gift,
907 note_data,
908 parent_hash,
909 }
910 }
911
912 #[wasm_bindgen(js_name = newSinglePkh)]
913 pub fn new_single_pkh(
914 pkh: WasmDigest,
915 gift: Nicks,
916 parent_hash: WasmDigest,
917 include_lock_data: bool,
918 memo: Option<JsValue>,
919 ) -> Result<Self, JsValue> {
920 let memo = memo_from_js(memo)?;
921 let seed = Seed::new_single_pkh(
922 pkh.to_internal()?,
923 gift,
924 parent_hash.to_internal()?,
925 include_lock_data,
926 memo,
927 );
928 Ok(seed.into())
929 }
930
931 #[wasm_bindgen(getter, js_name = outputSource)]
932 pub fn output_source(&self) -> Option<WasmSource> {
933 self.output_source.clone()
934 }
935
936 #[wasm_bindgen(setter, js_name = outputSource)]
937 pub fn set_output_source(&mut self, output_source: Option<WasmSource>) {
938 self.output_source = output_source;
939 }
940
941 #[wasm_bindgen(getter, js_name = lockRoot)]
942 pub fn lock_root(&self) -> WasmLockRoot {
943 self.lock_root.clone()
944 }
945
946 #[wasm_bindgen(setter, js_name = lockRoot)]
947 pub fn set_lock_root(&mut self, lock_root: WasmLockRoot) {
948 self.lock_root = lock_root;
949 }
950
951 #[wasm_bindgen(getter)]
952 pub fn gift(&self) -> Nicks {
953 self.gift
954 }
955
956 #[wasm_bindgen(setter)]
957 pub fn set_gift(&mut self, gift: Nicks) {
958 self.gift = gift;
959 }
960
961 #[wasm_bindgen(getter, js_name = noteData)]
962 pub fn note_data(&self) -> WasmNoteData {
963 self.note_data.clone()
964 }
965
966 #[wasm_bindgen(setter, js_name = noteData)]
967 pub fn set_note_data(&mut self, note_data: WasmNoteData) {
968 self.note_data = note_data;
969 }
970
971 #[wasm_bindgen(getter, js_name = parentHash)]
972 pub fn parent_hash(&self) -> WasmDigest {
973 self.parent_hash.clone()
974 }
975
976 #[wasm_bindgen(setter, js_name = parentHash)]
977 pub fn set_parent_hash(&mut self, parent_hash: WasmDigest) {
978 self.parent_hash = parent_hash;
979 }
980
981 fn to_internal(&self) -> Result<Seed, String> {
982 Ok(Seed {
983 output_source: self
984 .output_source
985 .as_ref()
986 .map(WasmSource::to_internal)
987 .transpose()?,
988 lock_root: self.lock_root.to_internal(),
989 gift: self.gift,
990 note_data: self.note_data.to_internal()?,
991 parent_hash: self.parent_hash.to_internal()?,
992 })
993 }
994}
995
996impl From<Seed> for WasmSeed {
997 fn from(value: Seed) -> Self {
998 Self {
999 output_source: value.output_source.as_ref().map(WasmSource::from_internal),
1000 lock_root: WasmLockRoot::from_internal(value.lock_root),
1001 gift: value.gift,
1002 note_data: WasmNoteData::from_internal(&value.note_data),
1003 parent_hash: WasmDigest::from_internal(&value.parent_hash),
1004 }
1005 }
1006}
1007
1008#[wasm_bindgen]
1009impl WasmSeed {
1010 #[wasm_bindgen(js_name = toProtobuf)]
1011 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
1012 let seed = self.to_internal().map_err(|e| JsValue::from_str(&e))?;
1013 let pb = pb::Seed::from(seed);
1014 serde_wasm_bindgen::to_value(&pb).map_err(|e| e.into())
1015 }
1016
1017 fn from_internal(seed: Seed) -> Self {
1018 seed.into()
1019 }
1020
1021 #[wasm_bindgen(js_name = fromProtobuf)]
1022 pub fn from_protobuf(value: JsValue) -> Result<WasmSeed, JsValue> {
1023 let pb: pb::Seed = serde_wasm_bindgen::from_value(value)?;
1024 let seed: Seed = pb
1025 .try_into()
1026 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
1027 Ok(WasmSeed::from_internal(seed))
1028 }
1029}
1030
1031#[wasm_bindgen(js_name = TxBuilder)]
1036pub struct WasmTxBuilder {
1037 builder: TxBuilder,
1038}
1039
1040#[wasm_bindgen(js_class = TxBuilder)]
1041impl WasmTxBuilder {
1042 #[wasm_bindgen(constructor)]
1044 pub fn new(fee_per_word: Nicks) -> Self {
1045 Self {
1046 builder: TxBuilder::new(fee_per_word),
1047 }
1048 }
1049
1050 #[wasm_bindgen(js_name = fromTx)]
1055 pub fn from_tx(
1056 tx: WasmRawTx,
1057 notes: Vec<WasmNote>,
1058 spend_conditions: Vec<WasmSpendCondition>,
1059 ) -> Result<Self, JsValue> {
1060 if notes.len() != spend_conditions.len() {
1061 return Err(JsValue::from_str(
1062 "notes and spend_conditions must have the same length",
1063 ));
1064 }
1065
1066 let internal_notes: Result<BTreeMap<Name, (Note, SpendCondition)>, String> = notes
1067 .iter()
1068 .zip(spend_conditions.iter())
1069 .map(|(n, sc)| Ok((n.to_internal()?, sc.to_internal()?)))
1070 .map(|v| v.map(|(a, b)| (a.name.clone(), (a, b))))
1071 .collect();
1072 let internal_notes = internal_notes.map_err(|e| JsValue::from_str(&e.to_string()))?;
1073
1074 let builder = TxBuilder::from_tx(tx.internal, internal_notes).map_err(|e| e.to_string())?;
1075
1076 Ok(Self { builder })
1077 }
1078
1079 #[allow(clippy::too_many_arguments)]
1105 #[wasm_bindgen(js_name = simpleSpend)]
1106 pub fn simple_spend(
1107 &mut self,
1108 notes: Vec<WasmNote>,
1109 spend_conditions: Vec<WasmSpendCondition>,
1110 recipient: WasmDigest,
1111 gift: Nicks,
1112 fee_override: Option<Nicks>,
1113 refund_pkh: WasmDigest,
1114 include_lock_data: bool,
1115 memo: Option<JsValue>,
1116 ) -> Result<(), JsValue> {
1117 if notes.len() != spend_conditions.len() {
1118 return Err(JsValue::from_str(
1119 "notes and spend_conditions must have the same length",
1120 ));
1121 }
1122
1123 let internal_notes: Result<Vec<(Note, SpendCondition)>, String> = notes
1124 .iter()
1125 .zip(spend_conditions.iter())
1126 .map(|(n, sc)| Ok((n.to_internal()?, sc.to_internal()?)))
1127 .collect();
1128 let internal_notes = internal_notes.map_err(|e| JsValue::from_str(&e.to_string()))?;
1129 let memo = memo_from_js(memo)?;
1130
1131 self.builder
1132 .simple_spend_base(
1133 internal_notes,
1134 recipient.to_internal()?,
1135 gift,
1136 refund_pkh.to_internal()?,
1137 include_lock_data,
1138 memo,
1139 )
1140 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
1141
1142 if let Some(fee) = fee_override {
1143 self.builder
1144 .set_fee_and_balance_refund(fee, false, include_lock_data)
1145 } else {
1146 self.builder.recalc_and_set_fee(include_lock_data)
1147 }
1148 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
1149
1150 Ok(())
1151 }
1152
1153 pub fn spend(&mut self, spend: WasmSpendBuilder) -> Option<WasmSpendBuilder> {
1155 self.builder.spend(spend.into()).map(|v| v.into())
1156 }
1157
1158 #[wasm_bindgen(js_name = setFeeAndBalanceRefund)]
1166 pub fn set_fee_and_balance_refund(
1167 &mut self,
1168 fee: Nicks,
1169 adjust_fee: bool,
1170 include_lock_data: bool,
1171 ) -> Result<(), JsValue> {
1172 self.builder
1173 .set_fee_and_balance_refund(fee, adjust_fee, include_lock_data)
1174 .map_err(|e| e.to_string())?;
1175 Ok(())
1176 }
1177
1178 #[wasm_bindgen(js_name = recalcAndSetFee)]
1180 pub fn recalc_and_set_fee(&mut self, include_lock_data: bool) -> Result<(), JsValue> {
1181 self.builder
1182 .recalc_and_set_fee(include_lock_data)
1183 .map_err(|e| e.to_string())?;
1184 Ok(())
1185 }
1186
1187 #[wasm_bindgen(js_name = addPreimage)]
1189 pub fn add_preimage(&mut self, preimage_jam: &[u8]) -> Result<Option<WasmDigest>, JsValue> {
1190 let preimage = cue(preimage_jam).ok_or("Unable to cue preimage jam")?;
1191 Ok(self
1192 .builder
1193 .add_preimage(preimage)
1194 .map(|v| WasmDigest::from_internal(&v)))
1195 }
1196
1197 #[wasm_bindgen]
1201 pub fn sign(&mut self, signing_key_bytes: &[u8]) -> Result<(), JsValue> {
1202 if signing_key_bytes.len() != 32 {
1203 return Err(JsValue::from_str("Private key must be 32 bytes"));
1204 }
1205 let signing_key = PrivateKey(UBig::from_be_bytes(signing_key_bytes));
1206
1207 self.builder.sign(&signing_key);
1208
1209 Ok(())
1210 }
1211
1212 #[wasm_bindgen]
1214 pub fn validate(&mut self) -> Result<(), JsValue> {
1215 self.builder
1216 .validate()
1217 .map_err(|v| JsValue::from_str(&v.to_string()))?;
1218
1219 Ok(())
1220 }
1221
1222 #[wasm_bindgen(js_name = curFee)]
1224 pub fn cur_fee(&self) -> Nicks {
1225 self.builder.cur_fee()
1226 }
1227
1228 #[wasm_bindgen(js_name = calcFee)]
1237 pub fn calc_fee(&self) -> Nicks {
1238 self.builder.calc_fee()
1239 }
1240
1241 #[wasm_bindgen(js_name = allNotes)]
1242 pub fn all_notes(&self) -> WasmTxNotes {
1243 let mut ret = WasmTxNotes {
1244 notes: vec![],
1245 spend_conditions: vec![],
1246 };
1247 self.builder
1248 .all_notes()
1249 .into_values()
1250 .for_each(|(note, spend_condition)| {
1251 ret.notes.push(WasmNote::from_internal(note));
1252 ret.spend_conditions
1253 .push(WasmSpendCondition::from_internal(spend_condition));
1254 });
1255 ret
1256 }
1257
1258 #[wasm_bindgen]
1259 pub fn build(&self) -> Result<WasmNockchainTx, JsValue> {
1260 let tx = self.builder.build();
1261 Ok(WasmNockchainTx::from_internal(&tx))
1262 }
1263
1264 #[wasm_bindgen(js_name = allSpends)]
1265 pub fn all_spends(&self) -> Vec<WasmSpendBuilder> {
1266 self.builder
1267 .all_spends()
1268 .values()
1269 .map(WasmSpendBuilder::from_internal)
1270 .collect()
1271 }
1272}
1273
1274#[wasm_bindgen(js_name = TxNotes)]
1275pub struct WasmTxNotes {
1276 #[wasm_bindgen(skip)]
1277 pub notes: Vec<WasmNote>,
1278 #[wasm_bindgen(skip)]
1279 pub spend_conditions: Vec<WasmSpendCondition>,
1280}
1281
1282#[wasm_bindgen(js_class = TxNotes)]
1283impl WasmTxNotes {
1284 #[wasm_bindgen(getter)]
1285 pub fn notes(&self) -> Vec<WasmNote> {
1286 self.notes.clone()
1287 }
1288
1289 #[wasm_bindgen(getter, js_name = spendConditions)]
1290 pub fn spend_conditions(&self) -> Vec<WasmSpendCondition> {
1291 self.spend_conditions.clone()
1292 }
1293}
1294
1295#[wasm_bindgen(js_name = SpendBuilder)]
1300pub struct WasmSpendBuilder {
1301 builder: SpendBuilder,
1302}
1303
1304#[wasm_bindgen(js_class = SpendBuilder)]
1305impl WasmSpendBuilder {
1306 #[wasm_bindgen(constructor)]
1308 pub fn new(
1309 note: WasmNote,
1310 spend_condition: WasmSpendCondition,
1311 refund_lock: Option<WasmSpendCondition>,
1312 ) -> Result<Self, JsValue> {
1313 Ok(Self {
1314 builder: SpendBuilder::new(
1315 note.to_internal()?,
1316 spend_condition.to_internal()?,
1317 refund_lock.map(|v| v.to_internal()).transpose()?,
1318 ),
1319 })
1320 }
1321
1322 pub fn fee(&mut self, fee: Nicks) {
1324 self.builder.fee(fee);
1325 }
1326
1327 #[wasm_bindgen(js_name = computeRefund)]
1329 pub fn compute_refund(&mut self, include_lock_data: bool) {
1330 self.builder.compute_refund(include_lock_data);
1331 }
1332
1333 #[wasm_bindgen(js_name = curRefund)]
1335 pub fn cur_refund(&self) -> Option<WasmSeed> {
1336 self.builder.cur_refund().map(|v| WasmSeed::from(v.clone()))
1337 }
1338
1339 #[wasm_bindgen(js_name = isBalanced)]
1343 pub fn is_balanced(&self) -> bool {
1344 self.builder.is_balanced()
1345 }
1346
1347 pub fn seed(&mut self, seed: WasmSeed) -> Result<(), JsValue> {
1354 self.builder.seed(seed.to_internal()?);
1355 Ok(())
1356 }
1357
1358 #[wasm_bindgen(js_name = invalidateSigs)]
1364 pub fn invalidate_sigs(&mut self) {
1365 self.builder.invalidate_sigs();
1366 }
1367
1368 #[wasm_bindgen(js_name = missingUnlocks)]
1377 pub fn missing_unlocks(&self) -> Result<Vec<JsValue>, JsValue> {
1378 self.builder
1379 .missing_unlocks()
1380 .into_iter()
1381 .map(|v| serde_wasm_bindgen::to_value(&WasmMissingUnlocks::from_internal(&v)))
1382 .collect::<Result<Vec<_>, _>>()
1383 .map_err(|e| e.into())
1384 }
1385
1386 #[wasm_bindgen(js_name = addPreimage)]
1388 pub fn add_preimage(&mut self, preimage_jam: &[u8]) -> Result<Option<WasmDigest>, JsValue> {
1389 let preimage = cue(preimage_jam).ok_or("Unable to cue preimage jam")?;
1390 Ok(self
1391 .builder
1392 .add_preimage(preimage)
1393 .map(|v| WasmDigest::from_internal(&v)))
1394 }
1395
1396 pub fn sign(&mut self, signing_key_bytes: &[u8]) -> Result<bool, JsValue> {
1398 if signing_key_bytes.len() != 32 {
1399 return Err(JsValue::from_str("Private key must be 32 bytes"));
1400 }
1401 let signing_key = PrivateKey(UBig::from_be_bytes(signing_key_bytes));
1402 Ok(self.builder.sign(&signing_key))
1403 }
1404
1405 fn from_internal(internal: &SpendBuilder) -> Self {
1406 Self {
1407 builder: internal.clone(),
1408 }
1409 }
1410
1411 #[allow(unused)]
1412 fn to_internal(&self) -> SpendBuilder {
1413 self.builder.clone()
1414 }
1415}
1416
1417impl From<SpendBuilder> for WasmSpendBuilder {
1418 fn from(builder: SpendBuilder) -> Self {
1419 Self { builder }
1420 }
1421}
1422
1423impl From<WasmSpendBuilder> for SpendBuilder {
1424 fn from(value: WasmSpendBuilder) -> Self {
1425 value.builder
1426 }
1427}
1428
1429#[derive(Serialize, Deserialize)]
1430pub enum WasmMissingUnlocks {
1431 Pkh {
1432 num_sigs: u64,
1433 sig_of: BTreeSet<String>,
1434 },
1435 Hax {
1436 preimages_for: BTreeSet<String>,
1437 },
1438 Brn,
1439}
1440
1441impl WasmMissingUnlocks {
1442 fn from_internal(internal: &MissingUnlocks) -> Self {
1443 match internal {
1444 MissingUnlocks::Pkh { num_sigs, sig_of } => Self::Pkh {
1445 num_sigs: *num_sigs,
1446 sig_of: sig_of
1447 .iter()
1448 .map(|v| WasmDigest::from_internal(v).value)
1449 .collect(),
1450 },
1451 MissingUnlocks::Hax { preimages_for } => Self::Hax {
1452 preimages_for: preimages_for
1453 .iter()
1454 .map(|v| WasmDigest::from_internal(v).value)
1455 .collect(),
1456 },
1457 MissingUnlocks::Brn => Self::Brn,
1458 }
1459 }
1460}
1461
1462#[wasm_bindgen(js_name = RawTx)]
1467pub struct WasmRawTx {
1468 #[wasm_bindgen(skip)]
1470 pub(crate) internal: RawTx,
1471}
1472
1473#[wasm_bindgen(js_class = RawTx)]
1474impl WasmRawTx {
1475 #[wasm_bindgen(getter)]
1476 pub fn version(&self) -> WasmVersion {
1477 WasmVersion::from_internal(&self.internal.version)
1478 }
1479
1480 #[wasm_bindgen(getter)]
1481 pub fn id(&self) -> WasmDigest {
1482 WasmDigest::from_internal(&self.internal.id)
1483 }
1484
1485 #[wasm_bindgen(getter)]
1486 pub fn name(&self) -> String {
1487 self.internal.id.to_string()
1488 }
1489
1490 fn from_internal(tx: &RawTx) -> Self {
1491 Self {
1492 internal: tx.clone(),
1493 }
1494 }
1495
1496 #[wasm_bindgen(js_name = toProtobuf)]
1498 pub fn to_protobuf(&self) -> Result<JsValue, JsValue> {
1499 let pb_tx = pb::RawTransaction::from(self.internal.clone());
1500 serde_wasm_bindgen::to_value(&pb_tx)
1501 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
1502 }
1503
1504 #[wasm_bindgen(js_name = fromProtobuf)]
1505 pub fn from_protobuf(value: JsValue) -> Result<WasmRawTx, JsValue> {
1506 let pb: pb::RawTransaction = serde_wasm_bindgen::from_value(value)?;
1507 let tx: RawTx = pb
1508 .try_into()
1509 .map_err(|e| JsValue::from_str(&format!("{}", e)))?;
1510 Ok(WasmRawTx::from_internal(&tx))
1512 }
1513
1514 #[wasm_bindgen(js_name = toJam)]
1516 pub fn to_jam(&self) -> js_sys::Uint8Array {
1517 let n = self.internal.to_noun();
1518 js_sys::Uint8Array::from(&jam(n)[..])
1519 }
1520
1521 #[wasm_bindgen(js_name = fromJam)]
1522 pub fn from_jam(jam: &[u8]) -> Result<Self, JsValue> {
1523 let n = cue(jam).ok_or("Unable to decode jam")?;
1524 let tx: RawTx = NounDecode::from_noun(&n).ok_or("Unable to decode noun")?;
1525 Ok(Self::from_internal(&tx))
1526 }
1527
1528 #[wasm_bindgen]
1530 pub fn outputs(&self) -> Vec<WasmNote> {
1531 self.internal
1532 .outputs()
1533 .into_iter()
1534 .map(WasmNote::from_internal)
1535 .collect()
1536 }
1537
1538 #[wasm_bindgen(js_name = toNockchainTx)]
1539 pub fn to_nockchain_tx(&self) -> WasmNockchainTx {
1540 WasmNockchainTx::from_internal(&self.internal.to_nockchain_tx())
1541 }
1542
1543 #[wasm_bindgen(js_name = signAll)]
1548 pub fn sign_all(&mut self, signing_key_bytes: &[u8]) -> Result<(), JsValue> {
1549 if signing_key_bytes.len() != 32 {
1550 return Err(JsValue::from_str("Private key must be 32 bytes"));
1551 }
1552 let signing_key = PrivateKey(UBig::from_be_bytes(signing_key_bytes));
1553 self.internal.sign_all(&signing_key);
1554 Ok(())
1555 }
1556}
1557
1558#[wasm_bindgen(js_name = NockchainTx)]
1559pub struct WasmNockchainTx {
1560 #[wasm_bindgen(skip)]
1561 pub(crate) internal: NockchainTx,
1562}
1563
1564#[wasm_bindgen(js_class = NockchainTx)]
1565impl WasmNockchainTx {
1566 #[wasm_bindgen(getter)]
1567 pub fn version(&self) -> WasmVersion {
1568 WasmVersion::from_internal(&self.internal.version)
1569 }
1570
1571 #[wasm_bindgen(getter)]
1572 pub fn id(&self) -> WasmDigest {
1573 WasmDigest::from_internal(&self.internal.id)
1574 }
1575
1576 #[wasm_bindgen(getter)]
1577 pub fn name(&self) -> String {
1578 self.internal.id.to_string()
1579 }
1580
1581 fn from_internal(tx: &NockchainTx) -> Self {
1582 Self {
1583 internal: tx.clone(),
1584 }
1585 }
1586
1587 #[wasm_bindgen(js_name = toJam)]
1589 pub fn to_jam(&self) -> js_sys::Uint8Array {
1590 let n = self.internal.to_noun();
1591 js_sys::Uint8Array::from(&jam(n)[..])
1592 }
1593
1594 #[wasm_bindgen(js_name = fromJam)]
1596 pub fn from_jam(jam: &[u8]) -> Result<Self, JsValue> {
1597 let n = cue(jam).ok_or("Unable to decode jam")?;
1598 let tx: NockchainTx = NounDecode::from_noun(&n).ok_or("Unable to decode noun")?;
1599 Ok(Self::from_internal(&tx))
1600 }
1601
1602 #[wasm_bindgen]
1603 pub fn outputs(&self) -> Vec<WasmNote> {
1604 self.internal
1605 .outputs()
1606 .into_iter()
1607 .map(WasmNote::from_internal)
1608 .collect()
1609 }
1610
1611 #[wasm_bindgen(js_name = toRawTx)]
1612 pub fn to_raw_tx(&self) -> WasmRawTx {
1613 WasmRawTx::from_internal(&self.internal.to_raw_tx())
1614 }
1615}