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

# Wallet integration

# Wallet Integration

The `WalletDelegate` protocol is the bridge between the Privacy Boost SDK and your wallet. This guide covers implementing it for various wallet types, expanding on the brief examples in [Authentication](/sdk/concepts/authentication).

## Protocol Definition

```swift theme={null}
public protocol WalletDelegate: AnyObject {
    /// Get the connected wallet address
    func getAddress() async throws -> String

    /// Get the current chain ID
    func getChainId() async throws -> UInt64

    /// Sign a personal message (EIP-191)
    func signMessage(message: String) async throws -> String

    /// Sign EIP-712 typed data
    func signTypedData(typedDataJson: String) async throws -> String

    /// Send a transaction
    func sendTransaction(toAddress: String, value: String, data: String) async throws -> String

    /// Wait until a transaction is mined and return its receipt
    func waitForTransactionReceipt(txHash: String) async throws -> TransactionReceipt
}
```

## Implementation Requirements

### Return Values

* **Addresses**: Checksummed hex strings with `0x` prefix (42 characters)
* **Signatures**: Hex strings with `0x` prefix (132 characters for 65 bytes)
* **Transaction hashes**: Hex strings with `0x` prefix (66 characters)
* **Transaction receipts**: Mined receipt status and logs from `eth_getTransactionReceipt`
* **Chain ID**: Integer matching expected network

### Async Behavior

All WalletDelegate methods are `async throws`. The SDK awaits each call, so your implementation can use native Swift concurrency (async/await).
The SDK calls `waitForTransactionReceipt` after `sendTransaction` to confirm on-chain inclusion and read emitted events.

## WalletConnect Integration

Example using WalletConnect Swift SDK:

```swift theme={null}
import WalletConnectSwift

class WalletConnectDelegate: WalletDelegate {
    private let session: Session
    private let client: Client

    init(session: Session, client: Client) {
        self.session = session
        self.client = client
    }

    func getAddress() async throws -> String {
        guard let account = session.walletInfo?.accounts.first else {
            throw WalletError.notConnected
        }
        return account
    }

    func getChainId() async throws -> UInt64 {
        guard let chainId = session.walletInfo?.chainId else {
            throw WalletError.notConnected
        }
        return UInt64(chainId)
    }

    func signMessage(message: String) async throws -> String {
        let semaphore = DispatchSemaphore(value: 0)
        var result: Result<String, Error>?

        let address = try await getAddress()

        try client.personal_sign(
            url: session.url,
            message: message,
            account: address
        ) { response in
            switch response {
            case .success(let signature):
                result = .success(signature)
            case .failure(let error):
                result = .failure(error)
            }
            semaphore.signal()
        }

        semaphore.wait()

        switch result {
        case .success(let signature):
            return signature
        case .failure(let error):
            throw error
        case .none:
            throw WalletError.timeout
        }
    }

    func signTypedData(typedDataJson: String) async throws -> String {
        let semaphore = DispatchSemaphore(value: 0)
        var result: Result<String, Error>?

        let address = try await getAddress()

        try client.eth_signTypedData(
            url: session.url,
            account: address,
            message: typedDataJson
        ) { response in
            switch response {
            case .success(let signature):
                result = .success(signature)
            case .failure(let error):
                result = .failure(error)
            }
            semaphore.signal()
        }

        semaphore.wait()

        switch result {
        case .success(let signature):
            return signature
        case .failure(let error):
            throw error
        case .none:
            throw WalletError.timeout
        }
    }

    func sendTransaction(toAddress: String, value: String, data: String) async throws -> String {
        let semaphore = DispatchSemaphore(value: 0)
        var result: Result<String, Error>?

        let address = try await getAddress()

        let transaction = Client.Transaction(
            from: address,
            to: toAddress,
            data: data,
            gas: nil,
            gasPrice: nil,
            value: value,
            nonce: nil
        )

        try client.eth_sendTransaction(
            url: session.url,
            transaction: transaction
        ) { response in
            switch response {
            case .success(let txHash):
                result = .success(txHash)
            case .failure(let error):
                result = .failure(error)
            }
            semaphore.signal()
        }

        semaphore.wait()

        switch result {
        case .success(let txHash):
            return txHash
        case .failure(let error):
            throw error
        case .none:
            throw WalletError.timeout
        }
    }

    func waitForTransactionReceipt(txHash: String) async throws -> TransactionReceipt {
        // Poll eth_getTransactionReceipt via your RPC client until it returns a receipt.
        return try await pollReceipt(txHash: txHash)
    }
}

enum WalletError: Error {
    case notConnected
    case timeout
    case userRejected
}
```

## Web3.swift Integration

Example using web3.swift with a local keystore:

```swift theme={null}
import web3swift

class Web3Delegate: WalletDelegate {
    private let web3: web3
    private let keystoreManager: KeystoreManager
    private let address: EthereumAddress

    init(web3: web3, keystoreManager: KeystoreManager, address: EthereumAddress) {
        self.web3 = web3
        self.keystoreManager = keystoreManager
        self.address = address
    }

    func getAddress() async throws -> String {
        return address.address
    }

    func getChainId() async throws -> UInt64 {
        guard let chainId = web3.provider.network?.chainID else {
            throw Web3Error.connectionError
        }
        return UInt64(chainId)
    }

    func signMessage(message: String) async throws -> String {
        let messageData = message.data(using: .utf8)!
        let signature = try web3.wallet.signPersonalMessage(
            messageData,
            account: address,
            password: ""  // Or prompt user
        )
        return signature.toHexString().addHexPrefix()
    }

    func signTypedData(typedDataJson: String) async throws -> String {
        // Parse typed data and sign
        let data = typedDataJson.data(using: .utf8)!
        let typedData = try JSONDecoder().decode(EIP712TypedData.self, from: data)

        let signature = try web3.wallet.signTypedData(
            typedData,
            account: address,
            password: ""
        )
        return signature.toHexString().addHexPrefix()
    }

    func sendTransaction(toAddress: String, value: String, data: String) async throws -> String {
        var transaction = EthereumTransaction(
            to: EthereumAddress(toAddress)!,
            value: BigUInt(value)!,
            data: Data(hex: data)
        )

        let result = try web3.eth.sendTransaction(transaction)
        return result.hash
    }

    func waitForTransactionReceipt(txHash: String) async throws -> TransactionReceipt {
        // Poll eth_getTransactionReceipt via your web3 provider until it returns a receipt.
        return try await pollReceipt(txHash: txHash)
    }
}
```

## Testing with Mock Wallet

For testing, create a mock delegate:

```swift theme={null}
class MockWalletDelegate: WalletDelegate {
    let mockAddress = "0x1234567890123456789012345678901234567890"
    let mockChainId: UInt64 = 1

    var signMessageHandler: ((String) -> String)?
    var signTypedDataHandler: ((String) -> String)?
    var sendTransactionHandler: ((String, String, String) -> String)?

    func getAddress() async throws -> String {
        return mockAddress
    }

    func getChainId() async throws -> UInt64 {
        return mockChainId
    }

    func signMessage(message: String) async throws -> String {
        if let handler = signMessageHandler {
            return handler(message)
        }
        // Return a valid-looking signature
        return "0x" + String(repeating: "ab", count: 65)
    }

    func signTypedData(typedDataJson: String) async throws -> String {
        if let handler = signTypedDataHandler {
            return handler(typedDataJson)
        }
        return "0x" + String(repeating: "cd", count: 65)
    }

    func sendTransaction(toAddress: String, value: String, data: String) async throws -> String {
        if let handler = sendTransactionHandler {
            return handler(toAddress, value, data)
        }
        return "0x" + String(repeating: "ef", count: 32)
    }

    func waitForTransactionReceipt(txHash: String) async throws -> TransactionReceipt {
        return TransactionReceipt(txHash: txHash, status: true, logs: [])
    }
}
```

## Error Handling

Throw appropriate errors from delegate methods:

```swift theme={null}
enum WalletDelegateError: Error, LocalizedError {
    case notConnected
    case userRejected
    case networkError(String)
    case invalidSignature
    case transactionFailed(String)

    var errorDescription: String? {
        switch self {
        case .notConnected:
            return "Wallet not connected"
        case .userRejected:
            return "User rejected the request"
        case .networkError(let message):
            return "Network error: \(message)"
        case .invalidSignature:
            return "Invalid signature returned"
        case .transactionFailed(let message):
            return "Transaction failed: \(message)"
        }
    }
}
```

## Best Practices

1. **Validate inputs** - Check addresses and data formats before processing
2. **Handle timeouts** - Set reasonable timeouts for wallet operations
3. **User feedback** - Show loading indicators during signing/transactions
4. **Error messages** - Provide clear error messages for user-facing errors
5. **Thread safety** - Ensure delegate methods can be called from any thread

## Next Steps

* [Getting Started](../getting-started) - iOS SDK overview
