First Contract

In under five minutes, you’ll have a working Covenant contract compiled to EVM bytecode.

Create a project

covenant new my-contract
cd my-contract

This creates:

my-contract/
├── covenant.toml        # Project config
├── src/
│   └── main.cov         # Your contract source
└── tests/
    └── main_test.cov    # Test file

Open the source file

src/main.cov contains a minimal counter contract:

contract Counter {
  field count: U256 = 0
  field owner: Address

  event Incremented(new_value: U256)
  event Reset()

  action init(owner_addr: Address) {
    self.owner = owner_addr
    self.count = 0
  }

  action increment() {
    self.count += 1
    emit Incremented(new_value: self.count)
  }

  action reset() {
    only(self.owner)
    self.count = 0
    emit Reset()
  }

  @view action get_count() -> U256 {
    self.count
  }
}

Compile

covenant build

Output:

✓ Parsed    src/main.cov
✓ Type check passed
✓ Guard analysis: all actions guarded
✓ CEI ordering: verified
✓ Compiled  target/counter.artifact  (1,204 bytes)
✓ ABI       target/counter.abi.json

The .artifact file is EVM-compatible bytecode. The .abi.json is a standard Ethereum ABI for use with ethers.js, viem, or any other tooling.

Run tests

covenant test
Test suite: counter
✓ test_init
✓ test_increment
✓ test_reset_by_owner
✓ test_reset_fails_for_non_owner
Passed: 4 / 4

Run the linter

covenant lint
✓ No issues found
Detectors run: 38
Files analyzed: 1

Explore the ABI

cat target/counter.abi.json

The ABI is standard Ethereum JSON ABI format — paste it directly into Etherscan, Remix, or use it with ethers.js:

import { ethers } from 'ethers';
import abi from './target/counter.abi.json';

const contract = new ethers.Contract(address, abi, signer);
await contract.increment();
const count = await contract.get_count();

Next

03 — CLI Reference