Skip to content

Commit 07481ee

Browse files
authored
Merge pull request #14 from etherspot/PRO-2468-Paymaster_Changes
PRO-2468 - Paymaster changes for EPv7
2 parents bcd7fdb + a362af8 commit 07481ee

File tree

8 files changed

+51
-44
lines changed

8 files changed

+51
-44
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# Changelog
2+
## [1.0.3] - 2024-06-24
3+
### Bug Fixes
4+
- Added support for paymaster executions according to EPv7
5+
26
## [1.0.2] - 2024-06-17
37
### New
48
- Added support for XDC Testnet.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@etherspot/modular-sdk",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"description": "Etherspot Modular SDK - build with ERC-7579 smart accounts modules",
55
"keywords": [
66
"ether",

src/sdk/base/BaseAccountAPI.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ export abstract class BaseAccountAPI {
4949

5050
// entryPoint connected to "zero" address. allowed to make static calls (e.g. to getSenderAddress)
5151
protected readonly entryPointView: IEntryPoint;
52-
protected readonly nonceManager: INonceManager;
5352

5453
provider: Provider;
5554
overheads?: Partial<GasOverheads>;
@@ -102,9 +101,6 @@ export abstract class BaseAccountAPI {
102101
this.entryPointView = EntryPoint__factory.connect(params.entryPointAddress, params.provider).connect(
103102
ethers.constants.AddressZero,
104103
);
105-
this.nonceManager = INonceManager__factory.connect(params.entryPointAddress, params.provider).connect(
106-
ethers.constants.AddressZero
107-
);
108104
}
109105

110106
get state(): StateService {
@@ -440,19 +436,22 @@ export abstract class BaseAccountAPI {
440436
};
441437

442438

443-
let paymasterAndData: PaymasterResponse | undefined = null;
439+
let paymasterData: PaymasterResponse | undefined = null;
444440
if (this.paymasterAPI != null) {
445-
// fill (partial) preVerificationGas (all except the cost of the generated paymasterAndData)
441+
// fill (partial) preVerificationGas (all except the cost of the generated paymasterData)
446442
const userOpForPm = {
447443
...partialUserOp,
448444
preVerificationGas: this.getPreVerificationGas(partialUserOp),
449445
};
450-
paymasterAndData = (await this.paymasterAPI.getPaymasterAndData(userOpForPm));
451-
partialUserOp.verificationGasLimit = paymasterAndData.result.verificationGasLimit;
452-
partialUserOp.preVerificationGas = paymasterAndData.result.preVerificationGas;
453-
partialUserOp.callGasLimit = paymasterAndData.result.callGasLimit;
446+
paymasterData = (await this.paymasterAPI.getPaymasterData(userOpForPm));
447+
partialUserOp.verificationGasLimit = paymasterData.result.verificationGasLimit;
448+
partialUserOp.preVerificationGas = paymasterData.result.preVerificationGas;
449+
partialUserOp.callGasLimit = paymasterData.result.callGasLimit;
450+
partialUserOp.paymaster = paymasterData.result.paymaster;
451+
partialUserOp.paymasterVerificationGasLimit = paymasterData.result.paymasterVerificationGasLimit;
452+
partialUserOp.paymasterPostOpGasLimit = paymasterData.result.paymasterPostOpGasLimit;
454453
}
455-
partialUserOp.paymasterAndData = paymasterAndData ? paymasterAndData.result.paymasterAndData : '0x';
454+
partialUserOp.paymasterData = paymasterData ? paymasterData.result.paymasterData : '0x';
456455
return {
457456
...partialUserOp,
458457
preVerificationGas: this.getPreVerificationGas(partialUserOp),
@@ -466,10 +465,13 @@ export abstract class BaseAccountAPI {
466465
*/
467466
async signUserOp(userOp: UserOperation): Promise<UserOperation> {
468467
if (this.paymasterAPI != null) {
469-
const paymasterAndData = await this.paymasterAPI.getPaymasterAndData(userOp);
470-
userOp.verificationGasLimit = paymasterAndData.result.verificationGasLimit;
471-
userOp.preVerificationGas = paymasterAndData.result.preVerificationGas;
472-
userOp.callGasLimit = paymasterAndData.result.callGasLimit;
468+
const paymasterData = await this.paymasterAPI.getPaymasterData(userOp);
469+
userOp.verificationGasLimit = paymasterData.result.verificationGasLimit;
470+
userOp.preVerificationGas = paymasterData.result.preVerificationGas;
471+
userOp.callGasLimit = paymasterData.result.callGasLimit;
472+
userOp.paymaster = paymasterData.result.paymaster;
473+
userOp.paymasterVerificationGasLimit = paymasterData.result.paymasterVerificationGasLimit;
474+
userOp.paymasterPostOpGasLimit = paymasterData.result.paymasterPostOpGasLimit;
473475
}
474476
const userOpHash = await this.getUserOpHash(userOp);
475477
const signature = await this.signUserOpHash(userOpHash);

src/sdk/base/EtherspotWalletAPI.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export class EtherspotWalletAPI extends BaseAccountAPI {
153153
? ethers.utils.getAddress(this.multipleOwnerECDSAValidatorAddress) + "00000000"
154154
: ethers.utils.getAddress(key.toHexString()) + "00000000";
155155

156-
return await this.nonceManager.getNonce(accountAddress, BigInt(dummyKey));
156+
return await this.entryPointView.getNonce(accountAddress, BigInt(dummyKey));
157157
}
158158

159159
/**

src/sdk/base/PaymasterAPI.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import { PaymasterResponse } from './VerifyingPaymasterAPI';
66
*/
77
export class PaymasterAPI {
88
/**
9-
* @param userOp a partially-filled UserOperation (without signature and paymasterAndData
9+
* @param userOp a partially-filled UserOperation (without signature and paymasterData
1010
* note that the "preVerificationGas" is incomplete: it can't account for the
11-
* paymasterAndData value, which will only be returned by this method..
12-
* @returns the value to put into the PaymasterAndData, undefined to leave it empty
11+
* paymasterData value, which will only be returned by this method..
12+
* @returns the value to put into the PaymasterData, undefined to leave it empty
1313
*/
1414
// eslint-disable-next-line @typescript-eslint/no-unused-vars
15-
async getPaymasterAndData(userOp: Partial<UserOperationStruct>): Promise<PaymasterResponse | undefined> {
16-
return { result: {paymasterAndData: '0x', verificationGasLimit: '0x', preVerificationGas: '0x', callGasLimit: '0x' }};
15+
async getPaymasterData(userOp: Partial<UserOperationStruct>): Promise<PaymasterResponse | undefined> {
16+
return { result: { paymaster: '0x', paymasterData: '0x', paymasterPostOpGasLimit: '0x', paymasterVerificationGasLimit: '0x', preVerificationGas: '0x', verificationGasLimit: '0x', callGasLimit: '0x' }};
1717
}
1818
}

src/sdk/base/VerifyingPaymasterAPI.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ethers } from 'ethers';
1+
import { BigNumber, ethers } from 'ethers';
22
import fetch from 'cross-fetch';
33
import { calcPreVerificationGas } from './calcPreVerificationGas';
44
import { PaymasterAPI } from './PaymasterAPI';
@@ -8,12 +8,16 @@ import { UserOperation } from '../common';
88
const DUMMY_PAYMASTER_AND_DATA =
99
'0x0101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000001010101010100000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101';
1010

11+
// Expected EntryPoint v0.7 Paymaster Response
1112
export interface PaymasterResponse {
1213
result: {
13-
paymasterAndData: string;
14-
verificationGasLimit: string;
14+
paymaster: string;
15+
paymasterData: string;
1516
preVerificationGas: string;
17+
verificationGasLimit: string;
1618
callGasLimit: string;
19+
paymasterVerificationGasLimit: string;
20+
paymasterPostOpGasLimit: string;
1721
}
1822
}
1923

@@ -28,8 +32,8 @@ export class VerifyingPaymasterAPI extends PaymasterAPI {
2832
this.context = context;
2933
}
3034

31-
async getPaymasterAndData(userOp: Partial<UserOperation>): Promise<PaymasterResponse> {
32-
// Hack: userOp includes empty paymasterAndData which calcPreVerificationGas requires.
35+
async getPaymasterData(userOp: Partial<UserOperation>): Promise<PaymasterResponse> {
36+
// Hack: userOp includes empty paymasterData which calcPreVerificationGas requires.
3337
try {
3438
// userOp.preVerificationGas contains a promise that will resolve to an error.
3539
await ethers.utils.resolveProperties(userOp);
@@ -44,37 +48,34 @@ export class VerifyingPaymasterAPI extends PaymasterAPI {
4448
verificationGasLimit: userOp.verificationGasLimit,
4549
maxFeePerGas: userOp.maxFeePerGas,
4650
maxPriorityFeePerGas: userOp.maxPriorityFeePerGas,
47-
// A dummy value here is required in order to calculate a correct preVerificationGas value.
48-
paymasterData: DUMMY_PAYMASTER_AND_DATA,
49-
signature: userOp.signature ?? '0x',
50-
paymaster: userOp.paymaster,
51-
paymasterVerificationGasLimit: userOp.paymasterVerificationGasLimit,
52-
paymasterPostOpGasLimit: userOp.paymasterPostOpGasLimit,
5351
};
5452
const op = await ethers.utils.resolveProperties(pmOp);
5553
op.preVerificationGas = calcPreVerificationGas(op);
5654

57-
// Ask the paymaster to sign the transaction and return a valid paymasterAndData value.
58-
const paymasterAndData = await fetch(this.paymasterUrl, {
55+
// Ask the paymaster to sign the transaction and return a valid paymasterData value.
56+
const paymasterData = await fetch(this.paymasterUrl, {
5957
method: 'POST',
6058
headers: {
6159
'Accept': 'application/json',
6260
'Content-Type': 'application/json',
6361
},
64-
body: JSON.stringify({ params: [await toJSON(op), this.entryPoint, this.context], jsonrpc: '2', id: 2 }),
62+
body: JSON.stringify({ method: 'pm_sponsorUserOperation', params: [await toJSON(op), this.entryPoint, this.context], jsonrpc: '2', id: 2 }),
6563
})
6664
.then(async (res) => {
6765
const response = await await res.json();
6866
if (response.error) {
6967
throw new Error(response.error);
7068
}
69+
// Since the value of paymasterVerificationGasLimit is defined by the paymaster provider itself, it could be number in string
70+
if (response.result && response.result.paymasterVerificationGasLimit)
71+
response.result.paymasterVerificationGasLimit = BigNumber.from(response.result.paymasterVerificationGasLimit).toHexString()
7172
return response
7273
})
7374
.catch((err) => {
7475
throw new Error(err.message);
7576
})
7677

77-
return paymasterAndData;
78+
return paymasterData;
7879
}
7980
}
8081

src/sdk/errorHandler/constants.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ export const entryPointErrorMsg = {
2121
'AA14 initCode must return sender': 'The initCode does not return the sender address. Check the initCode or the factory contract',
2222
'AA15 initCode must create sender': 'The initCode in the user operation does not create an account. Check the initCode or the factory contract',
2323
'AA20 account not deployed': 'The sender of the user operation is not deployed and there is no initCode specified. If this is the first transaction by this account make sure an initCode is included. Otherwise, check that the correct sender address is specified and is an ERC-4337 account',
24-
"AA21 didn't pay prefund": `The sender did not have enough to prefund the EntryPoint for the user operation. If you are using a paymaster, the paymasterAndData field is likely not set. If you aren't using a paymaster, the address of the sender does not have enough gas token. After the user operation is executed, the remainder of the prefund is credited back to the sender`,
24+
"AA21 didn't pay prefund": `The sender did not have enough to prefund the EntryPoint for the user operation. If you are using a paymaster, the paymasterData field is likely not set. If you aren't using a paymaster, the address of the sender does not have enough gas token. After the user operation is executed, the remainder of the prefund is credited back to the sender`,
2525
'AA22 expired or not due': 'The signature is not valid because it is outside of the specified time range',
26-
'AA23 reverted (or OOG)': `The sender does not have sufficient native tokens to cover the User Operation's gas costs. If you intended to use a Paymaster for sponsorship, ensure that the paymasterAndData field of the user operation is correctly set to enable proper handling of gas fees`,
26+
'AA23 reverted (or OOG)': `The sender does not have sufficient native tokens to cover the User Operation's gas costs. If you intended to use a Paymaster for sponsorship, ensure that the paymasterData field of the user operation is correctly set to enable proper handling of gas fees`,
2727
'AA24 signature error': `Check the signature field of the user operation. It may be in an incompatible format`,
2828
'AA25 invalid account nonce': 'The nonce is invalid. The user operation may be re-using an old nonce, or formatted the nonce incorrectly',
29-
'AA30 paymaster not deployed': 'The paymaster address specified by paymasterAndData contains no code. Check that the first characters of the paymasterAndData field are the paymaster address you intend to use',
29+
'AA30 paymaster not deployed': 'The paymaster address specified by paymasterData contains no code. Check that the first characters of the paymasterData field are the paymaster address you intend to use',
3030
'AA31 paymaster deposit too low': `The paymaster is out of funds. More gas tokens must be deposited into the EntryPoint for the paymaster. This is usually done by calling the paymaster contract's deposit function. If you are using a paymaster service, contact them immediately`,
3131
'AA32 paymaster expired or not due': `The paymaster's signature is not valid because it is outside of the specified time range`,
32-
'AA33 reverted (or OOG)': `The paymaster validation was rejected or ran out of gas. "OOG" is an abbreviation for Out-Of-Gas. First check the paymaster's signature in paymasterAndData. If the signature is correct, the verificationGasLimit may be too low`,
33-
'AA34 signature error': `The paymaster's signature is invalid. Check the format of the signature in paymasterAndData`,
32+
'AA33 reverted (or OOG)': `The paymaster validation was rejected or ran out of gas. "OOG" is an abbreviation for Out-Of-Gas. First check the paymaster's signature in paymasterData. If the signature is correct, the verificationGasLimit may be too low`,
33+
'AA34 signature error': `The paymaster's signature is invalid. Check the format of the signature in paymasterData`,
3434
'AA40 over verificationGasLimit': `The verification gas limit was exceeded. Check the verificationGasLimit in your user operation`,
3535
'AA41 too little verificationGas': `Verifying the user operation took too much gas and did not complete. You may need to increase verificationGasLimit`,
3636
'AA50 postOp reverted': `After the user operation was completed, the execution of additional logic by the EntryPoint reverted`,

0 commit comments

Comments
 (0)