Skip to 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