Use Openfort Backend Wallets
Openfort backend wallets are developer-controlled EOAs whose private keys are held by Openfort. Your server calls a typed SDK to sign hashes, messages, EIP-712 typed data, and transactions — the key never leaves Openfort’s infrastructure. This makes them a clean fit for autonomous agents, treasuries, and any server-side automation on SKALE.Openfort’s Account Abstraction features (paymasters, EIP-7702 delegated accounts) are not currently available on SKALE Chains. This guide uses the EOA signing path:
account.signTransaction(...) followed by eth_sendRawTransaction via viem. Gas on SKALE Base is paid in CREDIT — fund the wallet once and it can transact freely.Prerequisites
- Node.js 18 or higher
- An Openfort account (dashboard.openfort.io)
- A SKALE chain endpoint — this guide uses SKALE Base Sepolia (see Connect to SKALE)
- CREDIT on the wallet to pay for transactions — for testnet, the SKALE Base Sepolia faucet
Overview
| Wallet type | Control | Best for |
|---|---|---|
| Backend wallet | Developer (server-side API) | Agents, treasury, automation, server signing |
| Embedded wallet | End user (auth + recovery) | Consumer apps where the user holds the keys |
sign, signMessage, signTypedData, and signTransaction directly on the account object returned by create().
Implementation
Configure environment variables
From the Openfort dashboard, grab a secret API key (
sk_test_... or sk_live_...) and generate a wallet secret. The wallet secret is a base64-encoded EC P-256 private key — Openfort holds the matching public key to verify your signing requests.Create a .env file:Never commit the wallet secret. It authenticates every backend wallet write operation — treat it like a root credential.
Define the SKALE chain
Create For mainnet, swap
chain.ts:id: 1187947933, rpcUrls.default.http: ["https://skale-base.skalenodes.com/v1/base"], and testnet: false.Create a backend wallet
Create Run it:Two things to record from the output:
create-wallet.ts:- Address (
0x…) — where you’ll send CREDIT in the next step. - Account ID (
acc_…) — the stable identifier for this wallet across runs. Paste it intoOPENFORT_ACCOUNT_IDin your.env. Subsequent scripts retrieve the same wallet viaopenfort.accounts.evm.backend.get({ id: process.env.OPENFORT_ACCOUNT_ID })— without it, every run would create a new wallet.
openfort.accounts.evm.backend.list() or by looking the wallet up in the Openfort dashboard.Fund the wallet with CREDIT
On testnet, request CREDIT for the address at the SKALE Base Sepolia faucet. On mainnet, buy CREDIT at base.skalenodes.com/credits and transfer to the wallet address.
Sign and broadcast a transaction
Create
send-transaction.ts:account.signTransaction(...) returns a 0x02...-prefixed serialized EIP-1559 transaction — the same wire format every Ethereum client produces. Broadcasting means submitting that signed payload to the network over JSON-RPC (eth_sendRawTransaction) so validators see it, validate it, and include it in a block. The signature proves the wallet authorized the transaction; broadcasting is what puts it onchain. viem’s sendRawTransaction does the JSON-RPC call for you.The explorer link the script prints is your own verification — open it and you’ll see the transaction, the signer address, the value, and the block it was included in.Confine the wallet with a policy
When a backend wallet drives an agent, the SDK credentials sit on a server the agent has access to. A policy turns the Openfort API itself into a guardrail: signing requests that don’t match an allowlist are rejected server-side, regardless of what the agent code asks for.Openfort’s
evmNetwork policy criterion is currently limited to chains where Openfort runs Account Abstraction — SKALE chain IDs aren’t accepted there yet. Use the chain-agnostic criteria (evmAddress, ethValue, evmData) instead, which describe the transaction’s content rather than the network. Your code already pins the chain via defineChain, so the agent can’t redirect to another network without changing your code.policy.ts:
openfort.policies.evaluate() reports what the policy would decide for a hypothetical operation without signing anything:
account.signTransaction(...) calls that violate the rules fail with a Forbidden API error before any signature is produced. The agent cannot route around it.
| Criterion type | What it checks |
|---|---|
evmAddress | The transaction’s to against an allow/deny list |
ethValue | Native value (wei) against a numeric bound |
evmData | Calldata against a pattern (e.g. specific function selectors) |
evmMessage | signEvmMessage text against an RE2 regex |
evmTypedDataField | EIP-712 field values for signEvmTypedData |
openfort.policies.update(id, ...).
What you’ve built
A SKALE-connected agent or service can now:- Create a wallet on demand via the Openfort API
- Sign and broadcast transactions on SKALE using standard EIP-1559 RLP — the same wire format every Ethereum client uses
- Sign offchain messages and EIP-712 payloads for attestations or session auth
- Operate under a policy that rejects out-of-bounds signing requests server-side, before any signature is produced
Next steps
- Openfort Node SDK — full SDK reference, including signing policies and key rotation
- SKALE Chains — chain IDs and RPC endpoints for every active SKALE Chain
- Build an Agent — pair a backend wallet with x402 payments for autonomous services
