Skip to content

Commit b635d59

Browse files
Merge pull request #360 from LIT-Protocol/fix/remove-approveandswap-from-uniswap
Remove `ApproveAndSwap` option from Uniswap Ability
2 parents 15a284b + 6de48aa commit b635d59

File tree

7 files changed

+208
-105
lines changed

7 files changed

+208
-105
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
ability-uniswap-swap: major
3+
---
4+
5+
Remove the ApproveAndSwap Ability Action from the Uniswap Swap Ability, and format returned tokenIn allowances and balances as decimal strings

packages/apps/abilities-e2e/test-e2e/swap-no-native.spec.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,24 @@ import {
4646
const ALCHEMY_GAS_SPONSOR_API_KEY = getEnv('ALCHEMY_GAS_SPONSOR_API_KEY');
4747
const ALCHEMY_GAS_SPONSOR_POLICY_ID = getEnv('ALCHEMY_GAS_SPONSOR_POLICY_ID');
4848

49-
// const SWAP_AMOUNT = 80;
50-
// const SWAP_TOKEN_IN_ADDRESS = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'; // DEGEN
51-
// const SWAP_TOKEN_IN_DECIMALS = 18;
52-
53-
const SWAP_AMOUNT = 0.0003;
54-
const SWAP_TOKEN_IN_ADDRESS = '0x4200000000000000000000000000000000000006'; // WETH
49+
const SWAP_AMOUNT = 80;
50+
const SWAP_TOKEN_IN_ADDRESS = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'; // DEGEN
5551
const SWAP_TOKEN_IN_DECIMALS = 18;
5652

53+
// const SWAP_AMOUNT = 0.0003;
54+
// const SWAP_TOKEN_IN_ADDRESS = '0x4200000000000000000000000000000000000006'; // WETH
55+
// const SWAP_TOKEN_IN_DECIMALS = 18;
56+
5757
// const SWAP_AMOUNT = 0.1;
5858
// const SWAP_TOKEN_IN_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC
5959
// const SWAP_TOKEN_IN_DECIMALS = 6;
6060

61-
// const SWAP_TOKEN_OUT_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC
62-
// const SWAP_TOKEN_OUT_DECIMALS = 6;
61+
const SWAP_TOKEN_OUT_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC
62+
const SWAP_TOKEN_OUT_DECIMALS = 6;
6363
// const SWAP_TOKEN_OUT_ADDRESS = '0x4200000000000000000000000000000000000006'; // WETH
6464
// const SWAP_TOKEN_OUT_DECIMALS = 18;
65-
const SWAP_TOKEN_OUT_ADDRESS = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'; // DEGEN
66-
const SWAP_TOKEN_OUT_DECIMALS = 18;
65+
// const SWAP_TOKEN_OUT_ADDRESS = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'; // DEGEN
66+
// const SWAP_TOKEN_OUT_DECIMALS = 18;
6767

6868
const RPC_URL = BASE_RPC_URL;
6969
const CHAIN_ID = 8453;
@@ -344,7 +344,7 @@ describe('Uniswap Swap Ability E2E Tests', () => {
344344
// Try to precheck with the malicious quote
345345
const precheckResult = await uniswapSwapAbilityClient.precheck(
346346
{
347-
action: AbilityAction.ApproveAndSwap,
347+
action: AbilityAction.Approve,
348348
rpcUrlForUniswap: RPC_URL,
349349
signedUniswapQuote: {
350350
quote: signedUniswapQuote.quote,
@@ -486,12 +486,21 @@ describe('Uniswap Swap Ability E2E Tests', () => {
486486
expect(precheckResult.result!.nativeTokenBalance).toBeUndefined();
487487
expect(precheckResult.result!.tokenInAddress).toBe(SWAP_TOKEN_IN_ADDRESS);
488488
expect(precheckResult.result!.tokenInBalance).toBeDefined();
489-
expect(BigInt(precheckResult.result!.tokenInBalance as string)).toBeGreaterThan(0n);
490-
expect(BigInt(precheckResult.result!.currentTokenInAllowanceForSpender)).toBeGreaterThan(0n);
489+
expect(
490+
ethers.utils
491+
.parseUnits(precheckResult.result!.tokenInBalance as string, SWAP_TOKEN_IN_DECIMALS)
492+
.toBigInt(),
493+
).toBeGreaterThan(0n);
494+
expect(precheckResult.result!.currentTokenInAllowanceForSpender as string).toBe(
495+
ethers.utils.formatUnits(
496+
ethers.utils.parseUnits(SWAP_AMOUNT.toString(), SWAP_TOKEN_IN_DECIMALS),
497+
SWAP_TOKEN_IN_DECIMALS,
498+
),
499+
);
491500
expect(precheckResult.result!.spenderAddress).toBe(signedUniswapQuote.quote.to);
492501
});
493502

494-
it('should execute the Uniswap Swap Ability with the Agent Wallet PKP', async () => {
503+
it('should execute the Uniswap Swap Ability with the Agent Wallet PKP using Swap', async () => {
495504
const signedUniswapQuote = await validateSignedUniswapQuoteIsDefined(SIGNED_UNISWAP_QUOTE);
496505
const uniswapSwapAbilityClient = getUniswapSwapAbilityClient();
497506

packages/apps/abilities-e2e/test-e2e/swap.spec.ts

Lines changed: 110 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,29 @@ import {
4646
const ALCHEMY_GAS_SPONSOR_API_KEY = getEnv('ALCHEMY_GAS_SPONSOR_API_KEY');
4747
const ALCHEMY_GAS_SPONSOR_POLICY_ID = getEnv('ALCHEMY_GAS_SPONSOR_POLICY_ID');
4848

49-
// const SWAP_AMOUNT = 80;
50-
// const SWAP_TOKEN_IN_ADDRESS = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'; // DEGEN
51-
// const SWAP_TOKEN_IN_DECIMALS = 18;
49+
const SWAP_AMOUNT = 80;
50+
const SWAP_TOKEN_IN_ADDRESS = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'; // DEGEN
51+
const SWAP_TOKEN_IN_DECIMALS = 18;
5252

5353
// const SWAP_AMOUNT = 0.0003;
5454
// const SWAP_TOKEN_IN_ADDRESS = '0x4200000000000000000000000000000000000006'; // WETH
5555
// const SWAP_TOKEN_IN_DECIMALS = 18;
5656

57-
const SWAP_AMOUNT = 0.1;
58-
const SWAP_TOKEN_IN_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC
59-
const SWAP_TOKEN_IN_DECIMALS = 6;
57+
// const SWAP_AMOUNT = 0.1;
58+
// const SWAP_TOKEN_IN_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC
59+
// const SWAP_TOKEN_IN_DECIMALS = 6;
60+
61+
const EXPECTED_SWAP_AMOUNT = ethers.utils.formatUnits(
62+
ethers.utils.parseUnits(SWAP_AMOUNT.toString(), SWAP_TOKEN_IN_DECIMALS),
63+
SWAP_TOKEN_IN_DECIMALS,
64+
);
6065

61-
// const SWAP_TOKEN_OUT_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC
62-
// const SWAP_TOKEN_OUT_DECIMALS = 6;
66+
const SWAP_TOKEN_OUT_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC
67+
const SWAP_TOKEN_OUT_DECIMALS = 6;
6368
// const SWAP_TOKEN_OUT_ADDRESS = '0x4200000000000000000000000000000000000006'; // WETH
6469
// const SWAP_TOKEN_OUT_DECIMALS = 18;
65-
const SWAP_TOKEN_OUT_ADDRESS = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'; // DEGEN
66-
const SWAP_TOKEN_OUT_DECIMALS = 18;
70+
// const SWAP_TOKEN_OUT_ADDRESS = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'; // DEGEN
71+
// const SWAP_TOKEN_OUT_DECIMALS = 18;
6772

6873
const RPC_URL = BASE_RPC_URL;
6974
const CHAIN_ID = 8453;
@@ -359,6 +364,41 @@ describe('Uniswap Swap Ability E2E Tests', () => {
359364
});
360365

361366
describe('Precheck and Execute without Alchemy Gas Sponsorship', () => {
367+
it('should fail precheck because of invalid ability action', async () => {
368+
const signedUniswapQuote = await validateSignedUniswapQuoteIsDefined(SIGNED_UNISWAP_QUOTE);
369+
const uniswapSwapAbilityClient = getUniswapSwapAbilityClient();
370+
371+
// Try to precheck with the malicious quote
372+
const precheckResult = await uniswapSwapAbilityClient.precheck(
373+
{
374+
// @ts-expect-error - invalid ability action
375+
action: 'invalid',
376+
rpcUrlForUniswap: RPC_URL,
377+
signedUniswapQuote: {
378+
quote: signedUniswapQuote.quote,
379+
signature: signedUniswapQuote.signature,
380+
},
381+
alchemyGasSponsor: false,
382+
},
383+
{
384+
delegatorPkpEthAddress: TEST_CONFIG.userPkp!.ethAddress!,
385+
},
386+
);
387+
console.log(
388+
'[should fail precheck because of invalid ability action]',
389+
util.inspect(precheckResult, { depth: 10 }),
390+
);
391+
392+
// Precheck should fail with recipient validation error
393+
expect(precheckResult).toBeDefined();
394+
expect(precheckResult.success).toBe(false);
395+
396+
if (precheckResult.success === false) {
397+
expect(precheckResult.runtimeError).toContain('Invalid precheck parameters.');
398+
expect(precheckResult.schemaValidationError).toBeTruthy();
399+
}
400+
});
401+
362402
it('should fail precheck because of insufficient tokenIn allowance', async () => {
363403
const signedUniswapQuote = await validateSignedUniswapQuoteIsDefined(SIGNED_UNISWAP_QUOTE);
364404
const uniswapSwapAbilityClient = getUniswapSwapAbilityClient();
@@ -394,10 +434,38 @@ describe('Uniswap Swap Ability E2E Tests', () => {
394434
);
395435
expect(innerResult.spenderAddress).toBe(signedUniswapQuote.quote.to);
396436
expect(innerResult.tokenAddress).toBe(SWAP_TOKEN_IN_ADDRESS);
397-
expect(innerResult.requiredAllowance).toBe(
398-
ethers.utils.parseUnits(SWAP_AMOUNT.toString(), SWAP_TOKEN_IN_DECIMALS).toString(),
437+
expect(innerResult.requiredAllowance).toBe(EXPECTED_SWAP_AMOUNT);
438+
expect(innerResult.currentAllowance).toBe('0.0');
439+
});
440+
441+
it('should fail execute because of invalid ability action', async () => {
442+
const signedUniswapQuote = await validateSignedUniswapQuoteIsDefined(SIGNED_UNISWAP_QUOTE);
443+
const uniswapSwapAbilityClient = getUniswapSwapAbilityClient();
444+
445+
const executeResult = await uniswapSwapAbilityClient.execute(
446+
{
447+
// @ts-expect-error - invalid ability action
448+
action: 'invalid',
449+
rpcUrlForUniswap: RPC_URL,
450+
signedUniswapQuote: signedUniswapQuote,
451+
alchemyGasSponsor: false,
452+
},
453+
{
454+
delegatorPkpEthAddress: TEST_CONFIG.userPkp!.ethAddress!,
455+
},
399456
);
400-
expect(innerResult.currentAllowance).toBe('0');
457+
console.log(
458+
'[should fail execute because of invalid ability action]',
459+
util.inspect(executeResult, { depth: 10 }),
460+
);
461+
462+
expect(executeResult).toBeDefined();
463+
expect(executeResult.success).toBe(false);
464+
465+
if (executeResult.success === false) {
466+
expect(executeResult.runtimeError).toContain('Invalid execute parameters.');
467+
expect(executeResult.schemaValidationError).toBeTruthy();
468+
}
401469
});
402470

403471
it('should make a new ERC20 approval transaction for the Uniswap router', async () => {
@@ -466,10 +534,8 @@ describe('Uniswap Swap Ability E2E Tests', () => {
466534

467535
expect(executeResult.result).toBeDefined();
468536
expect(executeResult.result.approvalTxHash).toBeUndefined();
469-
expect(executeResult.result.currentAllowance).toBeDefined();
470-
expect(BigInt(executeResult.result.currentAllowance!)).toBeGreaterThanOrEqual(
471-
BigInt(ethers.utils.parseUnits(SWAP_AMOUNT.toString(), SWAP_TOKEN_IN_DECIMALS).toString()),
472-
);
537+
expect(executeResult.result.currentAllowance!).toBe(EXPECTED_SWAP_AMOUNT);
538+
expect(executeResult.result.requiredAllowance!).toBe(EXPECTED_SWAP_AMOUNT);
473539
});
474540

475541
it('should successfully run precheck on the Uniswap Swap Ability', async () => {
@@ -516,12 +582,23 @@ describe('Uniswap Swap Ability E2E Tests', () => {
516582
// Verify the result is properly populated
517583
expect(precheckResult.result).toBeDefined();
518584
expect(precheckResult.result!.nativeTokenBalance).toBeDefined();
519-
expect(BigInt(precheckResult.result!.nativeTokenBalance as string)).toBeGreaterThan(0n);
585+
expect(
586+
ethers.utils.parseEther(precheckResult.result!.nativeTokenBalance as string).toBigInt(),
587+
).toBeGreaterThan(0n);
588+
520589
expect(precheckResult.result!.tokenInAddress).toBe(SWAP_TOKEN_IN_ADDRESS);
521590
expect(precheckResult.result!.tokenInBalance).toBeDefined();
522-
expect(BigInt(precheckResult.result!.tokenInBalance as string)).toBeGreaterThan(0n);
523-
expect(BigInt(precheckResult.result!.currentTokenInAllowanceForSpender)).toBeGreaterThan(0n);
591+
expect(
592+
ethers.utils
593+
.parseUnits(precheckResult.result!.tokenInBalance as string, SWAP_TOKEN_IN_DECIMALS)
594+
.toBigInt(),
595+
).toBeGreaterThan(0n);
596+
597+
expect(precheckResult.result!.currentTokenInAllowanceForSpender as string).toBe(
598+
EXPECTED_SWAP_AMOUNT,
599+
);
524600
expect(precheckResult.result!.spenderAddress).toBe(signedUniswapQuote.quote.to);
601+
expect(precheckResult.result!.requiredTokenInAllowance!).toBe(EXPECTED_SWAP_AMOUNT);
525602
});
526603

527604
it('should execute the Uniswap Swap Ability with the Agent Wallet PKP', async () => {
@@ -619,10 +696,8 @@ describe('Uniswap Swap Ability E2E Tests', () => {
619696
);
620697
expect(innerResult.spenderAddress).toBe(signedUniswapQuote.quote.to);
621698
expect(innerResult.tokenAddress).toBe(SWAP_TOKEN_IN_ADDRESS);
622-
expect(innerResult.requiredAllowance).toBe(
623-
ethers.utils.parseUnits(SWAP_AMOUNT.toString(), SWAP_TOKEN_IN_DECIMALS).toString(),
624-
);
625-
expect(innerResult.currentAllowance).toBe('0');
699+
expect(innerResult.requiredAllowance).toBe(EXPECTED_SWAP_AMOUNT);
700+
expect(innerResult.currentAllowance).toBe('0.0');
626701
});
627702

628703
it('should make a new ERC20 approval transaction for the Uniswap router', async () => {
@@ -704,10 +779,8 @@ describe('Uniswap Swap Ability E2E Tests', () => {
704779

705780
expect(executeResult.result).toBeDefined();
706781
expect(executeResult.result.approvalTxHash).toBeUndefined();
707-
expect(executeResult.result.currentAllowance).toBeDefined();
708-
expect(BigInt(executeResult.result.currentAllowance!)).toBeGreaterThanOrEqual(
709-
BigInt(ethers.utils.parseUnits(SWAP_AMOUNT.toString(), SWAP_TOKEN_IN_DECIMALS).toString()),
710-
);
782+
expect(executeResult.result.currentAllowance!).toBe(EXPECTED_SWAP_AMOUNT);
783+
expect(executeResult.result.requiredAllowance!).toBe(EXPECTED_SWAP_AMOUNT);
711784
});
712785

713786
it('should successfully run precheck on the Uniswap Swap Ability', async () => {
@@ -757,10 +830,16 @@ describe('Uniswap Swap Ability E2E Tests', () => {
757830
expect(precheckResult.result).toBeDefined();
758831
expect(precheckResult.result!.nativeTokenBalance).toBeUndefined();
759832
expect(precheckResult.result!.tokenInAddress).toBe(SWAP_TOKEN_IN_ADDRESS);
760-
expect(precheckResult.result!.tokenInBalance).toBeDefined();
761-
expect(BigInt(precheckResult.result!.tokenInBalance as string)).toBeGreaterThan(0n);
762-
expect(BigInt(precheckResult.result!.currentTokenInAllowanceForSpender)).toBeGreaterThan(0n);
833+
expect(
834+
ethers.utils
835+
.parseUnits(precheckResult.result!.tokenInBalance as string, SWAP_TOKEN_IN_DECIMALS)
836+
.toBigInt(),
837+
).toBeGreaterThan(0n);
838+
expect(precheckResult.result!.currentTokenInAllowanceForSpender as string).toBe(
839+
EXPECTED_SWAP_AMOUNT,
840+
);
763841
expect(precheckResult.result!.spenderAddress).toBe(signedUniswapQuote.quote.to);
842+
expect(precheckResult.result!.requiredTokenInAllowance!).toBe(EXPECTED_SWAP_AMOUNT);
764843
});
765844

766845
it('should execute the Uniswap Swap Ability with the Agent Wallet PKP', async () => {

packages/apps/ability-uniswap-swap/src/generated/lit-action.js

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"ipfsCid": "QmRy1kzA8c1fQpSrudVA3FNEkZuKWhnZcPLhwKz421A3U2"
2+
"ipfsCid": "QmbF1E3QZiP2pHqbDMsrp167cx8JSa8Zz5CsWPWGQKeKah"
33
}

packages/apps/ability-uniswap-swap/src/lib/schemas.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { z } from 'zod';
22

33
export const abilityParamsSchema = z.object({
44
action: z
5-
.enum(['approve', 'swap', 'approveAndSwap'])
5+
.enum(['approve', 'swap'])
66
.describe(
7-
'Dictates whether to perform an ERC20 approval, a swap, or both using the signed Uniswap quote',
7+
'Dictates whether to perform an ERC20 approval or a swap using the signed Uniswap quote',
88
),
99
rpcUrlForUniswap: z
1010
.string()
@@ -72,6 +72,10 @@ export const precheckSuccessSchema = z.object({
7272
.string()
7373
.describe('The current allowance of the input token used for the swap'),
7474
spenderAddress: z.string().describe('The Uniswap router address that will be used for the swap'),
75+
requiredTokenInAllowance: z
76+
.string()
77+
.describe('The required allowance of the input token for the swap for the ERC20 spender')
78+
.optional(),
7579
});
7680

7781
export const precheckFailSchema = z.object({
@@ -120,4 +124,8 @@ export const executeSuccessSchema = z.object({
120124
.string()
121125
.describe('The current allowance of the input token used for the swap for the ERC20 spender')
122126
.optional(),
127+
requiredAllowance: z
128+
.string()
129+
.describe('The required allowance of the input token used for the swap for the ERC20 spender')
130+
.optional(),
123131
});

0 commit comments

Comments
 (0)