diff --git a/crates/cargo-pvm-contract/src/main.rs b/crates/cargo-pvm-contract/src/main.rs index b9910982..06a819c2 100644 --- a/crates/cargo-pvm-contract/src/main.rs +++ b/crates/cargo-pvm-contract/src/main.rs @@ -185,10 +185,12 @@ fn init_command(args: PvmContractArgs) -> Result<()> { match init_type { InitType::Blank => { + let memory_model = prompt_memory_model(args.memory_model)?; let contract_name = prompt_name(args.name, None)?; check_dir_exists(&contract_name)?; - debug!("Initializing blank contract: {contract_name}"); - scaffold::init_blank_contract(&contract_name) + let use_alloc = memory_model == MemoryModel::AllocWithAlloy; + debug!("Initializing blank contract: {contract_name} with alloc: {use_alloc}"); + scaffold::init_blank_contract(&contract_name, use_alloc) } InitType::Example => { let examples = load_examples()?; diff --git a/crates/cargo-pvm-contract/src/scaffold.rs b/crates/cargo-pvm-contract/src/scaffold.rs index 266fd66c..a6c81998 100644 --- a/crates/cargo-pvm-contract/src/scaffold.rs +++ b/crates/cargo-pvm-contract/src/scaffold.rs @@ -1,3 +1,8 @@ +#[derive(Template)] +#[template(path = "scaffold/blank_sol.sol.txt")] +struct BlankSolTemplate<'a> { + contract_name: &'a str, +} use anyhow::{Context, Result}; use askama::Template; use convert_case::{Case, Casing}; @@ -39,6 +44,10 @@ struct CargoTomlTemplate<'a> { #[template(path = "scaffold/contract_blank.rs.txt")] struct ContractBlankTemplate; +#[derive(Template)] +#[template(path = "scaffold/contract_blank_alloc.rs.txt")] +struct ContractBlankAllocTemplate; + #[derive(Template)] #[template(path = "scaffold/build.rs.txt")] struct BuildRsTemplate; @@ -185,7 +194,7 @@ fn format_bytes32_multiline(bytes: &[u8; 32]) -> String { } /// Create a new blank contract project. -pub fn init_blank_contract(contract_name: &str) -> Result<()> { +pub fn init_blank_contract(contract_name: &str, use_alloc: bool) -> Result<()> { let contract_name = contract_name.to_case(Case::Kebab); let target_dir = std::env::current_dir()?.join(&contract_name); if target_dir.exists() { @@ -226,7 +235,18 @@ pub fn init_blank_contract(contract_name: &str) -> Result<()> { "[toolchain]\nchannel = \"nightly\"\n", )?; fs::create_dir(target_dir.join("src"))?; - let lib_rs_content = generate_blank_contract()?; + + // Write a minimal Solidity interface companion file using the template + let contract_name_pascal = contract_name.to_case(Case::Pascal); + let sol_file_name = format!("{}.sol", contract_name_pascal); + let sol_content = BlankSolTemplate { + contract_name: &contract_name_pascal, + } + .render() + .context("Failed to render blank Solidity interface template")?; + fs::write(target_dir.join(&sol_file_name), sol_content)?; + + let lib_rs_content = generate_blank_contract(use_alloc)?; fs::write( target_dir.join(format!("src/{}.rs", contract_name)), lib_rs_content, @@ -235,7 +255,7 @@ pub fn init_blank_contract(contract_name: &str) -> Result<()> { let build_rs_content = generate_build_rs()?; fs::write(target_dir.join("build.rs"), build_rs_content)?; - let cargo_toml_content = generate_cargo_toml(&contract_name, &contract_name, false)?; + let cargo_toml_content = generate_cargo_toml(&contract_name, &contract_name, use_alloc)?; fs::write(target_dir.join("Cargo.toml"), cargo_toml_content)?; println!("Successfully initialized blank contract project: {target_dir:?}"); @@ -453,10 +473,16 @@ fn extract_solc_metadata_from_bytes( Ok((metadata, contract_name.clone())) } -fn generate_blank_contract() -> Result { - ContractBlankTemplate - .render() - .context("Failed to render blank contract template") +fn generate_blank_contract(use_alloc: bool) -> Result { + if use_alloc { + ContractBlankAllocTemplate + .render() + .context("Failed to render blank alloc contract template") + } else { + ContractBlankTemplate + .render() + .context("Failed to render blank contract template") + } } fn generate_build_rs() -> Result { diff --git a/crates/cargo-pvm-contract/templates/scaffold/blank_sol.sol.txt b/crates/cargo-pvm-contract/templates/scaffold/blank_sol.sol.txt new file mode 100644 index 00000000..d69b3049 --- /dev/null +++ b/crates/cargo-pvm-contract/templates/scaffold/blank_sol.sol.txt @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title {{ contract_name }} +/// @notice Blank contract interface +interface {{ contract_name }} { + // Add function signatures here +} diff --git a/crates/cargo-pvm-contract/templates/scaffold/contract_blank_alloc.rs.txt b/crates/cargo-pvm-contract/templates/scaffold/contract_blank_alloc.rs.txt new file mode 100644 index 00000000..0804aa51 --- /dev/null +++ b/crates/cargo-pvm-contract/templates/scaffold/contract_blank_alloc.rs.txt @@ -0,0 +1,44 @@ +#![no_main] +#![no_std] + +use pallet_revive_uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +extern crate alloc; +use alloc::vec; + +#[global_allocator] +static mut ALLOC: picoalloc::Mutex>> = { + static mut ARRAY: picoalloc::Array<1024> = picoalloc::Array([0u8; 1024]); + + picoalloc::Mutex::new(picoalloc::Allocator::new(unsafe { + picoalloc::ArrayPointer::new(&raw mut ARRAY) + })) +}; + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + // Safety: The unimp instruction is guaranteed to trap + unsafe { + core::arch::asm!("unimp"); + core::hint::unreachable_unchecked(); + } +} + +/// This is the constructor which is called once per contract. +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +/// This is the regular entry point when the contract is called. +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let call_data_len = api::call_data_size(); + let mut call_data = vec![0u8; call_data_len as usize]; + api::call_data_copy(&mut call_data, 0); + + // TODO: Implement your contract logic here + // Example: Parse the call data selector and dispatch to functions + // let selector: [u8; 4] = call_data[0..4].try_into().unwrap(); + // match selector { ... } + + api::return_value(ReturnFlags::empty(), &[]); +}