1mod account_id;
102mod decode;
103mod encode;
104pub mod env_types;
105mod scon;
106mod transcoder;
107mod util;
108
109pub use self::{
110 account_id::AccountId32,
111 scon::{
112 Hex,
113 Map,
114 Tuple,
115 Value,
116 },
117 transcoder::{
118 Transcoder,
119 TranscoderBuilder,
120 },
121};
122
123use anyhow::{
124 Context,
125 Result,
126};
127pub use ink_metadata;
128use ink_metadata::{
129 ConstructorSpec,
130 InkProject,
131 MessageSpec,
132};
133use itertools::Itertools;
134use scale::{
135 Compact,
136 Decode,
137 Input,
138};
139use scale_info::{
140 form::{
141 Form,
142 PortableForm,
143 },
144 Field,
145};
146use std::{
147 cmp::Ordering,
148 fmt::Debug,
149 path::Path,
150};
151
152pub struct ContractMessageTranscoder {
155 metadata: InkProject,
156 transcoder: Transcoder,
157}
158
159fn did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String>
164where
165 T: AsRef<str>,
166 I: IntoIterator<Item = T>,
167{
168 let mut candidates: Vec<(f64, String)> = possible_values
169 .into_iter()
170 .map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned()))
171 .filter(|(confidence, _)| *confidence > 0.7)
172 .collect();
173 candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
174 candidates.into_iter().map(|(_, pv)| pv).collect()
175}
176
177impl ContractMessageTranscoder {
178 pub fn new(metadata: InkProject) -> Self {
179 let transcoder = TranscoderBuilder::new(metadata.registry())
180 .register_custom_type_transcoder::<<ink_env::DefaultEnvironment as ink_env::Environment>::AccountId, _>(env_types::AccountId)
181 .register_custom_type_decoder::<<ink_env::DefaultEnvironment as ink_env::Environment>::Hash, _>(env_types::Hash)
182 .done();
183 Self {
184 metadata,
185 transcoder,
186 }
187 }
188
189 pub fn load<P>(metadata_path: P) -> Result<Self>
192 where
193 P: AsRef<Path>,
194 {
195 let path = metadata_path.as_ref();
196 let metadata: contract_metadata::ContractMetadata =
197 contract_metadata::ContractMetadata::load(&metadata_path)?;
198 let ink_metadata = serde_json::from_value(serde_json::Value::Object(
199 metadata.abi,
200 ))
201 .context(format!(
202 "Failed to deserialize ink project metadata from file {}",
203 path.display()
204 ))?;
205
206 Ok(Self::new(ink_metadata))
207 }
208
209 pub fn encode<I, S>(&self, name: &str, args: I) -> Result<Vec<u8>>
210 where
211 I: IntoIterator<Item = S>,
212 S: AsRef<str> + Debug,
213 {
214 let (selector, spec_args) = match (
215 self.find_constructor_spec(name),
216 self.find_message_spec(name),
217 ) {
218 (Some(c), None) => (c.selector(), c.args()),
219 (None, Some(m)) => (m.selector(), m.args()),
220 (Some(_), Some(_)) => {
221 return Err(anyhow::anyhow!(
222 "Invalid metadata: both a constructor and message found with name '{}'",
223 name
224 ))
225 }
226 (None, None) => {
227 let constructors = self.constructors().map(|c| c.label());
228 let messages = self.messages().map(|c| c.label());
229 let possible_values: Vec<_> = constructors.chain(messages).collect();
230 let help_txt = did_you_mean(name, possible_values.clone())
231 .first()
232 .map(|suggestion| format!("Did you mean '{}'?", suggestion))
233 .unwrap_or_else(|| {
234 format!("Should be one of: {}", possible_values.iter().join(", "))
235 });
236
237 return Err(anyhow::anyhow!(
238 "No constructor or message with the name '{name}' found.\n{help_txt}",
239 ))
240 }
241 };
242
243 let args: Vec<_> = args.into_iter().collect();
244 if spec_args.len() != args.len() {
245 anyhow::bail!(
246 "Invalid number of input arguments: expected {}, {} provided",
247 spec_args.len(),
248 args.len()
249 )
250 }
251
252 let mut encoded = selector.to_bytes().to_vec();
253 for (spec, arg) in spec_args.iter().zip(args) {
254 let value = scon::parse_value(arg.as_ref())?;
255 self.transcoder.encode(
256 self.metadata.registry(),
257 spec.ty().ty().id,
258 &value,
259 &mut encoded,
260 )?;
261 }
262 Ok(encoded)
263 }
264
265 pub fn decode(&self, type_id: u32, input: &mut &[u8]) -> Result<Value> {
266 self.transcoder
267 .decode(self.metadata.registry(), type_id, input)
268 }
269
270 pub fn metadata(&self) -> &InkProject {
271 &self.metadata
272 }
273
274 fn constructors(&self) -> impl Iterator<Item = &ConstructorSpec<PortableForm>> {
275 self.metadata.spec().constructors().iter()
276 }
277
278 fn messages(&self) -> impl Iterator<Item = &MessageSpec<PortableForm>> {
279 self.metadata.spec().messages().iter()
280 }
281
282 fn find_message_spec(&self, name: &str) -> Option<&MessageSpec<PortableForm>> {
283 self.messages().find(|msg| msg.label() == &name.to_string())
284 }
285
286 fn find_constructor_spec(
287 &self,
288 name: &str,
289 ) -> Option<&ConstructorSpec<PortableForm>> {
290 self.constructors()
291 .find(|msg| msg.label() == &name.to_string())
292 }
293
294 pub fn decode_contract_event<Hash>(
295 &self,
296 event_sig_topic: &Hash,
297 data: &mut &[u8],
298 ) -> Result<Value>
299 where
300 Hash: AsRef<[u8]>,
301 {
302 let _len = <Compact<u32>>::decode(data)?;
306 let event_spec = self
307 .metadata
308 .spec()
309 .events()
310 .iter()
311 .find(|event| {
312 if let Some(sig_topic) = event.signature_topic() {
313 sig_topic.as_bytes() == event_sig_topic.as_ref()
314 } else {
315 false
316 }
317 })
318 .ok_or_else(|| {
319 anyhow::anyhow!(
320 "Event with signature topic {} not found in contract metadata",
321 hex::encode(event_sig_topic)
322 )
323 })?;
324 tracing::debug!("Decoding contract event '{}'", event_spec.label());
325
326 let mut args = Vec::new();
327 for arg in event_spec.args() {
328 let name = arg.label().to_string();
329 let value = self.decode(arg.ty().ty().id, data)?;
330 args.push((Value::String(name), value));
331 }
332
333 Self::validate_length(data, event_spec.label(), &args)?;
334
335 let name = event_spec.label().to_string();
336 let map = Map::new(Some(&name), args.into_iter().collect());
337
338 Ok(Value::Map(map))
339 }
340
341 pub fn decode_contract_message(&self, data: &mut &[u8]) -> Result<Value> {
342 let mut msg_selector = [0u8; 4];
343 data.read(&mut msg_selector)?;
344 let msg_spec = self
345 .messages()
346 .find(|x| msg_selector == x.selector().to_bytes())
347 .ok_or_else(|| {
348 anyhow::anyhow!(
349 "Message with selector {} not found in contract metadata",
350 hex::encode_upper(msg_selector)
351 )
352 })?;
353 tracing::debug!("Decoding contract message '{}'", msg_spec.label());
354
355 let mut args = Vec::new();
356 for arg in msg_spec.args() {
357 let name = arg.label().to_string();
358 let value = self.decode(arg.ty().ty().id, data)?;
359 args.push((Value::String(name), value));
360 }
361
362 Self::validate_length(data, msg_spec.label(), &args)?;
363
364 let name = msg_spec.label().to_string();
365 let map = Map::new(Some(&name), args.into_iter().collect());
366
367 Ok(Value::Map(map))
368 }
369
370 pub fn decode_contract_constructor(&self, data: &mut &[u8]) -> Result<Value> {
371 let mut msg_selector = [0u8; 4];
372 data.read(&mut msg_selector)?;
373 let msg_spec = self
374 .constructors()
375 .find(|x| msg_selector == x.selector().to_bytes())
376 .ok_or_else(|| {
377 anyhow::anyhow!(
378 "Constructor with selector {} not found in contract metadata",
379 hex::encode_upper(msg_selector)
380 )
381 })?;
382 tracing::debug!("Decoding contract constructor '{}'", msg_spec.label());
383
384 let mut args = Vec::new();
385 for arg in msg_spec.args() {
386 let name = arg.label().to_string();
387 let value = self.decode(arg.ty().ty().id, data)?;
388 args.push((Value::String(name), value));
389 }
390
391 Self::validate_length(data, msg_spec.label(), &args)?;
392
393 let name = msg_spec.label().to_string();
394 let map = Map::new(Some(&name), args.into_iter().collect());
395
396 Ok(Value::Map(map))
397 }
398
399 pub fn decode_constructor_return(
400 &self,
401 name: &str,
402 data: &mut &[u8],
403 ) -> Result<Value> {
404 let ctor_spec = self.find_constructor_spec(name).ok_or_else(|| {
405 anyhow::anyhow!("Failed to find constructor spec with name '{}'", name)
406 })?;
407 let return_ty = ctor_spec.return_type().ret_type();
408 self.decode(return_ty.ty().id, data)
409 }
410
411 pub fn decode_message_return(&self, name: &str, data: &mut &[u8]) -> Result<Value> {
412 let msg_spec = self.find_message_spec(name).ok_or_else(|| {
413 anyhow::anyhow!("Failed to find message spec with name '{}'", name)
414 })?;
415 let return_ty = msg_spec.return_type().ret_type();
416 self.decode(return_ty.ty().id, data)
417 }
418
419 fn validate_length(data: &[u8], label: &str, args: &[(Value, Value)]) -> Result<()> {
421 if !data.is_empty() {
422 let arg_list_string: String =
423 args.iter().fold(format!("`{label}`"), |init, arg| {
424 format!("{}, `{}`", init, arg.0)
425 });
426 let encoded_bytes = hex::encode_upper(data);
427 return Err(anyhow::anyhow!(
428 "input length was longer than expected by {} byte(s).\nManaged to decode {} but `{}` bytes were left unread",
429 data.len(),
430 arg_list_string,
431 encoded_bytes
432 ));
433 }
434 Ok(())
435 }
436}
437
438impl TryFrom<contract_metadata::ContractMetadata> for ContractMessageTranscoder {
439 type Error = anyhow::Error;
440
441 fn try_from(
442 metadata: contract_metadata::ContractMetadata,
443 ) -> Result<Self, Self::Error> {
444 Ok(Self::new(serde_json::from_value(
445 serde_json::Value::Object(metadata.abi),
446 )?))
447 }
448}
449
450#[derive(Debug)]
451pub enum CompositeTypeFields {
452 Named(Vec<CompositeTypeNamedField>),
453 Unnamed(Vec<Field<PortableForm>>),
454 NoFields,
455}
456
457#[derive(Debug)]
458pub struct CompositeTypeNamedField {
459 name: <PortableForm as Form>::String,
460 field: Field<PortableForm>,
461}
462
463impl CompositeTypeNamedField {
464 pub fn name(&self) -> &str {
465 &self.name
466 }
467
468 pub fn field(&self) -> &Field<PortableForm> {
469 &self.field
470 }
471}
472
473impl CompositeTypeFields {
474 pub fn from_fields(fields: &[Field<PortableForm>]) -> Result<Self> {
475 if fields.iter().next().is_none() {
476 Ok(Self::NoFields)
477 } else if fields.iter().all(|f| f.name.is_some()) {
478 let fields = fields
479 .iter()
480 .map(|field| {
481 CompositeTypeNamedField {
482 name: field
483 .name
484 .as_ref()
485 .expect("All fields have a name; qed")
486 .to_owned(),
487 field: field.clone(),
488 }
489 })
490 .collect();
491 Ok(Self::Named(fields))
492 } else if fields.iter().all(|f| f.name.is_none()) {
493 Ok(Self::Unnamed(fields.to_vec()))
494 } else {
495 Err(anyhow::anyhow!(
496 "Struct fields should either be all named or all unnamed"
497 ))
498 }
499 }
500}
501
502#[cfg(test)]
503mod tests {
504 use super::*;
505 use ink_env::{
506 DefaultEnvironment,
507 Environment,
508 };
509 use primitive_types::H256;
510 use scale::Encode;
511 use scon::Value;
512 use std::str::FromStr;
513
514 use crate::scon::Hex;
515
516 #[allow(clippy::extra_unused_lifetimes, unexpected_cfgs, non_local_definitions)]
517 #[ink::contract]
518 pub mod transcode {
519 #[ink(storage)]
520 pub struct Transcode {
521 value: bool,
522 }
523
524 #[ink(event)]
525 pub struct Event1 {
526 #[ink(topic)]
527 name: Hash,
528 #[ink(topic)]
529 from: AccountId,
530 }
531
532 impl Transcode {
533 #[ink(constructor)]
534 pub fn new(init_value: bool) -> Self {
535 Self { value: init_value }
536 }
537
538 #[ink(constructor)]
539 pub fn default() -> Self {
540 Self::new(Default::default())
541 }
542
543 #[ink(message)]
544 pub fn flip(&mut self) {
545 self.value = !self.value;
546 }
547
548 #[ink(message)]
549 pub fn get(&self) -> bool {
550 self.value
551 }
552
553 #[ink(message)]
554 pub fn set_account_id(&self, account_id: AccountId) {
555 let _ = account_id;
556 }
557
558 #[ink(message)]
559 pub fn set_account_ids_vec(&self, account_ids: Vec<AccountId>) {
560 let _ = account_ids;
561 }
562
563 #[ink(message)]
564 pub fn primitive_vec_args(&self, args: Vec<u32>) {
565 let _ = args;
566 }
567
568 #[ink(message)]
569 pub fn uint_args(
570 &self,
571 _u8: u8,
572 _u16: u16,
573 _u32: u32,
574 _u64: u64,
575 _u128: u128,
576 ) {
577 }
578
579 #[ink(message)]
580 pub fn uint_array_args(&self, arr: [u8; 4]) {
581 let _ = arr;
582 }
583 }
584 }
585
586 fn generate_metadata() -> InkProject {
587 extern "Rust" {
588 fn __ink_generate_metadata() -> InkProject;
589 }
590
591 unsafe { __ink_generate_metadata() }
592 }
593
594 #[test]
595 fn encode_single_primitive_arg() -> Result<()> {
596 let metadata = generate_metadata();
597 let transcoder = ContractMessageTranscoder::new(metadata);
598
599 let encoded = transcoder.encode("new", ["true"])?;
600 let encoded_args = &encoded[4..];
602
603 assert_eq!(true.encode(), encoded_args);
604 Ok(())
605 }
606
607 #[test]
608 fn encode_misspelled_arg() {
609 let metadata = generate_metadata();
610 let transcoder = ContractMessageTranscoder::new(metadata);
611 assert_eq!(
612 transcoder.encode("fip", ["true"]).unwrap_err().to_string(),
613 "No constructor or message with the name 'fip' found.\nDid you mean 'flip'?"
614 );
615 }
616
617 #[test]
618 fn encode_mismatching_args_length() {
619 let metadata = generate_metadata();
620 let transcoder = ContractMessageTranscoder::new(metadata);
621
622 let result: Result<Vec<u8>> = transcoder.encode("new", Vec::<&str>::new());
623 assert!(result.is_err(), "Should return an error");
624 assert_eq!(
625 result.unwrap_err().to_string(),
626 "Invalid number of input arguments: expected 1, 0 provided"
627 );
628
629 let result: Result<Vec<u8>> = transcoder.encode("new", ["true", "false"]);
630 assert!(result.is_err(), "Should return an error");
631 assert_eq!(
632 result.unwrap_err().to_string(),
633 "Invalid number of input arguments: expected 1, 2 provided"
634 );
635 }
636
637 #[test]
638 fn encode_account_id_custom_ss58_encoding() -> Result<()> {
639 let metadata = generate_metadata();
640 let transcoder = ContractMessageTranscoder::new(metadata);
641
642 let encoded = transcoder.encode(
643 "set_account_id",
644 ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"],
645 )?;
646
647 let encoded_args = &encoded[4..];
649
650 let expected =
651 AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
652 .unwrap();
653 assert_eq!(expected.encode(), encoded_args);
654 Ok(())
655 }
656
657 #[test]
658 fn encode_account_ids_vec_args() -> Result<()> {
659 let metadata = generate_metadata();
660 let transcoder = ContractMessageTranscoder::new(metadata);
661
662 let encoded = transcoder.encode(
663 "set_account_ids_vec",
664 ["[5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY, 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty]"],
665 )?;
666
667 let encoded_args = &encoded[4..];
669
670 let expected = vec![
671 AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
672 .unwrap(),
673 AccountId32::from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")
674 .unwrap(),
675 ];
676 assert_eq!(expected.encode(), encoded_args);
677 Ok(())
678 }
679
680 #[test]
681 fn encode_primitive_vec_args() -> Result<()> {
682 let metadata = generate_metadata();
683 let transcoder = ContractMessageTranscoder::new(metadata);
684
685 let encoded = transcoder.encode("primitive_vec_args", ["[1, 2]"])?;
686
687 let encoded_args = &encoded[4..];
689
690 let expected = vec![1, 2];
691 assert_eq!(expected.encode(), encoded_args);
692 Ok(())
693 }
694
695 #[test]
696 fn encode_uint_hex_literals() -> Result<()> {
697 let metadata = generate_metadata();
698 let transcoder = ContractMessageTranscoder::new(metadata);
699
700 let encoded = transcoder.encode(
701 "uint_args",
702 [
703 "0x00",
704 "0xDEAD",
705 "0xDEADBEEF",
706 "0xDEADBEEF12345678",
707 "0xDEADBEEF0123456789ABCDEF01234567",
708 ],
709 )?;
710
711 let encoded_args = &encoded[4..];
713
714 let expected = (
715 0x00u8,
716 0xDEADu16,
717 0xDEADBEEFu32,
718 0xDEADBEEF12345678u64,
719 0xDEADBEEF0123456789ABCDEF01234567u128,
720 );
721 assert_eq!(expected.encode(), encoded_args);
722 Ok(())
723 }
724
725 #[test]
726 fn encode_uint_arr_hex_literals() -> Result<()> {
727 let metadata = generate_metadata();
728 let transcoder = ContractMessageTranscoder::new(metadata);
729
730 let encoded =
731 transcoder.encode("uint_array_args", ["[0xDE, 0xAD, 0xBE, 0xEF]"])?;
732
733 let encoded_args = &encoded[4..];
735
736 let expected: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
737 assert_eq!(expected.encode(), encoded_args);
738 Ok(())
739 }
740
741 #[test]
742 fn decode_primitive_return() {
743 let metadata = generate_metadata();
744 let transcoder = ContractMessageTranscoder::new(metadata);
745
746 let encoded = Result::<bool, ink::primitives::LangError>::Ok(true).encode();
747 let decoded = transcoder
748 .decode_message_return("get", &mut &encoded[..])
749 .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
750
751 let expected = Value::Tuple(Tuple::new(
752 "Ok".into(),
753 [Value::Bool(true)].into_iter().collect(),
754 ));
755 assert_eq!(expected, decoded);
756 }
757
758 #[test]
759 fn decode_lang_error() {
760 use ink::primitives::LangError;
761
762 let metadata = generate_metadata();
763 let transcoder = ContractMessageTranscoder::new(metadata);
764
765 let encoded =
766 Result::<bool, LangError>::Err(LangError::CouldNotReadInput).encode();
767 let decoded = transcoder
768 .decode_message_return("get", &mut &encoded[..])
769 .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
770
771 let expected = Value::Tuple(Tuple::new(
772 "Err".into(),
773 [Value::Tuple(Tuple::new(
774 Some("CouldNotReadInput"),
775 Vec::new(),
776 ))]
777 .to_vec(),
778 ));
779 assert_eq!(expected, decoded);
780 }
781
782 #[test]
783 fn decode_contract_event() -> Result<()> {
784 let metadata = generate_metadata();
785 let transcoder = ContractMessageTranscoder::new(metadata);
786
787 let signature_topic: <DefaultEnvironment as Environment>::Hash =
788 <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
789 .unwrap()
790 .into();
791 let encoded = ([0u32; 8], [1u32; 8]).encode();
793 let encoded_bytes = encoded.encode();
795 let _ = transcoder
796 .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])?;
797
798 Ok(())
799 }
800
801 #[test]
802 fn decode_hash_as_hex_encoded_string() -> Result<()> {
803 let metadata = generate_metadata();
804 let transcoder = ContractMessageTranscoder::new(metadata);
805
806 let hash = [
807 52u8, 40, 235, 225, 70, 245, 184, 36, 21, 218, 130, 114, 75, 207, 117, 240,
808 83, 118, 135, 56, 220, 172, 95, 131, 171, 125, 130, 167, 10, 15, 242, 222,
809 ];
810 let signature_topic: <DefaultEnvironment as Environment>::Hash =
811 <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
812 .unwrap()
813 .into();
814 let encoded = (hash, [0u32; 8]).encode();
816 let encoded_bytes = encoded.encode();
818 let decoded = transcoder
819 .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])?;
820
821 if let Value::Map(ref map) = decoded {
822 let name_field = &map[&Value::String("name".into())];
823 if let Value::Hex(hex) = name_field {
824 assert_eq!(&Hex::from_str("0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de")?, hex);
825 Ok(())
826 } else {
827 Err(anyhow::anyhow!(
828 "Expected a name field hash encoded as Hex value, was {:?}",
829 name_field
830 ))
831 }
832 } else {
833 Err(anyhow::anyhow!(
834 "Expected a Value::Map for the decoded event"
835 ))
836 }
837 }
838
839 #[test]
840 fn decode_contract_message() -> Result<()> {
841 let metadata = generate_metadata();
842 let transcoder = ContractMessageTranscoder::new(metadata);
843
844 let encoded_bytes = hex::decode("633aa551").unwrap();
845 let _ = transcoder.decode_contract_message(&mut &encoded_bytes[..])?;
846
847 Ok(())
848 }
849
850 #[test]
851 #[should_panic(
852 expected = "input length was longer than expected by 1 byte(s).\nManaged to decode `flip` but `00` bytes were left unread"
853 )]
854 fn fail_decode_input_with_extra_bytes() {
855 let metadata = generate_metadata();
856 let transcoder = ContractMessageTranscoder::new(metadata);
857
858 let encoded_bytes = hex::decode("633aa55100").unwrap();
859 let _ = transcoder
860 .decode_contract_message(&mut &encoded_bytes[..])
861 .unwrap();
862 }
863
864 #[test]
865 #[should_panic(
866 expected = "input length was longer than expected by 2 byte(s).\nManaged to decode `Event1`, `name`, `from` but `0C10` bytes were left unread"
867 )]
868 fn fail_decode_contract_event_with_extra_bytes() {
869 let metadata = generate_metadata();
870 let transcoder = ContractMessageTranscoder::new(metadata);
871
872 let signature_topic: H256 =
873 <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
874 .unwrap()
875 .into();
876 let encoded = ([0u32; 8], [1u32; 8], [12u8, 16u8]).encode();
878 let encoded_bytes = encoded.encode();
880 let _ = transcoder
881 .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])
882 .unwrap();
883 }
884}