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

# Sign and Broadcast Transaction

> To complete any action using the DeFi API, sign the final unsigned transaction and broadcast it to the network.

## 1. Prepare unsigned transaction

Obtain the unsigned transaction object either in the `encoded` or `json` format provided by the [Craft Transaction Step](/reference/craft-transaction-step) endpoint.

## 2. Sign transaction

Since the DeFi API flow is determined by the individual schema depending on the action type, protocol, and strategy, construction of multiple transactions may be required. As a result, there are two types of unsigned transaction objects provided by the DeFi API — *intermediate* and *final*.

* *Intermediate transactions* are typically required to grant permissions to smart contracts involved in the integration process. These objects need a signature, as they must be included in the payload of the final transaction, but there is no need to broadcast such transactions to the network.

* *Final transactions*, on the other hand, require both signing and broadcasting. The steps at which these transactions are constructed have the word "final" in their name and are executed last according to the schema's order. Such objects must contain the data of all intermediate transactions in the payload.

Check your transaction object type and sign it using the examples of code provided below.

<Warning>
  **Note that for intermediate and final types, different signing libraries are used.**

  It is possible to sign the final transaction with the code for the intermediate one, but this will lead to the validation error on attempting to broadcast such a transaction to the network.
</Warning>

### For intermediate transactions

> NB: signing only, no broadcasting.

Use any of the code examples for the `base` network below.

<CodeGroup>
  ```javascript With private key theme={null}
  import {
    signTypedData,
    sign,
    serializeSignature,
    privateKeyToAccount,
  } from 'viem/accounts';
  import { createWalletClient, custom } from 'viem';
  import { base } from 'viem/chains';

  // Example API response shape
  // response = { json: { domain, types, primaryType, message }, encoded: "0x..." }

  // -----------------------------------------------------------------------------
  // 1. With private key — use response.json (typedData)
  // -----------------------------------------------------------------------------
  async function signResponseWithPrivateKey(privateKey, response) {
    const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
    const account = privateKeyToAccount(privateKeyHex);
    const { json: typedData } = response;

    return signTypedData({
      account,
      domain: typedData.domain,
      types: typedData.types,
      primaryType: typedData.primaryType,
      message: typedData.message,
    });
  }

  // -----------------------------------------------------------------------------
  // 2. With private key — use response.encoded (hash only)
  // -----------------------------------------------------------------------------
  async function signResponseHashWithPrivateKey(privateKey, response) {
    const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
    const hashHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`;

    const sig = await sign({
      privateKey: privateKeyHex,
      hash: hashHex,
    });
    return serializeSignature(sig);
  }
  ```

  ```javascript Via provider theme={null}
  import {
    signTypedData,
    sign,
    serializeSignature,
    privateKeyToAccount,
  } from 'viem/accounts';
  import { createWalletClient, custom } from 'viem';
  import { base } from 'viem/chains';

  // Example API response shape
  // response = { json: { domain, types, primaryType, message }, encoded: "0x..." }

  // -----------------------------------------------------------------------------
  // 3. Via provider (frontend) — use response.json (typedData), preferred
  // -----------------------------------------------------------------------------
  async function signResponseViaProvider(provider, response) {
    const walletClient = createWalletClient({
      chain: base,
      transport: custom(provider),
    });

    const [address] = await walletClient.getAddresses();
    if (!address) throw new Error('Wallet not connected');

    const { json: typedData } = response;
    return walletClient.signTypedData({
      account: address,
      domain: typedData.domain,
      types: typedData.types,
      primaryType: typedData.primaryType,
      message: typedData.message,
    });
  }

  // -----------------------------------------------------------------------------
  // 4. Via provider (frontend) — use response.encoded (hash only), eth_sign
  // -----------------------------------------------------------------------------
  async function signResponseHashViaProvider(provider, response, address) {
    const hashHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`;
    if (hashHex.length !== 66) throw new Error('Hash must be 32 bytes (66 hex chars with 0x)');

    return provider.request({
      method: 'eth_sign',
      params: [address, hashHex],
    });
  }
  ```
</CodeGroup>

### For final transactions

> NB: contain the word "final" in the step name; require signing and broadcasting.

Use any of the code examples for the `base` network below.

<CodeGroup>
  ```javascript With private key theme={null}
  import { createWalletClient, custom, http, parseTransaction } from 'viem';
  import { privateKeyToAccount, signTransaction } from 'viem/accounts';
  import { base } from 'viem/chains';

  // Example API response shape
  // response = { json: { domain, types, primaryType, message }, encoded: "0x..." }

  // -----------------------------------------------------------------------------
  // 1. With private key — use response.encoded (raw hex), then sign
  // -----------------------------------------------------------------------------
  async function signRawTransactionWithPrivateKey(privateKey, response, rpcUrl) {
    const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
    const account = privateKeyToAccount(privateKeyHex);
    const rawTxHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`;

    const transaction = parseTransaction(rawTxHex);
    const walletClient = createWalletClient({
      chain: base,
      account,
      transport: http(rpcUrl),
    });

    const request = await walletClient.prepareTransactionRequest({
      ...transaction,
      gas: transaction.gas,
    });

    return walletClient.signTransaction(request); // signed hex "0x02..."
  }

  // -----------------------------------------------------------------------------
  // 2. With private key — use response.json (transaction object), then sign
  // -----------------------------------------------------------------------------
  async function signTransactionObjectWithPrivateKey(privateKey, response, rpcUrl) {
    const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
    const account = privateKeyToAccount(privateKeyHex);
    const txRequest = response.json; // { to, data, value, gas, nonce, chainId, maxFeePerGas, maxPriorityFeePerGas, type }

    const walletClient = createWalletClient({
      chain: base,
      account,
      transport: http(rpcUrl),
    });

    const prepared = await walletClient.prepareTransactionRequest(txRequest);
    return walletClient.signTransaction(prepared);
  }

  // -----------------------------------------------------------------------------
  // 3. With private key — sign only (no RPC), from encoded, using viem/accounts
  // -----------------------------------------------------------------------------
  async function signRawTransactionWithPrivateKeyNoRpc(privateKey, response) {
    const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
    const rawTxHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`;
    const transaction = parseTransaction(rawTxHex);

    const signedHex = await signTransaction({
      privateKey: privateKeyHex,
      transaction,
    });
    return signedHex;
  }
  ```

  ```javascript Via provider theme={null}
  import { createWalletClient, custom, http, parseTransaction } from 'viem';
  import { privateKeyToAccount, signTransaction } from 'viem/accounts';
  import { base } from 'viem/chains';

  // Example API response shape
  // response = { json: { domain, types, primaryType, message }, encoded: "0x..." }

  // -----------------------------------------------------------------------------
  // 4. Via provider (frontend) — use response.encoded (raw hex), wallet signs
  // -----------------------------------------------------------------------------
  async function signRawTransactionViaProvider(provider, response) {
    const rawTxHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`;
    const transaction = parseTransaction(rawTxHex);

    const walletClient = createWalletClient({
      chain: base,
      transport: custom(provider),
    });

    const [address] = await walletClient.getAddresses();
    if (!address) throw new Error('Wallet not connected');

    const request = await walletClient.prepareTransactionRequest({
      ...transaction,
      gas: transaction.gas,
      account: address,
    });

    return walletClient.signTransaction(request);
  }

  // -----------------------------------------------------------------------------
  // 5. Via provider (frontend) — use response.json (transaction object)
  // -----------------------------------------------------------------------------
  async function signTransactionObjectViaProvider(provider, response) {
    const walletClient = createWalletClient({
      chain: base,
      transport: custom(provider),
    });

    const [address] = await walletClient.getAddresses();
    if (!address) throw new Error('Wallet not connected');

    const request = await walletClient.prepareTransactionRequest({
      ...response.json,
      account: address,
    });

    return walletClient.signTransaction(request);
  }
  ```
</CodeGroup>

## 3. Broadcast signed final transaction

> NB: required for final transaction type only.

To submit the final transaction to the network, send a POST request to [/api/defi/v1/transactions/steps/broadcast](/reference/broadcast-transaction).

Example request:

<CodeGroup>
  ```bash bash theme={null}
  curl --request POST \
    --url 'https://edge.p2p.org/api/defi/v1/transactions/steps/broadcast' \
    --header 'accept: application/json' \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{
        "chain": "base",
        "protocol": "morpho",
        "strategy": "Steakhouse USDC",
        "signedPayload": "0x…"
      }'
  ```
</CodeGroup>

* `signedTransaction` — signed final transaction in the hexadecimal format which needs to be broadcasted to the network.

Example response:

<CodeGroup>
  ```json JSON theme={null}
  {
    "error": null,
    "result": {
      "transactionHash": "0x1234567890abcdef..."
    },
    "meta": {
      "time": 0,
      "requestId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    }
  }
  ```
</CodeGroup>

* `transactionHash` — hash of the final transaction.
* `time` — request execution time in ms.
* `requestId` — unique identifier of the request.


## Related topics

- [DeFi API reference](/reference/get-protocols.md)
