> 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-1.md).

# Ethers SDK

The `@armchain-ethersv6/ethers` library is Armchain's official JavaScript/TypeScript SDK. If you're familiar with **ethers.js v6**, this is essentially a drop-in replacement with the same API and same patterns, but with all cryptographic operations swapped to use **ML-DSA44 post-quantum signatures**.

## Why This SDK?

Standard ethers.js uses ECDSA for signing, which Armchain nodes reject. Any application that **signs transactions or messages** needs this SDK. Read-only operations (querying balances, reading contract state) work fine with standard ethers.js.

| Operation                | Standard ethers.js | Armchain Ethers SDK  |
| ------------------------ | ------------------ | -------------------- |
| Read blockchain state    | ✅ Works            | ✅ Works              |
| Query balances/blocks    | ✅ Works            | ✅ Works              |
| Call view functions      | ✅ Works            | ✅ Works              |
| **Sign transactions**    | ❌ Rejected         | ✅ ML-DSA44           |
| **Sign messages**        | ❌ Incompatible     | ✅ ML-DSA44           |
| **Create wallets**       | ❌ ECDSA keys       | ✅ ML-DSA44 keys      |
| **HD wallet derivation** | ❌ Wrong coin type  | ✅ Armchain coin type |

## Installation

```bash
npm install @armchain-ethersv6/ethers
```

## Quick Start

### Connect to Armchain

```typescript
import { JsonRpcProvider } from "@armchain-ethersv6/ethers";

// Devnet
const provider = new JsonRpcProvider("https://www.armchain.org/devnet");

// Local (Fakenet)
const localProvider = new JsonRpcProvider("http://localhost:4000");

// Query blockchain
const blockNumber = await provider.getBlockNumber();
const balance = await provider.getBalance("0xADDRESS");
console.log("Block:", blockNumber, "Balance:", balance.toString());
```

### Create a Wallet

```typescript
import { Wallet, JsonRpcProvider } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("https://www.armchain.org/devnet");

// From private key (ML-DSA44, 2,560 bytes)
const wallet = new Wallet(privateKey, provider);
console.log("Address:", wallet.address);

// Random wallet
const randomWallet = Wallet.createRandom();
console.log("New address:", randomWallet.address);
console.log("New private key:", randomWallet.privateKey);
```

### Send a Transaction

```typescript
import { Wallet, JsonRpcProvider, parseEther } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new Wallet(privateKey, provider);

const tx = await wallet.sendTransaction({
  to: "0xRECIPIENT_ADDRESS",
  value: parseEther("1.0"),
  // type: 3 is automatically set by the SDK
});

console.log("TX Hash:", tx.hash);

console.log("Confirmed in block:", receipt.blockNumber);
```

### Interact with a Smart Contract

```typescript
import { Contract, Wallet, JsonRpcProvider, parseEther } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new Wallet(privateKey, provider);

// ERC-20 token contract
const tokenAbi = [
  "function balanceOf(address) view returns (uint256)",
  "function transfer(address to, uint256 amount) returns (bool)",
  "function symbol() view returns (string)",
  "event Transfer(address indexed from, address indexed to, uint256 value)"
];

const token = new Contract("0xTOKEN_ADDRESS", tokenAbi, wallet);

// Read (no signature needed)
const symbol = await token.symbol();
const balance = await token.balanceOf(wallet.address);
console.log(`${symbol} balance:`, balance.toString());

// Write (auto-signed with ML-DSA44)
const tx = await token.transfer("0xRECIPIENT", parseEther("100"));
const receipt = await tx.wait();
console.log("Transfer confirmed:", receipt.hash);
```

### Deploy a Contract

```typescript
import { ContractFactory, Wallet, JsonRpcProvider } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("http://localhost:4000");
const wallet = new Wallet(privateKey, provider);

const abi = [/* contract ABI */];
const bytecode = "0x...";  // Compiled contract bytecode

const factory = new ContractFactory(abi, bytecode, wallet);
const contract = await factory.deploy(/* constructor args */);
await contract.waitForDeployment();

console.log("Deployed at:", await contract.getAddress());
```

## HD Wallet (Hierarchical Deterministic)

Armchain uses a custom HD wallet derivation path:

```
m/44'/65536'/account'/0/address'
```

| Component     | Value           | Description                     |
| ------------- | --------------- | ------------------------------- |
| Purpose       | `44'`           | BIP-44 standard                 |
| Coin Type     | `65536'`        | Armchain's registered coin type |
| Account       | `0'`, `1'`, ... | Account index (hardened)        |
| Change        | `0`             | External addresses              |
| Address Index | `0'`, `1'`, ... | Address index (hardened)        |

> **All derivation is hardened** (denoted by `'`). Non-hardened derivation is not supported with ML-DSA44.

### Create from Mnemonic

```typescript
import { HDNodeWallet, Mnemonic } from "@armchain-ethersv6/ethers";

// Create from seed phrase
const mnemonic = "word1 word2 word3 ...";  // 12 or 24 words
const wallet = HDNodeWallet.fromPhrase(mnemonic);
console.log("Address:", wallet.address);

// Derive multiple accounts
for (let i = 0; i < 5; i++) {
  const path = `m/44'/65536'/${i}'/0/0'`;
  const child = wallet.derivePath(path);
  console.log(`Account ${i}:`, child.address);
}
```

### Generate a New Mnemonic

```typescript
import { Wallet } from "@armchain-ethersv6/ethers";

const wallet = Wallet.createRandom();
console.log("Mnemonic:", wallet.mnemonic.phrase);
console.log("Address:", wallet.address);
console.log("Path:", wallet.path);
```

## Signing Messages

```typescript
import { Wallet, hashMessage } from "@armchain-ethersv6/ethers";

const wallet = new Wallet(privateKey);

// Sign a message
const message = "Hello, Armchain!";
const signature = await wallet.signMessage(message);
console.log("Signature:", signature);

// The signature contains the full PQC signature blob:
// - Raw ML-DSA44 signature (2,420 bytes) + public key (1,312 bytes) = 3,732 bytes total
```

## Transaction Types

The SDK automatically creates **Type 3 (PQC) transactions**:

```typescript
// All transactions are Type 3 by default
const tx = await wallet.sendTransaction({
  to: recipient,
  value: parseEther("1.0"),
  // type: 3 is auto-set
  // gasLimit, maxFeePerGas, maxPriorityFeePerGas are auto-estimated
});
```

### Manual Transaction Construction

```typescript
import { Transaction, parseEther, parseUnits } from "@armchain-ethersv6/ethers";

const tx = Transaction.from({
  type: 3,
  chainId: 55,
  nonce: 42,
  to: "0xRECIPIENT",
  value: parseEther("1.0"),
  data: "0x",
  gasLimit: 21000n,
  maxFeePerGas: parseUnits("20", "gwei"),
  maxPriorityFeePerGas: parseUnits("2", "gwei"),
});

// Sign
const signedTx = await wallet.signTransaction(tx);
console.log("Signed TX:", signedTx);

// Broadcast
const response = await provider.broadcastTransaction(signedTx);
```

## Key Sizes and Formats

```typescript
import { Wallet } from "@armchain-ethersv6/ethers";

// Generate a new wallet (handles ML-DSA44 key generation internally)
const wallet = Wallet.createRandom();
console.log("Address:", wallet.address);
console.log("Private key size:", wallet.privateKey.length);  // Large: ML-DSA44 keys are 2,560 bytes
```

### Size Reference

| Component      | Size        | Description                                                          |
| -------------- | ----------- | -------------------------------------------------------------------- |
| Private Key    | 2,560 bytes | ML-DSA44 secret key                                                  |
| Public Key     | 1,312 bytes | ML-DSA44 public key                                                  |
| Raw Signature  | 2,420 bytes | ML-DSA44 detached signature                                          |
| Signature Blob | 3,732 bytes | Raw signature (2,420) + public key (1,312), included in transactions |

## API Compatibility

The SDK maintains the same API as ethers.js v6 wherever possible:

```typescript
// These all work the same as standard ethers.js v6
import {
  JsonRpcProvider,
  WebSocketProvider,
  Contract,
  ContractFactory,
  Wallet,
  HDNodeWallet,
  parseEther,
  parseUnits,
  formatEther,
  formatUnits,
  isAddress,
  getAddress,
  keccak256,
  AbiCoder,
  Interface,
  EventLog,
  // ... and more
} from "@armchain-ethersv6/ethers";
```

### Removed/Changed Methods

| Method                             | Status  | Reason                              |
| ---------------------------------- | ------- | ----------------------------------- |
| `Signature.recoverPublicKey()`     | Removed | Public key is embedded in signature |
| `SigningKey.computeSharedSecret()` | Removed | ECDH not applicable to DSA          |
| `SigningKey.compressedPublicKey`   | Removed | No compression in ML-DSA44          |
| `Transaction.unsignedSerialized`   | Removed | All PQC txns must be signed         |

### Legacy Compatibility Properties

For migration convenience, these properties still exist but return compatibility values:

```typescript
const sig = Signature.from(signatureBlob);
sig.r;        // First 32 bytes of signature (for compatibility)
sig.s;        // Next 32 bytes (for compatibility)  
sig.v;        // Always 27 (not used in PQC)
sig.yParity;  // Always 0 (not applicable)
```

## Error Handling

```typescript
try {
  const tx = await wallet.sendTransaction({
    to: recipient,
    value: parseEther("1000000"),  // More than balance
  });
} catch (error) {
  if (error.code === "INSUFFICIENT_FUNDS") {
    console.log("Not enough ARM");
  } else if (error.code === "NONCE_EXPIRED") {
    console.log("Nonce already used");
  } else if (error.code === "UNPREDICTABLE_GAS_LIMIT") {
    console.log("Transaction would revert");
  } else {
    console.error("Unknown error:", error);
  }
}
```

## Migration from Standard ethers.js

If you're porting an Ethereum dApp to Armchain:

### Step 1: Replace the Package

```bash
npm uninstall ethers
npm install @armchain-ethersv6/ethers
```

### Step 2: Update Imports

```typescript
// Before
import { ethers } from "ethers";

// After
import { ethers } from "@armchain-ethersv6/ethers";
```

### Step 3: Update RPC Endpoint

```typescript
// Before
const provider = new JsonRpcProvider("https://eth-mainnet.g.alchemy.com/...");

// After
const provider = new JsonRpcProvider("https://www.armchain.org/devnet");
```

### Step 4: Key Generation

```typescript
// Before (ECDSA)
const wallet = Wallet.createRandom();  // 32-byte private key

// After (ML-DSA44)
const wallet = Wallet.createRandom();  // 2,560-byte private key
// Same API, larger keys internally
```

### Step 5: No Other Changes

Contract interactions, event listeners, and transaction building work identically to standard ethers.js.

## Performance Notes

| Operation              | Time     |
| ---------------------- | -------- |
| Key Generation         | \~2.0 ms |
| Signature Creation     | \~3.5 ms |
| Signature Verification | \~5.0 ms |
| HD Child Derivation    | \~4.1 ms |
| Address Computation    | \~0.3 ms |

These are approximately 4x slower than ECDSA but negligible for typical dApp usage.

## Further Reading

* [PQC Transaction Types](/pqc/transaction-types.md) for the detailed Type 3 format
* [ML-DSA44 Deep Dive](/pqc/mldsa44.md) for the cryptographic details
* [Smart Contracts](/developers/overview.md) for writing contracts on Armchain
