Skip to content

Commit

Permalink
fix: rollback request state in case of error in input-event-controller
Browse files Browse the repository at this point in the history
  • Loading branch information
khanti42 committed Nov 25, 2024
1 parent 24e4666 commit e2bda06
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,84 @@ describe('UserInputEventController', () => {
},
);

it.each([STRK_SEPOLIA_TESTNET, ETHER_SEPOLIA_TESTNET])(
'rollsback the transaction request with the original request state if state update fails: feeToken - %symbol',
async (token: Erc20Token) => {
const feeToken = FeeToken[token.symbol];
const {
event,
account,
network,
getEstimatedFeesSpy,
hasSufficientFundsForFeeSpy,
updateExecuteTxnFlowSpy,
mockGetEstimatedFeesResponse,
upsertTransactionRequestSpy,
transactionRequest,
} = await prepareHandleFeeTokenChange(feeToken);
const feeTokenAddress = token.address;
const { signer, calls } = transactionRequest;
const { publicKey, privateKey, address } = account;
const { suggestedMaxFee } = mockGetEstimatedFeesResponse;
const rollbackSnapshot = {
maxFee: transactionRequest.maxFee,
selectedFeeToken: transactionRequest.selectedFeeToken,
includeDeploy: transactionRequest.includeDeploy,
resourceBounds: [...transactionRequest.resourceBounds],
};

upsertTransactionRequestSpy.mockRejectedValue(new Error('Failed!'));

const controller = createMockController(event);
await controller.handleFeeTokenChange();

expect(getEstimatedFeesSpy).toHaveBeenCalledWith(
network,
signer,
privateKey,
publicKey,
[
{
type: TransactionType.INVOKE,
payload: calls.map((call) => ({
calldata: call.calldata,
contractAddress: call.contractAddress,
entrypoint: call.entrypoint,
})),
},
],
{
version: controller.feeTokenToTransactionVersion(feeToken),
},
);
expect(hasSufficientFundsForFeeSpy).toHaveBeenCalledWith({
address,
network,
calls,
feeTokenAddress,
suggestedMaxFee,
});
// transactionRequest will be pass by reference, so we can use this to check the updated value
expect(transactionRequest.maxFee).toStrictEqual(suggestedMaxFee);
expect(updateExecuteTxnFlowSpy).toHaveBeenCalledWith(
controller.eventId,
transactionRequest,
);
expect(upsertTransactionRequestSpy).toHaveBeenCalledWith(
transactionRequest,
);
expect(updateExecuteTxnFlowSpy).toHaveBeenCalledWith(
controller.eventId,
{ ...transactionRequest, ...rollbackSnapshot },
{
errors: {
fees: `Failed to calculate the fees, switching back to ${transactionRequest.selectedFeeToken}`,
},
},
);
},
);

it('updates the transaction request with an insufficient funds error message if the account balance is insufficient to cover the fee.', async () => {
const {
event,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,20 @@ export class UserInputEventController {
return token.address;
}

protected createRollbackSnapshot(
request: TransactionRequest,
): Partial<TransactionRequest> {
return {
maxFee: request.maxFee,
selectedFeeToken: request.selectedFeeToken,
includeDeploy: request.includeDeploy,
resourceBounds: [...request.resourceBounds],
};
}

protected async handleFeeTokenChange() {
const request = this.context?.request as TransactionRequest;
const rollbackSnapshot = this.createRollbackSnapshot(request);
const { addressIndex, calls, signer, chainId } = request;
const feeToken = (this.event as InputChangeEvent)
.value as unknown as FeeToken;
Expand Down Expand Up @@ -196,9 +208,13 @@ export class UserInputEventController {
: `Failed to calculate the fees, switching back to ${request.selectedFeeToken}`;

// On failure, display ExecuteTxnUI with an error message
await updateExecuteTxnFlow(this.eventId, request, {
errors: { fees: errorMessage },
});
await updateExecuteTxnFlow(
this.eventId,
{ ...request, ...rollbackSnapshot },
{
errors: { fees: errorMessage },
},
);
}
}
}

0 comments on commit e2bda06

Please sign in to comment.