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

# Contract calls

> Call a smart contract method from a vault account by encoding the calldata and sending a CONTRACT_CALL transaction through the Fireblocks API, SDKs or CLI.

A `CONTRACT_CALL` transaction asks Fireblocks to sign and broadcast a call to a method on an EVM smart contract, using calldata that you encode yourself. This page shows how to encode the calldata and send the transaction, and it assumes you have already initialised a `fireblocks` client as described in [Choose an interface](/transactions/create-transactions#choose-an-interface).

## Choose between a contract call and the Web3 Provider

Use a `CONTRACT_CALL` transaction when you want to call the Create Transaction endpoint directly from your own backend, with full control over the calldata and the destination. If you would rather keep using your existing development tools, such as Hardhat, Foundry or ethers.js, which send standard JSON-RPC requests, use the [EVM Web3 Provider](/reference/evm-web3-provider) for JavaScript-based tools or the [Local JSON-RPC](/reference/evm-local-json-rpc) for other languages. [Interact with smart contracts](/docs/interact-with-smart-contracts) compares the options in more detail.

## Encode the calldata

The `contractCallData` value is the hex-encoded ABI payload for the method you want to call. You can build it with any ABI library, and the following example uses ethers v6 to encode an ERC-20 `approve` call with placeholder values.

```typescript TypeScript theme={"system"}
import { Interface } from "ethers";

const erc20 = new Interface([
  "function approve(address spender, uint256 amount)",
]);

const contractCallData = erc20.encodeFunctionData("approve", [
  "<SPENDER_ADDRESS>",
  1000000n,
]);
```

## Send the contract call

The transaction uses the `CONTRACT_CALL` operation and passes the encoded calldata in `extraParameters.contractCallData`. The `destination` is the contract, which can be a one-time address or a contract wallet that you have added to your allowlist, and the `amount` is `"0"` for a non-payable method. The examples use the Sepolia testnet asset `ETH_TEST5`, and the SDK examples pass the calldata encoded in the previous step as `<ENCODED_CALLDATA_HEX>`.

<Tabs>
  <Tab title="SDK">
    <CodeGroup>
      ```typescript TypeScript theme={"system"}
      import { TransferPeerPathType, TransactionOperation } from "@fireblocks/ts-sdk";

      const response = await fireblocks.transactions.createTransaction({
        transactionRequest: {
          operation: TransactionOperation.ContractCall,
          assetId: "ETH_TEST5",
          amount: "0", // Conditional - Use "0" for non-payable methods, or the amount of native asset to send to a payable method
          source: {
            type: TransferPeerPathType.VaultAccount,
            id: "<SOURCE_VAULT_ACCOUNT_ID>",
          },
          destination: {
            type: TransferPeerPathType.OneTimeAddress, // Conditional - Use TransferPeerPathType.Contract with a contract wallet ID for an allowlisted contract
            oneTimeAddress: { address: "<CONTRACT_ADDRESS>" },
          },
          extraParameters: {
            contractCallData, // Required - Hex-encoded calldata from the previous step
          },
          note: "Approve spender", // Optional - Free-text note shown in your workspace, not sent to the blockchain
          externalTxId: "<YOUR_UNIQUE_ID>", // Optional - Your unique ID; Fireblocks rejects later requests with the same value
        },
      });
      console.log(response.data);
      ```

      ```python Python theme={"system"}
      from fireblocks.models.transaction_request import TransactionRequest
      from fireblocks.models.transaction_operation import TransactionOperation
      from fireblocks.models.source_transfer_peer_path import SourceTransferPeerPath
      from fireblocks.models.destination_transfer_peer_path import DestinationTransferPeerPath
      from fireblocks.models.transfer_peer_path_type import TransferPeerPathType
      from fireblocks.models.one_time_address import OneTimeAddress
      from fireblocks.models.extra_parameters import ExtraParameters
      from fireblocks.models.transaction_request_amount import TransactionRequestAmount

      transaction_request = TransactionRequest(
          operation=TransactionOperation.CONTRACT_CALL,
          asset_id="ETH_TEST5",
          amount=TransactionRequestAmount("0"),  # Conditional - Use "0" for non-payable methods, or the amount of native asset to send to a payable method
          source=SourceTransferPeerPath(
              type=TransferPeerPathType.VAULT_ACCOUNT,
              id="<SOURCE_VAULT_ACCOUNT_ID>",
          ),
          destination=DestinationTransferPeerPath(
              type=TransferPeerPathType.ONE_TIME_ADDRESS,  # Conditional - Use TransferPeerPathType.CONTRACT with a contract wallet ID for an allowlisted contract
              one_time_address=OneTimeAddress(address="<CONTRACT_ADDRESS>"),
          ),
          extra_parameters=ExtraParameters(
              contract_call_data="<ENCODED_CALLDATA_HEX>",  # Required - Hex-encoded calldata from the previous step
          ),
          note="Approve spender",  # Optional - Free-text note shown in your workspace, not sent to the blockchain
          external_tx_id="<YOUR_UNIQUE_ID>",  # Optional - Your unique ID; Fireblocks rejects later requests with the same value
      )

      future = fireblocks.transactions.create_transaction(
          transaction_request=transaction_request,
      )
      print(future.result().data)  # create_transaction returns a Future
      ```

      ```java Java theme={"system"}
      import com.fireblocks.sdk.model.*;

      TransactionRequest transactionRequest = new TransactionRequest()
          .operation(TransactionOperation.CONTRACT_CALL)
          .assetId("ETH_TEST5")
          .amount(new TransactionRequestAmount("0")) // Conditional - Use "0" for non-payable methods, or the amount of native asset to send to a payable method
          .source(new SourceTransferPeerPath()
              .type(TransferPeerPathType.VAULT_ACCOUNT)
              .id("<SOURCE_VAULT_ACCOUNT_ID>"))
          .destination(new DestinationTransferPeerPath()
              .type(TransferPeerPathType.ONE_TIME_ADDRESS) // Conditional - Use TransferPeerPathType.CONTRACT with a contract wallet ID for an allowlisted contract
              .oneTimeAddress(new OneTimeAddress().address("<CONTRACT_ADDRESS>")))
          .extraParameters(new ExtraParameters()
              .contractCallData("<ENCODED_CALLDATA_HEX>")) // Required - Hex-encoded calldata from the previous step
          .note("Approve spender") // Optional - Free-text note shown in your workspace, not sent to the blockchain
          .externalTxId("<YOUR_UNIQUE_ID>"); // Optional - Your unique ID; Fireblocks rejects later requests with the same value

      CreateTransactionResponse transaction = fireblocks.transactions()
          .createTransaction(
              transactionRequest,
              null, // Optional - xEndUserWalletId (UUID), only for end-user wallet operations
              null) // Optional - Not used for transactions; use externalTxId in the request body
          .get()
          .getData();
      System.out.println(transaction);
      ```
    </CodeGroup>
  </Tab>

  <Tab title="API">
    ```http theme={"system"}
    POST /v1/transactions
    ```

    ```json theme={"system"}
    {
      "operation": "CONTRACT_CALL",
      "assetId": "ETH_TEST5",
      "amount": "0",
      "source": { "type": "VAULT_ACCOUNT", "id": "<SOURCE_VAULT_ACCOUNT_ID>" },
      "destination": {
        "type": "ONE_TIME_ADDRESS",
        "oneTimeAddress": { "address": "<CONTRACT_ADDRESS>" }
      },
      "extraParameters": { "contractCallData": "<ENCODED_CALLDATA_HEX>" },
      "note": "Approve spender",
      "externalTxId": "<YOUR_UNIQUE_ID>"
    }
    ```

    Sign the request with a JWT; see [Signing a request](/reference/signing-a-request-jwt-structure).
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    # Preview the request without sending it
    fireblocks transactions create-transaction \
      --data '{"operation":"CONTRACT_CALL","assetId":"ETH_TEST5","amount":"0","source":{"type":"VAULT_ACCOUNT","id":"<SOURCE_VAULT_ACCOUNT_ID>"},"destination":{"type":"ONE_TIME_ADDRESS","oneTimeAddress":{"address":"<CONTRACT_ADDRESS>"}},"extraParameters":{"contractCallData":"<ENCODED_CALLDATA_HEX>"},"externalTxId":"<YOUR_UNIQUE_ID>"}' \
      --dry-run

    # Execute
    fireblocks transactions create-transaction \
      --data '{"operation":"CONTRACT_CALL","assetId":"ETH_TEST5","amount":"0","source":{"type":"VAULT_ACCOUNT","id":"<SOURCE_VAULT_ACCOUNT_ID>"},"destination":{"type":"ONE_TIME_ADDRESS","oneTimeAddress":{"address":"<CONTRACT_ADDRESS>"}},"extraParameters":{"contractCallData":"<ENCODED_CALLDATA_HEX>"},"externalTxId":"<YOUR_UNIQUE_ID>"}' \
      --no-confirm
    ```
  </Tab>
</Tabs>

The `contractCallData` field cannot be combined with `rawMessageData` or `inputsSelection` in the same request, and the full request schema is on the [Create Transaction](/reference/createtransaction) reference page. To retry a request safely, set a unique `externalTxId` on every request, as described in [Transaction idempotency](/reference/api-idempotency#transaction-idempotency).

## Read the results of a contract call

Once the transaction completes, the response from the contract is available in the `networkRecords` of the transaction, which is described in [Interpret contract call results with networkRecords](/docs/interact-with-smart-contracts#interpret-contract-call-results-with-networkrecords).
