-
Notifications
You must be signed in to change notification settings - Fork 229
/
Copy pathSimpleAccountAPI.ts
108 lines (96 loc) · 3.49 KB
/
SimpleAccountAPI.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
import { BigNumber, BigNumberish } from 'ethers'
import {
SimpleAccount,
SimpleAccount__factory, SimpleAccountFactory,
SimpleAccountFactory__factory
} from '@account-abstraction/utils'
import { arrayify } from 'ethers/lib/utils'
import { Signer } from '@ethersproject/abstract-signer'
import { BaseApiParams, BaseAccountAPI, FactoryParams } from './BaseAccountAPI'
import { ecsign, toRpcSig } from 'ethereumjs-util'
/**
* constructor params, added no top of base params:
* @param owner the signer object for the account owner
* @param factoryAddress address of contract "factory" to deploy new contracts (not needed if account already deployed)
* @param index nonce value used when creating multiple accounts for the same owner
*/
export interface SimpleAccountApiParams extends BaseApiParams {
owner: Signer
factoryAddress?: string
index?: BigNumberish
}
/**
* An implementation of the BaseAccountAPI using the SimpleAccount contract.
* - contract deployer gets "entrypoint", "owner" addresses and "index" nonce
* - owner signs requests using normal "Ethereum Signed Message" (ether's signer.signMessage())
* - nonce method is "nonce()"
* - execute method is "execFromEntryPoint()"
*/
export class SimpleAccountAPI extends BaseAccountAPI {
factoryAddress?: string
owner: Signer
index: BigNumberish
/**
* our account contract.
* should support the "execFromEntryPoint" and "nonce" methods
*/
accountContract?: SimpleAccount
factory?: SimpleAccountFactory
constructor (params: SimpleAccountApiParams) {
super(params)
this.factoryAddress = params.factoryAddress
this.owner = params.owner
this.index = BigNumber.from(params.index ?? 0)
}
async _getAccountContract (): Promise<SimpleAccount> {
if (this.accountContract == null) {
this.accountContract = SimpleAccount__factory.connect(await this.getAccountAddress(), this.provider)
}
return this.accountContract
}
/**
* return the value to put into the "initCode" field, if the account is not yet deployed.
* this value holds the "factory" address, followed by this account's information
*/
async getFactoryData (): Promise<FactoryParams | null> {
if (this.factory == null) {
if (this.factoryAddress != null && this.factoryAddress !== '') {
this.factory = SimpleAccountFactory__factory.connect(this.factoryAddress, this.provider)
} else {
throw new Error('no factory to get initCode')
}
}
return {
factory: this.factory.address,
factoryData: this.factory.interface.encodeFunctionData('createAccount', [await this.owner.getAddress(), this.index])
}
}
async getNonce (): Promise<BigNumber> {
if (await this.checkAccountPhantom()) {
return BigNumber.from(0)
}
const accountContract = await this._getAccountContract()
return await accountContract.getNonce()
}
/**
* encode a method call from entryPoint to our contract
* @param target
* @param value
* @param data
*/
async encodeExecute (target: string, value: BigNumberish, data: string): Promise<string> {
const accountContract = await this._getAccountContract()
return accountContract.interface.encodeFunctionData(
'execute',
[
target,
value,
data
])
}
async signUserOpHash (userOpHash: string): Promise<string> {
const privateKey = (this.owner as any).privateKey
const sig = ecsign(Buffer.from(arrayify(userOpHash)), Buffer.from(arrayify(privateKey)))
return toRpcSig(sig.v, sig.r, sig.s)
}
}