radix_clis/scrypto_bindgen/
mod.rs

1use scrypto_bindgen::schema;
2use scrypto_bindgen::translation;
3use scrypto_bindgen::types;
4
5use clap::Parser;
6use radix_common::prelude::*;
7use radix_engine::system::system_db_reader::SystemDatabaseReader;
8use radix_substate_store_interface::interface::SubstateDatabase;
9use std::io::Write;
10
11use crate::resim::*;
12
13use self::schema::*;
14
15/// Generates interfaces for Scrypto packages to ease the use of external packages.
16#[derive(Parser, Debug)]
17#[clap(author, version, about, long_about = None, name = "scrypto-bindgen")]
18pub struct Args {
19    /// The address of the package to generate the bindings for.
20    package_address: String,
21
22    /// When enabled, the ledger will be cleared and bootstrapped again before being used to obtain
23    /// the bindings.
24    #[clap(short, long)]
25    reset_ledger: bool,
26
27    #[clap(short, long)]
28    func_sig_change: Vec<types::FunctionSignatureReplacementsInput>,
29}
30
31#[derive(Debug)]
32pub enum Error {
33    Bech32DecodeError(AddressBech32DecodeError),
34    PackageAddressError(ParsePackageAddressError),
35    ResimError(crate::resim::Error),
36    SchemaError(SchemaError),
37    IOError(std::io::Error),
38}
39
40pub fn run() -> Result<(), Error> {
41    let args = Args::parse();
42
43    // Everything will be written to the std-out
44    let mut out = std::io::stdout();
45
46    let env = if args.reset_ledger {
47        SimulatorEnvironment::new_reset().map_err(Error::ResimError)?
48    } else {
49        SimulatorEnvironment::new().map_err(Error::ResimError)?
50    };
51    let db = env.db;
52
53    let blueprint_replacement_map = types::prepare_replacement_map(&args.func_sig_change);
54
55    // Decode the package address without network context.
56    let package_address = {
57        let (_, _, bytes) =
58            AddressBech32Decoder::validate_and_decode_ignore_hrp(&args.package_address)
59                .map_err(Error::Bech32DecodeError)?;
60        PackageAddress::try_from(bytes.as_slice()).map_err(Error::PackageAddressError)?
61    };
62
63    // Generating the bindings
64    let bindings = {
65        let reader = SystemDatabaseReader::new(&db);
66        let definition = reader.get_package_definition(package_address);
67        let schema_resolver = SchemaResolver::new(package_address, &db);
68
69        let package_interface =
70            schema::package_interface_from_package_definition(definition, &schema_resolver)
71                .map_err(Error::SchemaError)?;
72        let mut ast_package_interface = translation::package_schema_interface_to_ast_interface(
73            package_interface,
74            package_address,
75            &schema_resolver,
76            &blueprint_replacement_map,
77        )
78        .map_err(Error::SchemaError)?;
79
80        // Scrypto-bindgen does not generate the aux-types. Only ledger-tools does.
81        ast_package_interface.auxiliary_types = Default::default();
82
83        ast_package_interface
84    };
85
86    writeln!(&mut out, "{}", quote::quote!(#bindings)).map_err(Error::IOError)?;
87
88    Ok(())
89}
90
91pub struct SchemaResolver<'s, S>(PackageAddress, SystemDatabaseReader<'s, S>)
92where
93    S: SubstateDatabase;
94
95impl<'s, S> SchemaResolver<'s, S>
96where
97    S: SubstateDatabase,
98{
99    pub fn new(node_id: PackageAddress, substate_database: &'s S) -> Self {
100        let reader = SystemDatabaseReader::new(substate_database);
101        Self(node_id, reader)
102    }
103}
104
105impl<'s, S> PackageSchemaResolver for SchemaResolver<'s, S>
106where
107    S: SubstateDatabase,
108{
109    fn lookup_schema(&self, schema_hash: &SchemaHash) -> Option<Rc<VersionedScryptoSchema>> {
110        self.1.get_schema(self.0.as_node_id(), schema_hash).ok()
111    }
112
113    fn resolve_type_kind(
114        &self,
115        type_identifier: &ScopedTypeId,
116    ) -> Result<LocalTypeKind<ScryptoCustomSchema>, schema::SchemaError> {
117        self.lookup_schema(&type_identifier.0)
118            .ok_or(schema::SchemaError::FailedToGetSchemaFromSchemaHash)?
119            .as_latest_version()
120            .ok_or(schema::SchemaError::FailedToGetSchemaFromSchemaHash)?
121            .resolve_type_kind(type_identifier.1)
122            .ok_or(schema::SchemaError::NonExistentLocalTypeIndex(
123                type_identifier.1,
124            ))
125            .cloned()
126    }
127
128    fn resolve_type_metadata(
129        &self,
130        type_identifier: &ScopedTypeId,
131    ) -> Result<TypeMetadata, schema::SchemaError> {
132        self.lookup_schema(&type_identifier.0)
133            .ok_or(schema::SchemaError::FailedToGetSchemaFromSchemaHash)?
134            .as_latest_version()
135            .ok_or(schema::SchemaError::FailedToGetSchemaFromSchemaHash)?
136            .resolve_type_metadata(type_identifier.1)
137            .ok_or(schema::SchemaError::NonExistentLocalTypeIndex(
138                type_identifier.1,
139            ))
140            .cloned()
141    }
142
143    fn resolve_type_validation(
144        &self,
145        type_identifier: &ScopedTypeId,
146    ) -> Result<TypeValidation<ScryptoCustomTypeValidation>, schema::SchemaError> {
147        self.lookup_schema(&type_identifier.0)
148            .ok_or(schema::SchemaError::FailedToGetSchemaFromSchemaHash)?
149            .as_latest_version()
150            .ok_or(schema::SchemaError::FailedToGetSchemaFromSchemaHash)?
151            .resolve_type_validation(type_identifier.1)
152            .ok_or(schema::SchemaError::NonExistentLocalTypeIndex(
153                type_identifier.1,
154            ))
155            .cloned()
156    }
157
158    fn package_address(&self) -> PackageAddress {
159        self.0
160    }
161}