Skip to content

Commit fe26cfe

Browse files
authored
Add callback for guarded transcations (#1701) (#1702)
* Add callback for guarded transcations * Update changelog * Remove log * Fix ts-ignore * Update tests * Update type * Update props * Update readme * Update
2 parents 79e4f74 + 5a436b0 commit fe26cfe

File tree

15 files changed

+141
-32
lines changed

15 files changed

+141
-32
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ All notable changes will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [Unreleased]
9+
10+
## [[5.5.1](https://github.com/multiversx/mx-sdk-dapp/pull/1702)] - 2025-12-04
11+
12+
- [Exposed setIsSigningUiEnabled and added callback for signTransactions](https://github.com/multiversx/mx-sdk-dapp/pull/1701)
913
- [Extend tests for actions](https://github.com/multiversx/mx-sdk-dapp/pull/1699)
1014
- [Extended tests](https://github.com/multiversx/mx-sdk-dapp/pull/1698)
1115
- [IframeProviderStrategy tests](https://github.com/multiversx/mx-sdk-dapp/pull/1695)

README.md

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
[![Integration tests](https://github.com/multiversx/mx-sdk-dapp/actions/workflows/run-template-dapps-integration.yml/badge.svg)](https://github.com/multiversx/mx-sdk-dapp/actions/workflows/run-template-dapps-integration.yml)
66
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/multiversx/mx-sdk-dapp)
77

8-
98
MultiversX Front-End SDK for JavaScript and TypeScript (written in TypeScript).
109

1110
## Introduction
@@ -304,6 +303,47 @@ const provider = getAccountProvider();
304303
const signedTransactions = await provider.signTransactions(transactions);
305304
```
306305

306+
#### 4.1.1 Signing Options
307+
308+
The `signTransactions` method accepts an optional second parameter with signing options:
309+
310+
```typescript
311+
import type { SignTransactionsOptionsType } from '@multiversx/sdk-dapp/out/providers/DappProvider/helpers/signTransactions/signTransactionsWithProvider';
312+
313+
const options: SignTransactionsOptionsType = {
314+
skipGuardian?: boolean; // Skip guardian validation for guarded accounts
315+
callback?: (signedTransactions: Transaction[]) => Promise<Transaction[]>; // Post-signing callback
316+
};
317+
318+
const signedTransactions = await provider.signTransactions(transactions, options);
319+
```
320+
321+
**Available options:**
322+
323+
- `skipGuardian` (`boolean`): Optional. When `true`, skips the guardian co-signing step for guarded accounts. Useful for transactions that don't require guardian approval.
324+
325+
- `callback` (`(signedTransactions: Transaction[]) => Promise<Transaction[]>`): Optional. A callback function that receives the signed transactions after all transactions have been signed by the user, but before guardian validation. This allows you to modify, filter, or perform additional processing on the signed transactions. The returned transactions will proceed to guardian validation (if applicable).
326+
327+
**Example: Using the callback option**
328+
329+
The `callback` option is particularly useful for:
330+
331+
- **Guarded accounts**: Processing signed transactions before they are sent to the guardian service for co-signing
332+
- **Custom validation**: Adding additional validation logic after user signing
333+
- **Logging/Analytics**: Recording signed transactions before final submission
334+
335+
```typescript
336+
const signedTransactions = await provider.signTransactions(transactions, {
337+
callback: async (signedTxs) => {
338+
// Example: Log signed transactions before guardian processing
339+
console.log('User signed transactions:', signedTxs);
340+
341+
// Return the transactions to proceed with guardian validation
342+
return signedTxs;
343+
}
344+
});
345+
```
346+
307347
#### 4.2 Sending and tracking transactions
308348

309349
Then, to send the transactions, you need to use the `TransactionManager` class and pass in the `signedTransactions` to the `send` method. You can then track the transactions by using the `track` method. This will create a toast notification with the transaction hash and its status.
@@ -589,24 +629,27 @@ import { TransactionManagerTrackOptionsType } from '@multiversx/sdk-dapp/out/man
589629

590630
const options: TransactionManagerTrackOptionsType = {
591631
disableToasts: false, // `false` by default
592-
transactionsDisplayInfo: { // optional. If left `undefined`, it will use the default messages
632+
transactionsDisplayInfo: {
633+
// optional. If left `undefined`, it will use the default messages
593634
errorMessage: 'Failed adding stake',
594635
successMessage: 'Stake successfully added',
595636
receivedMessage: 'Stake successfully added', // optional, add it in case of multiple transactions
596-
processingMessage: 'Staking in progress',
637+
processingMessage: 'Staking in progress'
597638
// submittedMessage: 'Stake submitted',
598-
// timedOutMessage: 'Transaction timed out',
639+
// timedOutMessage: 'Transaction timed out',
599640
// invalidMessage: 'Invalid transaction',
600641
},
601642
sessionInformation: {
602643
// `undefined` by default. Use to perform additional actions based on the session information
603644
stakeAmount: '1000000000000000000000000'
604645
},
605-
onSuccess: async(sessionId) => { // optional
646+
onSuccess: async (sessionId) => {
647+
// optional
606648
// overrides the the global `onSuccess` callback set in the `initApp` method for the current session only
607649
console.log('Session successful', sessionId);
608650
},
609-
onFail: async(sessionId) => { // optional
651+
onFail: async (sessionId) => {
652+
// optional
610653
console.log('Session failed', sessionId);
611654
}
612655
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@multiversx/sdk-dapp",
3-
"version": "5.5.0",
3+
"version": "5.5.1",
44
"description": "A library to hold the main logic for a dapp on the MultiversX blockchain",
55
"author": "MultiversX",
66
"license": "MIT",

src/providers/DappProvider/helpers/signTransactions/signTransactionsWithProvider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { computeNonces } from '../computeNonces/computeNonces';
1515

1616
export type SignTransactionsOptionsType = {
1717
skipGuardian?: boolean;
18+
callback?: (signedTransactions: Transaction[]) => Promise<Transaction[]>;
1819
};
1920

2021
type SignTransactionsType = {
@@ -53,7 +54,7 @@ export async function signTransactionsWithProvider({
5354
: transactionsWithComputedNonce;
5455

5556
const signedTransactions: Transaction[] =
56-
(await provider.signTransactions(transactionsToSign)) ?? [];
57+
(await provider.signTransactions(transactionsToSign, options)) ?? [];
5758

5859
setAccountNonce(nonce + signedTransactions.length);
5960

src/providers/strategies/BaseProviderStrategy/BaseProviderStrategy.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Transaction, Message } from 'lib/sdkCore';
44
import { IDAppProviderOptions, IDAppProviderAccount } from 'lib/sdkDappUtils';
55
import { PendingTransactionsEventsEnum } from 'managers/internal/PendingTransactionsStateManager';
66
import { getAddress } from 'methods/account/getAddress';
7+
import { SignTransactionsOptionsType } from 'providers/DappProvider/helpers/signTransactions/signTransactionsWithProvider';
78
import { IProvider, ProviderType } from 'providers/types/providerFactory.types';
89
import { providerSettingsSelector } from 'store/selectors/configSelectors';
910
import { getState } from 'store/store';
@@ -47,7 +48,7 @@ export abstract class BaseProviderStrategy<
4748

4849
signTransaction(
4950
_transaction: Transaction,
50-
_options?: IDAppProviderOptions
51+
_options?: SignTransactionsOptionsType
5152
): Promise<Transaction | null> {
5253
throw new Error('Method not implemented.');
5354
}

src/providers/strategies/CrossWindowProviderStrategy/CrossWindowProviderStrategy.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { providerLabels } from 'constants/providerFactory.constants';
22
import { Message, Transaction } from 'lib/sdkCore';
33
import { IDAppProviderAccount } from 'lib/sdkDappUtils';
44
import { CrossWindowProvider } from 'lib/sdkWebWalletCrossWindowProvider';
5+
import { SignTransactionsOptionsType } from 'providers/DappProvider/helpers/signTransactions/signTransactionsWithProvider';
56
import {
67
ProviderTypeEnum,
78
ProviderType
@@ -11,7 +12,6 @@ import { getState } from 'store/store';
1112
import { ProviderErrorsEnum } from 'types/provider.types';
1213
import { BaseProviderStrategy } from '../BaseProviderStrategy/BaseProviderStrategy';
1314
import { signMessage } from '../helpers/signMessage/signMessage';
14-
import { guardTransactions } from '../helpers/signTransactions/helpers/guardTransactions/guardTransactions';
1515

1616
type CrossWindowProviderProps = {
1717
address?: string;
@@ -76,7 +76,10 @@ export class CrossWindowProviderStrategy extends BaseProviderStrategy {
7676
this.provider.cancelAction();
7777
};
7878

79-
signTransactions = async (transactions: Transaction[]) => {
79+
signTransactions = async (
80+
transactions: Transaction[],
81+
_options?: SignTransactionsOptionsType
82+
) => {
8083
if (!this.provider) {
8184
throw new Error(ProviderErrorsEnum.notInitialized);
8285
}
@@ -87,10 +90,7 @@ export class CrossWindowProviderStrategy extends BaseProviderStrategy {
8790
const signedTransactions: Transaction[] =
8891
(await this.provider.signTransactions(transactions)) ?? [];
8992

90-
const optionallyGuardedTransactions =
91-
await guardTransactions(signedTransactions);
92-
93-
return optionallyGuardedTransactions;
93+
return signedTransactions;
9494
} catch (error) {
9595
await onClose({ shouldCancelAction: true });
9696

src/providers/strategies/CrossWindowProviderStrategy/tests/CrossWindowProviderStrategy.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,21 +143,18 @@ describe('CrossWindowProviderStrategy tests', () => {
143143
expect(result).toBe(true);
144144
});
145145

146-
it('signTransactions resolves guarded transactions and closes UI', async () => {
146+
it('signTransactions resolves signed transactions and closes UI', async () => {
147147
const handlers = buildHandlers();
148148
mockGetPendingTransactionsHandlers.mockResolvedValue(handlers);
149149
const signedTx = [{ hash: 'signed' } as any];
150-
const guardedTx = [{ hash: 'guarded' } as any];
151150
mockCrossWindowProvider.signTransactions.mockResolvedValue(signedTx);
152-
mockGuardTransactions.mockResolvedValue(guardedTx);
153151

154152
const strategy = new CrossWindowProviderStrategy();
155153
const result = await strategy.signTransactions([{} as any]);
156154

157155
expect(mockCrossWindowProvider.signTransactions).toHaveBeenCalled();
158-
expect(mockGuardTransactions).toHaveBeenCalledWith(signedTx);
159156
expect(handlers.manager.closeUI).toHaveBeenCalled();
160-
expect(result).toBe(guardedTx);
157+
expect(result).toEqual(signedTx);
161158
});
162159

163160
it('signTransactions cancels action on error', async () => {

src/providers/strategies/ExtensionProviderStrategy/ExtensionProviderStrategy.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ExtensionProvider } from '@multiversx/sdk-extension-provider/out/extens
22
import { providerLabels } from 'constants/providerFactory.constants';
33
import { Message, Transaction } from 'lib/sdkCore';
44
import { IDAppProviderAccount } from 'lib/sdkDappUtils';
5+
import { SignTransactionsOptionsType } from 'providers/DappProvider/helpers/signTransactions/signTransactionsWithProvider';
56
import {
67
ProviderTypeEnum,
78
ProviderType
@@ -58,7 +59,10 @@ export class ExtensionProviderStrategy extends BaseProviderStrategy {
5859
this.provider.cancelAction();
5960
};
6061

61-
signTransactions = async (transactions: Transaction[]) => {
62+
signTransactions = async (
63+
transactions: Transaction[],
64+
_options?: SignTransactionsOptionsType
65+
) => {
6266
if (!this.provider) {
6367
throw new Error(ProviderErrorsEnum.notInitialized);
6468
}

src/providers/strategies/IframeProviderStrategy/IframeProviderStrategy.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { providerLabels } from 'constants/providerFactory.constants';
22
import { Message, Transaction } from 'lib/sdkCore';
33
import { IDAppProviderAccount } from 'lib/sdkDappUtils';
44
import { IframeProvider } from 'lib/sdkWebWalletIframeProvider';
5+
import { SignTransactionsOptionsType } from 'providers/DappProvider/helpers/signTransactions/signTransactionsWithProvider';
56
import {
67
ProviderTypeEnum,
78
ProviderType
@@ -85,7 +86,10 @@ export class IframeProviderStrategy extends BaseProviderStrategy {
8586
this.provider.cancelAction();
8687
};
8788

88-
signTransactions = async (transactions: Transaction[]) => {
89+
signTransactions = async (
90+
transactions: Transaction[],
91+
options?: SignTransactionsOptionsType
92+
) => {
8993
if (!this.provider) {
9094
throw new Error(ProviderErrorsEnum.notInitialized);
9195
}
@@ -95,7 +99,8 @@ export class IframeProviderStrategy extends BaseProviderStrategy {
9599
try {
96100
const signedTransactions = await signTransactions({
97101
transactions,
98-
handleSign: this.provider.signTransactions.bind(this.provider)
102+
handleSign: this.provider.signTransactions.bind(this.provider),
103+
options
99104
});
100105

101106
return signedTransactions;

src/providers/strategies/LedgerProviderStrategy/LedgerProviderStrategy.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Message, Transaction } from 'lib/sdkCore';
55
import { IDAppProviderAccount } from 'lib/sdkDappUtils';
66
import { LedgerConnectStateManager } from 'managers/internal/LedgerConnectStateManager/LedgerConnectStateManager';
77
import { getIsLoggedIn } from 'methods/account/getIsLoggedIn';
8+
import { SignTransactionsOptionsType } from 'providers/DappProvider/helpers/signTransactions/signTransactionsWithProvider';
89
import {
910
ProviderTypeEnum,
1011
ProviderType
@@ -151,7 +152,10 @@ export class LedgerProviderStrategy extends BaseProviderStrategy {
151152
await ledgerConnectManager.init(anchor);
152153
};
153154

154-
signTransactions = async (transactions: Transaction[]) => {
155+
signTransactions = async (
156+
transactions: Transaction[],
157+
options?: SignTransactionsOptionsType
158+
) => {
155159
if (!this.provider) {
156160
throw new Error(ProviderErrorsEnum.notInitialized);
157161
}
@@ -160,7 +164,8 @@ export class LedgerProviderStrategy extends BaseProviderStrategy {
160164

161165
const signedTransactions = await signTransactions({
162166
transactions,
163-
handleSign: this.provider.signTransactions.bind(this.provider)
167+
handleSign: this.provider.signTransactions.bind(this.provider),
168+
options
164169
});
165170

166171
return signedTransactions;

0 commit comments

Comments
 (0)