Skip to content

Commit b504b5c

Browse files
authored
feat: add borrow action command (#353)
1 parent 5277e51 commit b504b5c

4 files changed

Lines changed: 140 additions & 0 deletions

File tree

.changeset/tasty-regions-tell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@aave/cli": patch
3+
---
4+
5+
**feat:** add borrow action command

packages/cli/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
"commands": "./dist/commands",
5454
"topicSeparator": " ",
5555
"topics": {
56+
"action": {
57+
"description": "Actions to perform in the Aave v4 protocol"
58+
},
5659
"hubs": {
5760
"description": "List available liquidity hubs"
5861
},
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import {
2+
type BorrowRequest,
3+
bigDecimal,
4+
evmAddress,
5+
InvariantError,
6+
invariant,
7+
ok,
8+
type Reserve,
9+
ResultAsync,
10+
reserveId,
11+
type SendWithError,
12+
type TimeoutError,
13+
type TransactionReceipt,
14+
type UnexpectedError,
15+
} from '@aave/client';
16+
import { borrow, reserve } from '@aave/client/actions';
17+
import { sendWith, toViemChain } from '@aave/client/viem';
18+
import { Flags } from '@oclif/core';
19+
import { createWalletClient, http } from 'viem';
20+
import { privateKeyToAccount } from 'viem/accounts';
21+
22+
import * as common from '../../common.js';
23+
24+
export default class ActionBorrow extends common.V4Command {
25+
static override description = 'Borrow ERC20 tokens from a reserve';
26+
27+
static override flags = {
28+
'reserve-id': Flags.string({
29+
required: true,
30+
description: 'Reserve ID of the reserve to borrow from',
31+
}),
32+
amount: Flags.string({
33+
required: true,
34+
description: 'Amount of the token to borrow',
35+
}),
36+
'private-key': common.privateKey({
37+
required: false,
38+
}),
39+
};
40+
41+
private getBorrowRequest(): ResultAsync<
42+
{
43+
request: BorrowRequest;
44+
reserve: Reserve;
45+
amount: string;
46+
privateKey: `0x${string}`;
47+
},
48+
InvariantError | UnexpectedError
49+
> {
50+
return ResultAsync.fromPromise(
51+
this.parse(ActionBorrow),
52+
(error) => new InvariantError(String(error)),
53+
).andThen(({ flags }) => {
54+
const privateKey = (flags['private-key'] ??
55+
process.env.PRIVATE_KEY) as `0x${string}`;
56+
invariant(
57+
privateKey,
58+
'Provide --private-key or PRIVATE_KEY environment variable',
59+
);
60+
61+
const parsedReserveId = reserveId(flags['reserve-id']);
62+
const amount = flags.amount.trim();
63+
64+
invariant(amount.length > 0, 'Amount cannot be empty');
65+
66+
const account = privateKeyToAccount(privateKey);
67+
const sender = evmAddress(account.address);
68+
69+
return reserve(this.client, {
70+
query: { reserveId: parsedReserveId },
71+
user: sender,
72+
}).andThen((reserveData) => {
73+
invariant(reserveData, `Reserve not found: ${flags['reserve-id']}`);
74+
75+
return ok({
76+
request: {
77+
reserve: reserveData.id,
78+
amount: {
79+
erc20: {
80+
value: bigDecimal(amount),
81+
},
82+
},
83+
sender,
84+
},
85+
reserve: reserveData,
86+
amount,
87+
privateKey,
88+
});
89+
});
90+
});
91+
}
92+
93+
async run(): Promise<
94+
TransactionReceipt | InvariantError | SendWithError | TimeoutError
95+
> {
96+
const result = await this.getBorrowRequest().andThen(
97+
({ request, reserve, amount, privateKey }) => {
98+
const wallet = createWalletClient({
99+
account: privateKeyToAccount(privateKey),
100+
chain: toViemChain(reserve.chain),
101+
transport: http(reserve.chain.rpcUrl),
102+
});
103+
104+
return borrow(this.client, request)
105+
.andThen(sendWith(wallet))
106+
.andTee((txResult) =>
107+
this.log(`Borrow transaction sent with hash: ${txResult.txHash}`),
108+
)
109+
.andThen(this.client.waitForTransaction)
110+
.map((txResult) => {
111+
this.display([
112+
['Reserve ID', reserve.id],
113+
['Amount', `${amount} - ${reserve.asset.underlying.info.symbol}`],
114+
['Transaction Hash', txResult.txHash],
115+
['Url', `${reserve.chain.explorerUrl}/tx/${txResult.txHash}`],
116+
]);
117+
118+
return txResult;
119+
});
120+
},
121+
);
122+
123+
if (result.isErr()) {
124+
this.error(result.error);
125+
}
126+
127+
return result.value;
128+
}
129+
}

packages/cli/src/commands/action/supply.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ export default class ActionSupply extends common.V4Command {
113113

114114
return supply(this.client, request)
115115
.andThen(sendWith(wallet))
116+
.andTee((txResult) =>
117+
this.log(`Supply transaction sent with hash: ${txResult.txHash}`),
118+
)
116119
.andThen(this.client.waitForTransaction)
117120
.map((txResult) => {
118121
this.display([

0 commit comments

Comments
 (0)