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

# SignerAdapter

> Unified signing interface with implementations for passkeys, direct keys, and external providers.

<Warning>
  `@chipi-stack/core` is in development (v0.1.0). APIs may change before stable release.
</Warning>

## Overview

`SignerAdapter` is an interface that abstracts over different key management strategies. Instead of hardwiring passkey logic into transaction flows, you program against the `SignerAdapter` interface and swap implementations as needed.

## The Interface

```typescript theme={null}
interface SignerAdapter {
  getPublicKey(): Promise<string>;
  sign(messageHash: string): Promise<SignResult>;
  getAccountAddress(): Promise<string>;
}

interface SignResult {
  r: string;
  s: string;
}
```

## Implementations

### DirectSigner

Signs with a local private key. For development and testing only.

```typescript theme={null}
import { DirectSigner } from "@chipi-stack/core";

const signer = new DirectSigner(
  "0x1234...privateKey",
  "0xabcd...accountAddress" // optional
);

const pubKey = await signer.getPublicKey();
const sig = await signer.sign(messageHash);
```

<Danger>
  Never use DirectSigner with real private keys in production. It exists for local development and testing.
</Danger>

#### Constructor

```typescript theme={null}
new DirectSigner(privateKey: string, accountAddress?: string)
```

* `privateKey` (string): Hex-encoded StarkNet private key (must start with `0x`)
* `accountAddress` (string, optional): Account address. Defaults to the derived public key.

#### Throws

* If `privateKey` is not a valid hex string starting with `0x`

### PasskeySigner

Wraps a WebAuthn assertion function that produces Starknet-compatible signatures `{r, s}` from a challenge. Users authenticate via biometrics (Face ID, fingerprint) and the result is used to sign transactions.

```typescript theme={null}
import { PasskeySigner } from "@chipi-stack/core";

// Your WebAuthn assertion function that returns a Starknet signature
async function passkeySign(challenge: string): Promise<{ r: string; s: string }> {
  // Call your WebAuthn authentication flow here
  // The challenge is the message hash to sign
  const assertion = await navigator.credentials.get({
    publicKey: { challenge: new TextEncoder().encode(challenge), /* ... */ },
  });
  // Extract r, s from the assertion response
  return { r: "0x...", s: "0x..." };
}

const signer = new PasskeySigner({
  authenticateFn: passkeySign,
  publicKey: userPasskeyPublicKey,
  accountAddress: userAccountAddress,
});

// Now use with TxBuilder via the Account
const sig = await signer.sign(messageHash);
```

#### Constructor

```typescript theme={null}
new PasskeySigner(opts: {
  authenticateFn: (challenge: string) => Promise<{ r: string; s: string }>;
  publicKey: string;
  accountAddress: string;
})
```

* `authenticateFn`: A function that takes a challenge hash and returns a Starknet-compatible signature `{r, s}` via WebAuthn assertion
* `publicKey`: The passkey public key obtained during registration
* `accountAddress`: The user's StarkNet account address

<Note>Most developers should use the `usePasskeyAuth` hook from `@chipi-stack/chipi-passkey/hooks` instead of `PasskeySigner` directly. `PasskeySigner` is for advanced use cases where you need low-level control over the signing flow.</Note>

### ExternalSigner

Generic adapter for any external signing provider. You supply the three interface methods and ExternalSigner wraps them.

```typescript theme={null}
import { ExternalSigner } from "@chipi-stack/core";

// Example: Argent Web Wallet integration
const signer = new ExternalSigner({
  getPublicKey: async () => argentWallet.getPublicKey(),
  sign: async (hash) => {
    const sig = await argentWallet.signMessage(hash);
    return { r: sig.r, s: sig.s };
  },
  getAccountAddress: async () => argentWallet.address,
});
```

#### Constructor

```typescript theme={null}
new ExternalSigner(opts: {
  getPublicKey: () => Promise<string>;
  sign: (messageHash: string) => Promise<SignResult>;
  getAccountAddress: () => Promise<string>;
})
```

## Use Cases

### Backend Automation

Use `DirectSigner` with a key stored in a vault/HSM for programmatic transaction execution:

```typescript theme={null}
const signer = new DirectSigner(process.env.AUTOMATION_KEY!);
// Build and sign transactions programmatically
```

### Multi-Wallet Support

Support multiple wallet providers without branching transaction code:

```typescript theme={null}
function getSignerForUser(user: User): SignerAdapter {
  switch (user.walletType) {
    case "passkey":
      return new PasskeySigner({ ... });
    case "argent":
      return new ExternalSigner({ ... });
    case "braavos":
      return new ExternalSigner({ ... });
  }
}

// Transaction code is the same regardless of signer
const signer = getSignerForUser(currentUser);
```

### Easy Testing

Mock signers for unit tests:

```typescript theme={null}
const mockSigner = new ExternalSigner({
  getPublicKey: async () => "0xtest_pubkey",
  sign: async (hash) => ({ r: "0x1", s: "0x2" }),
  getAccountAddress: async () => "0xtest_account",
});
```

### Auth Provider Migration

If you switch from Privy to another provider, only the SignerAdapter implementation changes. All TxBuilder, Erc20, and Amount code stays the same.

## Connecting to TxBuilder

`createAccount()` bridges any `SignerAdapter` into a starknet.js `Account` for use with `TxBuilder`.

### Gasless (recommended)

```typescript theme={null}
import { createAccount, DirectSigner, TxBuilder } from "@chipi-stack/core";
import { ChipiServerSDK } from "@chipi-stack/backend";

const sdk = new ChipiServerSDK({ apiPublicKey: "pk_...", apiSecretKey: "sk_..." });
const paymaster = sdk.createPaymasterAdapter();

const provider = { nodeUrl: "https://starknet-mainnet.infura.io/v3/YOUR_KEY" };
const accountAddress = "0xYOUR_ACCOUNT_ADDRESS";
const signer = new DirectSigner(process.env.PRIVATE_KEY!, accountAddress);
const account = await createAccount({ signer, provider });

const txHash = await new TxBuilder(account, { paymaster })
  .transfer(USDC, [{ to, amount }])
  .sendSponsored(); // gasless!
```

### Direct (user pays gas)

```typescript theme={null}
const provider = { nodeUrl: "https://starknet-mainnet.infura.io/v3/YOUR_KEY" };
const accountAddress = "0xYOUR_ACCOUNT_ADDRESS";
const signer = new DirectSigner(process.env.PRIVATE_KEY!, accountAddress);
const account = await createAccount({ signer, provider });

const result = await new TxBuilder(account)
  .transfer(USDC, [{ to, amount }])
  .send(); // user pays STRK for gas
```

## Related

* [TxBuilder](/sdk/core/tx-builder): Uses the Account (which uses the signer) to execute transactions
* [Introduction](/sdk/core/introduction): Overview of when to use core vs hooks
