@abstraxn/server-signer
v1.2.2
Published
Server wallet SDK for backend integrations with Abstraxn wallet-service
Readme
@abstraxn/server-signer
Backend SDK for Abstraxn server-wallet authentication, signing, and transaction execution.
This package is for server environments (Node.js). It provides:
- server-wallet auth (
create,exchange,authenticate) - automatic access-token refresh on
401 - provider-stamped backend operations (
whoami, signing, export) - Viem-style transaction flow (
prepare,estimate,sign,send,receipt) - relayer meta-tx signer for gasless flows with
@abstraxn/relayer
Installation
npm install @abstraxn/server-signerRequired Inputs
ABSTRAXN_API_KEY: backend API keyuserIdentity: unique identity string for your server wallet useraccessKey: 64-char hex server wallet access key (optional on first run, required to recover same identity later)
Quick Start (Recommended)
import { ServerSignerClient } from '@abstraxn/server-signer';
const client = new ServerSignerClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
});
const session = await client.authenticate({
userIdentity: 'merchant-backend-user-001',
// Optional: provide existing key. If omitted, SDK generates one.
accessKey: process.env.SERVER_WALLET_ACCESS_KEY,
userName: 'Merchant Backend',
userEmail: '[email protected]',
});
console.log(session.didCreate); // true if create+exchange happened, false if token already existed
console.log(session.accessToken); // current access token
console.log(session.accessKey); // persist this securely
console.log(session.targetPublicKey);
const whoami = await client.whoami();Authentication Model
authenticate() is idempotent:
- if token exists in token store -> reuses it
- if token is missing -> derives public key from
accessKey, runs create+exchange, stores token
Automatic Token Refresh
Authenticated calls automatically retry once on 401:
- calls
POST /auth/refreshwithcredentials: 'include' - updates access token if refresh succeeds
No configuration is required for default behavior.
Viem-Style Public Client
Create a public client wrapper with backend signing:
const publicClient = client.createPublicClient({
rpcUrl: process.env.RPC_URL!,
chainId: 137,
organizationId: 'fdbbad46-047f-4ff7-a13c-9e14bca69857',
fromAddress: '0x5bBF0b7847A7a35E419eD431069fE92542B4f5c3',
});Transaction Flow
const prepared = await publicClient.prepareTransaction({
to: '0x1111111111111111111111111111111111111111',
value: 1_000_000_000_000_000n,
});
const txHash = await publicClient.signAndSendPreparedTransaction(prepared.unsignedTransaction);
const receipt = await publicClient.waitForTransactionReceipt(txHash);Sign Features
const messageSig = await publicClient.signMessage({
message: 'hello from server-signer',
});
const typedDataSig = 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' }],
},
},
});
const rawSig = await publicClient.signRawPayload({
payload: '0x1234',
encoding: 'PAYLOAD_ENCODING_HEXADECIMAL',
hashFunction: 'HASH_FUNCTION_SHA256',
});Gasless Relayer (with @abstraxn/relayer)
Use an Abstraxn Server Wallet as the meta-transaction signer. Abstraxn signs the authorization;
the relayer submits executeMetaTransaction and pays gas.
npm install @abstraxn/server-signer @abstraxn/relayer ethersimport { Relayer } from '@abstraxn/relayer';
import { ServerSignerClient } from '@abstraxn/server-signer';
import { ChainId } from '@abstraxn/core-types';
import { JsonRpcProvider, type Signer } from 'ethers';
const client = new ServerSignerClient({ apiKey: process.env.ABSTRAXN_API_KEY! });
await client.authenticate({
userIdentity: 'backend-agent-001',
accessKey: process.env.SERVER_WALLET_ACCESS_KEY!,
});
const rpcUrl = process.env.RPC_URL!;
const { signer } = client.createRelayerSigner({
rpcUrl,
chainId: 137,
organizationId: process.env.TURNKEY_ORG_ID!,
fromAddress: '0xYourServerWalletAddress',
});
const relayer = new Relayer({
relayerUrl: process.env.RELAYER_URL!,
chainId: ChainId.POLYGON_MAINNET,
provider: new JsonRpcProvider(rpcUrl),
// RelayerMetaTxSigner matches the runtime surface ethers Signer needs for meta-tx builds.
signer: signer as unknown as Signer,
});
const txData = await relayer.buildRelayerTxEIP712({
contractAddress: '0xYourContract',
abi: yourContractAbi,
method: 'yourMethod',
args: ['arg1'],
});
const { transactionId } = await relayer.sendRelayerTx(txData);Notes:
fromAddressmust own the on-chain meta-tx nonce (getNonceon the target contract).- Use
buildRelayerTxEIP712orbuildRelayerTxto match your contract's verification scheme. - Whitelist the contract on your relayer app and fund the relayer gas tank.
See server-signer-demo in this repo for a runnable example (RUN_RELAYER_DEMO=true).
Export Private Key
const decryptedPrivateKey = await client.exportPrivateKey({
organizationId: 'fdbbad46-047f-4ff7-a13c-9e14bca69857',
address: '0x5bBF0b7847A7a35E419eD431069fE92542B4f5c3',
embeddedPrivateKey: process.env.SERVER_WALLET_ACCESS_KEY!,
blockchain: 'evm', // or 'solana'
});Main Methods
authenticate(input)- primary auth entrypointwhoami(input?)createPublicClient(config)- viem-style transaction/sign helpercreateRelayerSigner(config)- returns{ wallet, signer, fromAddress }for gasless relayer flowssignTransaction(payload)- raw backend sign endpoint wrappersignMessage/signTypedData/signRawPayload(via public client)exportPrivateKey(input)mfaStatus,mfaEnable,mfaVerifySetup,mfaVerify,mfaVerifySign,mfaDisable
Backward-compatible aliases are still available:
ensureServerWalletSession(...)(deprecated; useauthenticate)createViemClient(...)(deprecated; usecreatePublicClient)
Errors
SDK throws typed errors (ValidationError, UnauthorizedError, ConflictError, etc.) with backend code/message preserved when available.
Common cases:
409 CONFLICTon create:userIdentityalready exists with a different access key/public key400 BAD_REQUESTon whoami/sign: missing stamped payload fields401 UNAUTHORIZED: token missing/expired (SDK auto-refreshes once)
Security Notes
- Store
accessKeyin a secure secrets manager. - Never expose
accessKeyor decrypted private keys in frontend code. - Avoid logging raw keys or sensitive signatures in production.
