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
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 : 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.
.package(url: "https://github.com/iotexproject/userop-swift.git", from: "x.y.z")
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!
}
}
let accountBuilder = try await SimpleAccountBuilder(
signer: signer,
rpcUrl: rpc,
bundleRpcUrl: bundler,
entryPoint: entryPointAddress,
factory: factoryAddress,
salt: 1
)
let senderAddress = accountBuilder.sender.address
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.
accountBuilder.execute(to: address, value: Utilities.parseToBigUInt("1", units: .ether)!, data: Data())
let response = try await client.sendUserOperation(builder: accountBuilder)
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)