---
title: "Update rebalancer configuration"
method: PUT
path: "/v1/config"
tags: ["User Configuration"]
---

# Update rebalancer configuration

`PUT /v1/config`

Creates or updates the rebalancing configuration for a specific wallet address and token. This endpoint allows users to set custom protocols and pools for automated rebalancing of their funds.

**Authentication**: Requires a valid JWT token. The wallet address in the JWT must match the `walletAddress` field in the request body.

**Configuration per Token**: Each request configures one specific token (USDC, MUSD, USDT, RLUSD, USDG, USDE, or PYUSD). To configure multiple tokens, make separate PUT requests for each token. After successfully setting a configuration for a token, the `hasSignedConfig` flag for that token will become `true` when retrieved via GET `/v1/config/{walletAddress}`.

**Configuration Structure**: The configuration specifies arrays of domain IDs, protocol IDs, and pool contract addresses. All three arrays must have the same length, with each index representing one pool configuration:
- `domainIds[0]`, `protocolIds[0]`, `poolAddresses[0]` = first pool
- `domainIds[1]`, `protocolIds[1]`, `poolAddresses[1]` = second pool
- And so on...

**Array Limits**: `domainIds`, `protocolIds`, and `poolAddresses` may each contain at most 200 entries. Additionally, the `(domainId, protocolId, poolAddress)` triple at each index must be unique across the request - duplicate entries are rejected.

**Domain IDs**:
- `11` = LINEA
- `6` = BASE
- `7` = POLYGON
- `3` = ARBITRUM
- `1` = AVALANCHE
- `2` = OPTIMISM
- `0` = ETHEREUM

**Protocol IDs**: Maps to supported protocols:
- `0` = Aave
- `1` = Morpho
- `2` = Euler

**Collateral Exposure**: Collateral exposure symbols for the pools in the configuration, must be taken from the pool catalog from GET /v1/pools, unique and not empty. If you want to disable the filter, pass null.
example: ["WETH", "cbBTC", "wstETH"]

Tvl and liquidity multiplier are optional and can be used to filter the pools in the configuration.
Example:
- requiredTvl: 5000000
- requiredLiquidityMultiplier: 10
This will filter the pools in the configuration to only include pools that have a TVL of at least $5M and a liquidity multiplier of at least 10.
Example:
- requiredTvl: 0
- requiredLiquidityMultiplier: 0
This will disable the filter and include all pools in the configuration.
During deposits, pools that do not comply with filters (exposure, liquidity, tvl) are ignored. Active balances are also monitored periodically and if a pool with a deposit no longer complies with the filters, rebalance immediately happens outside to first available pool with highest APY %

**Pool Contracts**: Each pool contract address must be a valid checksummed Ethereum address (EIP-55) corresponding to a pool on the specified protocol. Pool addresses can be obtained from the `/v1/pools` endpoint.

**Signature Requirement**: The `signature` field must be created using the following steps:

1. ABI-encode the following parameters in order:
  - `address` — `rebalancingManagerAddress` (from `GET /v1/config/{walletAddress}`)
  - `uint8[]` — `protocolIds` array
  - `address[]` — `poolAddresses` array
  - `uint32[]` — `domainIds` array
  - `uint256` — `spendingCapRaw` (use `"0"` if not set, can be set only for USDC, when setting this value, AAVE protocol on LINEA must be included in the configuration with a non-zero spending cap, otherwise the request will be rejected)

2. Compute `keccak256` of the ABI-encoded bytes.

3. Sign the resulting hash using `personal_sign` (i.e. `eth_sign` with the standard Ethereum prefix `\x19Ethereum Signed Message:\n32`). In viem this is `walletClient.signMessage({ account, message: { raw: hash } })`.

The signature must be 132 characters long (0x + 130 hex characters) and must be signed by the wallet address in the `walletAddress` field.

**Smart Contract Wallets / ERC-6492**: For contract wallet signatures, the backend validates the configuration signature on every chain contained within the `domainIds`. If the signature is rejected on any of those chains, the entire configuration is rejected. The error response will indicate which domain IDs were rejected: `"Contract wallets configs can only contain chains verified by supported signature. Found rejected domain IDs: <rejectedIds>"`.

**Include Rewards APY**: The optional `includeRewardsApy` boolean flag (default: `true`) determines whether reward APYs should be included in APY calculations during rebalancing decisions.

**Response**: Returns `201 Created` if this is the first configuration for the wallet, or `200 OK` if updating an existing configuration. The response includes the deployed rebalancer contract address and the list of configured tokens.

**Rate limiting**: 1 request per 6 seconds per client IP. Excess requests receive HTTP `429` with `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers (see `RateLimitExceeded` response).

## Request body

- CreateOrUpdateConfigRequest
  - `walletAddress` string, required — User's Ethereum wallet address (must be checksummed per EIP-55)
  - `token` 'USDC' | 'MUSD' | 'USDT' | 'RLUSD' | 'USDG' | 'USDE' | 'PYUSD', required — Token symbol to configure (must be uppercase)
  - `domainIds` integer[], required — Domain IDs - must match length and index values must be relevant to protocolIds and poolAddresses. Maximum 200 entries. The (domainId, protocolId, poolAddress) triple at each index must be unique - the same pool cannot be configured twice for the same domain.
  - `protocolIds` integer[], required — Protocol IDs (0=Aave, 1=Morpho, 2=Euler) - must match length and index values must be relevant to domainIds and poolAddresses. Maximum 200 entries.
  - `poolAddresses` string[], required — Pool contract addresses (must be checksummed per EIP-55) - must match length and index values must be relevant to domainIds and protocolIds. Maximum 200 entries. The (domainId, protocolId, poolAddress) triple at each index must be unique - the same pool cannot be included more than once for the same domain and protocol.
  - `signature` string, required — User signature for configuration
  - `includeRewardsApy` boolean — Include rewards in APY calculations
  - `requiredTvl` number — *(Recommended)* Minimum TVL (in USD) a pool must have before the rebalancer will route funds into it. It is recommended to set this to avoid routing funds into low-liquidity pools. Pass `0` only to explicitly disable this filter. Example: `5000000` = $5M minimum TVL.
  - `requiredLiquidityMultiplier` number — *(Recommended)* Minimum ratio of pool liquidity to the user's position size. It is recommended to set this as a safety guard against thin markets. Pass `0` only to explicitly disable this filter. Example: `10` = pool must hold at least 10× the user's position in available liquidity.
  - `spendingCapRaw` string — *(Optional)* Maximum amount in raw token units (unformatted integer) the rebalancer is allowed to move in a single transaction. Pass `"0"` for no cap. Example for USDC (6 decimals): `"1000000000"` = $1,000 cap.
  - `collateralExposure` string[], nullable — Collateral exposure symbols for the pools in the configuration, must be taken from the pool catalog from GET /v1/pools, unique and not empty. If you want to disable the filter, pass null.

## Response `200`

Configuration updated successfully

- CreateOrUpdateConfigResponse
  - `walletAddress` string, required — User's wallet address
  - `rebalancerAddress` string, required — Deployed rebalancer contract address
  - `configuredToken` string, required — Configured token symbol
  - `operation` 'CREATED' | 'UPDATED', required — Operation performed

## Other responses

- `201` — Configuration created successfully
- `400` — Validation error
- `401` — No authorization header provided
- `403` — User is not authorized to perform this action
- `409` — Conflict - config update not allowed
- `429` — Too many requests for this endpoint. Limits are enforced per client IP for the public API routes backed by `RebalancerResource` (and related resources using the same filter). Response headers (when throttled): - `Retry-After`: seconds to wait before retrying (matches the rate-limit window duration for that endpoint). - `X-RateLimit-Limit`: maximum requests allowed in the window (e.g. `1`). - `X-RateLimit-Remaining`: remaining requests in the window (`0` when throttled). - `X-RateLimit-Reset`: Unix timestamp (seconds) when the limit window resets. `internalCode` in the JSON body is `THROTTLE_PER_IP` for IP-scoped limits (other values may apply for different scopes in the backend).
- `500` — Internal server error

---

[API](https://skmtc.net/metalend/apis/metalend-rebalancing-api.md) · [All operations](https://skmtc.net/metalend/apis/metalend-rebalancing-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/metalend/metalend-rebalancing-api/versions/1b9900a9e91e/schema)
