Deploy an ADL Contract
This tutorial walks you through deploying a smart contract to the Delta chain using the ADL (Alpha Delta Language) programming language.
Prerequisites
- ACDC CLI installed
- ADL compiler (
adlc) installed - A funded wallet on Delta testnet
- Basic programming knowledge
Step 1: Install the ADL Compiler
# Install ADL compiler
curl -sSf https://install.ac-dc.network/adl | sh
# Verify installation
adlc --version
# adlc 0.1.0
Step 2: Create a New Project
# Create project directory
mkdir my-contract
cd my-contract
# Initialize ADL project
adlc init
# Project structure:
# my-contract/
# adl.toml # Project configuration
# src/
# main.adl # Main contract file
# tests/
# test_main.adl # Test file
Step 3: Write Your Contract
Edit src/main.adl:
// src/main.adl
// A simple counter contract
contract Counter {
// State variables
state count: u64;
state owner: Address;
// Initialize the contract
init() {
self.count = 0;
self.owner = msg.sender;
}
// Public function to increment
pub fn increment() {
self.count += 1;
emit CountChanged(self.count);
}
// Public function to decrement
pub fn decrement() {
require(self.count > 0, "Count cannot be negative");
self.count -= 1;
emit CountChanged(self.count);
}
// Owner-only function to set count
pub fn set_count(value: u64) {
require(msg.sender == self.owner, "Only owner can set count");
self.count = value;
emit CountChanged(self.count);
}
// View function to get current count
pub fn get_count() -> u64 {
return self.count;
}
// View function to get owner
pub fn get_owner() -> Address {
return self.owner;
}
// Events
event CountChanged(new_count: u64);
}
Step 4: Compile the Contract
# Compile to bytecode
adlc build
# Output:
# Compiling Counter...
# Generated: build/Counter.wasm (2.3 KB)
# Generated: build/Counter.abi.json
# Build successful!
The compiler generates:
build/Counter.wasm- Compiled WebAssembly bytecodebuild/Counter.abi.json- ABI for interacting with the contract
Step 5: Write Tests
Edit tests/test_main.adl:
// tests/test_main.adl
test "initial count is zero" {
let counter = Counter::deploy();
assert_eq(counter.get_count(), 0);
}
test "increment increases count" {
let counter = Counter::deploy();
counter.increment();
assert_eq(counter.get_count(), 1);
counter.increment();
assert_eq(counter.get_count(), 2);
}
test "decrement decreases count" {
let counter = Counter::deploy();
counter.increment();
counter.increment();
counter.decrement();
assert_eq(counter.get_count(), 1);
}
test "decrement fails at zero" {
let counter = Counter::deploy();
expect_revert(counter.decrement(), "Count cannot be negative");
}
test "only owner can set count" {
let counter = Counter::deploy();
let other = Address::random();
// Owner can set
counter.set_count(100);
assert_eq(counter.get_count(), 100);
// Other cannot set
with_sender(other) {
expect_revert(counter.set_count(50), "Only owner can set count");
}
}
Run tests:
adlc test
# Output:
# Running 5 tests...
# test initial count is zero ... ok
# test increment increases count ... ok
# test decrement decreases count ... ok
# test decrement fails at zero ... ok
# test only owner can set count ... ok
#
# All 5 tests passed!
Step 6: Deploy to Testnet
Using CLI
# Deploy contract
acdc contract deploy build/Counter.wasm --network testnet
# Output:
# Deploying Counter...
# Transaction: 0xabc123...
# Waiting for confirmation...
# Contract deployed!
# Address: dx1contract123...
Using TypeScript SDK
import { AcdcClient, Wallet, ContractFactory } from '@acdc/sdk';
import * as fs from 'fs';
async function deploy() {
// Connect to testnet
const client = new AcdcClient({
chain: 'delta',
network: 'testnet'
});
// Load wallet
const wallet = Wallet.fromMnemonic(process.env.MNEMONIC!);
const signer = wallet.connect(client);
// Load compiled contract
const bytecode = fs.readFileSync('build/Counter.wasm');
const abi = JSON.parse(fs.readFileSync('build/Counter.abi.json', 'utf8'));
// Create factory
const factory = new ContractFactory(abi, bytecode, signer);
console.log('Deploying Counter contract...');
// Deploy (calls init() automatically)
const contract = await factory.deploy();
console.log('Transaction:', contract.deployTransaction.hash);
console.log('Waiting for confirmation...');
await contract.deployed();
console.log('Contract deployed at:', contract.address);
return contract;
}
deploy().catch(console.error);
Step 7: Interact with Your Contract
Using CLI
# Call view function
acdc contract call dx1contract123... get_count
# Output: 0
# Send transaction to increment
acdc contract send dx1contract123... increment
# Call view function again
acdc contract call dx1contract123... get_count
# Output: 1
# Set count (owner only)
acdc contract send dx1contract123... set_count 100
Using TypeScript SDK
import { AcdcClient, Wallet, Contract } from '@acdc/sdk';
import * as fs from 'fs';
async function interact() {
const client = new AcdcClient({
chain: 'delta',
network: 'testnet'
});
const wallet = Wallet.fromMnemonic(process.env.MNEMONIC!);
const signer = wallet.connect(client);
// Load ABI
const abi = JSON.parse(fs.readFileSync('build/Counter.abi.json', 'utf8'));
// Connect to deployed contract
const contract = new Contract('dx1contract123...', abi, signer);
// Read current count
const count = await contract.get_count();
console.log('Current count:', count.toString());
// Increment
console.log('Incrementing...');
const tx = await contract.increment();
await tx.wait();
// Read new count
const newCount = await contract.get_count();
console.log('New count:', newCount.toString());
// Listen for events
contract.on('CountChanged', (newCount) => {
console.log('Count changed to:', newCount.toString());
});
// Set count
const setTx = await contract.set_count(50);
await setTx.wait();
}
interact().catch(console.error);
Step 8: Verify Contract
Make your contract source code publicly verifiable:
# Verify on explorer
acdc contract verify dx1contract123... \
--source src/main.adl \
--compiler adlc-0.1.0
# Output:
# Verifying contract...
# Source code matches deployed bytecode!
# Contract verified: https://explorer.ac-dc.network/address/dx1contract123...
Advanced: Token Contract
Here is a more complete example - an ADL token contract:
// src/token.adl
contract Token {
state name: String;
state symbol: String;
state decimals: u8;
state total_supply: u256;
state balances: Map<Address, u256>;
state allowances: Map<Address, Map<Address, u256>>;
init(name: String, symbol: String, initial_supply: u256) {
self.name = name;
self.symbol = symbol;
self.decimals = 18;
self.total_supply = initial_supply;
self.balances[msg.sender] = initial_supply;
emit Transfer(Address::zero(), msg.sender, initial_supply);
}
pub fn balance_of(account: Address) -> u256 {
return self.balances[account];
}
pub fn transfer(to: Address, amount: u256) -> bool {
require(self.balances[msg.sender] >= amount, "Insufficient balance");
self.balances[msg.sender] -= amount;
self.balances[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
pub fn approve(spender: Address, amount: u256) -> bool {
self.allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
pub fn allowance(owner: Address, spender: Address) -> u256 {
return self.allowances[owner][spender];
}
pub fn transfer_from(from: Address, to: Address, amount: u256) -> bool {
require(self.balances[from] >= amount, "Insufficient balance");
require(self.allowances[from][msg.sender] >= amount, "Insufficient allowance");
self.balances[from] -= amount;
self.balances[to] += amount;
self.allowances[from][msg.sender] -= amount;
emit Transfer(from, to, amount);
return true;
}
event Transfer(from: Address, to: Address, amount: u256);
event Approval(owner: Address, spender: Address, amount: u256);
}
Deploy with constructor arguments:
adlc build
acdc contract deploy build/Token.wasm \
--args "MyToken" "MTK" "1000000000000000000000000" \
--network testnet
Gas Estimation
Before deploying, estimate gas costs:
// Estimate deployment gas
const deployGas = await factory.estimateGas.deploy();
console.log('Deployment gas:', deployGas.toString());
// Estimate function call gas
const incrementGas = await contract.estimateGas.increment();
console.log('Increment gas:', incrementGas.toString());
Upgradeability
For upgradeable contracts, use the proxy pattern:
// src/counter_v2.adl
contract CounterV2 {
state count: u64;
state owner: Address;
state step: u64; // New in V2
// Upgrade initializer
upgrade_init(step: u64) {
require(msg.sender == self.owner, "Only owner");
self.step = step;
}
pub fn increment() {
self.count += self.step; // Now increments by step
emit CountChanged(self.count);
}
// ... rest of implementation
}
Deploy upgrade:
acdc contract upgrade dx1proxy... build/CounterV2.wasm \
--args 5 \
--network testnet
Troubleshooting
Compilation Errors
# Show detailed error output
adlc build --verbose
# Check syntax only
adlc check src/main.adl
Deployment Failures
Error: Insufficient funds
- Get more testnet tokens from the faucet
Error: Gas estimation failed
- Your contract may have infinite loops or excessive storage
Error: Contract size exceeds limit
- Optimize your code or split into multiple contracts
Next Steps
- Participate in Governance - Vote on proposals
- ADL Language Reference - Complete ADL documentation
- Contract Security Guide - Best practices