# 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.

## 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


---

# Agent Instructions: 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:

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

The question should be specific, self-contained, and written in natural language.
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.
