Skip to content

Commit

Permalink
add p256 account builder
Browse files Browse the repository at this point in the history
  • Loading branch information
ququzone committed Aug 8, 2023
1 parent 72a6277 commit b391a56
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 11 deletions.
6 changes: 2 additions & 4 deletions scripts/simple/create.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Client } from "userop"
import { Client, Presets } from "userop"
import { ethers } from "hardhat"
import { Presets } from "userop"
import { EntryPoint } from "@account-abstraction/contracts"

async function main() {
const rpc = "https://babel-api.testnet.iotex.io"
//const bundlerRpc = "https://bundler.testnet.w3bstream.com"
const bundlerRpc = "http://localhost:4337"
const bundlerRpc = "https://bundler.testnet.w3bstream.com"
const entryPoint = (await ethers.getContract("EntryPoint")) as EntryPoint
const accountFactory = await ethers.getContract("SimpleAccountFactory")
const client = await Client.init(rpc, {
Expand Down
11 changes: 4 additions & 7 deletions scripts/simple/transfer-paymaster-bundler.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { Client } from "userop"
import { Client, Presets } from "userop"
import { ethers } from "hardhat"
import { Presets } from "userop"
import { EntryPoint } from "@account-abstraction/contracts"
import { JsonRpcProvider } from "@ethersproject/providers"

async function main() {
const rpc = "https://babel-api.testnet.iotex.io"
//const bundlerRpc = "https://bundler.testnet.w3bstream.com"
const bundlerRpc = "http://localhost:4337"
const bundlerRpc = "https://bundler.testnet.w3bstream.com"
const entryPoint = (await ethers.getContract("EntryPoint")) as EntryPoint
const accountFactory = await ethers.getContract("SimpleAccountFactory")
const accountTpl = await ethers.getContractFactory("P256Account")
Expand All @@ -26,10 +23,10 @@ async function main() {
overrideBundlerRpc: bundlerRpc,
factory: accountFactory.address,
entryPoint: entryPoint.address,
salt: 1,
salt: "1",
paymasterMiddleware: Presets.Middleware.verifyingPaymaster(
// paymaster rpc
"http://localhost:8888/rpc/1234567890",
`https://paymaster.testnet.w3bstream.com/rpc/${process.env.API_KEY}`,
""
),
})
Expand Down
109 changes: 109 additions & 0 deletions scripts/userop/p256-account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import * as ethers from "ethers"
import { EntryPoint, EntryPoint__factory } from "@account-abstraction/contracts"
import {
P256AccountFactory__factory,
P256Account__factory,
P256Account as P256AccountImpl,
P256AccountFactory,
} from "../../typechain"
import {
BundlerJsonRpcProvider,
IPresetBuilderOpts,
UserOperationBuilder,
UserOperationMiddlewareFn,
Presets,
} from "userop"
import { P2565Signer, P256Signature } from "./signature"

export const ERC4337 = {
EntryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
P256Account: {
Factory: "0x9406Cc6185a346906296840746125a0E44976454",
},
}

export class P256Account extends UserOperationBuilder {
private signer: P2565Signer
private provider: ethers.providers.JsonRpcProvider
private entryPoint: EntryPoint
private factory: P256AccountFactory
private initCode: string
proxy: P256AccountImpl

private constructor(signer: P2565Signer, rpcUrl: string, opts?: IPresetBuilderOpts) {
super()
this.signer = signer
this.provider = new BundlerJsonRpcProvider(rpcUrl).setBundlerRpc(opts?.overrideBundlerRpc)
this.entryPoint = EntryPoint__factory.connect(
opts?.entryPoint || ERC4337.EntryPoint,
this.provider
)
this.factory = P256AccountFactory__factory.connect(
opts?.factory || ERC4337.P256Account.Factory,
this.provider
)
this.initCode = "0x"
this.proxy = P256Account__factory.connect(ethers.constants.AddressZero, this.provider)
}

private resolveAccount: UserOperationMiddlewareFn = async (ctx) => {
ctx.op.nonce = await this.entryPoint.getNonce(ctx.op.sender, 0)
ctx.op.initCode = ctx.op.nonce.eq(0) ? this.initCode : "0x"
}

public static async init(
signer: P2565Signer,
rpcUrl: string,
opts?: IPresetBuilderOpts
): Promise<P256Account> {
const instance = new P256Account(signer, rpcUrl, opts)

try {
instance.initCode = await ethers.utils.hexConcat([
instance.factory.address,
instance.factory.interface.encodeFunctionData("createAccount", [
signer.publicKey(),
ethers.BigNumber.from(opts?.salt ?? 0),
]),
])
await instance.entryPoint.callStatic.getSenderAddress(instance.initCode)

throw new Error("getSenderAddress: unexpected result")
} catch (error: any) {
const addr = error?.errorArgs?.sender
if (!addr) throw error

instance.proxy = P256Account__factory.connect(addr, instance.provider)
}

const base = instance
.useDefaults({
sender: instance.proxy.address,
signature: await instance.signer.sign(ethers.utils.keccak256("0xdead")),
})
.useMiddleware(instance.resolveAccount)
.useMiddleware(Presets.Middleware.getGasPrice(instance.provider))

const withPM = opts?.paymasterMiddleware
? base.useMiddleware(opts.paymasterMiddleware)
: base.useMiddleware(Presets.Middleware.estimateUserOperationGas(instance.provider))

return withPM.useMiddleware(P256Signature(instance.signer))
}

execute(to: string, value: ethers.BigNumberish, data: ethers.BytesLike) {
return this.setCallData(
this.proxy.interface.encodeFunctionData("execute", [to, value, data])
)
}

executeBatch(
to: Array<string>,
values: Array<ethers.BigNumberish>,
data: Array<ethers.BytesLike>
) {
return this.setCallData(
this.proxy.interface.encodeFunctionData("executeBatch", [to, values, data])
)
}
}
50 changes: 50 additions & 0 deletions scripts/userop/signature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import crypto from "crypto"
import { bufferToHex, sha256 } from "ethereumjs-util"
import { defaultAbiCoder } from "ethers/lib/utils"
import { UserOperationMiddlewareFn } from "userop"

// @ts-ignore
export const sign = (keyPair: any, message: any) => {
const messageHash = bufferToHex(sha256(message))

const signer = crypto.createSign("RSA-SHA256")
signer.update(message)
let sigString = signer.sign(keyPair.encodePrivateKey(), "hex")

// @ts-ignore
const xlength = 2 * ("0x" + sigString.slice(6, 8))
sigString = sigString.slice(8)
const signatureArray = ["0x" + sigString.slice(0, xlength), "0x" + sigString.slice(xlength + 4)]
const signature = defaultAbiCoder.encode(
["uint256", "uint256"],
[signatureArray[0], signatureArray[1]]
)

return {
messageHash,
signature,
}
}

export class P2565Signer {
private keyPair: any

constructor(keyPair: any) {
this.keyPair = keyPair
}

publicKey(): string {
return "0x" + this.keyPair.getPublicKey("hex").substring(2)
}

async sign(opHash: string): Promise<string> {
const result = sign(this.keyPair, Buffer.from(opHash.substring(2), "hex"))
return result.signature
}
}

export const P256Signature =
(signer: P2565Signer): UserOperationMiddlewareFn =>
async (ctx) => {
ctx.op.signature = await signer.sign(ctx.getUserOpHash())
}

0 comments on commit b391a56

Please sign in to comment.