Skip to content

Latest commit

 

History

History
130 lines (94 loc) · 5.78 KB

README.md

File metadata and controls

130 lines (94 loc) · 5.78 KB

userop-swift

userop-kotlin version is here

About Account Abstraction Using Alt Mempool

An account abstraction proposal which completely avoids the need for consensus-layer protocol changes. Instead of adding new protocol features and changing the bottom-layer transaction type, this proposal instead introduces a higher-layer pseudo-transaction object called a UserOperation. Users send UserOperation objects into a separate mempool. A special class of actor called bundlers package up a set of these objects into a transaction making a handleOps call to a special contract, and that transaction then gets included in a block.

ERC-4337: Account Abstraction Using Alt Mempool

Advanced Supported P256 Account for Trusted Environment

On the basis of userop.js signature, we advanced supported secp256r1-based signature.

The "secp256r1" elliptic curve is a standardized curve by NIST which has the same calculations by different input parameters with""secp256k1” elliptic curve used by the "ecrecover" precompiled contract. The cost of combined attacks and the security conditions are almost the same for both curves. Adding a precompiled contract which is similar to "ecrecover" can provide signature verifications using the "secp256r1" elliptic curve in the smart contracts and multi-faceted benefits can occur. One important factor is that this curve is widely used and supported in many modern devices such as Apple’s Secure Enclave, Webauthn, Android Keychain which proves the user adoption. Additionally, the introduction of this precompile could enable valuable features in the account abstraction which allows more efficient and flexible management of accounts by transaction signs in mobile devices. Most of the modern devices and applications rely on the "secp256r1" elliptic curve. The addition of this precompiled contract enables the verification of device native transaction signing mechanisms. For example:

  • Apple’s Secure Enclave :shipit:: There is a separate "Trusted Execution Environment" in Apple hardware which can sign arbitrary messages and can only be accessed by biometric identification. Webauthn: Web Authentication (WebAuthn) is a web standard published by the World Wide Web Consortium (W3C). WebAuthn aims to standardize an interface for authenticating users to web-based applications and services using public-key cryptography. It is being used by almost all of the modern web browsers.
  • Android Keystore: Android Keystore is an API that manages the private keys and signing methods. The private keys are not processed while using Keystore as the applications’ signing method. Also, it can be done in the "Trusted Execution Environment" in the microchip.

Reffer to EIP-7212

Install

.package(url: "https://github.com/iotexproject/userop-swift.git", from: "x.y.z")

Usage

Create Signer

Example for secp256k1

struct SimpleSigner: Signer {
    private let privateKey: Data

    init(privateKey: Data) {
        self.privateKey = privateKey
    }

    func getAddress() async -> EthereumAddress {
        try! await Utilities.publicToAddress(getPublicKey())!
    }

    func getPublicKey() async throws -> Data {
        Utilities.privateToPublic(privateKey)!
    }

    func signMessage(_ data: Data) async throws -> Data {
        let (compressedSignature, _) = SECP256K1.signForRecovery(hash: data,
                                                                 privateKey: privateKey,
                                                                 useExtraEntropy: false)
        return compressedSignature!
    }
}

Example for secpk256r1

struct P256Signer: Signer {
    private let privateKey: SecKey!

    func getAddress() async -> Web3Core.EthereumAddress {
        fatalError("Don't call this function, get address from p256 key is not supported.")
    }
    
    func getPublicKey() async throws -> Data {
        let data = SecKeyCopyExternalRepresentation(privateKey, nil)
        return (data! as Data)[1...]
    }
    
    func signMessage(_ data: Data) async throws -> Data {
        let signed = SecKeyCreateSignature(privateKey, .ecdsaSignatureMessageX962SHA256, data as CFData, nil)! as Data
        let xLength = UInt(from: signed[3..<4].toHexString())!

        let signatureArray = [
            signed[4..<xLength + 4],
            signed[(xLength + 6)...]
        ]

        let encoded = ABIEncoder.encode(types: [.uint(bits: 256), .uint(bits: 256)], values: signatureArray)
        return encoded!
    }
}

Get Sender Address

let accountBuilder =  try await SimpleAccountBuilder(
    signer: signer,
    rpcUrl: rpc,
    bundleRpcUrl: bundler,
    entryPoint: entryPointAddress,
    factory: factoryAddress,
    salt: 1
)
let senderAddress = accountBuilder.sender.address

Active Account

let client = try await Client(rpcUrl: rpc, overrideBundlerRpc: bundler, entryPoint: entryPointAddress)
let response = try await client.sendUserOperation(builder: accountBuilder)
let eventLog = try await response.wait()
let hash = eventLog?.transactionHash

Once you activeated your account, you can pass it as senderAddress into AccountBuilder. In this case, salt will be ignored.

Send ETH

accountBuilder.execute(to: address, value: Utilities.parseToBigUInt("1", units: .ether)!, data: Data())

let response = try await client.sendUserOperation(builder: accountBuilder)

Send ERC20

let erc20 = try EthereumContract(erc20_abi)
let data = erc20.method("transfer", parameters: [to, value])

accountBuilder.execute(to: erc20_address, value: 0, data: data)

let response = try await client.sendUserOperation(builder: accountBuilder)