Skip to main content

Overview

The sendSignedTransaction method submits a signed transaction to the NEAR network and waits for it to be finalized. This method is used to execute state-changing operations like transfers, function calls, and account management.

Method Signature

client.sendSignedTransaction(args: SendSignedTransactionArgs)
  : Promise<Result<SendSignedTransactionOutput, SendSignedTransactionError>>
Source: packages/near-api-ts/universal/src/client/methods/transaction/sendSignedTransaction/sendSignedTransaction.ts:22

Parameters

signedTransaction
SignedTransaction
required
The signed transaction object containing:
transaction
Transaction
required
The transaction details including:
  • signerId - Account ID of the transaction signer
  • publicKey - Public key used to sign the transaction
  • nonce - Transaction nonce (must be greater than current account nonce)
  • receiverId - Account ID of the transaction receiver
  • actions - Array of actions to execute
  • blockHash - Recent block hash for replay protection
signature
Signature
required
Cryptographic signature of the transaction
policies
object
transport
TransportPolicy
Transport-level policies for the request (retry, timeout, etc.)
options
object
signal
AbortSignal
AbortSignal for canceling the request

Response

ok
boolean
required
Indicates if the transaction was successful
value
SendSignedTransactionOutput
rawRpcResult
RpcTransactionResponse
required
Raw RPC response containing:
transaction
object
The transaction that was executed
transaction_outcome
object
Outcome of the transaction execution
receipts_outcome
array
Outcomes of all receipts generated by the transaction
status
object
Final status of the transaction:
  • SuccessValue - Transaction succeeded with a return value
  • SuccessReceiptId - Transaction succeeded with a receipt ID
  • Failure - Transaction failed (contains error details)
error
SendSignedTransactionError
Error object if the transaction failed. Possible error kinds:Transaction Validation Errors:
  • Client.SendSignedTransaction.Args.InvalidSchema - Invalid arguments
  • Client.SendSignedTransaction.Rpc.Transaction.Expired - Transaction expired
  • Client.SendSignedTransaction.Rpc.Transaction.Nonce.Invalid - Invalid nonce
  • Client.SendSignedTransaction.Rpc.Transaction.Signature.Invalid - Invalid signature
Account Errors:
  • Client.SendSignedTransaction.Rpc.Transaction.Signer.NotFound - Signer account not found
  • Client.SendSignedTransaction.Rpc.Transaction.Receiver.NotFound - Receiver account not found
  • Client.SendSignedTransaction.Rpc.Transaction.Signer.Balance.TooLow - Insufficient balance
Action Errors:
  • Client.SendSignedTransaction.Rpc.Transaction.Action.CreateAccount.AlreadyExist - Account already exists
  • Client.SendSignedTransaction.Rpc.Transaction.Action.Stake.BelowThreshold - Stake too low
  • Client.SendSignedTransaction.Rpc.Transaction.Action.Stake.Balance.TooLow - Insufficient balance for staking
Transport Errors:
  • Client.SendSignedTransaction.Timeout - Request timed out
  • Client.SendSignedTransaction.Aborted - Request was aborted
  • Client.SendSignedTransaction.Exhausted - Retries exhausted

Implementation Details

The method serializes the signed transaction to Borsh format and sends it via RPC:
// Source: packages/near-api-ts/universal/src/client/methods/transaction/sendSignedTransaction/sendSignedTransaction.ts:37-47
const rpcResponse = await context.sendRequest({
  method: 'send_tx',
  params: {
    signed_tx_base64: base64.encode(
      toBorshSignedTransaction(validArgs.data.signedTransaction),
    ),
    wait_until: 'FINAL',
  },
  transportPolicy: args.policies?.transport,
  signal: args.options?.signal,
});
The transaction is always sent with wait_until: 'FINAL', meaning the method waits for the transaction to be finalized on the blockchain.

Examples

Basic Transfer Transaction

import { createClient, mainnet } from '@near-js/client';
import { createKeyPair } from '@near-js/crypto';

const client = createClient({ network: mainnet });
const keyPair = createKeyPair('ed25519', privateKey);

// Create and sign transaction
const signedTx = await createTransaction({
  signerId: 'alice.near',
  receiverId: 'bob.near',
  actions: [
    {
      type: 'Transfer',
      params: {
        deposit: '1000000000000000000000000', // 1 NEAR
      },
    },
  ],
  publicKey: keyPair.getPublicKey(),
  nonce: await client.getAccessKeyNonce(/* ... */),
  blockHash: await client.getRecentBlockHash(),
});

const signature = keyPair.sign(serializeTransaction(signedTx.transaction));

const result = await client.sendSignedTransaction({
  signedTransaction: {
    transaction: signedTx.transaction,
    signature,
  },
});

if (result.ok) {
  console.log('Transaction successful!');
  console.log('Transaction hash:', result.value.rawRpcResult.transaction.hash);
  console.log('Status:', result.value.rawRpcResult.status);
} else {
  console.error('Transaction failed:', result.error.kind);
}

Function Call Transaction

const signedTx = await createTransaction({
  signerId: 'alice.near',
  receiverId: 'contract.near',
  actions: [
    {
      type: 'FunctionCall',
      params: {
        methodName: 'set_value',
        args: JSON.stringify({ value: 42 }),
        gas: '30000000000000',
        deposit: '0',
      },
    },
  ],
  // ... other params
});

const result = await client.sendSignedTransaction({
  signedTransaction: signedTx,
});

if (result.ok) {
  const status = result.value.rawRpcResult.status;
  if ('SuccessValue' in status) {
    const returnValue = Buffer.from(status.SuccessValue, 'base64').toString();
    console.log('Function returned:', returnValue);
  }
}

Error Handling

const result = await client.sendSignedTransaction({
  signedTransaction: signedTx,
});

if (!result.ok) {
  const error = result.error;
  
  switch (error.kind) {
    case 'Client.SendSignedTransaction.Rpc.Transaction.Signer.Balance.TooLow':
      console.error('Insufficient balance:', error.context);
      break;
      
    case 'Client.SendSignedTransaction.Rpc.Transaction.Nonce.Invalid':
      console.error('Invalid nonce. Need to refresh and retry.');
      break;
      
    case 'Client.SendSignedTransaction.Rpc.Transaction.Expired':
      console.error('Transaction expired. Need new block hash.');
      break;
      
    case 'Client.SendSignedTransaction.Timeout':
      console.error('Transaction timed out:', error.context);
      break;
      
    default:
      console.error('Transaction error:', error.kind, error.context);
  }
}

With Custom Transport Policy

const result = await client.sendSignedTransaction({
  signedTransaction: signedTx,
  policies: {
    transport: {
      maxRetries: 5,
      retryDelay: 1000,
      timeout: 30000,
    },
  },
});

With Abort Signal

const controller = new AbortController();

// Abort after 20 seconds
setTimeout(() => controller.abort(), 20000);

try {
  const result = await client.sendSignedTransaction({
    signedTransaction: signedTx,
    options: {
      signal: controller.signal,
    },
  });
  
  if (result.ok) {
    console.log('Transaction completed');
  }
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Transaction request was cancelled');
  }
}

Inspecting Transaction Outcomes

const result = await client.sendSignedTransaction({
  signedTransaction: signedTx,
});

if (result.ok) {
  const rpcResult = result.value.rawRpcResult;
  
  // Transaction outcome
  console.log('Gas burned:', rpcResult.transaction_outcome.outcome.gas_burnt);
  console.log('Tokens burned:', rpcResult.transaction_outcome.outcome.tokens_burnt);
  
  // Receipt outcomes
  for (const receipt of rpcResult.receipts_outcome) {
    console.log('Receipt ID:', receipt.id);
    console.log('Gas burned:', receipt.outcome.gas_burnt);
    if (receipt.outcome.logs.length > 0) {
      console.log('Logs:', receipt.outcome.logs);
    }
  }
  
  // Check for action errors
  if ('Failure' in rpcResult.status) {
    console.error('Action failed:', rpcResult.status.Failure);
  }
}

Transaction Lifecycle

  1. Creation - Build transaction with actions, nonce, and block hash
  2. Signing - Sign transaction with private key
  3. Serialization - Convert to Borsh format (done automatically)
  4. Submission - Send to RPC node via send_tx method
  5. Execution - Transaction is included in a block and executed
  6. Finalization - Wait for block finalization (2-3 blocks, ~2-3 seconds)
  7. Response - Receive transaction outcome and receipt results

Notes

  • Transactions must be signed with the correct private key for the signer account
  • The nonce must be exactly 1 greater than the current access key nonce
  • Block hash must be recent (within ~2 minutes)
  • Gas is charged for transaction execution
  • The method waits for finalization (FINAL) by default
  • Failed transactions still consume gas
  • Receipt outcomes may include cross-contract call results