After authenticating, create a public client to interact with the blockchain. The public client wraps RPC calls with backend-stamped signing so your server can prepare, sign, and broadcast transactions.

Create a Public Client

const publicClient = client.createPublicClient({
  rpcUrl: process.env.RPC_URL!,
  chainId: 137,
  organizationId: 'fdbbad46-047f-4ff7-a13c-9e14bca69857',
  fromAddress: '0x5bBF0b7847A7a35E419eD431069fE92542B4f5c3',
});
ParameterTypeDescription
rpcUrlstringJSON-RPC endpoint for the target chain
chainIdnumberChain ID (e.g. 137 for Polygon, 1 for Ethereum)
organizationIdstringOrganization ID from your authenticated session
fromAddressstringWallet address to sign and send from

Transaction Flow

The SDK follows a prepare → sign → send → receipt pattern, similar to Viem.

Step 1: Prepare

Build the unsigned transaction with nonce, chain ID, and gas parameters populated automatically:
const prepared = await publicClient.prepareTransaction({
  to: '0x1111111111111111111111111111111111111111',
  value: 1_000_000_000_000_000n,  // 0.001 ETH/MATIC
});
You can also pass data for contract calls:
const prepared = await publicClient.prepareTransaction({
  to: '0xContractAddress...',
  value: 0n,
  data: '0xa9059cbb...',  // encoded function call
});

Step 2: Sign and Send

Sign the prepared transaction with your server wallet key and broadcast it to the network:
const txHash = await publicClient.signAndSendPreparedTransaction(
  prepared.unsignedTransaction,
);
console.log('Transaction hash:', txHash);

Step 3: Wait for Receipt

Block until the transaction is confirmed on-chain:
const receipt = await publicClient.waitForTransactionReceipt(txHash);
console.log('Block:', receipt.blockNumber);
console.log('Status:', receipt.status);  // 'success' or 'reverted'

Full Example

import { ServerSignerClient } from '@abstraxn/server-signer';

const client = new ServerSignerClient({
  apiKey: process.env.ABSTRAXN_API_KEY!,
});

const session = await client.authenticate({
  userIdentity: 'payout-service',
  accessKey: process.env.SERVER_WALLET_ACCESS_KEY,
});

const publicClient = client.createPublicClient({
  rpcUrl: 'https://polygon-rpc.com',
  chainId: 137,
  organizationId: session.organizationId,
  fromAddress: session.walletAddress,
});

const prepared = await publicClient.prepareTransaction({
  to: '0xRecipientAddress...',
  value: 1_000_000_000_000_000n,
});

const txHash = await publicClient.signAndSendPreparedTransaction(
  prepared.unsignedTransaction,
);

const receipt = await publicClient.waitForTransactionReceipt(txHash);
console.log('Payout confirmed:', receipt.transactionHash);

Sign a Message

Sign an arbitrary string message (EIP-191 personal sign):
const signature = await publicClient.signMessage({
  message: 'hello from server-signer',
});
console.log('Signature:', signature);

Sign Typed Data (EIP-712)

Sign structured data following the EIP-712 standard:
const signature = await publicClient.signTypedData({
  typedData: {
    domain: {
      name: 'Abstraxn',
      version: '1',
      chainId: 137,
    },
    message: {
      contents: 'Hi',
    },
    primaryType: 'Mail',
    types: {
      EIP712Domain: [
        { name: 'name', type: 'string' },
        { name: 'version', type: 'string' },
        { name: 'chainId', type: 'uint256' },
      ],
      Mail: [
        { name: 'contents', type: 'string' },
      ],
    },
  },
});

Sign Raw Payload

Sign an arbitrary hex-encoded payload with a specific hash function:
const signature = await publicClient.signRawPayload({
  payload: '0x1234',
  encoding: 'PAYLOAD_ENCODING_HEXADECIMAL',
  hashFunction: 'HASH_FUNCTION_SHA256',
});
ParameterTypeOptions
payloadstringHex-encoded data to sign
encodingstringPAYLOAD_ENCODING_HEXADECIMAL, PAYLOAD_ENCODING_UTF8
hashFunctionstringHASH_FUNCTION_SHA256, HASH_FUNCTION_KECCAK256, HASH_FUNCTION_NO_OP

Next Steps