Skip to main content

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 bytecode
  • build/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