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

# Fireblocks

> Integrate RebelFi with Fireblocks institutional custody

This guide shows how to sign and submit RebelFi transactions using [Fireblocks](https://www.fireblocks.com/) as your custody provider. Fireblocks is a common choice for exchanges, funds, and institutions that need policy-based signing and MPC key management.

<Info>
  For Fireblocks setup, API keys, and vault configuration, see the [Fireblocks Developer Docs](https://developers.fireblocks.com/).
</Info>

## How It Works

```
1. Plan operation        →  RebelFi returns unsigned EVM transaction(s)
2. Create Fireblocks tx  →  Pass calldata to Fireblocks CONTRACT_CALL
3. Fireblocks signs      →  MPC signing + policy engine approval
4. Poll for completion   →  Wait for Fireblocks to broadcast and confirm
5. Submit hash           →  Report the on-chain txHash back to RebelFi
```

RebelFi returns unsigned transactions with `to`, `data`, and gas parameters. You pass the `to` address and `data` to Fireblocks as a `CONTRACT_CALL`, and Fireblocks handles signing, gas estimation, and broadcasting.

## Prerequisites

* A Fireblocks workspace with API access
* A vault account with an EVM asset (e.g., `ETH_TEST5` for testnet, `ETH` for mainnet)
* The vault's deposit address registered as a wallet in RebelFi
* `fireblocks-sdk` installed: `npm install fireblocks-sdk`

## Vault-to-Wallet Mapping

Your Fireblocks vault account has a deposit address for each asset. This address is what you register with RebelFi:

```typescript theme={null}
import { FireblocksSDK } from 'fireblocks-sdk';

const fireblocks = new FireblocksSDK(privateKey, apiKey, apiBaseUrl);

// Get the vault's deposit address for an EVM asset
const addresses = await fireblocks.getDepositAddresses(vaultAccountId, assetId);
const walletAddress = addresses[0].address;

// Register this address with RebelFi
const wallet = await rebelfi.wallets.register({
  walletAddress,
  blockchain: 'ethereum', // or 'polygon', etc.
});
```

<Warning>
  The wallet address you register with RebelFi **must** match the Fireblocks vault's deposit address for that asset. If they don't match, transaction signing will fail.
</Warning>

## The Fireblocks Signer

The core of the integration is a function that takes RebelFi's unsigned transaction fields and submits them through Fireblocks:

```typescript theme={null}
import fs from 'node:fs';
import {
  FireblocksSDK,
  PeerType,
  TransactionOperation,
  TransactionStatus,
} from 'fireblocks-sdk';
import type { EvmTransactionFields } from '@rebelfi/sdk';

const POLL_INTERVAL_MS = 3_000;
const MAX_POLL_ATTEMPTS = 120; // 6 minutes max

const TERMINAL_STATUSES = new Set([
  TransactionStatus.COMPLETED,
  TransactionStatus.CONFIRMED,
  TransactionStatus.FAILED,
  TransactionStatus.CANCELLED,
  TransactionStatus.REJECTED,
  TransactionStatus.BLOCKED,
  TransactionStatus.TIMEOUT,
]);

const fireblocks = new FireblocksSDK(
  fs.readFileSync(process.env.FIREBLOCKS_SECRET_KEY_PATH, 'utf-8'),
  process.env.FIREBLOCKS_API_KEY,
  process.env.FIREBLOCKS_API_BASE_URL
);

async function signAndBroadcast(
  evmTx: EvmTransactionFields,
  vaultAccountId: string,
): Promise<{ txHash: string }> {
  // Create a CONTRACT_CALL transaction in Fireblocks
  const { id: fbTxId } = await fireblocks.createTransaction({
    operation: TransactionOperation.CONTRACT_CALL,
    assetId: process.env.FIREBLOCKS_ASSET_ID, // e.g., 'ETH' or 'ETH_TEST5'
    source: {
      type: PeerType.VAULT_ACCOUNT,
      id: vaultAccountId,
    },
    destination: {
      type: PeerType.ONE_TIME_ADDRESS,
      oneTimeAddress: { address: evmTx.to },
    },
    amount: '0',
    extraParameters: {
      contractCallData: evmTx.data,
    },
    note: 'RebelFi operation',
  });

  // Poll until Fireblocks signs and broadcasts
  for (let i = 0; i < MAX_POLL_ATTEMPTS; i++) {
    await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
    const txInfo = await fireblocks.getTransactionById(fbTxId);

    if (!TERMINAL_STATUSES.has(txInfo.status)) continue;

    if (
      txInfo.status === TransactionStatus.COMPLETED ||
      txInfo.status === TransactionStatus.CONFIRMED
    ) {
      return { txHash: txInfo.txHash };
    }

    throw new Error(
      `Fireblocks transaction ${fbTxId} ended with status: ${txInfo.status}`
    );
  }

  throw new Error(`Fireblocks transaction ${fbTxId} timed out`);
}
```

<Note>
  Fireblocks handles gas estimation and broadcasting. You pass `amount: '0'` because the contract call itself doesn't transfer native tokens — it interacts with an ERC-20 contract.
</Note>

## Supply Example

EVM supply operations produce **two transactions**: a token approval and the protocol deposit. Sign and submit them sequentially through Fireblocks:

```typescript theme={null}
import { RebelfiClient } from '@rebelfi/sdk';

const rebelfi = new RebelfiClient({ apiKey: process.env.REBELFI_API_KEY });

async function supply(
  walletId: number,
  strategyId: number,
  amount: string,
  tokenAddress: string,
  vaultAccountId: string,
) {
  // 1. Plan the supply — RebelFi returns unsigned transactions
  const operation = await rebelfi.operations.supply({
    walletId,
    strategyId,
    amount,
    tokenAddress,
  });
  console.log(`Operation ${operation.operationId} created`);

  // 2. Fetch the unsigned transactions
  const unsignedTxs = await rebelfi.operations.getUnsignedTransactions(
    operation.operationId,
  );
  console.log(`${unsignedTxs.length} transaction(s) to sign`);

  // 3. Sign and submit each transaction via Fireblocks
  for (const tx of unsignedTxs) {
    console.log(`Signing: ${tx.description}`);
    const { txHash } = await signAndBroadcast(
      tx.evmTransaction,
      vaultAccountId,
    );

    // 4. Report the hash back to RebelFi
    await rebelfi.transactions.submitHash({
      operationId: operation.operationId,
      txHash,
      transactionId: tx.attemptId,
    });
    console.log(`Submitted: ${tx.description} (${txHash})`);
  }

  // 5. Confirm final status
  const final = await rebelfi.operations.get(operation.operationId);
  console.log(`Final status: ${final.status}`);
  return final;
}
```

### What Happens Step by Step

<Steps>
  <Step title="Plan the supply">
    Call `operations.supply()` with your wallet, strategy, amount, and token address. RebelFi returns an operation with status `AWAITING_SIGNATURE`.
  </Step>

  <Step title="Fetch unsigned transactions">
    Call `operations.getUnsignedTransactions()` to get fresh unsigned transactions. For EVM supply, you'll get two:

    * **Transaction 1**: Token approval (`approve` call on the ERC-20 contract)
    * **Transaction 2**: Supply to protocol (deposit into the yield strategy)
  </Step>

  <Step title="Sign via Fireblocks">
    For each unsigned transaction, create a Fireblocks `CONTRACT_CALL` with the `to` address and `data` field. Fireblocks signs with MPC, applies your policy rules, and broadcasts on-chain.
  </Step>

  <Step title="Submit hash to RebelFi">
    Once Fireblocks confirms the transaction, take the `txHash` and submit it to RebelFi via `transactions.submitHash()`. Include the `transactionId` to identify which transaction in the operation you're submitting.
  </Step>

  <Step title="Confirm">
    RebelFi monitors the chain and updates the operation status to `CONFIRMED` once all transactions land.
  </Step>
</Steps>

## Unwind Example

Unwinding (withdrawing from a yield strategy) follows the same pattern:

```typescript theme={null}
async function unwind(
  walletId: number,
  strategyId: number,
  amount: string,
  vaultAccountId: string,
) {
  const operation = await rebelfi.operations.unwind({
    walletId,
    strategyId,
    amount,
  });

  const unsignedTxs = await rebelfi.operations.getUnsignedTransactions(
    operation.operationId,
  );

  for (const tx of unsignedTxs) {
    const { txHash } = await signAndBroadcast(
      tx.evmTransaction,
      vaultAccountId,
    );

    await rebelfi.transactions.submitHash({
      operationId: operation.operationId,
      txHash,
      transactionId: tx.attemptId,
    });
  }

  return rebelfi.operations.get(operation.operationId);
}
```

<Tip>
  For a full withdrawal, pass `fullWithdrawal: true` instead of `amount`:

  ```typescript theme={null}
  const operation = await rebelfi.operations.unwind({
    walletId,
    strategyId,
    fullWithdrawal: true,
  });
  ```
</Tip>

<Warning>
  For sandbox/testnet, use `https://sandbox-api.fireblocks.io` as the base URL and a testnet asset ID like `ETH_TEST5`.
</Warning>

## Troubleshooting

<AccordionGroup>
  <Accordion title="No deposit address found" icon="circle-exclamation">
    **Cause**: The EVM asset hasn't been added to your Fireblocks vault account.

    **Solution**: In the Fireblocks console, go to your vault account and add the asset (e.g., ETH, ETH\_TEST5). Then fetch the deposit address.
  </Accordion>

  <Accordion title="Transaction REJECTED or BLOCKED" icon="shield">
    **Cause**: Fireblocks policy engine rejected the transaction.

    **Solution**: Check your Transaction Authorization Policy (TAP) rules in the Fireblocks console. The `CONTRACT_CALL` to a `ONE_TIME_ADDRESS` may need explicit approval or a policy rule allowing it.
  </Accordion>

  <Accordion title="Transaction timed out" icon="clock">
    **Cause**: Fireblocks transaction took too long to reach a terminal status.

    **Solution**: Check the transaction status in the Fireblocks console. It may be pending human approval (if your policy requires it). Increase `MAX_POLL_ATTEMPTS` if your approval flow takes longer.
  </Accordion>

  <Accordion title="Wallet address mismatch" icon="key">
    **Cause**: The vault deposit address doesn't match the wallet registered with RebelFi.

    **Solution**: Use `fireblocks.getDepositAddresses(vaultAccountId, assetId)` to get the correct address, and ensure that's the address registered with RebelFi.
  </Accordion>
</AccordionGroup>

## Resources

* [Fireblocks Developer Docs](https://developers.fireblocks.com/)
* [Fireblocks SDK (npm)](https://www.npmjs.com/package/fireblocks-sdk)
* [RebelFi SDK Reference](/sdk/reference/client)
* [EVM Integration Guide](/sdk/evm-integration)
* [Wallet Profiles](/guides/wallet-profiles)
