Skip to content

Commit

Permalink
Merge pull request #1065 from AmbireTech/feature/paymaster-step
Browse files Browse the repository at this point in the history
Feature/paymaster step
  • Loading branch information
borislav-itskov authored Nov 7, 2024
2 parents c760777 + bd5788e commit 86df3d8
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 12 deletions.
14 changes: 12 additions & 2 deletions src/controllers/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,9 @@ export class MainController extends EventEmitter {
this.callRelayer,
() => {
this.estimateSignAccountOp()
},
() => {
return this.isSignRequestStillActive
}
)

Expand Down Expand Up @@ -2273,7 +2276,7 @@ export class MainController extends EventEmitter {
(isRelayer && message.includes('Failed to fetch'))
) {
message =
'Currently, the Ambire relayer seems to be down. Please try again a few moments later or broadcast with an EOA'
'Currently, the Ambire relayer seems to be down. Please try again a few moments later or broadcast with a Basic Account'
} else {
// Trip the error message, errors coming from the RPC can be huuuuuge
message = message.length > 300 ? `${message.substring(0, 300)}...` : message
Expand All @@ -2292,12 +2295,19 @@ export class MainController extends EventEmitter {
return Promise.reject(new EmittableError({ level: 'major', message, error }))
}

get isSignRequestStillActive(): boolean {
if (!this.signAccountOp) return false

return !!this.actions.actionsQueue.find((a) => a.id === this.signAccountOp!.fromActionId)
}

// includes the getters in the stringified instance
toJSON() {
return {
...this,
...super.toJSON(),
banners: this.banners
banners: this.banners,
isSignRequestStillActive: this.isSignRequestStillActive
}
}
}
1 change: 1 addition & 0 deletions src/controllers/signAccountOp/signAccountOp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ const init = async (
1,
op,
callRelayer,
() => {},
() => {}
)

Expand Down
46 changes: 36 additions & 10 deletions src/controllers/signAccountOp/signAccountOp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export enum SigningStatus {
*/
UpdatesPaused = 'updates-paused',
InProgress = 'in-progress',
WaitingForPaymaster = 'waiting-for-paymaster-response',
Done = 'done'
}

Expand Down Expand Up @@ -98,7 +99,8 @@ type SpeedCalc = {
const noStateUpdateStatuses = [
SigningStatus.InProgress,
SigningStatus.Done,
SigningStatus.UpdatesPaused
SigningStatus.UpdatesPaused,
SigningStatus.WaitingForPaymaster
]

export class SignAccountOpController extends EventEmitter {
Expand Down Expand Up @@ -148,6 +150,8 @@ export class SignAccountOpController extends EventEmitter {

#reEstimate: Function

#isSignRequestStillActive: Function

rbfAccountOps: { [key: string]: SubmittedAccountOp | null }

signedAccountOp: AccountOp | null
Expand All @@ -166,7 +170,8 @@ export class SignAccountOpController extends EventEmitter {
fromActionId: AccountOpAction['id'],
accountOp: AccountOp,
callRelayer: Function,
reEstimate: Function
reEstimate: Function,
isSignRequestStillActive: Function
) {
super()

Expand All @@ -180,6 +185,7 @@ export class SignAccountOpController extends EventEmitter {
this.accountOp = structuredClone(accountOp)
this.#callRelayer = callRelayer
this.#reEstimate = reEstimate
this.#isSignRequestStillActive = isSignRequestStillActive

this.gasUsedTooHigh = false
this.gasUsedTooHighAgreed = false
Expand Down Expand Up @@ -503,7 +509,9 @@ export class SignAccountOpController extends EventEmitter {
}

// no status updates on these two
const isInTheMiddleOfSigning = this.status?.type === SigningStatus.InProgress
const isInTheMiddleOfSigning =
this.status?.type === SigningStatus.InProgress ||
this.status?.type === SigningStatus.WaitingForPaymaster
const isDone = this.status?.type === SigningStatus.Done
if (isInTheMiddleOfSigning || isDone) return

Expand Down Expand Up @@ -1078,6 +1086,12 @@ export class SignAccountOpController extends EventEmitter {
return this.#emitSigningErrorAndResetToReadyToSign(message)
}

if (this.accountOp.gasFeePayment.isERC4337 && shouldUsePaymaster(this.#network)) {
this.status = { type: SigningStatus.WaitingForPaymaster }
} else {
this.status = { type: SigningStatus.InProgress }
}

// we update the FE with the changed status (in progress) only after the checks
// above confirm everything is okay to prevent two different state updates
this.emitUpdate()
Expand Down Expand Up @@ -1194,18 +1208,25 @@ export class SignAccountOpController extends EventEmitter {

if (usesPaymaster) {
try {
const response = await this.#callRelayer(
`/v2/paymaster/${this.accountOp.networkId}/sign`,
'POST',
{
// request the paymaster with a timeout window
const response = await Promise.race([
this.#callRelayer(`/v2/paymaster/${this.accountOp.networkId}/sign`, 'POST', {
// send without the requestType prop
userOperation: (({ requestType, activatorCall, ...o }) => o)(userOperation),
paymaster: AMBIRE_PAYMASTER,
bytecode: this.account.creation!.bytecode,
salt: this.account.creation!.salt,
key: this.account.associatedKeys[0]
}
)
}),
new Promise((_resolve, reject) => {
setTimeout(() => reject(new Error('Ambire relayer error')), 8000)
})
])

// go back to in progress after paymaster has been confirmed
this.status = { type: SigningStatus.InProgress }
this.emitUpdate()

userOperation.paymasterData = response.data.paymasterData
if (usesOneTimeNonce) {
userOperation.nonce = getOneTimeNonce(userOperation)
Expand All @@ -1214,7 +1235,7 @@ export class SignAccountOpController extends EventEmitter {
let message = e.message
if (e.message.includes('Failed to fetch') || e.message.includes('Ambire relayer')) {
message =
'Currently, the paymaster seems to be down. Please try again a few moments later or broadcast with an EOA'
'Currently, the paymaster seems to be down. Please try again a few moments later or broadcast with a Basic Account'
}
this.emitError({
level: 'major',
Expand All @@ -1228,6 +1249,11 @@ export class SignAccountOpController extends EventEmitter {
}
}

// query the application state from memory to understand if the user
// hasn't actually rejected the request while waiting for the
// paymaster to respond
if (!this.#isSignRequestStillActive()) return

if (userOperation.requestType === 'standard') {
const typedData = getTypedData(
this.#network.chainId,
Expand Down

0 comments on commit 86df3d8

Please sign in to comment.