/
simpleAccount.ts
111 lines (99 loc) · 3.39 KB
/
simpleAccount.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { BigNumberish, BytesLike, ethers } from "ethers";
import { ERC4337 } from "../../constants";
import { UserOperationBuilder } from "../../builder";
import { BundlerJsonRpcProvider } from "../../provider";
import {
EOASignature,
estimateUserOperationGas,
getGasPrice,
} from "../middleware";
import {
EntryPoint,
EntryPoint__factory,
SimpleAccountFactory,
SimpleAccountFactory__factory,
SimpleAccount as SimpleAccountImpl,
SimpleAccount__factory,
} from "../../typechain";
import { IPresetBuilderOpts, UserOperationMiddlewareFn } from "../../types";
export class SimpleAccount extends UserOperationBuilder {
private signer: ethers.Signer;
private provider: ethers.providers.JsonRpcProvider;
private entryPoint: EntryPoint;
private factory: SimpleAccountFactory;
private initCode: string;
proxy: SimpleAccountImpl;
private constructor(
signer: ethers.Signer,
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 = SimpleAccountFactory__factory.connect(
opts?.factory || ERC4337.SimpleAccount.Factory,
this.provider
);
this.initCode = "0x";
this.proxy = SimpleAccount__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: ethers.Signer,
rpcUrl: string,
opts?: IPresetBuilderOpts
): Promise<SimpleAccount> {
const instance = new SimpleAccount(signer, rpcUrl, opts);
try {
instance.initCode = await ethers.utils.hexConcat([
instance.factory.address,
instance.factory.interface.encodeFunctionData("createAccount", [
await instance.signer.getAddress(),
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 = SimpleAccount__factory.connect(addr, instance.provider);
}
const base = instance
.useDefaults({
sender: instance.proxy.address,
signature: await instance.signer.signMessage(
ethers.utils.arrayify(ethers.utils.keccak256("0xdead"))
),
})
.useMiddleware(instance.resolveAccount)
.useMiddleware(getGasPrice(instance.provider));
const withPM = opts?.paymasterMiddleware
? base.useMiddleware(opts.paymasterMiddleware)
: base.useMiddleware(estimateUserOperationGas(instance.provider));
return withPM.useMiddleware(EOASignature(instance.signer));
}
execute(to: string, value: BigNumberish, data: BytesLike) {
return this.setCallData(
this.proxy.interface.encodeFunctionData("execute", [to, value, data])
);
}
executeBatch(to: Array<string>, data: Array<BytesLike>) {
return this.setCallData(
this.proxy.interface.encodeFunctionData("executeBatch", [to, data])
);
}
}