> For the complete documentation index, see [llms.txt](https://docs.armchain.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.armchain.org/developers/overview.md).

# Smart Contracts

Writing, testing, and deploying Solidity smart contracts on Armchain.

## Overview

If you've built smart contracts on Ethereum, you already know how to build on Armchain. The EVM is fully compatible (London fork), so **no Solidity changes are required**. The main differences are at the network level:

* Transactions use ML-DSA44 signatures (not ECDSA), handled by the SDK
* Transaction type is 3 (PQC), set automatically by the SDK
* Finality is instant, no need to wait for confirmations

## Development Setup

### Option 1: Remix IDE

No local setup required.

1. Open [Remix IDE](https://remix.ethereum.org)
2. Write your Solidity contract
3. In the **Solidity Compiler** tab, select version `0.8.24` and set EVM version to **london**
4. In the **Deploy & Run Transactions** tab, select **Injected Provider** (with Armchain Wallet connected)
5. For local development, select **External HTTP Provider** and enter `http://localhost:4000`

### Option 2: Hardhat (Partial Compatibility)

> **⚠ Compatibility Warning**: Hardhat's built-in signers and `hre.ethers` use ECDSA and **cannot sign Armchain transactions**. The following features are **not supported** with standard Hardhat:
>
> * `hre.ethers.getSigners()` / `hre.ethers.getContractFactory().deploy()` (ECDSA signing fails)
> * Self-funding test accounts
> * Fork testing against Armchain
>
> Hardhat **can** be used for: `npx hardhat compile`, artifact generation, and project structure. All signing must go through `@armchain-ethersv6/ethers`.

```bash
mkdir my-armchain-project && cd my-armchain-project
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npm install @armchain-ethersv6/ethers
npx hardhat init
```

Configure for Armchain:

```javascript
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: {
    version: "0.8.24",
    settings: {
      optimizer: { enabled: true, runs: 200 },
      evmVersion: "london",
    },
  },
  networks: {
    armchain: {
      url: "https://www.armchain.org/devnet",
      chainId: 55,
    },
    "armchain-local": {
      url: "http://localhost:4000",
      chainId: 55,
    },
  },
};
```

Compile with:

```bash
npx hardhat compile
```

## Writing Contracts

### Supported Solidity Versions

Use Solidity `0.8.x`. Recommended:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
```

### EVM Version

Set the EVM target to `london` in your compiler settings. Do **not** use:

* `shanghai` (`PUSH0` opcode not supported)
* `cancun` (transient storage not supported)

### Standard Patterns

All common Solidity patterns work on Armchain:

#### ERC-20 Token

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {
    constructor(uint256 initialSupply) 
        ERC20("My Armchain Token", "MAT") 
        Ownable(msg.sender) 
    {
        _mint(msg.sender, initialSupply * 10 ** decimals());
    }

    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}
```

#### NFT (ERC-721)

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyNFT is ERC721, ERC721URIStorage, Ownable {
    uint256 private _nextTokenId;

    constructor() ERC721("Armchain NFT", "ANFT") Ownable(msg.sender) {}

    function safeMint(address to, string memory uri) external onlyOwner {
        uint256 tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // Required overrides
    function tokenURI(uint256 tokenId)
        public view override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public view override(ERC721, ERC721URIStorage)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
```

#### Upgradeable Contract (Proxy Pattern)

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract MyUpgradeable is Initializable, OwnableUpgradeable, UUPSUpgradeable {
    uint256 public value;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(uint256 _value) public initializer {
        __Ownable_init(msg.sender);
        __UUPSUpgradeable_init();
        value = _value;
    }

    function setValue(uint256 _value) external onlyOwner {
        value = _value;
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
```

## Testing

> **⚠ Compatibility Warning**: Hardhat's default test environment (`hre.ethers.getSigners()`) provides ECDSA signers that cannot send Armchain transactions. Hardhat tests that deploy contracts or send transactions will fail. Unit tests that only test pure Solidity logic (no state changes) work fine.

For contract interaction tests, use the Armchain Ethers SDK with a [local Fakenet](/node-operators/fakenet.md):

```javascript
// test with Armchain Ethers SDK against a local fakenet
import { JsonRpcProvider, Wallet, ContractFactory } from "@armchain-ethersv6/ethers";
import MyTokenArtifact from "../artifacts/contracts/MyToken.sol/MyToken.json" assert { type: "json" };

const provider = new JsonRpcProvider("http://localhost:4000");
const wallet = new Wallet(process.env.FAKENET_KEY, provider);

// Deploy
const factory = new ContractFactory(MyTokenArtifact.abi, MyTokenArtifact.bytecode, wallet);
const token = await factory.deploy(1000000);
await token.waitForDeployment();

// Assert
const supply = await token.totalSupply();
console.assert(supply === 1000000n * 10n ** 18n, "Unexpected supply");
```

Remix IDE also has a built-in **Solidity Unit Testing** plugin for simpler logic checks.

## Deploying

### Remix IDE

Deploy using **Remix IDE** with the **Injected Provider** environment (Armchain Wallet). For a step-by-step walkthrough, see the [Quickstart](/get-started/quickstart.md).

### Hardhat + Armchain Ethers SDK

> **⚠ Do not use `hre.ethers` for deployments.** Use `@armchain-ethersv6/ethers` with artifacts generated by `npx hardhat compile`.

```javascript
// scripts/deploy.js
import { JsonRpcProvider, Wallet, ContractFactory } from "@armchain-ethersv6/ethers";
import MyTokenArtifact from "../artifacts/contracts/MyToken.sol/MyToken.json" assert { type: "json" };

const provider = new JsonRpcProvider(process.env.RPC_URL ?? "https://www.armchain.org/devnet");
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);

const factory = new ContractFactory(MyTokenArtifact.abi, MyTokenArtifact.bytecode, wallet);
const token = await factory.deploy(1000000);
await token.waitForDeployment();
console.log("MyToken deployed to:", await token.getAddress());
```

Run with Node.js directly (not via `npx hardhat run`, which injects an ECDSA signer):

```bash
node scripts/deploy.js
```

## Contract Verification

Submit your source code to the Armchain Explorer for public verification:

1. Deploy your contract
2. Navigate to the contract address on [explorer.armchain.org](https://www.armchain.org/explorer)
3. Click "Verify & Publish"
4. Submit your source code, compiler version, and constructor arguments

The explorer supports:

* **Single file** verification
* **Standard JSON input** verification
* **Multi-file** verification with imports

## Gas Optimization Tips

Since Armchain uses the same gas model as Ethereum (Berlin pricing):

1. **Use `calldata` instead of `memory`** for read-only function parameters
2. **Pack storage variables** so that `uint128` + `uint128` fits in one 32-byte slot
3. **Use `immutable` and `constant`** for values that don't change
4. **Minimize storage writes** since they cost 20,000-5,000 gas
5. **Use events for data that doesn't need on-chain reads**
6. **Enable the optimizer** with 200+ runs

```solidity
// Good: packed storage
struct User {
    uint128 balance;    // Slot 1 (first half)
    uint128 timestamp;  // Slot 1 (second half)
    address wallet;     // Slot 2
}

// Good: use immutable
address public immutable FACTORY;

// Good: use calldata
function process(bytes calldata data) external {
    // calldata is cheaper than memory for external functions
}
```

## Security Best Practices

1. **Use OpenZeppelin contracts** for standard implementations
2. **Follow checks-effects-interactions** pattern to prevent reentrancy
3. **Use `ReentrancyGuard`** for state-changing external calls
4. **Validate all inputs** by checking addresses, amounts, and array lengths
5. **Use access control** like `Ownable`, `AccessControl`, or custom modifiers
6. **Test thoroughly** with unit tests, integration tests, and fuzz tests
7. **Get audited** before production deployment
8. **Use timelocks** for admin functions in production

## Important Notes

* **No `PUSH0` opcode**: Set `evmVersion: "london"` in compiler settings.
* **Instant finality**: No confirmation waits. Once mined, the contract is live.
* **Same address derivation**: `CREATE` and `CREATE2` work identically to Ethereum.
* **Gas prices**: EIP-1559 fee market. Minimum gas price is 1 Gwei.

## Writing Custom AA Validation Contracts

If you are building a **custom AA account validation scheme** or a **paymaster contract** for Armchain's native Account Abstraction system, there is an important constraint to be aware of.

Armchain's AA system introduces new EVM opcodes that are not part of the standard Ethereum opcode set. The Solidity compiler (`solc`) does not recognize these opcodes and will reject contracts that use them. Any contract that needs to interact with the AA execution layer (validation hooks, paymaster hooks) must be written in **Yul**, the EVM intermediate representation language.

This applies to:

* Custom account validation scheme contracts
* Paymaster contracts that interact with AA-specific execution hooks
* Any extension of the system contract framework

Standard Solidity contracts that do not touch AA-specific opcodes are completely unaffected. ERC-20s, NFTs, DeFi protocols, and all other regular contracts compile and deploy normally.

> The Armchain team is evaluating a fork of the Solidity compiler that adds recognition of the new AA opcodes. Until that is available, Yul is the required path for AA system contract development.

For full context, see [Account Abstraction: System Contracts](/account-abstraction/system-contracts.md).

## Further Reading

* [Armchain Ethers SDK](/developers/overview-1.md) to interact with contracts from JavaScript
* [EVM Integration](/architecture/evm.md) for technical details of Armchain's EVM
* [Transaction Types](/pqc/transaction-types.md) for the Type 3 PQC transaction format
* [Quickstart](/get-started/quickstart.md) to deploy your first contract
* [Account Abstraction](/account-abstraction/overview.md) for building on top of native AA


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.armchain.org/developers/overview.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
