TypeScript

The Aave TypeScript SDK provides a type-safe, low-level API client for interacting with Aave Protocol V3. It offers a lightweight abstraction over the GraphQL API and is ideal for server-to-server communication.

Built with a modular, functional approach inspired by the viem client-actions architecture, the SDK structures functionality into distinct, reusable actions focused on specific protocol features like lending, borrowing, and vault management.

Getting Started

To get started, follow the steps below.

1

Install SDK

First, install the @aave/client package using your package manager of choice.

npm install @aave/client@latest

2

Create an AaveClient

Then, create an AaveClient instance that will be used to interact with the protocol.

client.ts
import { AaveClient } from "@aave/client";
export const client = AaveClient.create();

3

Start Building

That's it—you can now start using the @aave/client/actions to interact with the Aave Protocol.

example.ts
import { AaveClient, evmAddress, chainId } from "@aave/client";import { chains } from "@aave/client/actions";
import { client } from "./client.ts";
// Fetch all chains supported by Aaveconst result = await chains(client);
if (result.isOk()) {  console.log("Chains:", result.value);} else {  console.error("Error:", result.error);}

Action Return Types

Aave SDK actions use a functional approach to error handling. They return a ResultAsync<T, E>—a thenable object that represents one of two states:

  • Ok<T>: A successful result containing a value of type T

  • Err<E>: A failure containing an error of type E

When you await a ResultAsync<T, E>, it resolves to a Result<T, E>.

With a Result<T, E>, you can use the convenient isOk() and isErr() methods to check the outcome and narrow the type.
If you have a ResultAsync<T, E>, you must first await it to access those methods.

const result = await actionName(/* args */);
if (result.isOk()) {  console.log("Result:", result.value);} else {  console.error("Error:", result.error);}

This approach avoids reliance on try/catch blocks and promotes predictable, type-safe code by ensuring errors are handled explicitly.

See the NeverThrow documentation for more information. The NeverThrow library is re-exported for convenience.

Let’s explore the concept with a simple example function:

function parseNumber(input: string): Result<number, string> {  return isNaN(Number(input)) ? err("Invalid number") : ok(Number(input));}
const result = parseNumber("42");

You can chain multiple operations:

Chaining
function divide(a: number, b: number): Result<number, string> {  return b === 0 ? err("Division by zero") : ok(a / b);}
const result = parseNumber("42").andThen((num) => divide(num, 2));
if (result.isOk()) {  console.log("Result:", result.value);} else {  console.error("Error:", result.error);}

You can also provide a default value:

Default Value
const value = parseNumber("invalid").unwrapOr(0); // 0

NeverThrow also provides a ResultAsync type for handling asynchronous operations. This is a thenable object that can be awaited, and/or chained with other operations:

Async
const result = await ResultAsync.fromPromise(  fetch("https://api.example.com/data"))  .map((response) => response.json())  .mapErr((error) => `Failed to fetch data: ${error}`);
if (result.isOk()) {  console.log("Data:", result.value);} else {  console.error("Error:", result.error);}

Integrations

The Aave TypeScript SDK includes first-class support for viem, ethers v6, Privy, and thirdweb wallets.

Ensure you have viem package installed in your project.

npm install viem@2

Send Aave Transactions

Among the actions provided by the Aave TypeScript SDK, some are specifically designed to handle protocol interactions such as supplying, borrowing, withdrawing, repaying, and more.

There are two main types of transaction actions:

  • Simple transactions – single-step transactions that can be sent directly to the wallet.

  • Complex transactions – transactions that may require prior approvals before they can be executed.

To send transactions, follow the steps below.

1

Import the Helper

First, import the sendWith helper function for the wallet library of your choice.

Import the sendWith helper from the @aave/client/viem entry point.

Viem
import { sendWith } from "@aave/client/viem";

2

Send the Transaction

Then, use the sendWith helper to send the transaction.

To demonstrate how this helper works, assume we have a transaction action named foobar.

import { foobar } from "@aave/client/actions";import { client } from "./client";
const result = foobar(client);

To send the transaction, chain the action with sendWith for your wallet library, then use client.waitForTransaction to wait until it's confirmed and indexed by the Aave API.

import { foobar } from "@aave/client/actions";import { sendWith } from "@aave/client/viem";import { createWalletClient, http } from "viem";import { privateKeyToAccount } from "viem/accounts";
import { client } from "./client";
const wallet = createWalletClient({  account: privateKeyToAccount("<PRIVATE_KEY>"),  transport: http(),});
const result = foobar(client)  .andThen(sendWith(wallet))  .andThen(client.waitForTransaction);

3

Handle the Result

Finally, handle the result of the operation.

if (result.isErr()) {  switch (result.error.name) {    case "SigningError":      console.error(`Failed to sign the transaction: ${error.message}`);      break;
    case "TimeoutError":      console.error(`Transaction timed out: ${error.message}`);      break;
    case "TransactionError":      console.error(`Transaction failed: ${error.message}`);      break;
    case "UnexpectedError":      console.error(error.message);      break;  }} else {  console.log("Transaction sent with hash:", result.value);}

That'it—you've sent the transaction.

Sign ERC-20 Permits

Some operations support permit functionality using EIP-2612 signatures to authorize ERC-20 token transfers within the same transaction.

To use permits, you'll need both the permitTypeData action to generate the permit data and a signERC20PermitWith helper specific to your wallet library.

Import the signERC20PermitWith helper from the @aave/client/viem entry point.

Viem
import { signERC20PermitWith } from "@aave/client/viem";

Use permitTypeData to generate the permit data, then chain it with your wallet-specific signERC20PermitWith helper:

import { permitTypeData } from "@aave/client/actions";import { signERC20PermitWith } from "@aave/client/viem";import { createWalletClient, http } from "viem";import { privateKeyToAccount } from "viem/accounts";
import { client } from "./client";
const wallet = createWalletClient({  account: privateKeyToAccount("<PRIVATE_KEY>"),  transport: http(),});
const result = await permitTypeData(client, {  // permit parameters}).andThen(signERC20PermitWith(wallet));

For complete permit parameter examples, refer to the specific operation documentation.

Next Steps