Skip to content

EthereumPhone/DgenSubAccountSDK

Repository files navigation

DgenSubAccountSDK

Android SDK for managing smart contract sub-accounts on ethOS 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.

This library provides two SDKs:

  • SubWalletSDK — sub-account operations (local P-256 signing, ERC-4337 UserOperations)
  • WalletSDK — OS-level system wallet (from the transitive WalletSDK dependency)

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 signingpersonal_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
  • OS-level wallet access — transitive dependency on WalletSDK for system wallet operations

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, Alchemy)
  • 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.2.0")
}

Replace 0.2.0 with the latest release tag or use main-SNAPSHOT for the latest commit.

This single dependency gives you both SubWalletSDK and the OS-level WalletSDK (exposed transitively via api).

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 SubWalletSDK

import org.ethereumphone.subwalletsdk.SubWalletSDK
import org.ethereumphone.subwalletsdk.model.NoSysWalletException
import org.web3j.protocol.Web3j
import org.web3j.protocol.http.HttpService

try {
    val sdk = SubWalletSDK(
        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
}

Access the OS-level WalletSDK

The OS-level WalletSDK is available transitively — no extra dependency needed:

import org.ethereumphone.walletsdk.WalletSDK

val sysWallet = WalletSDK(context)
val sysAddress = sysWallet.getAddress()

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(
        SubWalletSDK.TxParams("0xAddr1", "1000000000000000000", "0x"),
        SubWalletSDK.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

SubWalletSDK Constructor

SubWalletSDK(
    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 SubWalletSDK.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 both SDKs. To run it:

  1. Add your API keys to local.properties:
ALCHEMY_API=your_alchemy_api_key
BUNDLER_API=your_pimlico_api_key
  1. Build and install:
./gradlew :app:installDebug

The demo app provides:

  • Chain selector dropdown
  • SDK initialization with error handling
  • Sub-wallet address and system wallet address display
  • P-256 public key display
  • Message signing (personal_sign)
  • Transaction sending form
  • Result log

Architecture

┌─────────────────────────────────────────────────┐
│                   Your App                       │
├────────────────────────┬────────────────────────┤
│     SubWalletSDK       │       WalletSDK        │
│   (sub-accounts)       │   (OS-level wallet)    │
│  ┌──────────┐  ┌─────┐│                         │
│  │ Local P-256│  │Create│  (from transitive dep) │
│  │ KeyStore  │  │2 Addr│                         │
│  └────┬─────┘  └─────┘│                         │
│       │                │                         │
│  ┌────▼──────────────┐ │                         │
│  │ Sign & Submit     │ │                         │
│  │ (ERC-4337)        │ │                         │
│  └────────┬──────────┘ │                         │
├───────────┼────────────┼─────────────────────────┤
│  ethOS System Service  │       Bundler RPC       │
│  (recovery addr)       │       (Pimlico, etc.)   │
└────────────────────────┴─────────────────────────┘
  • SubWalletSDK — local P-256 key (Android KeyStore) as primary wallet owner, signs all ERC-4337 operations
  • WalletSDK — OS-level system wallet, accessed via the ethOS system service
  • ethOS system service — provides the device recovery address (second owner for sub-accounts)
  • CoinbaseSmartWallet factory — CREATE2 address computation and wallet deployment
  • ERC-4337 bundler — gas estimation and UserOperation submission

License

See LICENSE for details.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages