A complete blockchain implementation featuring: - synord: Full node with GHOSTDAG consensus - explorer-web: Modern React blockchain explorer with 3D DAG visualization - CLI wallet and tools - Smart contract SDK and example contracts (DEX, NFT, token) - WASM crypto library for browser/mobile
267 lines
6.9 KiB
Bash
Executable file
267 lines
6.9 KiB
Bash
Executable file
#!/bin/bash
|
|
# =============================================================================
|
|
# Synor Contract Template Generator
|
|
# =============================================================================
|
|
# Creates a new smart contract project from template.
|
|
#
|
|
# Usage:
|
|
# ./scripts/new-contract.sh <contract-name> [--description "Description"]
|
|
#
|
|
# Example:
|
|
# ./scripts/new-contract.sh my-token --description "My custom token"
|
|
# =============================================================================
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
CONTRACTS_DIR="$PROJECT_DIR/contracts"
|
|
|
|
# Colors
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
print_usage() {
|
|
echo "Synor Contract Template Generator"
|
|
echo ""
|
|
echo "Usage: $0 <contract-name> [options]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --description, -d Contract description"
|
|
echo " --help, -h Show this help"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 my-token"
|
|
echo " $0 my-token --description \"Custom fungible token\""
|
|
}
|
|
|
|
# Parse arguments
|
|
CONTRACT_NAME=""
|
|
DESCRIPTION="A Synor smart contract"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--description|-d)
|
|
DESCRIPTION="$2"
|
|
shift 2
|
|
;;
|
|
--help|-h)
|
|
print_usage
|
|
exit 0
|
|
;;
|
|
-*)
|
|
echo "Unknown option: $1"
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
*)
|
|
if [[ -z "$CONTRACT_NAME" ]]; then
|
|
CONTRACT_NAME="$1"
|
|
fi
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$CONTRACT_NAME" ]]; then
|
|
echo "Error: Contract name required"
|
|
print_usage
|
|
exit 1
|
|
fi
|
|
|
|
# Validate contract name (lowercase, alphanumeric, hyphens)
|
|
if ! [[ "$CONTRACT_NAME" =~ ^[a-z][a-z0-9-]*$ ]]; then
|
|
echo "Error: Contract name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens"
|
|
exit 1
|
|
fi
|
|
|
|
# Convert to Rust package name (hyphens to underscores for module)
|
|
RUST_NAME=$(echo "$CONTRACT_NAME" | tr '-' '_')
|
|
PACKAGE_NAME="synor-$CONTRACT_NAME"
|
|
|
|
CONTRACT_DIR="$CONTRACTS_DIR/$CONTRACT_NAME"
|
|
|
|
# Check if already exists
|
|
if [[ -d "$CONTRACT_DIR" ]]; then
|
|
log_warn "Contract directory already exists: $CONTRACT_DIR"
|
|
read -p "Overwrite? (y/N) " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
exit 1
|
|
fi
|
|
rm -rf "$CONTRACT_DIR"
|
|
fi
|
|
|
|
log_info "Creating contract: $CONTRACT_NAME"
|
|
|
|
# Create directory structure
|
|
mkdir -p "$CONTRACT_DIR/src"
|
|
|
|
# Create Cargo.toml
|
|
cat > "$CONTRACT_DIR/Cargo.toml" << EOF
|
|
[package]
|
|
name = "$PACKAGE_NAME"
|
|
version = "0.1.0"
|
|
edition = "2021"
|
|
description = "$DESCRIPTION"
|
|
authors = ["Your Name <your@email.com>"]
|
|
license = "MIT"
|
|
|
|
[lib]
|
|
crate-type = ["cdylib"]
|
|
|
|
[dependencies]
|
|
synor-sdk = { path = "../../crates/synor-sdk", default-features = false }
|
|
borsh = { version = "1.3", default-features = false, features = ["derive"] }
|
|
|
|
[profile.release]
|
|
opt-level = "z" # Optimize for size
|
|
lto = true # Link-time optimization
|
|
codegen-units = 1 # Single codegen unit for better optimization
|
|
panic = "abort" # Abort on panic (smaller binaries)
|
|
strip = true # Strip symbols
|
|
EOF
|
|
|
|
# Create lib.rs template
|
|
cat > "$CONTRACT_DIR/src/lib.rs" << 'EOF'
|
|
//! CONTRACT_DESCRIPTION
|
|
//!
|
|
//! # Methods
|
|
//! - `init(...)` - Initialize the contract
|
|
//! - Add more methods here...
|
|
|
|
#![no_std]
|
|
|
|
extern crate alloc;
|
|
|
|
use alloc::vec::Vec;
|
|
use borsh::BorshDeserialize;
|
|
use synor_sdk::prelude::*;
|
|
use synor_sdk::require;
|
|
|
|
// ==================== Storage Keys ====================
|
|
|
|
mod keys {
|
|
pub const OWNER: &[u8] = b"CONTRACT_NAME:owner";
|
|
pub const INITIALIZED: &[u8] = b"CONTRACT_NAME:initialized";
|
|
// Add more storage keys here...
|
|
}
|
|
|
|
// ==================== Data Structures ====================
|
|
|
|
// Add your custom structs here...
|
|
// #[derive(BorshSerialize, BorshDeserialize)]
|
|
// pub struct MyStruct {
|
|
// pub field: u64,
|
|
// }
|
|
|
|
// ==================== Storage Helpers ====================
|
|
|
|
fn get_owner() -> Option<Address> {
|
|
storage::get::<Address>(keys::OWNER)
|
|
}
|
|
|
|
fn is_initialized() -> bool {
|
|
storage::get::<bool>(keys::INITIALIZED).unwrap_or(false)
|
|
}
|
|
|
|
// ==================== Entry Points ====================
|
|
|
|
synor_sdk::entry_point!(init, call);
|
|
|
|
/// Initialize the contract.
|
|
fn init(params: &[u8]) -> Result<()> {
|
|
require!(!is_initialized(), Error::invalid_args("Already initialized"));
|
|
|
|
#[derive(BorshDeserialize)]
|
|
struct InitParams {
|
|
// Add your initialization parameters here
|
|
// name: String,
|
|
// value: u64,
|
|
}
|
|
|
|
let _params = InitParams::try_from_slice(params)
|
|
.map_err(|_| Error::invalid_args("Invalid init params"))?;
|
|
|
|
// Store owner
|
|
storage::set(keys::OWNER, &caller());
|
|
storage::set(keys::INITIALIZED, &true);
|
|
|
|
// Initialize your contract state here...
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Handle contract calls.
|
|
fn call(selector: &[u8], params: &[u8]) -> Result<Vec<u8>> {
|
|
// Define method selectors
|
|
let owner_sel = synor_sdk::method_selector("owner");
|
|
let example_sel = synor_sdk::method_selector("example_method");
|
|
|
|
match selector {
|
|
s if s == owner_sel => {
|
|
let owner = get_owner().unwrap_or(Address::zero());
|
|
Ok(borsh::to_vec(&owner).unwrap())
|
|
}
|
|
|
|
s if s == example_sel => {
|
|
#[derive(BorshDeserialize)]
|
|
struct Args {
|
|
value: u64,
|
|
}
|
|
let args = Args::try_from_slice(params)
|
|
.map_err(|_| Error::invalid_args("Expected value"))?;
|
|
|
|
// Implement your logic here
|
|
let result = args.value * 2;
|
|
|
|
Ok(borsh::to_vec(&result).unwrap())
|
|
}
|
|
|
|
_ => Err(Error::InvalidMethod),
|
|
}
|
|
}
|
|
|
|
// ==================== Internal Functions ====================
|
|
|
|
// Add your internal helper functions here...
|
|
|
|
// ==================== Events ====================
|
|
|
|
// Define your events here...
|
|
// #[derive(borsh::BorshSerialize)]
|
|
// struct MyEvent {
|
|
// field: u64,
|
|
// }
|
|
EOF
|
|
|
|
# Replace placeholders
|
|
sed -i '' "s/CONTRACT_DESCRIPTION/$DESCRIPTION/g" "$CONTRACT_DIR/src/lib.rs" 2>/dev/null || \
|
|
sed -i "s/CONTRACT_DESCRIPTION/$DESCRIPTION/g" "$CONTRACT_DIR/src/lib.rs"
|
|
|
|
sed -i '' "s/CONTRACT_NAME/$RUST_NAME/g" "$CONTRACT_DIR/src/lib.rs" 2>/dev/null || \
|
|
sed -i "s/CONTRACT_NAME/$RUST_NAME/g" "$CONTRACT_DIR/src/lib.rs"
|
|
|
|
log_success "Contract created: $CONTRACT_DIR"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Edit $CONTRACT_DIR/src/lib.rs to implement your contract"
|
|
echo " 2. Build with:"
|
|
echo " cargo build --manifest-path $CONTRACT_DIR/Cargo.toml --target wasm32-unknown-unknown --release"
|
|
echo " 3. Deploy with:"
|
|
echo " synor contract deploy target/wasm32-unknown-unknown/release/${PACKAGE_NAME//-/_}.wasm"
|
|
echo ""
|