Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rust bytecode crate #44

Merged
merged 6 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions bytecode/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions bytecode/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "tnt-core-bytecode"
version = "0.1.0"
edition = "2021"
description = "Bytecode exports for TNT Core Solidity contracts"
license = "MIT"

[features]
default = ["build-script"]
build-script = []

[build-dependencies]
serde_json = "1.0"
105 changes: 105 additions & 0 deletions bytecode/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;

fn to_screaming_snake_case(name: &str) -> String {
let mut result = String::new();
let mut prev_char_is_lowercase = false;

for (i, c) in name.chars().enumerate() {
if i > 0
&& c.is_uppercase()
&& (prev_char_is_lowercase
|| (i + 1 < name.len() && name.chars().nth(i + 1).unwrap().is_lowercase()))
{
result.push('_');
}
result.push(c.to_ascii_uppercase());
prev_char_is_lowercase = c.is_lowercase();
}
result
}

fn main() {
// Only run the build script if the build-script feature is enabled
if env::var("CARGO_FEATURE_BUILD_SCRIPT").is_ok() {
// Your existing build script logic here
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=../src");

// Run forge build
let status = Command::new("forge")
.arg("build")
.current_dir("..")
.status()
.expect("Failed to build contracts");

if !status.success() {
panic!("Failed to build contracts");
}

// List of contracts to generate bytecode for
let contracts = vec!["MasterBlueprintServiceManager"];

let mut rust_code = String::from(
r#"//! TNT Core contract bytecode exports
//!
//! This crate exports the bytecode of TNT Core contracts as constant byte vectors
//! that can be easily imported and used in other Rust projects.

/// Module containing all contract bytecodes
pub mod bytecode {
"#,
);

for contract in contracts {
let json_path = Path::new("..")
.join("out")
.join(format!("{}.sol", contract))
.join(format!("{}.json", contract));

let json_str = fs::read_to_string(&json_path)
.unwrap_or_else(|_| panic!("Failed to read {}", json_path.display()));

let json: serde_json::Value =
serde_json::from_str(&json_str).expect("Failed to parse JSON");

let bytecode = json["bytecode"]
.as_object()
.and_then(|obj| obj.get("object"))
.and_then(|obj| obj.as_str())
.unwrap_or_else(|| json["bytecode"].as_str().expect("Failed to get bytecode"));

let bytecode = bytecode.strip_prefix("0x").unwrap_or(bytecode);
let bytes: Vec<String> = bytecode
.as_bytes()
.chunks(2)
.map(|chunk| {
let hex = std::str::from_utf8(chunk).unwrap();
format!("0x{}", hex)
})
.collect();

let const_name = to_screaming_snake_case(contract);

rust_code.push_str(&format!(
r#" /// Bytecode for the {} contract
pub const {}: &[u8] = &[{}];

"#,
contract,
const_name,
bytes.join(", ")
));
}

rust_code.push_str(
r#"}
"#,
);

// Write directly to lib.rs
fs::write(Path::new("src").join("lib.rs"), rust_code).expect("Failed to write to lib.rs");
}
}
Loading
Loading