Skip to content

Commit 116046a

Browse files
committed
Merge branch 'main' into juan/swap-sign-action-hook
2 parents df52fd7 + e686d24 commit 116046a

24 files changed

+701
-149
lines changed

β€Žexamples/supply/src/SupplyForm.tsxβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { useSendTransaction } from '@aave/react/viem';
21
import {
32
bigDecimal,
43
evmAddress,
54
type Reserve,
65
useSupply,
76
} from '@aave/react-next';
7+
import { useSendTransaction } from '@aave/react-next/viem';
88
import { useState } from 'react';
99
import type { WalletClient } from 'viem';
1010

β€Žpackages/client/src/actions/swap.tsβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { type CurrencyQueryOptions, DEFAULT_QUERY_OPTIONS } from '../options';
4848
* sell: { erc20: evmAddress('0x6B175474E...') },
4949
* amount: bigDecimal('1000'),
5050
* kind: SwapKind.SELL,
51+
* from: evmAddress('0x742d35cc...'),
5152
* });
5253
* ```
5354
*

β€Žpackages/client/src/actions/transactions.tsβ€Ž

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,20 @@ export function borrow(
7171
*
7272
* ```ts
7373
* const result = await supply(client, {
74+
* reserve: {
75+
* reserveId: "1234567890",
76+
* spoke: evmAddress('0x8787…'),
77+
* chainId: chainId(1),
78+
* },
7479
* amount: {
7580
* erc20: {
7681
* value: bigDecimal('1000'),
7782
* },
7883
* },
84+
* enableCollateral: true, // Optional, defaults to true
7985
* sender: evmAddress('0x9abc…'),
80-
* reserve: {
81-
* spoke: evmAddress('0x87870bca…'),
82-
* reserveId: reserveId(1),
83-
* chainId: chainId(1),
84-
* },
85-
* }).andThen(sendWith(wallet)).andThen(client.waitForTransaction);
86+
* // onBehalfOf: evmAddress('0xdef0…'), // Optional, if supplying on behalf of another user
87+
* });
8688
*
8789
* if (result.isErr()) {
8890
* // Handle error, e.g. insufficient balance, signing error, etc.

β€Žpackages/client/src/test-utils.tsβ€Ž

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,12 @@ export function fundNativeAddress(
137137
}
138138

139139
export function fundErc20Address(
140-
tokenAddress: EvmAddress,
141140
address: EvmAddress,
142-
amount: BigDecimal,
143-
decimals = 18,
141+
token: {
142+
address: EvmAddress;
143+
amount: BigDecimal;
144+
decimals?: number;
145+
},
144146
): ResultAsync<string, UnexpectedError> {
145147
const publicClient = createPublicClient({
146148
chain: {
@@ -156,14 +158,14 @@ export function fundErc20Address(
156158
});
157159

158160
// Convert amount to the smallest unit (e.g., wei for 18 decimals)
159-
const amountInSmallestUnit = parseUnits(amount, decimals);
161+
const amountInSmallestUnit = parseUnits(token.amount, token.decimals ?? 18);
160162
const amountHex = `0x${amountInSmallestUnit.toString(16)}`;
161163

162164
return ResultAsync.fromPromise(
163165
publicClient
164166
.request<TSetErc20BalanceRpc>({
165167
method: 'tenderly_setErc20Balance',
166-
params: [tokenAddress, address, amountHex],
168+
params: [token.address, address, amountHex],
167169
})
168170
.then(async (res) => {
169171
await wait(500); // Temporal fix to avoid tenderly issues with the balance not being set
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { assertOk, bigDecimal, evmAddress } from '@aave/client-next';
2+
import { borrow, userBorrows } from '@aave/client-next/actions';
3+
import {
4+
client,
5+
createNewWallet,
6+
ETHEREUM_USDC_ADDRESS,
7+
fundErc20Address,
8+
} from '@aave/client-next/test-utils';
9+
import { sendWith } from '@aave/client-next/viem';
10+
import type { Reserve } from '@aave/graphql-next';
11+
import { beforeAll, describe, expect, it } from 'vitest';
12+
import { assertSingleElementArray } from '../test-utils';
13+
import { supplyToRandomERC20Reserve } from './helper';
14+
15+
describe('Aave V4 Borrow Scenarios', () => {
16+
describe('Given a user with a supply position as collateral', () => {
17+
describe('When the user borrows an ERC20 asset', () => {
18+
const user = createNewWallet();
19+
let reserve: Reserve;
20+
21+
beforeAll(async () => {
22+
const setup = await fundErc20Address(
23+
evmAddress(user.account!.address),
24+
{
25+
address: ETHEREUM_USDC_ADDRESS,
26+
amount: bigDecimal('100'),
27+
decimals: 6,
28+
},
29+
).andThen(() =>
30+
supplyToRandomERC20Reserve(client, user, ETHEREUM_USDC_ADDRESS),
31+
);
32+
33+
assertOk(setup);
34+
reserve = setup.value;
35+
});
36+
37+
it(`Then the user's borrow positions are updated`, async () => {
38+
const amountToBorrow = bigDecimal('50');
39+
40+
const result = await borrow(client, {
41+
sender: evmAddress(user.account!.address),
42+
reserve: {
43+
spoke: reserve.spoke.address,
44+
reserveId: reserve.id,
45+
chainId: reserve.chain.chainId,
46+
},
47+
amount: {
48+
erc20: {
49+
value: amountToBorrow,
50+
},
51+
},
52+
})
53+
.andThen(sendWith(user))
54+
.andThen(client.waitForTransaction)
55+
.andThen(() =>
56+
userBorrows(client, {
57+
query: {
58+
userSpoke: {
59+
spoke: {
60+
address: reserve.spoke.address,
61+
chainId: reserve.chain.chainId,
62+
},
63+
user: evmAddress(user.account!.address),
64+
},
65+
},
66+
}),
67+
);
68+
69+
assertOk(result);
70+
assertSingleElementArray(result.value);
71+
// BUG: The amount is slightly different from the total borrow amount
72+
expect(result.value[0].amount.value.formatted).toBeBigDecimalCloseTo(
73+
amountToBorrow,
74+
4,
75+
);
76+
expect(result.value[0].amount.isWrappedNative).toBe(false);
77+
});
78+
});
79+
80+
describe('When the user borrows from a reserve that supports native borrowing', () => {
81+
it.todo(`Then the user's borrow positions are updated`);
82+
});
83+
});
84+
});
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import type { AaveClient, Reserve, SupplyRequest } from '@aave/client-next';
2+
import {
3+
bigDecimal,
4+
chainId,
5+
type EvmAddress,
6+
evmAddress,
7+
invariant,
8+
ReservesRequestFilter,
9+
type ResultAsync,
10+
type TxHash,
11+
} from '@aave/client-next';
12+
import { reserves, supply } from '@aave/client-next/actions';
13+
import { sendWith } from '@aave/client-next/viem';
14+
import type { WalletClient } from 'viem';
15+
16+
export function supplyToReserve(
17+
client: AaveClient,
18+
request: SupplyRequest,
19+
user: WalletClient,
20+
): ResultAsync<TxHash, Error> {
21+
return supply(client, request)
22+
.andThen(sendWith(user))
23+
.andThen(client.waitForTransaction);
24+
}
25+
26+
export function findReserveToSupply(
27+
client: AaveClient,
28+
token: EvmAddress,
29+
): ResultAsync<Reserve, Error> {
30+
return reserves(client, {
31+
query: {
32+
tokens: [
33+
{
34+
chainId: chainId(1),
35+
address: token,
36+
},
37+
],
38+
},
39+
filter: ReservesRequestFilter.Supply,
40+
}).map((listReserves) => {
41+
invariant(
42+
listReserves.length > 0,
43+
`No reserves found for the token ${token}`,
44+
);
45+
const reserveToSupply = listReserves.find(
46+
(reserve) => reserve.canSupply === true,
47+
);
48+
invariant(
49+
reserveToSupply,
50+
`No reserve found to supply to for the token ${token}`,
51+
);
52+
return reserveToSupply;
53+
});
54+
}
55+
56+
export function supplyToRandomERC20Reserve(
57+
client: AaveClient,
58+
user: WalletClient,
59+
token: EvmAddress,
60+
amount = bigDecimal('100'),
61+
): ResultAsync<Reserve, Error> {
62+
return findReserveToSupply(client, token).andThen((reserve) =>
63+
supplyToReserve(
64+
client,
65+
{
66+
reserve: {
67+
reserveId: reserve.id,
68+
chainId: reserve.chain.chainId,
69+
spoke: reserve.spoke.address,
70+
},
71+
amount: { erc20: { value: amount } },
72+
sender: evmAddress(user.account!.address),
73+
},
74+
user,
75+
).map(() => reserve),
76+
);
77+
}

0 commit comments

Comments
Β (0)