Repository: huff-language/huff-rs
Pull request #146 (2022-06-30 02:42:39)

feat(huff_cli): Interactive Constructor Args
## Overview

Introduces a `-n` or `--interactive` flag to the cli that overrides `-i` constructor arg inputs in favor of an interactive mode.

This allows the compiler to compile and generate artifacts for the specified contracts, and prompt the user for constructor args based on the contracts `CONSTRUCTOR` macros.

More detail provided in #88 

#### Checkclist

- [x] Interactive Mode

#### Replicating

<img width="881" alt="Screen Shot 2022-07-02 at 10 10 49 AM" src="https://user-images.githubusercontent.com/21288394/177004325-0fc9f093-1729-4768-8d77-ace79bbb0563.png">

#### NOTES

First attempts to parse constructor args using a `function` definition like so:
<img width="449" alt="Screen Shot 2022-07-02 at 10 34 00 AM" src="https://user-images.githubusercontent.com/21288394/177006066-fc742e35-8bb8-4c77-873b-765f9b08c126.png">

In the case where no `function` definition is present with the name `constructor`, arguments will be parsed from the macro definition like so:
<img width="523" alt="Screen Shot 2022-07-02 at 10 36 04 AM" src="https://user-images.githubusercontent.com/21288394/177006084-8cb1ce1a-f381-48aa-bdf1-9afe77e9d2f3.png">






Patch:
diff --git a/.gitignore b/.gitignore
index 3239a15..38a5e6e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,8 @@
 .idea
 
 # Compilation Artifacts
-artifacts
\ No newline at end of file
+artifacts
+
+# Foundry cache
+cache
+
diff --git a/Cargo.lock b/Cargo.lock
index 2be1f60..8841e70 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -651,10 +651,11 @@ dependencies = [
 
 [[package]]
 name = "huff_cli"
-version = "0.1.0"
+version = "0.2.0"
 dependencies = [
  "clap 3.2.1",
  "ethers-core",
+ "huff_codegen",
  "huff_core",
  "huff_utils",
  "isatty",
diff --git a/huff_cli/Cargo.toml b/huff_cli/Cargo.toml
index e62e6df..187d89c 100644
--- a/huff_cli/Cargo.toml
+++ b/huff_cli/Cargo.toml
@@ -1,19 +1,20 @@
 [package]
 name = "huff_cli"
-version = "0.1.0"
+version = "0.2.0"
 edition = "2021"
 authors = ["Andreas Bigger", "clabby", "exp.table"]
 readme = "README.md"
 repository = "https://github.com/huff-language/huff-rs/"
 license = "MIT OR Apache-2.0"
 description = """
-Efficient Huff compiler written in rust.
+Huff Language Compiler built in Pure Rust.
 """
 keywords = ["huff", "rust", "evm", "bytecode", "compiler"]
 
 [dependencies]
 clap = { version = "3.1.18", features = ["derive"] }
 huff_core = { path = "../huff_core", version = "0.x.x" }
+huff_codegen = { path = "../huff_codegen", version = "0.x.x" }
 huff_utils = { path = "../huff_utils", version = "0.x.x" }
 tracing = "0.1.34"
 ethers-core = "0.13.0"
diff --git a/huff_cli/README.md b/huff_cli/README.md
index 846e9bb..9e38b75 100644
--- a/huff_cli/README.md
+++ b/huff_cli/README.md
@@ -6,26 +6,28 @@ The `huffc` CLI is written using [clap's](https://docs.rs/clap) [derive feature]
 ## huffc
 
 ```
-huffc 0.1.0
-Efficient Huff compiler written in rust.
+huffc 0.2.0
+Huff Language Compiler built in Pure Rust.
 
 USAGE:
-    huffc [OPTIONS] [PATH]
+    huffc [OPTIONS] [--] [PATH]
 
 ARGS:
-    <PATH>
+    <PATH>    The main path
 
 OPTIONS:
+    -a, --artifacts                       Whether to generate artifacts or not
     -b, --bytecode                        Generate and log bytecode
     -d, --output-directory <OUTPUTDIR>    The output directory [default: ./artifacts]
     -h, --help                            Print help information
     -i, --inputs <INPUTS>...              The input constructor arguments
+    -n, --interactive                     Interactively input the constructor args
     -o, --output <OUTPUT>                 The output file path
     -p, --print                           Prints out to the terminal
     -s, --source-path <SOURCE>            The contracts source path [default: ./src]
     -v, --verbose                         Verbose output
     -V, --version                         Print version information
-    -z, --optimize                        Optimize compilation
+    -z, --optimize                        Optimize compilation [WIP]
 ```
 
 _NOTE: To generate the above output, run: `huffc --help`_
@@ -33,19 +35,20 @@ _NOTE: To generate the above output, run: `huffc --help`_
 
 ## Usage
 
-To run `huffc` from the command line, you can use the following command:
-
+To run `huffc` from the command line, you can simply run:
 ```bash
-cargo run --bin huffc
+huffc --help
 ```
 
-To pass arguments into the `huffc` binary, simply pass them in after a `--` flag. For example, to get the `huffc` version (a `-V` flag), you can run:
+By default, huffc will attempt to compile all contracts in the `contracts` directory. If there is no `contracts` directory present, the following will spit out an error like so:
+```bash,color=red
+~ huffc
+
+Error: Invalid File Directory ./contracts
 
-```bash
-cargo run --bin huffc -- -V
 ```
 
-**Example: Using `huff-examples`**
+#### Examples using [`huff-examples`](https://github.com/huff-language/huff-examples)
 
 The [huff-examples](https://github.com/huff-language/huff-examples) github repository is added as a submodule to this repo for testing.
 
@@ -54,7 +57,7 @@ To run `huffc` against one of the examples, the path may simply be passed to `hu
 For example, to compile huff-example's [ERC20.huff](../huff-examples/erc20/contracts/ERC20.huff) contract, run:
 
 ```bash
-cargo run --bin huffc -- --bytecode ./huff-examples/erc20/contracts/ERC20.huff
+huffc --bytecode ./huff-examples/erc20/contracts/ERC20.huff
 ```
 
 _NOTE: The `--bytecode` flag will output the full deploy bytecode._
@@ -62,18 +65,86 @@ _NOTE: The `--bytecode` flag will output the full deploy bytecode._
 `huffc` also supports tracing using the [`tracing`](https://docs.rs/tracing/0.1.29/tracing/) crate. To produce a verbose output using tracing, append the `--verbose` or `-v` flag like so:
 
 ```bash
-cargo run --bin huffc -- --verbose --bytecode ./huff-examples/erc20/contracts/ERC20.huff
+huffc --verbose --bytecode ./huff-examples/erc20/contracts/ERC20.huff
 ```
 
+#### Specifying Artifact Outputs
+
 **By default**, `huffc` will export json build artifacts to a `./artifacts` directory. This can be overidden using the `--output-directory` flag or shorthand `-d` flag and specifying a string following. For example:
 
 ```bash
-cargo run --bin huffc -- -d ./output ./huff-examples/erc20/contracts/ERC20.huff
+huffc -d ./output ./huff-examples/erc20/contracts/ERC20.huff
 ```
 
 _NOTE: The huff cli will gracefully remove double and single quotes, so the following will also compile:_
 
 ```bash
-cargo run --bin huffc -- -d "./output" './huff-examples/erc20/contracts/ERC20.huff'
+huffc -d "./output" './huff-examples/erc20/contracts/ERC20.huff'
+```
+
+If a specific contract is specified for compiling (ie not a directory), a single `json` file may be specified as an output location for the contract artifact like so:
+
+```bash
+huffc -o ./artifact.json ./huff-examples/erc20/contracts/ERC20.huff
+```
+
+**NOTE**: The following will _not_ compile since multiple artifacts cannot be output to the same artifact json file.
+```bash
+huffc -o ./artifact.json ./contracts/
+```
+
+
+#### Entering Constructor Arguments
+
+`huffc` supports passing in constructor arguments to the contract. This is done by passing in the `--interactive` (shorthand: `-n`) flag or passing the `--inputs` (shorthand: `-i`) flag.
+
+ and passing in the arguments as a comma separated list.
+
+For example, to compile a contract (let's call it `example.huff`) with the following constructor definition:
+```huff
+#define macro CONSTRUCTOR(uint256, address) = takes(0) returns (0) {
+    0x04 calldataload
+    0x00 sstore
+    0x24 calldataload
+    0x01 sstore
+}
+```
+
+You can enter the arguments `(100, 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef)` interactively by passing in the `-n` or `--interactive` flag like so:
+
+```bash
+$ huffc -b -n ./contracts/example.huff
+[INTERACTIVE] Constructor Arguments for Contract: "./contracts/example.huff"
+[INTERACTIVE] Enter a uint256 for constructor param: 100
+[INTERACTIVE] Enter a address for constructor param: 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+
+33600.....f30000000000000000000000000000000000000000000000000000000000000064000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+```
+
+Alternatively, you can enter the arguments as a comma separated list by using the `-i` or `--inputs` flag like so:
+
+```bash
+$ huffc -b -i 100, 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef ./contracts/example.huff
+33600.....f30000000000000000000000000000000000000000000000000000000000000064000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+```
+
+#### Other Options
+
+- `-v` or `--verbose`: Outputs detailed logs to the terminal using the [tracing](https://crates.io/crates/tracing) crate.
+- `-V` or `--version`: Prints the version of `huffc`.
+- `-z` or `--optimize`: Optimizes the contract compilation - a work in progress.
+
+
+## Building huffc from source
+
+To run `huffc` from the command line, you can use the following command:
+```bash
+cargo run --bin huffc
+```
+
+To pass arguments into the `huffc` binary, simply pass them in after a `--` flag. For example, to get the `huffc` version (a `-V` flag), you can run:
+```bash
+cargo run --bin huffc -- -V
 ```
 
+All commands specified in [Usage](#usage) are also available from source by passing them in after the `--` flag.
diff --git a/huff_cli/src/huffc.rs b/huff_cli/src/huffc.rs
index 55b85c9..f818ae9 100644
--- a/huff_cli/src/huffc.rs
+++ b/huff_cli/src/huffc.rs
@@ -8,24 +8,27 @@
 #![allow(deprecated)]
 
 use clap::Parser as ClapParser;
+use ethers_core::utils::hex;
+use huff_codegen::Codegen;
 use huff_core::Compiler;
 use huff_utils::prelude::{
-    unpack_files, AstSpan, CodegenError, CodegenErrorKind, CompilerError, FileSource, Span,
+    unpack_files, AstSpan, CodegenError, CodegenErrorKind, CompilerError, FileSource,
+    OutputLocation, Span,
 };
 use isatty::stdout_isatty;
 use spinners::{Spinner, Spinners};
-use std::{path::Path, sync::Arc};
+use std::{io::Write, path::Path, sync::Arc};
 use yansi::Paint;
 
 /// The Huff CLI Args
 #[derive(ClapParser, Debug, Clone)]
-#[clap(version, about, long_about = None)]
+#[clap(name = "huffc", version, about, long_about = None)]
 struct Huff {
     /// The main path
     pub path: Option<String>,
 
     /// The contracts source path.
-    #[clap(short = 's', long = "source-path", default_value = "./src")]
+    #[clap(short = 's', long = "source-path", default_value = "./contracts")]
     source: String,
 
     /// The output file path.
@@ -40,11 +43,15 @@ struct Huff {
     #[clap(short = 'i', long = "inputs", multiple_values = true)]
     inputs: Option<Vec<String>>,
 
+    /// Interactively input the constructor args
+    #[clap(short = 'n', long = "interactive")]
+    interactive: bool,
+
     /// Whether to generate artifacts or not
     #[clap(short = 'a', long = "artifacts")]
     artifacts: bool,
 
-    /// Optimize compilation.
+    /// Optimize compilation [WIP]
     #[clap(short = 'z', long = "optimize")]
     optimize: bool,
 
@@ -61,9 +68,21 @@ struct Huff {
     verbose: bool,
 }
 
+/// Helper function to read an stdin input
+pub(crate) fn get_input(prompt: &str) -> String {
+    // let mut sp = Spinner::new(Spinners::Line, format!("{}{}",
+    // Paint::blue("[INTERACTIVE]".to_string()), prompt));
+    print!("{} {} ", Paint::blue("[INTERACTIVE]".to_string()), prompt);
+    let mut input = String::new();
+    let _ = std::io::stdout().flush();
+    let _ = std::io::stdin().read_line(&mut input);
+    // sp.stop();
+    input.trim().to_string()
+}
+
 fn main() {
     // Parse the command line arguments
-    let cli = Huff::parse();
+    let mut cli = Huff::parse();
 
     // Initiate Tracing if Verbose
     if cli.verbose {
@@ -78,20 +97,32 @@ fn main() {
             std::process::exit(1);
         }
     };
+
+    if cli.interactive {
+        // Don't accept configured inputs
+        cli.inputs = None;
+        // Don't export artifacts are compile
+        // Have to first generate artifacts, prompt user for args,
+        // and finally save artifacts with the new constructor args.
+        cli.artifacts = false;
+    }
+
+    let output = match (&cli.output, cli.artifacts) {
+        (Some(o), true) => Some(o.clone()),
+        (None, true) => Some(cli.outputdir.clone()),
+        _ => None,
+    };
+
     let compiler: Compiler = Compiler {
         sources: Arc::clone(&sources),
-        output: match (&cli.output, cli.artifacts) {
-            (Some(o), true) => Some(o.clone()),
-            (None, true) => Some(cli.outputdir.clone()),
-            _ => None,
-        },
+        output,
         construct_args: cli.inputs,
         optimize: cli.optimize,
         bytecode: cli.bytecode,
     };
 
     // Create compiling spinner
-    tracing::debug!(target: "core", "[⠔] COMPILING");
+    tracing::debug!(target: "cli", "[⠔] COMPILING");
     let mut sp: Option<Spinner> = None;
     // If stdout is a TTY, create a spinner
     if stdout_isatty() {
@@ -105,7 +136,7 @@ fn main() {
         println!(" ");
     }
     match compile_res {
-        Ok(artifacts) => {
+        Ok(mut artifacts) => {
             if artifacts.is_empty() {
                 let e = CompilerError::CodegenError(CodegenError {
                     kind: CodegenErrorKind::AbiGenerationFailure,
@@ -127,11 +158,69 @@ fn main() {
                     ),
                     token: None,
                 });
-                tracing::error!(target: "core", "COMPILER ERRORED: {:?}", e);
+                tracing::error!(target: "cli", "COMPILER ERRORED: {:?}", e);
                 eprintln!("{}", Paint::red(format!("{}", e)));
                 std::process::exit(1);
             }
             if cli.bytecode {
+                if cli.interactive {
+                    tracing::info!(target: "cli", "ENTERING INTERACTIVE MODE");
+                    // let mut new_artifacts = vec![];
+                    for artifact in &mut artifacts {
+                        let mut appended_args = String::default();
+                        match artifact.abi {
+                            Some(ref abi) => match abi.constructor {
+                                Some(ref args) => {
+                                    println!(
+                                        "{} Constructor Arguments for Contract: \"{}\"",
+                                        Paint::blue("[INTERACTIVE]".to_string()),
+                                        artifact.file.path
+                                    );
+                                    for input in &args.inputs {
+                                        let arg_input = get_input(&format!(
+                                            "Enter a {:?} for constructor param{}:",
+                                            input.kind,
+                                            (!input.name.is_empty())
+                                                .then(|| format!(" \"{}\"", input.name))
+                                                .unwrap_or_default()
+                                        ));
+                                        let encoded =
+                                            Codegen::encode_constructor_args(vec![arg_input])
+                                                .iter()
+                                                .fold(String::default(), |acc, str| {
+                                                    let inner: Vec<u8> =
+                                                        ethers_core::abi::encode(&[str.clone()]);
+                                                    let hex_args: String =
+                                                        hex::encode(inner.as_slice());
+                                                    format!("{}{}", acc, hex_args)
+                                                });
+                                        appended_args.push_str(&encoded);
+                                    }
+                                }
+                                None => {
+                                    tracing::warn!(target: "cli", "NO CONSTRUCTOR FOR ABI: {:?}", abi)
+                                }
+                            },
+                            None => {
+                                tracing::warn!(target: "cli", "NO ABI FOR ARTIFACT: {:?}", artifact)
+                            }
+                        }
+                        match Arc::get_mut(artifact) {
+                            Some(art) => {
+                                art.bytecode = format!("{}{}", art.bytecode, appended_args);
+                            }
+                            None => {
+                                tracing::warn!(target: "cli", "FAILED TO ACQUIRE MUTABLE REF TO ARTIFACT")
+                            }
+                        }
+                    }
+                    tracing::debug!(target: "cli", "Re-exporting artifacts...");
+                    Compiler::export_artifacts(
+                        &artifacts,
+                        &OutputLocation(cli.output.unwrap_or_else(|| cli.outputdir.clone())),
+                    );
+                    tracing::info!(target: "cli", "RE-EXPORTED INTERACTIVE ARTIFACTS");
+                }
                 match sources.len() {
                     1 => print!("{}", artifacts[0].bytecode),
                     _ => artifacts
@@ -141,7 +230,7 @@ fn main() {
             }
         }
         Err(e) => {
-            tracing::error!(target: "core", "COMPILER ERRORED: {:?}", e);
+            tracing::error!(target: "cli", "COMPILER ERRORED: {:?}", e);
             eprintln!("{}", Paint::red(format!("{}", e)));
             std::process::exit(1);
         }
diff --git a/huff_utils/src/abi.rs b/huff_utils/src/abi.rs
index ed28c43..096ff29 100644
--- a/huff_utils/src/abi.rs
+++ b/huff_utils/src/abi.rs
@@ -35,7 +35,7 @@
 //! ```
 
 use serde::{Deserialize, Serialize};
-use std::collections::BTreeMap;
+use std::{collections::BTreeMap, fmt};
 
 use crate::ast::{self, FunctionType};
 
@@ -66,13 +66,46 @@ impl Abi {
 // Allows for simple ABI Generation by directly translating the AST
 impl From<ast::Contract> for Abi {
     fn from(contract: ast::Contract) -> Self {
-        let constructors = contract
-            .macros
+        // Try to get the constructor inputs from an overriden function
+        // Otherwise, use the CONSTRUCTOR macro if one exists
+        let constructor = contract
+            .functions
             .iter()
-            .filter(|m| m.name == "CONSTRUCTOR")
+            .filter(|m| m.name.to_lowercase() == "constructor")
             .cloned()
-            .collect::<Vec<ast::MacroDefinition>>();
-        let constructor: Option<&ast::MacroDefinition> = constructors.get(0);
+            .collect::<Vec<ast::Function>>()
+            .get(0)
+            .map(|func| Constructor {
+                inputs: func
+                    .inputs
+                    .iter()
+                    .map(|argument| FunctionParam {
+                        name: argument.name.clone().unwrap_or_default(),
+                        kind: argument.arg_type.clone().unwrap_or_default().into(),
+                        internal_type: None,
+                    })
+                    .collect(),
+            })
+            .or_else(|| {
+                contract
+                    .macros
+                    .iter()
+                    .filter(|m| m.name == "CONSTRUCTOR")
+                    .cloned()
+                    .collect::<Vec<ast::MacroDefinition>>()
+                    .get(0)
+                    .map(|func| Constructor {
+                        inputs: func
+                            .parameters
+                            .iter()
+                            .map(|argument| FunctionParam {
+                                name: argument.name.clone().unwrap_or_default(),
+                                kind: argument.name.clone().unwrap_or_default().into(),
+                                internal_type: None,
+                            })
+                            .collect(),
+                    })
+            });
 
         // Instantiate functions and events
         let mut functions = BTreeMap::new();
@@ -142,23 +175,7 @@ impl From<ast::Contract> for Abi {
                 let _ = events.insert(val.0, val.1);
             });
 
-        Self {
-            constructor: constructor.map(|c| Constructor {
-                inputs: c
-                    .parameters
-                    .iter()
-                    .map(|argument| FunctionParam {
-                        name: argument.name.clone().unwrap_or_default(),
-                        kind: argument.arg_type.clone().unwrap_or_default().into(),
-                        internal_type: None,
-                    })
-                    .collect(),
-            }),
-            functions,
-            events,
-            receive: false,
-            fallback: false,
-        }
+        Self { constructor, functions, events, receive: false, fallback: false }
     }
 }
 
@@ -230,7 +247,7 @@ pub struct FunctionParam {
 /// #### FunctionParamType
 ///
 /// The type of a function parameter
-#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
+#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
 pub enum FunctionParamType {
     /// An address
     Address,
@@ -252,7 +269,49 @@ pub enum FunctionParamType {
     Tuple(Vec<FunctionParamType>),
 }
 
+impl fmt::Debug for FunctionParamType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.display(f)
+    }
+}
+
+impl fmt::Display for FunctionParamType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.display(f)
+    }
+}
+
 impl FunctionParamType {
+    /// Print a function parameter type to a formatter
+    pub fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match &self {
+            FunctionParamType::Address => write!(f, "address"),
+            FunctionParamType::Bytes => write!(f, "bytes"),
+            FunctionParamType::Int(size) => write!(f, "int{}", size),
+            FunctionParamType::Uint(size) => write!(f, "uint{}", size),
+            FunctionParamType::Bool => write!(f, "bool"),
+            FunctionParamType::String => write!(f, "string"),
+            FunctionParamType::Array(fpt, sizes) => write!(
+                f,
+                "{}{}",
+                fpt,
+                sizes
+                    .iter()
+                    .map(|s| (!s.eq(&0))
+                        .then(|| format!("[{}]", s))
+                        .unwrap_or_else(|| "[]".to_string()))
+                    .collect::<Vec<_>>()
+                    .join("")
+            ),
+            FunctionParamType::FixedBytes(size) => write!(f, "bytes{}", size),
+            FunctionParamType::Tuple(inner) => write!(
+                f,
+                "({})",
+                inner.iter().map(|fpt| fpt.to_string()).collect::<Vec<_>>().join(", ")
+            ),
+        }
+    }
+
     /// Convert string to type
     pub fn convert_string_to_type(string: &str) -> Result<Self, Box<dyn std::error::Error>> {
         let input = string.to_string().to_lowercase();


Test patch:
diff --git a/huff_utils/tests/abi.rs b/huff_utils/tests/abi.rs
index ba6c03d..5ef6e00 100644
--- a/huff_utils/tests/abi.rs
+++ b/huff_utils/tests/abi.rs
@@ -32,3 +32,48 @@ fn convert_function_param_type() {
         assert_eq!(ac_func_type, *expected_fn_types.get(&index).unwrap());
     }
 }
+
+#[test]
+fn test_display_func_param_type() {
+    let func_types = [
+        "bool",
+        "string",
+        "address",
+        "uint198",
+        "uint",
+        "bytes32",
+        "bytes",
+        "uint256[][]",
+        "address[5][]",
+        "bool[][1][][2]",
+    ];
+
+    let expected_fn_types = HashMap::from([
+        (0, FunctionParamType::Bool),
+        (1, FunctionParamType::String),
+        (2, FunctionParamType::Address),
+        (3, FunctionParamType::Uint(198)),
+        (4, FunctionParamType::Uint(256)),
+        (5, FunctionParamType::FixedBytes(32)),
+        (6, FunctionParamType::Bytes),
+        (7, FunctionParamType::Array(Box::new(FunctionParamType::Uint(256)), vec![0, 0])),
+        (8, FunctionParamType::Array(Box::new(FunctionParamType::Address), vec![5, 0])),
+        (9, FunctionParamType::Array(Box::new(FunctionParamType::Bool), vec![0, 1, 0, 2])),
+    ]);
+
+    for (index, func_type) in func_types.into_iter().enumerate() {
+        let ac_func_type = FunctionParamType::convert_string_to_type(func_type).unwrap();
+        assert_eq!(ac_func_type, *expected_fn_types.get(&index).unwrap());
+        // manual check for uints since "uint" is coalesced to "uint256"
+        match ac_func_type {
+            FunctionParamType::Uint(s) => {
+                assert_eq!(ac_func_type.to_string(), format!("uint{}", s));
+                assert_eq!(format!("{:?}", ac_func_type), format!("uint{}", s));
+            }
+            _ => {
+                assert_eq!(ac_func_type.to_string(), func_types[index]);
+                assert_eq!(format!("{:?}", ac_func_type), func_types[index]);
+            }
+        }
+    }
+}