# dGEN SubAccount SDK

Android SDK for managing smart contract sub-accounts on [ethOS](https://ethosmobile.org/) devices. Uses local P-256 cryptography (Android KeyStore) combined with the ethOS system wallet service for recovery, supporting ERC-4337 Account Abstraction, ERC-6492 signatures, and EIP-712 typed data signing.

### Features

* **Local P-256 signing** via Android KeyStore (no private keys leave the device)
* **Smart wallet address computation** using CREATE2 (CoinbaseSmartWallet factory)
* **ERC-4337 UserOperations** — build, sign, and submit through any bundler
* **Message signing** — `personal_sign` (ERC-191) and `eth_signTypedData` (EIP-712)
* **ERC-6492** signature wrapping for undeployed wallets
* **Multi-chain support** — Ethereum, Base, Optimism, Polygon, Arbitrum, and more
* **Batch transactions** via `executeBatch`

### Requirements

* **Android minSdk 28** (Android 9+)
* **ethOS device** — the SDK requires the ethOS system wallet service. Throws `NoSysWalletException` on non-ethOS devices.
* **Bundler RPC endpoint** (e.g. [Pimlico](https://pimlico.io/), [Alchemy](https://alchemy.com/))
* **RPC endpoint** (e.g. Alchemy, Infura)

### Installation

#### JitPack

Add the JitPack repository to your **root** `settings.gradle.kts`:

```
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}
```

Add the dependency to your **module** `build.gradle.kts`:

```
dependencies {
    implementation("com.github.EthereumPhone:DgenSubAccountSDK:0.1.0")
}
```

> Replace `0.1.0` with the [latest release tag](https://github.com/EthereumPhone/DgenSubAccountSDK/releases) or use `main-SNAPSHOT` for the latest commit.

#### Packaging conflicts

If you encounter META-INF conflicts from web3j/netty transitive dependencies, add this to your app module's `build.gradle.kts`:

```
android {
    packaging {
        resources {
            pickFirsts += listOf(
                "META-INF/versions/9/OSGI-INF/MANIFEST.MF",
                "META-INF/DISCLAIMER",
            )
            excludes += listOf(
                "META-INF/INDEX.LIST",
                "META-INF/DEPENDENCIES",
                "META-INF/LICENSE.md",
                "META-INF/NOTICE.md",
                "META-INF/io.netty.versions.properties",
                "META-INF/FastDoubleParser-*",
                "META-INF/BigDecimal*",
            )
        }
    }
}
```

If using Java 17 records from web3j (AGP 8.x app modules), set:

```
android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = "17"
    }
}
```

### Quick Start

#### Initialize the SDK

```
import org.ethereumphone.walletsdk.WalletSDK
import org.ethereumphone.walletsdk.model.NoSysWalletException
import org.web3j.protocol.Web3j
import org.web3j.protocol.http.HttpService

try {
    val sdk = WalletSDK(
        context = applicationContext,
        web3jInstance = Web3j.build(HttpService("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY")),
        bundlerRPCUrl = "https://api.pimlico.io/v2/1/rpc?apikey=YOUR_KEY"
    )
} catch (e: NoSysWalletException) {
    // Not running on an ethOS device
}
```

#### Get wallet address

```
val address: String = sdk.getAddress()
```

#### Sign a message

```
// ERC-191 personal_sign
val signature = sdk.signMessage(
    message = "Hello, Ethereum!",
    chainId = 1,
    type = "personal_sign"
)

// EIP-712 typed data
val typedSig = sdk.signMessage(
    message = typedDataJsonString,
    chainId = 1,
    type = "eth_signTypedData"
)
```

#### Send a transaction

```
val txHash = sdk.sendTransaction(
    to = "0xRecipientAddress",
    value = "1000000000000000000", // 1 ETH in wei
    data = "0x",
    callGas = null, // auto-estimate
    chainId = 1
)
```

#### Batch transactions

```
val txHash = sdk.sendTransaction(
    txParamsList = listOf(
        WalletSDK.TxParams("0xAddr1", "1000000000000000000", "0x"),
        WalletSDK.TxParams("0xAddr2", "2000000000000000000", "0x"),
    ),
    callGas = null,
    chainId = 1
)
```

#### Switch chains

```
sdk.changeChain(
    chainId = 8453,
    rpcEndpoint = "https://base-mainnet.g.alchemy.com/v2/YOUR_KEY",
    mBundlerRPCUrl = "https://api.pimlico.io/v2/8453/rpc?apikey=YOUR_KEY"
)
```

### API Reference

#### Constructor

```
WalletSDK(
    context: Context,
    web3jInstance: Web3j = Web3j.build(HttpService("https://rpc.ankr.com/eth")),
    factoryAddress: String = "0x0BA5ED0c6AA8c49038F819E587E2633c4A9F428a",
    entryPointAddress: String = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
    bundlerRPCUrl: String,       // required
    keyAlias: String = "p256_walletsdk"
)
```

| Parameter           | Description                                      |
| ------------------- | ------------------------------------------------ |
| `context`           | Android Context                                  |
| `web3jInstance`     | Web3j RPC client (pass an authenticated RPC)     |
| `bundlerRPCUrl`     | ERC-4337 bundler endpoint (required)             |
| `factoryAddress`    | CoinbaseSmartWallet factory address              |
| `entryPointAddress` | ERC-4337 EntryPoint v0.6 address                 |
| `keyAlias`          | Android KeyStore alias for the P-256 signing key |

#### Core Methods

| Method                                                                                    | Returns                         | Description                                           |
| ----------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------- |
| `suspend getAddress()`                                                                    | `String`                        | Smart wallet counterfactual address                   |
| `suspend sendTransaction(to, value, data, callGas, chainId?, rpcEndpoint?, gasProvider?)` | `String`                        | Send a single transaction via UserOperation           |
| `suspend sendTransaction(txParamsList, callGas, chainId?, ...)`                           | `String`                        | Batch multiple transactions                           |
| `suspend sendTransaction(userOp, chainId?, ...)`                                          | `String`                        | Submit a pre-built UserOperation                      |
| `suspend signMessage(message, chainId, type?)`                                            | `String`                        | Sign message (`personal_sign` or `eth_signTypedData`) |
| `fun signTypedData(typedDataJson, chainId)`                                               | `String`                        | Sign EIP-712 typed data directly                      |
| `suspend changeChain(chainId, rpcEndpoint, bundlerRPCUrl)`                                | `String`                        | Switch RPC and bundler to a different chain           |
| `suspend getNonce(senderAddress, rpcEndpoint?)`                                           | `BigInteger`                    | Get EntryPoint nonce                                  |
| `fun isDeployed(address)`                                                                 | `Boolean`                       | Check if wallet contract is deployed                  |
| `suspend getPair()`                                                                       | `Pair<BigInteger, BigInteger>?` | Get P-256 public key coordinates (X, Y)               |
| `fun isEthOS()`                                                                           | `Boolean`                       | Always `true` (SDK requires ethOS)                    |

#### Data Classes

```
data class TxParams(val to: String, val value: String, val data: String)

data class UserOperation(
    val sender: String,
    val nonce: BigInteger,
    val initCode: String,
    val callData: String,
    val callGasLimit: BigInteger,
    val verificationGasLimit: BigInteger,
    val preVerificationGas: BigInteger,
    val maxFeePerGas: BigInteger,
    val maxPriorityFeePerGas: BigInteger,
    val paymasterAndData: String,
    var signature: String
)

data class GasEstimation(
    val preVerificationGas: BigInteger,
    val verificationGasLimit: BigInteger,
    val callGasLimit: BigInteger
)

data class GasPrice(
    val maxFeePerGas: BigInteger,
    val maxPriorityFeePerGas: BigInteger
)
```

#### Supported Chains

The SDK includes built-in RPC mappings via `WalletSDK.getRPCforChainId()`:

| Chain        | ID      |
| ------------ | ------- |
| Ethereum     | 1       |
| Optimism     | 10      |
| BNB Chain    | 56      |
| Polygon      | 137     |
| Arbitrum     | 42161   |
| Base         | 8453    |
| Base Sepolia | 84532   |
| Zora         | 7777777 |
| Avalanche    | 43114   |

Custom chains are supported by passing your own `web3jInstance` and `bundlerRPCUrl`.

### Demo App

The `app/` module contains a Compose-based demo app that exercises the SDK. To run it:

1. Add your API keys to `local.properties`:

```
ALCHEMY_API=your_alchemy_api_key
BUNDLER_API=your_pimlico_api_key
```

2. Build and install:

```
./gradlew :app:installDebug
```

The demo app provides:

* Chain selector dropdown
* SDK initialization with error handling
* Wallet address and public key display
* Message signing (personal\_sign)
* Transaction sending form
* Result log

### Architecture

```
┌─────────────────────────────────────────────┐
│                  Your App                    │
├─────────────────────────────────────────────┤
│               WalletSDK                      │
│  ┌──────────┐  ┌──────────┐  ┌───────────┐ │
│  │ Local P-256│  │ Create2   │  │ UserOp    │ │
│  │ KeyStore  │  │ Address   │  │ Builder   │ │
│  └────┬─────┘  └──────────┘  └─────┬─────┘ │
│       │                             │        │
│  ┌────▼─────────────────────────────▼─────┐ │
│  │         Sign & Submit Pipeline          │ │
│  └────────────────┬───────────────────────┘ │
├───────────────────┼─────────────────────────┤
│  ethOS System     │         Bundler RPC      │
│  Service          │         (Pimlico, etc.)  │
│  (recovery addr)  │                          │
└───────────────────┴──────────────────────────┘
```

* **Local P-256 key** (Android KeyStore) — primary wallet owner, signs all operations
* **ethOS system service** — provides the device recovery address (second owner)
* **CoinbaseSmartWallet factory** — CREATE2 address computation and wallet deployment
* **ERC-4337 bundler** — gas estimation and UserOperation submission

### Github Repo:

{% embed url="<https://github.com/EthereumPhone/DgenSubAccountSDK/>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.freedomfactory.io/sdks/dgen-subaccount-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
