Skip to content
Open
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
6 changes: 4 additions & 2 deletions crates/cargo-pvm-contract/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;
Expand Down
40 changes: 33 additions & 7 deletions crates/cargo-pvm-contract/src/scaffold.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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,
Expand All @@ -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:?}");
Expand Down Expand Up @@ -453,10 +473,16 @@ fn extract_solc_metadata_from_bytes(
Ok((metadata, contract_name.clone()))
}

fn generate_blank_contract() -> Result<String> {
ContractBlankTemplate
.render()
.context("Failed to render blank contract template")
fn generate_blank_contract(use_alloc: bool) -> Result<String> {
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<String> {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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<picoalloc::Allocator<picoalloc::ArrayPointer<1024>>> = {
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(), &[]);
}