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

# Create an onchain transaction

When a payment is in the `CRYPTO_FUNDS_PENDING` state, the OFI must initiate an
onchain transaction to fulfill the payment in USDC. CPN provides an API to help
prepare and validate the onchain transaction call data, and to broadcast the
transaction and monitor its state.

This guide describes the
[Transactions V2](/cpn/concepts/transactions/transactions-v2) flow. Note that
your quote must be created with the `transactionVersion` parameter set to
`VERSION_2` to match the API that you're using.

## Prerequisites

Before you begin, ensure that you have:

* Created a quote through the CPN API with the `transactionVersion` parameter
  set to `VERSION_2`.
* USDC in your sender wallet
* (EVM chains only) Granted a USDC allowance to the `Permit2` contract. See
  [How-to: Grant USDC Allowance to Permit2](/cpn/guides/transactions/grant-usdc-allowance-to-permit2)
  for more information.
* (Solana only) Ensure your Solana account has been initialized and funded.

<Note>
  Gas fees are not charged in native tokens, but in USDC. They are determined at
  quote creation instead of transaction creation in the quote `fees` field. The
  gas fee is collected by Circle's payment settlement smart contract during
  onchain transaction processing.
</Note>

## Steps

Use the following steps to create and broadcast a USDC transfer to the
blockchain:

### Step 1: Prepare the call data

Call the
[create transaction V2](/api-reference/cpn/cpn-platform/create-transaction-v2)
endpoint to get an unsigned message object. Note that transaction objects differ
between EVM blockchains and Solana.

The API call must include the sender wallet information (which must be an EOA
wallet). The rest of the transaction data such as amount, chain, destination
address, and other information is populated automatically by CPN using the
payment record.

### Step 2: Create and sign the transaction

Review the unsigned message object and ensure that it matches your expectations
for the crypto transaction. Depending on the blockchain, the signing process
varies.

#### EVM

When the unsigned data has been confirmed, you must sign the transaction payload
from the API endpoint using [EIP-712](https://eips.ethereum.org/EIPS/eip-712)
typed data signing from your sender wallet. The following is an example payload:

```json theme={null}
{
  "type": "PAYMENT_SETTLEMENT_CONTRACT_V1_0_PAYMENT_INTENT",
  "types": {
    "EIP712Domain": [
      {
        "name": "name",
        "type": "string"
      },
      {
        "name": "chainId",
        "type": "uint256"
      },
      {
        "name": "verifyingContract",
        "type": "address"
      }
    ],
    "PaymentIntent": [
      {
        "name": "from",
        "type": "address"
      },
      {
        "name": "to",
        "type": "address"
      },
      {
        "name": "value",
        "type": "uint256"
      },
      {
        "name": "validAfter",
        "type": "uint256"
      },
      {
        "name": "validBefore",
        "type": "uint256"
      },
      {
        "name": "nonce",
        "type": "bytes32"
      },
      {
        "name": "beneficiary",
        "type": "address"
      },
      {
        "name": "maxFee",
        "type": "uint256"
      },
      {
        "name": "requirePayeeSign",
        "type": "bool"
      },
      {
        "name": "attester",
        "type": "address"
      }
    ],
    "TokenPermissions": [
      {
        "name": "token",
        "type": "address"
      },
      {
        "name": "amount",
        "type": "uint256"
      }
    ],
    "PermitWitnessTransferFrom": [
      {
        "name": "permitted",
        "type": "TokenPermissions"
      },
      {
        "name": "spender",
        "type": "address"
      },
      {
        "name": "nonce",
        "type": "uint256"
      },
      {
        "name": "deadline",
        "type": "uint256"
      },
      {
        "name": "witness",
        "type": "PaymentIntent"
      }
    ]
  },
  "domain": {
    "name": "Permit2",
    "chainId": "11155111",
    "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3"
  },
  "message": {
    "nonce": "25668617285137697861288274946631174355105919960416755114569514179393151588120",
    "spender": "0xe2B17D0C1736dc7C462ABc4233C91BDb9F27DD1d",
    "witness": {
      "to": "0xc75c3e371d617b3e60db1b6f3fa2f0689562e5a7",
      "fee": "0",
      "from": "0x57414adbBbc4BBA36f1dE26b2dc1648b28ae7799",
      "nonce": "0x38bfec2b230187932870d575132e8ae1f83b34c10e3bf6d64c377f0c13245718",
      "value": 14174474,
      "attester": "0x768919ef04853b5fd444ccff48cea154768a0291",
      "validAfter": "1757358106",
      "beneficiary": "0x4f1c3a0359A7fAd8Fa8E9E872F7C06dAd97C91Fd",
      "validBefore": "1757361726",
      "requirePayeeSign": false
    },
    "deadline": "1757362866",
    "permitted": {
      "token": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
      "amount": "14174474"
    }
  },
  "primaryType": "PermitWitnessTransferFrom"
}
```

After signing the payload, you should have an EIP-712 data signature in hex
string format.

#### Solana

The transfer data to be signed is encoded in the `encodedMessageToBeSigned`
field from the
[POST /v2/cpn/payments/:paymentId/transactions](/api-reference/cpn/cpn-platform/create-transaction-v2)
endpoint. You would need to first decode it using the `base64` library and then
sign it using a Solana library with your wallet key pair.

```typescript theme={null}
import { Keypair, Transaction, Message } from "@solana/web3.js";
import bs58 from "bs58";

function signTransaction(
  encodedMessageToBeSigned: string,
  keypair: Keypair,
): string {
  // 1. Decode base64 message
  const messageBytes = Buffer.from(encodedMessageToBeSigned, "base64");

  // 2. Deserialize as Solana Message
  const message = Message.from(messageBytes);

  // 3. Create transaction with empty signatures
  const SIGNATURE_LENGTH = 64;
  const EMPTY_SIGNATURE_BASE58 = bs58.encode(
    Buffer.alloc(SIGNATURE_LENGTH).fill(0),
  );
  const numSignatures = message.header.numRequiredSignatures;

  const transaction = Transaction.populate(
    message,
    Array(numSignatures).fill(EMPTY_SIGNATURE_BASE58),
  );

  // 4. Sign with keypair
  transaction.partialSign(keypair);

  // 5. Serialize signed transaction, which you can then submit to the
  // POST /v2/cpn/payments/:paymentId/transactions/:transactionId/submit endpoint
  const signedTransaction = transaction
    .serialize({ requireAllSignatures: false })
    .toString("base64");

  return signedTransaction;
}
```

If you are using the Circle Wallets, you can use the
[sign transaction](/api-reference/wallets/developer-controlled-wallets/sign-transaction)
endpoint to sign the transaction object.

```typescript theme={null}
import { Keypair, Transaction, Message } from "@solana/web3.js";
import bs58 from "bs58";
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";

async function signTransactionWithCircleWallet(
  encodedMessageToBeSigned: string,
  walletId: string,
  apiKey: string,
  entitySecret: string,
): Promise<string> {
  // 1. Decode base64 message
  const messageBytes = Buffer.from(encodedMessageToBeSigned, "base64");

  // 2. Deserialize as Solana Message
  const message = Message.from(messageBytes);

  // 3. Create transaction with empty signatures
  const SIGNATURE_LENGTH = 64;
  const EMPTY_SIGNATURE_BASE58 = bs58.encode(
    Buffer.alloc(SIGNATURE_LENGTH).fill(0),
  );
  const numSignatures = message.header.numRequiredSignatures;

  const transaction = Transaction.populate(
    message,
    Array(numSignatures).fill(EMPTY_SIGNATURE_BASE58),
  );

  // 4. Serialize transaction
  const rawTransaction = transaction
    .serialize({ requireAllSignatures: false })
    .toString("base64");

  // 5. Initialize Circle client
  const circleClient = initiateDeveloperControlledWalletsClient({
    apiKey: apiKey,
    entitySecret: entitySecret,
  });

  // 6. Sign transaction, which you can then submit to the
  // POST /v2/cpn/payments/:paymentId/transactions/:transactionId/submit endpoint
  const signedTransaction = (
    await circleClient.signTransaction({
      walletId: walletId,
      rawTransaction: rawTransaction,
    })
  ).data.signedTransaction;

  return signedTransaction;
}
```

### Step 3: Submit the signed transaction

Call the
[submit transaction V2](/api-reference/cpn/cpn-platform/submit-transaction-v2)
endpoint to submit the signed transaction data to CPN. CPN broadcasts the
transaction and provides a webhook to notify you of the transaction status. This
webhook is also provided to the BFI.

Once the BFI confirms that they have received the desired amount for the
payment, the BFI initiates the fiat payment.

#### Handling failures

If a transaction fails and the payment is still valid (for example, it has not
expired), you can address the issues with the transaction and initiate a new
transaction. Because gas fees in native tokens are paid from a Circle-controlled
wallet, insufficient gas is unlikely to be an issue. You can use a similar
approach to address other onchain failures.

Only one active transaction is allowed at a time per payment, so you can only
initiate a new transaction once the previous one has failed.
