> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usewraith.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# EVM Primitives

> Stealth address crypto for EVM chains using secp256k1

Low-level stealth address functions for EVM-compatible chains (Horizen, Ethereum, Polygon, Base, etc.) using secp256k1. Import from `@wraith-protocol/sdk/chains/evm`.

Most developers should use the [Agent Client](/sdk/agent-client) instead. These primitives are for power users building custom stealth address integrations.

## Import

```typescript theme={null}
import {
  // Stealth kit (crypto)
  deriveStealthKeys,
  generateStealthAddress,
  checkStealthAddress,
  scanAnnouncements,
  deriveStealthPrivateKey,
  encodeStealthMetaAddress,
  decodeStealthMetaAddress,
  signNameRegistration,
  signNameRegistrationOnBehalf,
  signNameUpdate,
  signNameRelease,
  metaAddressToBytes,
  // Builders (transactions)
  buildSendStealth,
  buildSendERC20,
  buildRegisterName,
  buildUpdateName,
  buildReleaseName,
  buildRegisterMetaAddress,
  buildAnnounce,
  buildResolveName,
  // Chain data
  fetchAnnouncements,
  getDeployment,
  DEPLOYMENTS,
  // ABIs (if you need raw encoding)
  SENDER_ABI,
  NAMES_ABI,
  REGISTRY_ABI,
  ANNOUNCER_ABI,
  // Constants
  STEALTH_SIGNING_MESSAGE,
  SCHEME_ID,
  META_ADDRESS_PREFIX,
} from "@wraith-protocol/sdk/chains/evm";
```

## Types

```typescript theme={null}
type HexString = `0x${string}`;

interface StealthKeys {
  spendingKey: HexString;      // 32-byte private key
  viewingKey: HexString;       // 32-byte private key
  spendingPubKey: HexString;   // 33-byte compressed public key
  viewingPubKey: HexString;    // 33-byte compressed public key
}

interface GeneratedStealthAddress {
  stealthAddress: HexString;   // 20-byte EVM address
  ephemeralPubKey: HexString;  // 33-byte compressed public key
  viewTag: number;             // 0-255
}

interface Announcement {
  schemeId: bigint;
  stealthAddress: HexString;
  caller: HexString;
  ephemeralPubKey: HexString;
  metadata: HexString;         // first byte is view tag
}

interface MatchedAnnouncement extends Announcement {
  stealthPrivateKey: HexString;
}
```

## Constants

```typescript theme={null}
const STEALTH_SIGNING_MESSAGE = "Sign this message to generate your Wraith stealth keys.\n\nChain: Horizen\nNote: This signature is used for key derivation only and does not authorize any transaction.";
const SCHEME_ID = 1n;                    // bigint for on-chain compatibility
const META_ADDRESS_PREFIX = "st:eth:0x";
```

***

## Functions

### `deriveStealthKeys(signature)`

Derive spending and viewing key pairs from a wallet signature.

```typescript theme={null}
const signature = await wallet.signMessage(STEALTH_SIGNING_MESSAGE);
const keys = deriveStealthKeys(signature as HexString);

console.log(keys.spendingKey);    // "0x..." (32-byte private key)
console.log(keys.viewingKey);     // "0x..." (32-byte private key)
console.log(keys.spendingPubKey); // "0x02..." (33-byte compressed)
console.log(keys.viewingPubKey);  // "0x03..." (33-byte compressed)
```

**Algorithm:**

1. Split the 65-byte signature: `r = sig[0:32]`, `s = sig[32:64]`
2. `spendingKey = keccak256(r)`
3. `viewingKey = keccak256(s)`
4. Derive compressed public keys from each private key

Same signature always produces the same keys. The spending key is never equal to the viewing key.

### `generateStealthAddress(spendingPubKey, viewingPubKey, ephemeralKey?)`

Generate a one-time stealth address for a recipient.

```typescript theme={null}
const result = generateStealthAddress(
  keys.spendingPubKey,
  keys.viewingPubKey
);

console.log(result.stealthAddress);  // "0x..." (fresh one-time address)
console.log(result.ephemeralPubKey); // "0x..." (publish with announcement)
console.log(result.viewTag);        // 0-255 (publish with announcement)
```

**Algorithm:**

1. Generate random ephemeral key pair `(r, R = r * G)`
2. Compute ECDH shared secret `S = r * viewingPubKey`
3. `hashedSecret = keccak256(S)`
4. `viewTag = hashedSecret[0]`
5. `stealthPoint = spendingPubKey + hash(S) * G`
6. `stealthAddress = keccak256(stealthPoint)[12:32]`

Each call produces a different address (new ephemeral key). Pass an explicit `ephemeralKey` for deterministic testing.

### `checkStealthAddress(ephemeralPubKey, viewingKey, spendingPubKey, viewTag)`

Check if a stealth address announcement belongs to you.

```typescript theme={null}
const result = checkStealthAddress(
  announcement.ephemeralPubKey,
  keys.viewingKey,
  keys.spendingPubKey,
  viewTag
);

if (result.isMatch) {
  console.log(result.stealthAddress); // the address that matched
}
```

Uses view tag for fast rejection — eliminates \~255/256 non-matching announcements without computing the full stealth address.

### `scanAnnouncements(announcements, viewingKey, spendingPubKey, spendingKey)`

Scan an array of on-chain announcements and return the ones that belong to you.

```typescript theme={null}
const announcements: Announcement[] = [
  // from subgraph query or chain events
];

const matched = scanAnnouncements(
  announcements,
  keys.viewingKey,
  keys.spendingPubKey,
  keys.spendingKey
);

for (const m of matched) {
  console.log(m.stealthAddress);    // address you control
  console.log(m.stealthPrivateKey); // private key for this address
}
```

For each announcement:

1. Skip if `schemeId` doesn't match
2. Extract view tag from metadata
3. Check if it matches using `checkStealthAddress`
4. If matched, derive the stealth private key

### `deriveStealthPrivateKey(spendingKey, ephemeralPubKey, viewingKey)`

Compute the private key that controls a specific stealth address.

```typescript theme={null}
const privateKey = deriveStealthPrivateKey(
  keys.spendingKey,
  stealth.ephemeralPubKey,
  keys.viewingKey
);

// Use this key to sign transactions from the stealth address
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(privateKey);
console.log(account.address); // matches the stealth address
```

**Algorithm:**

1. `S = viewingKey * ephemeralPubKey` (shared secret)
2. `hashScalar = keccak256(S) mod n`
3. `stealthPrivateKey = (spendingKey + hashScalar) mod n`

### `encodeStealthMetaAddress(spendingPubKey, viewingPubKey)`

Encode two public keys into a stealth meta-address string.

```typescript theme={null}
const metaAddress = encodeStealthMetaAddress(
  keys.spendingPubKey,
  keys.viewingPubKey
);
// "st:eth:0x02abc...03def..."
```

Both keys must be 33-byte compressed secp256k1 points.

### `decodeStealthMetaAddress(metaAddress)`

Decode a meta-address back into its component public keys.

```typescript theme={null}
const { spendingPubKey, viewingPubKey } = decodeStealthMetaAddress(
  "st:eth:0x02abc...03def..."
);
```

Validates the prefix, length, and that both keys are valid curve points.

### `metaAddressToBytes(metaAddress)`

Strip the `st:eth:` prefix from a meta-address, returning the raw hex bytes.

```typescript theme={null}
const bytes = metaAddressToBytes("st:eth:0x02abc...03def...");
// "0x02abc...03def..."
```

### `signNameRegistration(name, metaAddressBytes, spendingKey)`

Sign a message for on-chain `.wraith` name registration.

```typescript theme={null}
const metaBytes = metaAddressToBytes(metaAddress);
const sig = signNameRegistration("alice", metaBytes, keys.spendingKey);
// 65-byte hex signature for the WraithNames contract
```

### `signNameRegistrationOnBehalf(name, metaAddressBytes, spendingKey, nonce)`

Sign with a nonce for delegated registration.

```typescript theme={null}
const sig = signNameRegistrationOnBehalf("alice", metaBytes, keys.spendingKey, 0n);
```

### `signNameUpdate(name, newMetaAddressBytes, spendingKey)`

Sign a name update to point to a new meta-address.

```typescript theme={null}
const sig = signNameUpdate("alice", newMetaBytes, keys.spendingKey);
```

### `signNameRelease(name, spendingKey)`

Sign a name release to give up ownership.

```typescript theme={null}
const sig = signNameRelease("alice", keys.spendingKey);
```

***

## End-to-End Flow

```typescript theme={null}
import {
  deriveStealthKeys,
  generateStealthAddress,
  scanAnnouncements,
  deriveStealthPrivateKey,
  encodeStealthMetaAddress,
  decodeStealthMetaAddress,
  STEALTH_SIGNING_MESSAGE,
  SCHEME_ID,
} from "@wraith-protocol/sdk/chains/evm";
import type { HexString, Announcement } from "@wraith-protocol/sdk/chains/evm";

// 1. Recipient: derive keys from wallet signature
const sig = await wallet.signMessage(STEALTH_SIGNING_MESSAGE);
const keys = deriveStealthKeys(sig as HexString);

// 2. Recipient: publish stealth meta-address
const metaAddress = encodeStealthMetaAddress(keys.spendingPubKey, keys.viewingPubKey);
// Share "st:eth:0x..." or register as a .wraith name

// 3. Sender: generate stealth address from meta-address
const { spendingPubKey, viewingPubKey } = decodeStealthMetaAddress(metaAddress);
const stealth = generateStealthAddress(spendingPubKey, viewingPubKey);

// 4. Sender: send ETH to stealth.stealthAddress
//    Sender: call announcer contract with (stealth.ephemeralPubKey, stealth.viewTag)

// 5. Recipient: fetch and scan announcements
const announcements = await fetchAnnouncements("horizen");
const matched = scanAnnouncements(
  announcements,
  keys.viewingKey,
  keys.spendingPubKey,
  keys.spendingKey
);

// 6. Recipient: spend from stealth address
for (const m of matched) {
  const account = privateKeyToAccount(m.stealthPrivateKey);
  // Sign and submit transactions from account.address
}
```

## Transaction Builders

Builders return `{ to, data, value? }` objects — submit with any library (viem, ethers, wagmi, raw RPC).

### `buildSendStealth(params)`

Send ETH privately via stealth address. Uses the WraithSender contract for atomic send + announce.

```typescript theme={null}
import { buildSendStealth } from "@wraith-protocol/sdk/chains/evm";

const { transaction, stealthAddress, ephemeralPubKey, viewTag } = buildSendStealth({
  recipientMetaAddress: "st:eth:0x...",
  amount: "0.1",
  chain: "horizen",
});

// Submit with viem
await walletClient.sendTransaction(transaction);
// Or ethers
await signer.sendTransaction(transaction);
// Or wagmi
await sendTransaction(transaction);
```

Returns: `{ transaction: { to, data, value }, stealthAddress, ephemeralPubKey, viewTag }`

### `buildSendERC20(params)`

Send an ERC-20 token privately. The sender must have approved the WraithSender contract first.

```typescript theme={null}
import { buildSendERC20 } from "@wraith-protocol/sdk/chains/evm";

const { transaction } = buildSendERC20({
  recipientMetaAddress: "st:eth:0x...",
  token: "0x4b36cb6E...",   // token contract address
  amount: 1000000n,          // raw token amount (with decimals)
  chain: "horizen",
  gasTip: "0.001",           // optional ETH tip for recipient's gas
});

await walletClient.sendTransaction(transaction);
```

### `buildRegisterName(params)`

Register a `.wraith` name on-chain. The name is bound to the spending key.

```typescript theme={null}
import { buildRegisterName, deriveStealthKeys } from "@wraith-protocol/sdk/chains/evm";

const keys = deriveStealthKeys(signature);
const tx = buildRegisterName({
  name: "alice",
  stealthKeys: keys,
  chain: "horizen",
});

await walletClient.sendTransaction(tx);
```

### `buildUpdateName(params)`

Update a `.wraith` name's meta-address. Must be signed by the current owner.

```typescript theme={null}
import { buildUpdateName } from "@wraith-protocol/sdk/chains/evm";

const tx = buildUpdateName({
  name: "alice",
  newStealthKeys: newKeys,
  currentSpendingKey: oldKeys.spendingKey,
  chain: "horizen",
});

await walletClient.sendTransaction(tx);
```

### `buildReleaseName(params)`

Release a `.wraith` name. After release, anyone can register it.

```typescript theme={null}
import { buildReleaseName } from "@wraith-protocol/sdk/chains/evm";

const tx = buildReleaseName({
  name: "alice",
  spendingKey: keys.spendingKey,
  chain: "horizen",
});

await walletClient.sendTransaction(tx);
```

### `buildRegisterMetaAddress(params)`

Register a stealth meta-address in the ERC-6538 registry. Makes it discoverable by wallet address.

```typescript theme={null}
import { buildRegisterMetaAddress } from "@wraith-protocol/sdk/chains/evm";

const tx = buildRegisterMetaAddress({
  metaAddress: "st:eth:0x...",
  chain: "horizen",
});

await walletClient.sendTransaction(tx);
```

### `buildAnnounce(params)`

Publish a stealth address announcement on-chain. Use this if you're sending assets directly (not via WraithSender) and need to announce separately.

```typescript theme={null}
import { buildAnnounce } from "@wraith-protocol/sdk/chains/evm";

const tx = buildAnnounce({
  stealthAddress: "0x...",
  ephemeralPubKey: "0x...",
  viewTag: 42,
  chain: "horizen",
});

await walletClient.sendTransaction(tx);
```

### `buildResolveName(params)`

Build calldata to resolve a `.wraith` name. Returns data for a static call (read-only, no transaction).

```typescript theme={null}
import { buildResolveName } from "@wraith-protocol/sdk/chains/evm";

const call = buildResolveName({ name: "alice", chain: "horizen" });

// Use with a static call
const result = await publicClient.call({ to: call.to, data: call.data });
```

### Contract ABIs

The raw ABIs are exported if you need to encode calls yourself:

```typescript theme={null}
import { SENDER_ABI, NAMES_ABI, REGISTRY_ABI, ANNOUNCER_ABI } from "@wraith-protocol/sdk/chains/evm";
```

## Chain Deployments

The SDK ships with deployed contract addresses and subgraph URLs for supported chains. No need to deploy contracts yourself.

### `getDeployment(chain)`

Returns the full deployment config for a chain:

```typescript theme={null}
const deployment = getDeployment("horizen");
// {
//   chainId: 2651420,
//   name: "Horizen Testnet",
//   rpcUrl: "https://horizen-testnet.rpc.caldera.xyz/http",
//   explorerUrl: "https://horizen-testnet.explorer.caldera.xyz",
//   subgraphUrl: "https://api.goldsky.com/api/public/...",
//   contracts: {
//     announcer: "0x8AE65c05E7eb48B9bA652781Bc0a3DBA09A484F3",
//     registry: "0x953E6cEdcdfAe321796e7637d33653F6Ce05c527",
//     sender: "0x226C5eb4e139D9fa01cc09eA318638b090b12095",
//     names: "0x3d46f709a99A3910f52bD292211Eb5D557F882D6",
//   },
// }
```

### `DEPLOYMENTS`

Access all deployments directly:

```typescript theme={null}
import { DEPLOYMENTS } from "@wraith-protocol/sdk/chains/evm";
console.log(Object.keys(DEPLOYMENTS)); // ["horizen"]
```

### Supported EVM Chains

| Chain           | Chain ID | Status |
| --------------- | -------- | ------ |
| Horizen Testnet | 2651420  | Live   |

## Fetching Announcements

### `fetchAnnouncements(chain, subgraphUrl?)`

Fetches all stealth address announcements from the Goldsky subgraph for the specified chain. Handles pagination automatically.

```typescript theme={null}
const announcements = await fetchAnnouncements("horizen");
// Returns Announcement[] — ready to pass to scanAnnouncements()
```

Override the subgraph URL if needed:

```typescript theme={null}
const announcements = await fetchAnnouncements("horizen", "https://my-custom-subgraph.com/graphql");
```
