Position Managers

Learn how position managers automate and delegate position management with user control.


Position managers are smart contracts users can authorize to manage their positions, enabling automated actions like supplying, withdrawing, borrowing, and repaying — while maintaining full user control. They unlock use cases such as:

  • Automated strategies — leverage management, yield optimization, and rebalancing

  • Vault protocols — external protocols that aggregate and manage user positions

  • Risk management — automated liquidation protection and position adjustments

  • Account abstraction — smart contract wallets managing DeFi positions

Position managers operate strictly within the spoke’s security model: they can only act for users who have explicitly authorized them. Users maintain full control and can revoke access at any time.

How They Work

  1. Registration — Position managers are registered with a spoke through governance before users can enable them.

  2. Authorization — Users explicitly enable selected managers to act on their behalf for a given position.

  3. Delegated Operations — Authorized managers can execute position operations on behalf of users.

  4. Revocation — Users can disable a manager at any time to revoke its access.

Each position manager is identified by its contract address and may include optional off-chain metadata, such as a name, for easier discovery.

Built-in Position Managers

Since registration requires governance approval, custom contracts cannot register themselves as position managers. Instead, Aave v4 ships with built-in position managers that collectively expose every operation a position manager can perform. They come in two groups: gateways and delegation managers.

Gateways

  • NativeTokenGateway — enables native token support (e.g., supply and borrow ETH)

  • SignatureGateway — enables ERC-20 Permits; see Supply and Repay for examples

Both contracts are automatically registered on every new spoke, and their addresses are available for convenience in the spoke's chain field:

interface Spoke {  __typename: "Spoke";  name: string;  address: EvmAddress;  chain: {    __typename: "Chain";    // …    nativeGateway: EvmAddress;    signatureGateway: EvmAddress;  };}

AaveKit automatically handles user authorization of these built-in position managers as part of each operation.

For example, when a user supplies native tokens (e.g., ETH):

  1. The SDK requests a signature authorizing the NativeTokenGateway to act on the user’s behalf for that position.

  2. The subsequent transaction sends ETH to the gateway.

  3. The gateway wraps the ETH into WETH and supplies it to the WETH reserve in the spoke on the user’s behalf.

This process happens automatically for other operations like borrowing, withdrawing, or repaying with native tokens, as well as when using ERC-20 permits to supply or repay. The SDK manages these flows transparently — users never need to interact with position managers directly.

Delegation Managers

Three additional managers let external contracts — vaults, automation bots, or your own flash loan contract — execute position operations on behalf of users without registering as position managers themselves:

  • GiverPositionManager — supplies and repays on behalf of a user (supplyOnBehalfOf, repayOnBehalfOf). The caller provides the funds, so no per-reserve allowance is required.

  • TakerPositionManager — withdraws and borrows on behalf of a user (withdrawOnBehalfOf, borrowOnBehalfOf), sending the funds to the caller. Gated by per-reserve allowances the user grants with approveWithdraw/approveBorrow, either onchain or via EIP-712 signatures.

  • ConfigPositionManager — updates position configuration on behalf of a user (e.g., setUsingAsCollateralOnBehalfOf). Gated by permissions the user delegates per action type.

To integrate a custom contract, route position operations through these managers instead of calling the spoke directly:

  1. The user authorizes the built-in manager on the spoke.

  2. For taker and config operations, the user grants your contract an allowance or permission on the manager.

  3. Your contract calls the manager's …OnBehalfOf methods to modify the user's position.

For example, a flash loan contract opening a leveraged position for a user:

import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";import { IGiverPositionManager } from "aave-v4/src/position-manager/interfaces/IGiverPositionManager.sol";import { ITakerPositionManager } from "aave-v4/src/position-manager/interfaces/ITakerPositionManager.sol";
// IGiverPositionManager giver = …// ITakerPositionManager taker = …// address collateralAsset = …
// Prerequisites (signed by the user, once per spoke):// - spoke.setUserPositionManager(address(giver), true, user)// - spoke.setUserPositionManager(address(taker), true, user)// - taker.approveBorrow(spoke, debtReserveId, address(this), borrowAmount)
function openLeveragedPosition(  address spoke,  uint256 collateralReserveId,  uint256 collateralAmount,  uint256 debtReserveId,  uint256 borrowAmount,  address user) external {  // Supply collateral to the user's position, funded by this contract  IERC20(collateralAsset).approve(address(giver), collateralAmount);  giver.supplyOnBehalfOf(spoke, collateralReserveId, collateralAmount, user);
  // Borrow against it, consuming the allowance granted by the user  taker.borrowOnBehalfOf(spoke, debtReserveId, borrowAmount, user);}

The production deployments on Ethereum are:

The contracts live in aave-v4/src/position-manager, and deployed addresses are listed under AaveV4EthereumPositionManagers in the Aave Address Book.

Available Position Managers

Get all available position managers for a specific spoke.

Use the paginated useSpokePositionManagers hook to fetch position managers for a spoke.

import {  type SpokePositionManagersRequest,  useSpokePositionManagers,} from "@aave/react";
function SpokePositionManagersList({  request,}: {  request: SpokePositionManagersRequest;}) {  const { data, loading, error } = useSpokePositionManagers(request);
  if (loading) return <div>Loading…</div>;
  if (error) return <div>Error: {error.message}</div>;
  // data: PaginatedSpokePositionManagerResult  return (    <div>      <h3>Position Managers</h3>      {data.items.map((manager) => (        <div key={manager.address}>          <h4>{manager.name}</h4>          <p>Address: {manager.address}</p>          <p>Status: {manager.active ? "Active" : "Inactive"}</p>        </div>      ))}    </div>  );}

See below some examples of how to use the hook.

const { data, loading, error } = useSpokePositionManagers({  spoke: spoke.id,});

User's Position Managers

Get all position managers that a specific user has enabled within a spoke.

Use the paginated useSpokeUserPositionManagers hook to fetch position managers enabled by a user.

import {  type EvmAddress,  type SpokeId,  useSpokeUserPositionManagers,} from "@aave/react";
function UserPositionManagersList({  spoke,  user,}: {  spoke: SpokeId;  user: EvmAddress;}) {  const { data, loading, error } = useSpokeUserPositionManagers({    spoke,    user,  });
  if (loading) return <div>Loading…</div>;
  if (error) return <div>Error: {error.message}</div>;
  // data: PaginatedSpokeUserPositionManagerResult  return (    <div>      <h3>User Position Managers</h3>      {data.items.map((manager) => (        <div key={manager.address}>          <h4>{manager.name}</h4>          <p>Address: {manager.address}</p>          <p>Approved: {manager.approvedOn.toLocaleDateString()}</p>          <p>Status: {manager.active ? "Approved" : "Not Approved"}</p>        </div>      ))}    </div>  );}

Authorize Position Manager

Authorize or revoke a position manager to act on behalf of a user within a spoke.

To enable or disable a position manager with AaveKit React, follow these steps.

1

Configure Wallet Integration

First, instantiate the useSendTransaction hook for the wallet library of your choice.

import { useWalletClient } from "wagmi";import { useSendTransaction } from "@aave/react/viem";
// …
const { data: wallet } = useWalletClient();const [sendTransaction] = useSendTransaction(wallet);

2

Define the Position Manager Flow

Use the useSetSpokeUserPositionManager hook to prepare the transaction request.

import { useSetSpokeUserPositionManager } from "@aave/react";
const [setPositionManager, { loading, error }] = useSetSpokeUserPositionManager(  (transaction) => sendTransaction(transaction),);

3

Execute the Transaction

Then, enable or disable the position manager.

import { type EvmAddress, type SpokeId } from "@aave/react";
const execute = async (  spoke: SpokeId,  manager: EvmAddress,  user: EvmAddress,) => {  const result = await setPositionManager({    spoke,    manager,    approve: true, // true to enable, false to disable    user,  });};
// …

4

Handle the Result

Finally, handle the result.

const execute = async (/* … */) => {  const result = await setPositionManager(/* … */);
  if (result.isErr()) {    switch (result.error.name) {      case "CancelError":        // The user cancelled the operation        return;
      case "SigningError":        console.error(          `Failed to sign the transaction: ${result.error.message}`,        );        break;
      case "TimeoutError":        console.error(`Transaction timed out: ${result.error.message}`);        break;
      case "TransactionError":        console.error(`Transaction failed: ${result.error.message}`);        break;
      case "UnexpectedError":        console.error(result.error.message);        break;    }    return;  }
  console.log("Position manager enabled with hash:", result.value.txHash);};