Skip to content

Commit 90fe1d3

Browse files
authored
Fix permission status check in prepareSpendCallData (#139)
* properly get the onchain status' * check is revoked status in prepareCharges
1 parent 48cfb84 commit 90fe1d3

File tree

4 files changed

+123
-39
lines changed

4 files changed

+123
-39
lines changed

packages/account-sdk/src/interface/public-utilities/spend-permission/methods/getPermissionStatus.test.ts

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ describe('getPermissionStatus - browser + node', () => {
9999
(getClient as Mock).mockReturnValue(mockClient);
100100
(readContract as Mock)
101101
.mockResolvedValueOnce(mockCurrentPeriod) // getCurrentPeriod
102-
.mockResolvedValueOnce(mockIsRevoked); // isRevoked
102+
.mockResolvedValueOnce(mockIsRevoked) // isRevoked
103+
.mockResolvedValueOnce(true); // isValid (approved onchain)
103104

104105
const result: GetPermissionStatusResponseType =
105106
await getPermissionStatus(mockSpendPermission);
@@ -110,19 +111,21 @@ describe('getPermissionStatus - browser + node', () => {
110111
isRevoked: false,
111112
isExpired: false, // current time (1234567890) < end time (1234654290)
112113
isActive: true, // not revoked and not expired
114+
isApprovedOnchain: true, // isValid returns true
113115
currentPeriod: mockCurrentPeriod,
114116
});
115117

116118
expect(getClient).toHaveBeenCalledWith(8453);
117119
expect(toSpendPermissionArgs).toHaveBeenCalledWith(mockSpendPermission);
118-
expect(readContract).toHaveBeenCalledTimes(2);
120+
expect(readContract).toHaveBeenCalledTimes(3);
119121

120122
// Test with node environment (no client in store)
121123
(getClient as Mock).mockReturnValue(null);
122124
getPublicClientFromChainIdSpy.mockReturnValue(mockClient as unknown as PublicClient);
123125
(readContract as Mock)
124126
.mockResolvedValueOnce(mockCurrentPeriod) // getCurrentPeriod
125-
.mockResolvedValueOnce(mockIsRevoked); // isRevoked
127+
.mockResolvedValueOnce(mockIsRevoked) // isRevoked
128+
.mockResolvedValueOnce(true); // isValid (approved onchain)
126129

127130
const nodeResult: GetPermissionStatusResponseType =
128131
await getPermissionStatus(mockSpendPermission);
@@ -150,22 +153,25 @@ describe('getPermissionStatus - browser + node', () => {
150153
(getClient as Mock).mockReturnValue(mockClient);
151154
(readContract as Mock)
152155
.mockResolvedValueOnce(mockCurrentPeriod)
153-
.mockResolvedValueOnce(mockIsRevoked);
156+
.mockResolvedValueOnce(mockIsRevoked)
157+
.mockResolvedValueOnce(true); // isValid
154158

155159
const result = await getPermissionStatus(mockSpendPermission);
156160

157161
expect(result.remainingSpend).toBe(BigInt(0));
158162
expect(result.isRevoked).toBe(false);
159163
expect(result.isExpired).toBe(false);
160164
expect(result.isActive).toBe(true);
165+
expect(result.isApprovedOnchain).toBe(true);
161166
expect(result.currentPeriod).toEqual(mockCurrentPeriod);
162167

163168
// Test with node environment
164169
(getClient as Mock).mockReturnValue(null);
165170
getPublicClientFromChainIdSpy.mockReturnValue(mockClient as unknown as PublicClient);
166171
(readContract as Mock)
167172
.mockResolvedValueOnce(mockCurrentPeriod)
168-
.mockResolvedValueOnce(mockIsRevoked);
173+
.mockResolvedValueOnce(mockIsRevoked)
174+
.mockResolvedValueOnce(true); // isValid
169175

170176
const nodeResult = await getPermissionStatus(mockSpendPermission);
171177

@@ -191,21 +197,24 @@ describe('getPermissionStatus - browser + node', () => {
191197
(getClient as Mock).mockReturnValue(mockClient);
192198
(readContract as Mock)
193199
.mockResolvedValueOnce(mockCurrentPeriod)
194-
.mockResolvedValueOnce(mockIsRevoked);
200+
.mockResolvedValueOnce(mockIsRevoked)
201+
.mockResolvedValueOnce(true); // isValid
195202

196203
const result = await getPermissionStatus(mockSpendPermission);
197204

198205
expect(result.isRevoked).toBe(true);
199206
expect(result.isExpired).toBe(false);
200207
expect(result.isActive).toBe(false);
208+
expect(result.isApprovedOnchain).toBe(true);
201209
expect(result.currentPeriod).toEqual(mockCurrentPeriod);
202210

203211
// Test with node environment
204212
(getClient as Mock).mockReturnValue(null);
205213
getPublicClientFromChainIdSpy.mockReturnValue(mockClient as unknown as PublicClient);
206214
(readContract as Mock)
207215
.mockResolvedValueOnce(mockCurrentPeriod)
208-
.mockResolvedValueOnce(mockIsRevoked);
216+
.mockResolvedValueOnce(mockIsRevoked)
217+
.mockResolvedValueOnce(true); // isValid
209218

210219
const nodeResult = await getPermissionStatus(mockSpendPermission);
211220

@@ -231,21 +240,24 @@ describe('getPermissionStatus - browser + node', () => {
231240
(getClient as Mock).mockReturnValue(mockClient);
232241
(readContract as Mock)
233242
.mockResolvedValueOnce(mockCurrentPeriod)
234-
.mockResolvedValueOnce(mockIsRevoked);
243+
.mockResolvedValueOnce(mockIsRevoked)
244+
.mockResolvedValueOnce(true); // isValid
235245

236246
const result = await getPermissionStatus(mockSpendPermission);
237247

238248
expect(result.isRevoked).toBe(false);
239249
expect(result.isExpired).toBe(true);
240250
expect(result.isActive).toBe(false);
251+
expect(result.isApprovedOnchain).toBe(true);
241252
expect(result.currentPeriod).toEqual(mockCurrentPeriod);
242253

243254
// Test with node environment
244255
(getClient as Mock).mockReturnValue(null);
245256
getPublicClientFromChainIdSpy.mockReturnValue(mockClient as unknown as PublicClient);
246257
(readContract as Mock)
247258
.mockResolvedValueOnce(mockCurrentPeriod)
248-
.mockResolvedValueOnce(mockIsRevoked);
259+
.mockResolvedValueOnce(mockIsRevoked)
260+
.mockResolvedValueOnce(true); // isValid
249261

250262
const nodeResult = await getPermissionStatus(mockSpendPermission);
251263

@@ -404,11 +416,12 @@ describe('getPermissionStatus - browser + node', () => {
404416

405417
(readContract as Mock)
406418
.mockResolvedValueOnce(mockCurrentPeriod)
407-
.mockResolvedValueOnce(false);
419+
.mockResolvedValueOnce(false)
420+
.mockResolvedValueOnce(true); // isValid
408421

409422
await getPermissionStatusFunc(mockSpendPermission);
410423

411-
expect(readContract).toHaveBeenCalledTimes(2);
424+
expect(readContract).toHaveBeenCalledTimes(3);
412425

413426
// Verify getCurrentPeriod call
414427
expect(readContract).toHaveBeenNthCalledWith(1, mockClient, {
@@ -426,6 +439,14 @@ describe('getPermissionStatus - browser + node', () => {
426439
args: [mockSpendPermissionArgs],
427440
});
428441

442+
// Verify isValid call
443+
expect(readContract).toHaveBeenNthCalledWith(3, mockClient, {
444+
address: spendPermissionManagerAddress,
445+
abi: spendPermissionManagerAbi,
446+
functionName: 'isValid',
447+
args: [mockSpendPermissionArgs],
448+
});
449+
429450
// Restore Date.now
430451
Date.now = originalDateNow;
431452
}
@@ -453,26 +474,32 @@ describe('getPermissionStatus - browser + node', () => {
453474
// Create promises that we can control
454475
let resolveGetCurrentPeriod: (value: any) => void;
455476
let resolveIsRevoked: (value: any) => void;
477+
let resolveIsValid: (value: any) => void;
456478

457479
const getCurrentPeriodPromise = new Promise((resolve) => {
458480
resolveGetCurrentPeriod = resolve;
459481
});
460482
const isRevokedPromise = new Promise((resolve) => {
461483
resolveIsRevoked = resolve;
462484
});
485+
const isValidPromise = new Promise((resolve) => {
486+
resolveIsValid = resolve;
487+
});
463488

464489
(readContract as Mock)
465490
.mockReturnValueOnce(getCurrentPeriodPromise)
466-
.mockReturnValueOnce(isRevokedPromise);
491+
.mockReturnValueOnce(isRevokedPromise)
492+
.mockReturnValueOnce(isValidPromise);
467493

468494
const statusPromise = getPermissionStatusFunc(mockSpendPermission);
469495

470496
// Verify all contract calls are made immediately
471-
expect(readContract).toHaveBeenCalledTimes(2);
497+
expect(readContract).toHaveBeenCalledTimes(3);
472498

473499
// Resolve all promises
474500
resolveGetCurrentPeriod!(mockCurrentPeriod);
475501
resolveIsRevoked!(false);
502+
resolveIsValid!(true);
476503

477504
await statusPromise;
478505

@@ -500,7 +527,10 @@ describe('getPermissionStatus - browser + node', () => {
500527

501528
// Test with browser environment
502529
(getClient as Mock).mockReturnValue(mockClient);
503-
(readContract as Mock).mockResolvedValueOnce(mockCurrentPeriod).mockResolvedValueOnce(false);
530+
(readContract as Mock)
531+
.mockResolvedValueOnce(mockCurrentPeriod)
532+
.mockResolvedValueOnce(false)
533+
.mockResolvedValueOnce(true); // isValid
504534

505535
const result = await getPermissionStatus(permissionWithZeroAllowance);
506536

@@ -510,7 +540,10 @@ describe('getPermissionStatus - browser + node', () => {
510540
// Test with node environment
511541
(getClient as Mock).mockReturnValue(null);
512542
getPublicClientFromChainIdSpy.mockReturnValue(mockClient as unknown as PublicClient);
513-
(readContract as Mock).mockResolvedValueOnce(mockCurrentPeriod).mockResolvedValueOnce(false);
543+
(readContract as Mock)
544+
.mockResolvedValueOnce(mockCurrentPeriod)
545+
.mockResolvedValueOnce(false)
546+
.mockResolvedValueOnce(true); // isValid
514547

515548
const nodeResult = await getPermissionStatus(permissionWithZeroAllowance);
516549

@@ -541,7 +574,10 @@ describe('getPermissionStatus - browser + node', () => {
541574

542575
// Test with browser environment
543576
(getClient as Mock).mockReturnValue(mockClient);
544-
(readContract as Mock).mockResolvedValueOnce(mockCurrentPeriod).mockResolvedValueOnce(false);
577+
(readContract as Mock)
578+
.mockResolvedValueOnce(mockCurrentPeriod)
579+
.mockResolvedValueOnce(false)
580+
.mockResolvedValueOnce(true); // isValid
545581

546582
const result = await getPermissionStatus(permissionWithLargeAllowance);
547583

@@ -553,7 +589,10 @@ describe('getPermissionStatus - browser + node', () => {
553589
// Test with node environment
554590
(getClient as Mock).mockReturnValue(null);
555591
getPublicClientFromChainIdSpy.mockReturnValue(mockClient as unknown as PublicClient);
556-
(readContract as Mock).mockResolvedValueOnce(mockCurrentPeriod).mockResolvedValueOnce(false);
592+
(readContract as Mock)
593+
.mockResolvedValueOnce(mockCurrentPeriod)
594+
.mockResolvedValueOnce(false)
595+
.mockResolvedValueOnce(true); // isValid
557596

558597
const nodeResult = await getPermissionStatus(permissionWithLargeAllowance);
559598

@@ -576,7 +615,10 @@ describe('getPermissionStatus - browser + node', () => {
576615

577616
// Test with browser environment
578617
(getClient as Mock).mockReturnValue(mockClient);
579-
(readContract as Mock).mockResolvedValueOnce(mockCurrentPeriod).mockResolvedValueOnce(false);
618+
(readContract as Mock)
619+
.mockResolvedValueOnce(mockCurrentPeriod)
620+
.mockResolvedValueOnce(false)
621+
.mockResolvedValueOnce(true); // isValid
580622

581623
const result = await getPermissionStatus(mockSpendPermission);
582624

@@ -586,7 +628,10 @@ describe('getPermissionStatus - browser + node', () => {
586628
// Test with node environment
587629
(getClient as Mock).mockReturnValue(null);
588630
getPublicClientFromChainIdSpy.mockReturnValue(mockClient as unknown as PublicClient);
589-
(readContract as Mock).mockResolvedValueOnce(mockCurrentPeriod).mockResolvedValueOnce(false);
631+
(readContract as Mock)
632+
.mockResolvedValueOnce(mockCurrentPeriod)
633+
.mockResolvedValueOnce(false)
634+
.mockResolvedValueOnce(true); // isValid
590635

591636
const nodeResult = await getPermissionStatus(mockSpendPermission);
592637

packages/account-sdk/src/interface/public-utilities/spend-permission/methods/getPermissionStatus.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type GetPermissionStatusResponseType = {
1616
isRevoked: boolean;
1717
isExpired: boolean;
1818
isActive: boolean;
19+
isApprovedOnchain: boolean;
1920
currentPeriod: {
2021
start: number;
2122
end: number;
@@ -80,7 +81,7 @@ const getPermissionStatusFn = async (
8081

8182
const spendPermissionArgs = toSpendPermissionArgs(permission);
8283

83-
const [currentPeriod, isRevoked] = await Promise.all([
84+
const [currentPeriod, isRevoked, isValid] = await Promise.all([
8485
readContract(client, {
8586
address: spendPermissionManagerAddress,
8687
abi: spendPermissionManagerAbi,
@@ -93,6 +94,12 @@ const getPermissionStatusFn = async (
9394
functionName: 'isRevoked',
9495
args: [spendPermissionArgs],
9596
}) as Promise<boolean>,
97+
readContract(client, {
98+
address: spendPermissionManagerAddress,
99+
abi: spendPermissionManagerAbi,
100+
functionName: 'isValid',
101+
args: [spendPermissionArgs],
102+
}) as Promise<boolean>,
96103
]);
97104

98105
// Calculate remaining spend in current period
@@ -111,12 +118,16 @@ const getPermissionStatusFn = async (
111118
// Permission is active if it's not revoked and not expired
112119
const isActive = !isRevoked && !isExpired;
113120

121+
// isApprovedOnchain indicates if the permission has been approved on the blockchain and is not revoked
122+
const isApprovedOnchain = isValid;
123+
114124
return {
115125
remainingSpend,
116126
nextPeriodStart: timestampInSecondsToDate(Number(nextPeriodStart)),
117127
isRevoked,
118128
isExpired,
119129
isActive,
130+
isApprovedOnchain,
120131
currentPeriod,
121132
};
122133
};

0 commit comments

Comments
 (0)