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

# Identity Key Derivation

export const Method = ({children, className = ""}) => {
  return <code className={`spark-method not-prose inline-block px-2 py-1 mt-12 text-sm font-mono font-semibold rounded-md border ${className}`}>
      {children}
    </code>;
};

Spark wallets use a hierarchical deterministic (HD) key derivation scheme purpose-built for the Spark protocol. This follows the general principles of BIP43, using a custom purpose field derived from the SHA256 hash of "spark" to define application-specific key derivations.

<Info>
  This derivation scheme is our recommendation so users can easily migrate between different wallet apps. You can implement a different scheme, but it will lose the ability to migrate to/from other Spark wallets.
</Info>

***

## Derivation Scheme

### Base Derivation Path

```
m/8797555'/accountNumber'/keyType'
```

Where:

* **8797555'**: Custom purpose field (derived from last 3 bytes of SHA256("spark") = 0x863d73)
* **accountNumber'**: Account index (hardened derivation)
* **keyType'**: Specific key type (hardened derivation)

### Key Types

Spark derives 5 key types from the master seed:

| Path               | Key Type                  | Purpose                                                |
| ------------------ | ------------------------- | ------------------------------------------------------ |
| `m/8797555'/n'/0'` | **Identity Key**          | Primary wallet identifier and Spark Address generation |
| `m/8797555'/n'/1'` | **Signing HD Key**        | Base key for leaf key derivation                       |
| `m/8797555'/n'/2'` | **Deposit Key**           | Receiving L1 Bitcoin deposits                          |
| `m/8797555'/n'/3'` | **Static Deposit HD Key** | Reusable deposit addresses (SSP integration)           |
| `m/8797555'/n'/4'` | **HTLC Preimage HD Key**  | Lightning HTLC preimage generation                     |

<Columns cols={2}>
  <Card title="Identity Key" icon="fingerprint">
    **Path:** `m/8797555'/n'/0'`

    Primary wallet identifier. Used for authentication, Spark Address generation, and message signing.
  </Card>

  <Card title="Signing HD Key" icon="key">
    **Path:** `m/8797555'/n'/1'`

    HD key used as the base for deriving leaf-specific signing keys. Each Spark leaf gets its own derived key.
  </Card>

  <Card title="Deposit Key" icon="arrow-down-to-line">
    **Path:** `m/8797555'/n'/2'`

    Used for receiving L1 Bitcoin deposits into Spark. Single key, not HD-derived.
  </Card>

  <Card title="Static Deposit HD Key" icon="repeat">
    **Path:** `m/8797555'/n'/3'`

    HD key for generating reusable static deposit addresses. Each index produces a different deposit address.
  </Card>
</Columns>

<Card title="HTLC Preimage HD Key" icon="bolt">
  **Path:** `m/8797555'/n'/4'`

  HD key for generating HTLC preimages in Lightning payments. The `htlcHMAC()` method uses this key.
</Card>

***

## Account Number

### Default Behavior

The `accountNumber` parameter controls which account's keys are derived. **The default value differs by network:**

| Network | Default Account Number |
| ------- | ---------------------- |
| REGTEST | `0`                    |
| MAINNET | `1`                    |

<Warning>
  MAINNET defaults to `1` for backwards compatibility with legacy wallets created before multi-account support. If you're integrating with the SDK and using account `0` internally, account for this off-by-one behavior on mainnet.
</Warning>

### Using Account Numbers

```typescript theme={null}
// Account 0 (first account)
const { wallet: account0 } = await SparkWallet.initialize({
  mnemonicOrSeed: mnemonic,
  accountNumber: 0,
  options: { network: "REGTEST" }
});

// Account 1 (second account, same mnemonic)
const { wallet: account1 } = await SparkWallet.initialize({
  mnemonicOrSeed: mnemonic,
  accountNumber: 1,
  options: { network: "REGTEST" }
});

// These are completely separate wallets
const address0 = await account0.getSparkAddress();
const address1 = await account1.getSparkAddress();
console.log(address0 !== address1); // true
```

### Explicit Account Numbers

When building wallet software, always specify the account number explicitly to avoid confusion:

```typescript theme={null}
// Explicit is better than implicit
const { wallet } = await SparkWallet.initialize({
  mnemonicOrSeed: mnemonic,
  accountNumber: 0,  // Always specify
  options: { network: "MAINNET" }
});
```

***

## Leaf Key Derivation

When accepting transfers, Spark assigns a unique leaf ID to each UTXO. The signing key for that leaf is derived from the Signing HD Key using the leaf ID.

### Derivation Formula

```typescript theme={null}
// 1. Hash the leaf ID
const hash = sha256(leafId);

// 2. Extract first 4 bytes as unsigned 32-bit integer (big-endian)
const hashValue = hash.slice(0, 4).readUInt32BE();

// 3. Calculate derivation index (hardened)
const leafIndex = (hashValue % 0x80000000) + 0x80000000;

// 4. Derive child key
const leafKey = signingHDKey.deriveChild(leafIndex);
```

### Full Derivation Path

```
m/8797555'/{accountNumber}'/1'/{leafIndex}'
```

Where `leafIndex` is calculated from the leaf ID as shown above.

### Implementation

```typescript theme={null}
import { createHash } from 'crypto';

function getLeafDerivationPath(leafId: string, accountNumber: number): string {
  // Step 1: Calculate SHA256 hash of leaf ID
  const hash = createHash('sha256').update(leafId).digest();
  
  // Step 2: Convert first 4 bytes to number
  const hashValue = hash.readUInt32BE(0);
  
  // Step 3: Calculate hardened index
  const leafIndex = (hashValue % 0x80000000) + 0x80000000;
  
  // Step 4: Build derivation path
  return `m/8797555'/${accountNumber}'/1'/${leafIndex}'`;
}

// Example
const path = getLeafDerivationPath("leaf-abc-123", 0);
// Returns something like: m/8797555'/0'/1'/2147483747'
```

### Using KeyDerivation

In the SDK, you typically don't derive leaf keys manually. Instead, use the `KeyDerivation` type:

```typescript theme={null}
import { KeyDerivationType } from "@buildonspark/spark-sdk";

// The signer handles derivation internally
const publicKey = await signer.getPublicKeyFromDerivation({
  type: KeyDerivationType.LEAF,
  path: leafId  // The leaf ID, not the derivation path
});
```

***

## Static Deposit Key Derivation

Static deposit keys are derived from the Static Deposit HD Key at path `/3'`:

```typescript theme={null}
// Derive static deposit key at index 5
const staticDepositPath = `m/8797555'/${accountNumber}'/3'/${index + 0x80000000}'`;
```

In the SDK:

```typescript theme={null}
// Get static deposit key at index 5
const publicKey = await signer.getStaticDepositSigningKey(5);
const privateKey = await signer.getStaticDepositSecretKey(5);
```

***

## Deposit Address Flow

For L1 Bitcoin deposits, the flow is:

1. **Generate Deposit Key**: Uses the Deposit Key at path `m/8797555'/n'/2'`
2. **Create Deposit Address**: Generate Bitcoin address from the key
3. **Receive Deposit**: User sends Bitcoin to the address
4. **Assign Leaf**: After tree creation, funds are assigned to a leaf with its own derived key

```typescript theme={null}
// Get the deposit signing key
const depositKey = await signer.getDepositSigningKey();

// The SDK handles address generation
const depositAddress = await wallet.getSingleUseDepositAddress();
```

***

## Usage in SparkWallet

### Initialize with Specific Account

```typescript theme={null}
import { SparkWallet } from "@buildonspark/spark-sdk";

const { wallet, mnemonic } = await SparkWallet.initialize({
  mnemonicOrSeed: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
  accountNumber: 0,
  options: {
    network: "REGTEST",
  },
});

// Identity key is at m/8797555'/0'/0'
const identityKey = await wallet.getIdentityPublicKey();
console.log("Identity key:", identityKey);
```

### Multiple Accounts from Same Mnemonic

```typescript theme={null}
const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";

// Create two separate accounts
const { wallet: wallet0 } = await SparkWallet.initialize({
  mnemonicOrSeed: mnemonic,
  accountNumber: 0,
  options: { network: "REGTEST" }
});

const { wallet: wallet1 } = await SparkWallet.initialize({
  mnemonicOrSeed: mnemonic,
  accountNumber: 1,
  options: { network: "REGTEST" }
});

// Each account has completely isolated keys
console.log(await wallet0.getSparkAddress()); // Different address
console.log(await wallet1.getSparkAddress()); // Different address
```

***

## Custom Derivation Paths

For non-standard use cases, you can provide a custom key generator:

```typescript theme={null}
import { DefaultSparkSigner, SparkValidationError } from "@buildonspark/spark-sdk";
import { HDKey } from "@scure/bip32";

class DerivationPathKeysGenerator {
  constructor(private readonly basePathTemplate: string) {}

  async deriveKeysFromSeed(seed: Uint8Array, accountNumber: number) {
    const hdkey = HDKey.fromMasterSeed(seed);

    const basePath = this.basePathTemplate.replace("?", String(accountNumber));
    const identityKey = hdkey.derive(`${basePath}/0'`);
    const signingKey = hdkey.derive(`${basePath}/1'`);
    const depositKey = hdkey.derive(`${basePath}/2'`);
    const staticDepositKey = hdkey.derive(`${basePath}/3'`);
    const htlcPreimageKey = hdkey.derive(`${basePath}/4'`);

    if (
      !identityKey.privateKey ||
      !identityKey.publicKey ||
      !signingKey.privateKey ||
      !signingKey.publicKey ||
      !depositKey.privateKey ||
      !depositKey.publicKey ||
      !staticDepositKey.privateKey ||
      !staticDepositKey.publicKey ||
      !htlcPreimageKey.privateKey ||
      !htlcPreimageKey.publicKey
    ) {
      throw new SparkValidationError("Failed to derive all required keys from seed");
    }

    return {
      identityKey: {
        privateKey: identityKey.privateKey,
        publicKey: identityKey.publicKey,
      },
      signingHDKey: {
        hdKey: signingKey,
        privateKey: signingKey.privateKey,
        publicKey: signingKey.publicKey,
      },
      depositKey: {
        privateKey: depositKey.privateKey,
        publicKey: depositKey.publicKey,
      },
      staticDepositHDKey: {
        hdKey: staticDepositKey,
        privateKey: staticDepositKey.privateKey,
        publicKey: staticDepositKey.publicKey,
      },
      HTLCPreimageHDKey: {
        hdKey: htlcPreimageKey,
        privateKey: htlcPreimageKey.privateKey,
        publicKey: htlcPreimageKey.publicKey,
      },
    };
  }
}

// Custom derivation template (? is replaced with account number)
const customGenerator = new DerivationPathKeysGenerator("m/44'/0'/?'");

const signer = new DefaultSparkSigner({
  sparkKeysGenerator: customGenerator
});

// The keys will be derived from the custom path:
// Identity:       m/44'/0'/n'/0'
// Signing HD:     m/44'/0'/n'/1'
// Deposit:        m/44'/0'/n'/2'
// Static Deposit: m/44'/0'/n'/3'
// HTLC Preimage:  m/44'/0'/n'/4'
```

<Warning>
  Custom derivation paths will not be compatible with other Spark wallets. Only use this if you have specific requirements that justify breaking compatibility.
</Warning>

***

## Migration Between Wallets

The standardized derivation scheme enables wallet migration:

### Export

```typescript theme={null}
// Save these securely
const walletBackup = {
  mnemonic: "your 12 or 24 word phrase",
  accountNumber: 0
};
```

### Import

```typescript theme={null}
const { wallet } = await SparkWallet.initialize({
  mnemonicOrSeed: walletBackup.mnemonic,
  accountNumber: walletBackup.accountNumber,
  options: { network: "MAINNET" }
});

// Same identity key, same Spark address
const address = await wallet.getSparkAddress();
```

***

## Security Considerations

### Hardened Derivation

All derivation paths use hardened derivation (indicated by the `'` suffix). This prevents:

* **Key leakage**: Child public keys cannot be used to derive parent keys
* **Chain code exposure**: Compromising a child key doesn't compromise siblings

### Key Isolation

Each account number creates a completely isolated set of keys:

```typescript theme={null}
// Account 0 and Account 1 share no key material
const account0Keys = deriveAccountKeys(mnemonic, 0);
const account1Keys = deriveAccountKeys(mnemonic, 1);

// No cryptographic relationship between them
assert(account0Keys.identity !== account1Keys.identity);
```

### Mnemonic Security

* Store mnemonic phrases securely (encrypted, offline if possible)
* Never transmit mnemonics over the network
* Consider using hardware wallets or secure enclaves for production

***

## Quick Reference

| Key Type       | Path                             | SDK Method                                                        |
| -------------- | -------------------------------- | ----------------------------------------------------------------- |
| Identity       | `m/8797555'/n'/0'`               | `signer.getIdentityPublicKey()`                                   |
| Signing HD     | `m/8797555'/n'/1'`               | Internal (leaf derivation base)                                   |
| Deposit        | `m/8797555'/n'/2'`               | `signer.getDepositSigningKey()`                                   |
| Static Deposit | `m/8797555'/n'/3'/i'`            | `signer.getStaticDepositSigningKey(i)`                            |
| HTLC Preimage  | `m/8797555'/n'/4'`               | `signer.htlcHMAC(transferId)`                                     |
| Leaf           | `m/8797555'/n'/1'/hash(leafId)'` | `signer.getPublicKeyFromDerivation({type: "leaf", path: leafId})` |
