npm package: @mentaproject/signer-react-native
Description: React Native Passkey signer for Menta wallet - implements SmartAccountSigner via WebAuthn
Author: Menta Project
Latest version: 0.0.18
Deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.

# @mentaproject/signer-react-native

React Native Passkey signer for Menta wallet. Implements `SmartAccountSigner` using WebAuthn/Passkeys with P-256 (secp256r1) keys stored in the device's secure enclave.

## Installation

```bash
npm install @mentaproject/passkey-signer-react-native
```

### Peer Dependencies

```bash
npm install react-native-passkey viem @mentaproject/core
```

### iOS Setup

Add the Associated Domains capability to your app:

1. In Xcode, go to your target's "Signing & Capabilities"
2. Add "Associated Domains"
3. Add `webcredentials:yourdomain.com`

Create an `apple-app-site-association` file on your server at `https://yourdomain.com/.well-known/apple-app-site-association`:

```json
{
  "webcredentials": {
    "apps": ["TEAMID.com.yourcompany.yourapp"]
  }
}
```

### Android Setup

Add to your `android/app/build.gradle`:

```gradle
android {
    defaultConfig {
        // ...
        resValue "string", "host", "yourdomain.com"
    }
}
```

Create a `assetlinks.json` file at `https://yourdomain.com/.well-known/assetlinks.json`:

```json
[{
  "relation": ["delegate_permission/common.get_login_creds", "delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.yourcompany.yourapp",
    "sha256_cert_fingerprints": ["YOUR_APP_FINGERPRINT"]
  }
}]
```

## Quick Start

### Register a New Passkey

```typescript
import { ReactNativePasskeySigner } from '@mentaproject/signer-react-native';

// Create a new passkey (triggers biometric prompt)
const signer = await ReactNativePasskeySigner.register('user@example.com', {
  rpId: 'yourdomain.com',
  rpName: 'Your App Name',
});

// Store the credential for later use
const credential = signer.getCredential();
await AsyncStorage.setItem('passkey', JSON.stringify(credential));

// Get the public key for smart account creation
const publicKeyX = signer.getPublicKeyX(); // 32 bytes hex
const publicKeyY = signer.getPublicKeyY(); // 32 bytes hex
```

### Restore from Stored Credential

```typescript
import { 
  ReactNativePasskeySigner,
  deserializeCredential 
} from '@mentaproject/signer-react-native';

// Load stored credential
const stored = await AsyncStorage.getItem('passkey');
const credential = deserializeCredential(JSON.parse(stored));

// Recreate the signer
const signer = ReactNativePasskeySigner.fromCredential(credential, {
  rpId: 'yourdomain.com',
  rpName: 'Your App Name',
});
```

### Sign a UserOperation

```typescript
// Sign a UserOpHash (triggers biometric prompt)
const signature = await signer.signMessage({
  message: { raw: userOpHash }, // Hex string
});

// Use the signature with your smart account
```

### Sign Typed Data (EIP-712)

```typescript
const signature = await signer.signTypedData({
  domain: {
    name: 'Your App',
    version: '1',
    chainId: 1,
    verifyingContract: '0x...',
  },
  types: {
    Message: [
      { name: 'content', type: 'string' },
    ],
  },
  primaryType: 'Message',
  message: {
    content: 'Hello World',
  },
});
```

## Signature Formats

The signer supports multiple signature encoding formats for different smart contract implementations:

```typescript
// Default: Kernel/ZeroDev format
const signer = await ReactNativePasskeySigner.register(username, config);

// Change format for Rhinestone
const rhineSigner = signer.withSignatureFormat('rhinestone');

// Raw format (r || s only)
const rawSigner = signer.withSignatureFormat('raw');
```

### Kernel Format (Default)

```solidity
abi.encode(
  bytes authenticatorData,
  string clientDataJSON,
  uint256 challengeIndex,
  uint256 responseTypeIndex,
  uint256 r,
  uint256 s
)
```

### Rhinestone Format

```solidity
abi.encode(
  bytes authenticatorData,
  bytes clientDataJSON,
  uint256[2] signature // [r, s]
)
```

## API Reference

### ReactNativePasskeySigner

#### Static Methods

| Method | Description |
|--------|-------------|
| `register(username, config)` | Creates a new passkey and returns a signer |
| `fromCredential(credential, config)` | Restores a signer from stored credential data |

#### Instance Properties

| Property | Type | Description |
|----------|------|-------------|
| `source` | `"passkey"` | Signer type identifier |
| `publicKey` | `Hex` | Uncompressed public key (65 bytes) |
| `credentialId` | `string` | Base64URL credential ID |
| `credentialIdHex` | `Hex` | Hex credential ID |

#### Instance Methods

| Method | Description |
|--------|-------------|
| `signMessage({ message })` | Signs a message (triggers biometric) |
| `signTypedData(typedData)` | Signs EIP-712 typed data |
| `getAddress()` | Returns derived address |
| `getPublicKeyX()` | Returns X coordinate (32 bytes) |
| `getPublicKeyY()` | Returns Y coordinate (32 bytes) |
| `getCredential()` | Returns credential for storage |
| `withSignatureFormat(format)` | Returns new signer with different format |

### Configuration

```typescript
interface ReactNativePasskeySignerConfig {
  // Required
  rpId: string;          // Relying Party ID (your domain)
  rpName: string;        // Display name for system UI
  
  // Optional
  timeout?: number;                    // Prompt timeout in ms (default: 60000)
  userVerification?: 'required' | 'preferred' | 'discouraged';
  signatureFormat?: 'kernel' | 'rhinestone' | 'raw';
}
```

### Credential Storage

```typescript
import { 
  serializeCredential, 
  deserializeCredential,
  isPasskeyCredential 
} from '@mentaproject/signer-react-native';

// Serialize for storage
const data = serializeCredential(signer.getCredential());
await SecureStorage.set('passkey', JSON.stringify(data));

// Deserialize on restore
const stored = JSON.parse(await SecureStorage.get('passkey'));
const credential = deserializeCredential(stored);

// Validate before use
if (isPasskeyCredential(credential)) {
  const signer = ReactNativePasskeySigner.fromCredential(credential, config);
}
```

## Integration with Smart Accounts

### With Kernel (ZeroDev)

```typescript
import { createKernelAccount } from '@zerodev/sdk';
import { ReactNativePasskeySigner } from '@mentaproject/signer-react-native';

const signer = await ReactNativePasskeySigner.register(username, {
  rpId: 'yourdomain.com',
  rpName: 'Your App',
});

// The public key coordinates are needed for the WebAuthn validator
const webAuthnKey = {
  pubKeyX: BigInt(signer.getPublicKeyX()),
  pubKeyY: BigInt(signer.getPublicKeyY()),
  authenticatorIdHash: keccak256(signer.credentialIdHex),
};

// Create the kernel account with WebAuthn validator
const account = await createKernelAccount(publicClient, {
  plugins: {
    sudo: await signerToWebAuthnValidator(publicClient, {
      webAuthnKey,
      signer, // Pass the signer directly
    }),
  },
});
```

### With permissionless.js

```typescript
import { createSmartAccountClient } from 'permissionless';

const smartAccountClient = createSmartAccountClient({
  account,
  chain,
  bundlerTransport: http(bundlerUrl),
  // The signer's signMessage will be called for UserOp signing
});
```

## Utilities

For advanced use cases, low-level utilities are exported:

```typescript
import {
  // COSE parsing
  extractP256PublicKey,
  extractPublicKeyFromAttestation,
  
  // Signature handling
  parseDERSignature,
  normalizeSignatureS,
  encodeWebAuthnSignature,
  
  // Encoding
  base64UrlToHex,
  hexToBase64Url,
} from '@mentaproject/signer-react-native';
```

## Error Handling

```typescript
import { 
  PasskeySignerError, 
  COSEParseError, 
  SignatureError 
} from '@mentaproject/signer-react-native';

try {
  const signer = await ReactNativePasskeySigner.register(username, config);
} catch (error) {
  if (error instanceof PasskeySignerError) {
    // User cancelled, biometric failed, or device not supported
    console.error('Passkey error:', error.message);
    console.error('Cause:', error.cause);
  }
}
```

## Security Considerations

1. **Secure Enclave**: Private keys never leave the device's secure enclave
2. **Biometric Required**: Each signature requires user authentication
3. **Low-S Normalization**: Signatures are normalized to prevent malleability
4. **Domain Binding**: Passkeys are bound to your domain (rpId)

## Requirements

- iOS 16+ or Android 9+
- React Native 0.70+
- Device with biometric authentication (Face ID, Touch ID, fingerprint)

## License

MIT