> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pods.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Notus Integration

> Account abstraction with Notus smart wallets and Pods strategy bytecodes

Use [Notus](https://notus.team/) smart wallets together with Pods so users run yield strategy flows as ERC-4337 user operations: Pods returns the exact calldata sequence for a strategy action; Notus wraps that sequence in a custom user operation, collects an EOA signature, and submits execution asynchronously.

This guide follows the same Pods patterns as the other external integrations (list strategies, request `/bytecode`), then adds the Notus-specific steps: resolve or register the smart wallet address, create a custom user operation from the bytecode list, sign `userOperationHash`, and call execute.

***

## Resources

| Resource                                                                                                                                 | Description                                       |
| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| [Pods Docs](https://docs.pods.finance/)                                                                                                  | Strategy listing and bytecode generation          |
| [Register Smart Wallet](https://docs.notus.team/docs/api-reference/smart-wallets/RegisterWalletController_handle_v1)                     | Notus `POST /api/v1/wallets/register`             |
| [Get Smart Wallet](https://docs.notus.team/docs/api-reference/smart-wallets/GetAccountAbstractionAddressController_handle_v1)            | Notus `GET /api/v1/wallets/address`               |
| [Create Custom User Operation](https://docs.notus.team/docs/api-reference/user-operations/CreateCustomUserOperationController_create_v1) | Notus `POST /api/v1/crypto/custom-user-operation` |
| [Execute User Operation](https://docs.notus.team/docs/api-reference/user-operations/ExecuteUserOperationController_executeUserOp_v1)     | Notus `POST /api/v1/crypto/execute-user-op`       |

***

## Prerequisites

Configure your backend or secure client with:

* **Pods**: base URL and `x-api-key` (same as other integrations).
* **Notus**: `X-Api-Key` header on every Notus request, plus the **factory** address and **salt** your project uses so the counterfactual smart wallet is deterministic for each EOA.

Notus uses `https://api.notus.team/api/v1` as the API prefix (combine with the paths below).

<Warning>
  Never expose the Notus API key or Pods API key in public client bundles
  unless you accept the risk of key abuse. Prefer a backend that proxies these
  calls.
</Warning>

***

## Reference client

The snippets below use `notusPost`, `getSmartWalletFromNotus`, `createSmartWalletFromNotus`, and `signRawHash` as shorthand. They are **not** part of Pods — implement them in your app (for example with `axios` and [viem](https://viem.sh/) `WalletClient`), following the same HTTP paths and headers as a typical Notus integration.

The structure below mirrors a small OOP-style client adapted from common Notus + viem setups (request builder, `X-Api-Key`, `factory` / `salt`, raw-hash signing for `userOperationHash`):

```typescript theme={null}
// Pseudocode — illustrative only; tighten types, validation, and errors for production.
import type { WalletClient } from "viem";

type NotusClientConfig = {
  /** e.g. https://api.notus.team/api/v1 — include `/api/v1` if your paths omit it */
  baseUrl: string;
  apiKey: string;
  factory: string;
  salt: string;
};

type NotusWalletDto = {
  wallet?: {
    accountAbstraction?: string;
    registeredAt?: string | null;
  };
};

export class NotusIntegrationClient {
  #cfg: NotusClientConfig;

  constructor(cfg: NotusClientConfig) {
    this.#cfg = cfg;
  }

  /** POST helper used by this guide for `/crypto/*` and `/wallets/register`. */
  async notusPost<T>(path: string, body: object): Promise<T> {
    const url = `${this.#cfg.baseUrl.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
    const res = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Api-Key": this.#cfg.apiKey,
      },
      body: JSON.stringify(body),
    });
    if (!res.ok) throw new Error(await res.text());
    return res.json() as Promise<T>;
  }

  /** GET `/wallets/address` — returns smart wallet address only when already registered. */
  async getSmartWalletFromNotus({
    address,
  }: {
    address: string;
  }): Promise<{ smartWallet: string } | null> {
    const q = new URLSearchParams({
      externallyOwnedAccount: address,
      factory: this.#cfg.factory,
      salt: String(this.#cfg.salt),
    });
    const url = `${this.#cfg.baseUrl.replace(/\/$/, "")}/wallets/address?${q}`;
    const res = await fetch(url, {
      headers: { "X-Api-Key": this.#cfg.apiKey },
    });
    if (!res.ok) return null;
    const data = (await res.json()) as NotusWalletDto;
    const w = data.wallet;
    if (w?.accountAbstraction && w?.registeredAt)
      return { smartWallet: w.accountAbstraction };
    return null;
  }

  /** POST `/wallets/register` — links factory + salt wallet to your Notus project. */
  async createSmartWalletFromNotus({
    address,
  }: {
    address: string;
  }): Promise<{ smartWallet: string } | null> {
    const data = await this.notusPost<NotusWalletDto>("/wallets/register", {
      externallyOwnedAccount: address,
      factory: this.#cfg.factory,
      salt: this.#cfg.salt,
    });
    const w = data.wallet;
    if (w?.accountAbstraction && w?.registeredAt)
      return { smartWallet: w.accountAbstraction };
    return null;
  }

  /**
   * Sign `userOperationHash` as raw bytes (per Notus execute flow).
   * Same idea as viem: signMessage({ message: { raw: hash } }).
   */
  async signRawHash(
    walletClient: WalletClient,
    userOperationHash: `0x${string}`,
  ): Promise<`0x${string}`> {
    return walletClient.signMessage({
      account: walletClient.account!,
      message: { raw: userOperationHash },
    });
  }
}
```

You can extend this class with `buildCustomUserOperation` / `sendUserOperation` methods that call `notusPost("/crypto/custom-user-operation", …)` and `notusPost("/crypto/execute-user-op", …)` when you want a single place for all Notus traffic.

***

## Integration pattern

### 1. Resolve the smart wallet (get or register)

The EOA is the address that will sign user operations. The **smart wallet** (account abstraction address) is derived from the EOA, factory, and salt.

Recommended flow:

1. **GET** `/wallets/address?externallyOwnedAccount=...&factory=...&salt=...` — [Get Smart Wallet](https://docs.notus.team/docs/api-reference/smart-wallets/GetAccountAbstractionAddressController_handle_v1).
2. If the wallet is not registered yet (`registeredAt` missing or your product rules say so), **POST** `/wallets/register` with the same `externallyOwnedAccount`, `factory`, and optional `salt` — [Register Smart Wallet](https://docs.notus.team/docs/api-reference/smart-wallets/RegisterWalletController_handle_v1).

From the JSON response, use `wallet.accountAbstraction` (or `wallet.walletAddress` when they match) as the **`walletAddress`** for Pods bytecode requests and for Notus user operations.

Conceptually:

```typescript theme={null}
const smartWallet =
  (await getSmartWalletFromNotus({ address: EOA }))?.smartWallet ??
  (await createSmartWalletFromNotus({ address: EOA }))?.smartWallet;
```

Only treat the address as usable for your app when Notus indicates registration succeeded (per your checks on `registeredAt` and error payloads such as `FACTORY_NOT_SUPPORTED`).

***

### 2. Request strategy bytecodes from Pods

| Parameter | Value                                                                                                                                    |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Method    | GET                                                                                                                                      |
| Path      | `/strategies/:strategy-id/bytecode`                                                                                                      |
| Query     | `action` (required), `wallet` (required — use the **Notus smart wallet** address), `amount` (required, integer string in token decimals) |
| Headers   | `x-api-key: <Pods API key>`                                                                                                              |

The response includes `bytecode[]` with `to`, `data`, `value`, and `chainId` per step. All steps in one Notus custom user operation must run in order on a **single** `chainId`; use the first item's `chainId` when building the operation (if Pods returns multiple chains, split into separate Notus flows).

```typescript theme={null}
type PodsBytecodeItem = {
  to: string;
  data: string;
  value: string;
  chainId: string;
};
```

***

### 3. Create a custom user operation on Notus

**POST** `/crypto/custom-user-operation` — [Create Custom User Operation](https://docs.notus.team/docs/api-reference/user-operations/CreateCustomUserOperationController_create_v1).

Map each Pods bytecode to a Notus **operation**:

| Pods    | Notus body field                                        |
| ------- | ------------------------------------------------------- |
| `to`    | `operations[].address`                                  |
| `data`  | `operations[].data`                                     |
| `value` | `operations[].value` (hex string; use `0x0` when empty) |

Also send:

* `chainId` — number (e.g. `137` for Polygon; see Notus docs for supported networks).
* `walletAddress` — the smart wallet address from step 1.
* `payGasFeeToken` — ERC-20 contract address used to pay gas (required in the current API; pick the token Notus should debit for fees on that chain).

<Note>
  For a **`lend`** action where the bytecode spends **the entire balance** of a
  token, you usually **cannot** use that same token for `payGasFeeToken`: Notus
  would try to pull fees from a balance already consumed by the lend flow, and
  the user operation can **revert**. Prefer paying gas with the strategy
  **`asset`** (or another token that still has leftover balance), not the
  **`underlyingAsset`** when that is what you are fully deploying. For
  **`withdraw`**, paying gas with the **`underlyingAsset`** is typically fine
  because the flow increases that balance rather than draining it for the core
  action.
</Note>

Example mapping:

```typescript theme={null}
const chainId = Number(bytecodes[0].chainId);
const operations = bytecodes.map(({ to, data, value }) => ({
  address: to,
  data,
  value: value ?? "0x0",
}));

const { userOperation } = await notusPost("/crypto/custom-user-operation", {
  chainId,
  operations,
  walletAddress: smartWallet,
  payGasFeeToken: gasTokenAddress,
});
```

The response includes `userOperation.userOperationHash` (and related fields). If Notus returns a **revert reason** preview, fix balances, allowances, or ordering before executing.

***

### 4. Sign and execute the user operation

1. **Sign** the `userOperationHash` with the **EOA** that owns the smart wallet (raw message signature). With viem, sign the hash as raw bytes (not a human-readable EIP-191 message unless Notus specifies otherwise). Notus documents that **`authorization`** may be required on the first transaction for an EIP-7702 wallet: follow their note on viem `signAuthorization()` vs serializing the signature for the execute endpoint — [Execute User Operation](https://docs.notus.team/docs/api-reference/user-operations/ExecuteUserOperationController_executeUserOp_v1).

2. **POST** `/crypto/execute-user-op` with:
   * `userOperationHash`
   * `signature`
   * `authorization` — only when required for that wallet / first operation

Execution is **asynchronous** (HTTP 202). Use Notus history or webhooks to track inclusion, as described in their API reference.

```typescript theme={null}
const signature = await signRawHash(
  walletClient,
  userOperation.userOperationHash,
);
await notusPost("/crypto/execute-user-op", {
  userOperationHash: userOperation.userOperationHash,
  signature,
  // authorization: '0x...' // when required
});
```

***

## Operational notes

* **Single chain per custom user operation**: Notus runs `operations` in order on one `chainId`. Multi-chain Pods flows (`crossChain.isCrossChain`) need a different orchestration than a single custom user operation.
* **Paymaster / gas token**: Ensure `payGasFeeToken` and balances match Notus expectations for your project tier (errors such as `UNAVAILABLE_COMPUTE_UNITS` are documented on their side).
* **Smart wallet deployment**: If the account is not yet deployed on the target chain, resolution or execution may fail until deployment rules for your factory are satisfied (see Notus factory compatibility and your dashboard settings).

***

## Next steps

<CardGroup cols={2}>
  <Card title="Privy Integration" icon="key" href="/external-integrations/privy-integration">
    Bytecode flow with embedded smart wallets
  </Card>

  <Card title="Dynamic Integration" icon="link" href="/external-integrations/dynamic-integration">
    Wallet auth and Pods API usage
  </Card>

  <Card title="Deposit to Yield" icon="arrow-up-right" href="/guides/yield/deposit">
    End-user deposit guide
  </Card>

  <Card title="Fireblocks Integration" icon="vault" href="/external-integrations/fireblocks-integration">
    Custody integration
  </Card>
</CardGroup>
