Introducing Compose: the framework for the offchain half of onchain finance
Meet Compose, our new framework for writing durable onchain finance operations in TypeScript

Software Engineer, Principal Software Engineer
Two weeks ago, Circle launched CPN Managed Payments. The pitch: banks can settle cross-border transactions on stablecoin rails while staying entirely in fiat. Circle handles the issuing and blockchain infrastructure under the hood so its banking partners don't have to.
Circle isn’t unusual here. Stripe shipped Tempo. Visa and Mastercard joined x402. Franklin Templeton’s BENJI tokenized money market fund now runs 24/7 across 10 blockchains. PayPal, Revolut, Robinhood, and dozens of other platforms are building onchain rails into their products.
What these announcements have in common isn't the token or the chain.
A bank wants to move dollars from New York to São Paulo faster and cheaper. An asset manager wants a tokenized fund that prices and settles 24/7 across jurisdictions. A payment platform wants to accept and pay out in dollars without holding dollars. A prediction market wants to resolve thousands of short-duration contracts on time. An AI agent wants to pay for the services it uses without a human approving every five-cent transaction.
In each case, the onchain part sits in the middle. Everything around it is offchain: the compliance check, the FX conversion, the settlement reconciliation, the audit trail back to the company’s books. What they’re really shipping is a backend that happens to have an asset attached. We built Compose for that backend.
Compose is the execution framework for money that talks to code, whether that money sits in a fintech app, stablecoin issuer, or bank ledger. Write TypeScript functions, wire them to a trigger (a cron schedule, an onchain event, or an HTTP call), and Compose handles wallets, gas, retries, state, and execution traces.
It looks like this:
export async function main({ fetch, evm, logEvent }: TaskContext) {
const { price, timestamp } = await fetch("https://data.example.com/v1/rates/eur-usd")
.then(r => r.json());
const wallet = await evm.wallet({ name: "price_publisher" });
const oracle = new evm.contracts.PriceFeed(ORACLE_CONTRACT, "base", wallet);
await oracle.update(price, timestamp);
await logEvent({ code: "PRICE_PUBLISHED", message: `EUR/USD ${price}` });
}Onchain finance is 80% offchain engineering
Step through what a financial product does when it touches a blockchain, and we’ll find that the onchain part is maybe 20% of the engineering. Here are a few patterns with onchain actions bolded.
Payment flows
A cross-border payment has the same shape whether you're a bank partner on Circle's CPN, a remittance provider, a payment platform expanding into stablecoins, or a payroll company paying contractors across 40 countries.
- Receive local currency
- Run compliance check against sender and recipient
- Convert to stablecoin at best available rate
- Select route across chains (bridge provider, gas cost, speed)
- Execute bridge transaction
- Wait for confirmation on destination chain
- Convert to recipient's local currency
- Settle to recipient's account
- Reconcile against your books
- Generate compliance trace for audit
Tokenization and digital asset issuance
Tokenizing real-world assets (Treasuries, real estate, equities) follows the same pattern.
- Receive deposit, confirm wire, or verify asset custody
- Run compliance
- Fetch current valuation from pricing source
- Calculate token count from valuation
- Issue tokens
- Record in books and system of record
- Update outstanding supply tracking
- Reconcile onchain supply against asset backing
Prediction market automation
Crypto markets in particular resolve on multiple fixed schedules. Various timeframes (5-minute, 15-minute, 1-hour) across multiple assets mean thousands of new markets per day, each requiring onchain contract interactions to create and resolve. No human is doing that by hand.
Polymarket runs this on Compose. Short-duration crypto markets now account for well over 20% of Polymarket's transaction volume – over $500 million in notional volume per week flowing through Compose workflows.
Resolution is a sequence of offchain operations followed by a single onchain write.
- Pull the resolution price at market expiry
- Verify the attestation and signature on the price
- Compute the outcome (which side won)
- Calculate payouts for each participant
- Settle funds to winners onchain
- Log the resolution trace for dispute handling
If the read fails, Compose retries. If the write partially lands, Compose checks onchain state before resubmitting. When Polygon reorgs (which happens), Compose replays with a new nonce: shallow confirmation after 3 blocks, then deep monitoring for ~250 more. Miss one and users complain, bots pull capital, Polymarket loses trust.
Agentic commerce
An AI agent needs to pay for things (API calls, compute, storage, data feeds, services from other agents) without a human approving every transaction. The mechanics vary – x402 is one emerging standard – but the operational shape is the same regardless of protocol.
- Agent encounters a paid resource (402 response / pricing endpoint / agent-to-agent quote)
- Agent evaluates the request against its spending policy
- If the amount exceeds a threshold, request human-in-the-loop approval
- Construct and sign the payment payload
- Settle payment onchain
- Retry the original request with payment proof attached
- Receive and process the resource
- Log the purchase
Emerging standards like x402 give agents a common HTTP-native way to handle steps 1 and 6. But the wallet, spending policy, retry logic, and audit trail are still the agent builder's problem regardless of which protocol wins. This matters more than in human-driven payments. An agent that double-pays, or pays without authorization, or loses its audit trail, does all of it at machine scale.
Across these patterns, the onchain side is maybe 20% of the system. The other 80% is the operational layer between the financial product and the chain.
Stablecoins, scale, and the shift to writing onchain
Smart contracts get auditors and audit reports and public review. The offchain backend that powers most of the actual product gets a VPS, a cron job, and a wallet with some ETH in it.
This made sense when onchain activity was mostly experimental. The contract was the novel part. The rest was a standard backend, and if the backend went down, the damage was a few angry messages in Discord.
A few things have changed in the past two years.
- Stakes escalated: Stablecoin volume crossed into trillions of dollars last year. Tokenized treasuries have become vehicles for institutional balance sheets. Major banks are piloting a stablecoin or tokenized deposit program, and the buyers have compliance teams and regulators looking over their shoulder. Regulators are starting to demand auditability of the whole pipeline, not just the smart contracts.
- Scope multiplied: Single-chain products are now the exception. Multi-chain is the default, which means the offchain work multiplies. Each new chain is another wallet to fund, RPC to route, reorg semantics to handle, gas market to track. The operational surface area grows faster than the team does.
- Engineering direction shifted: For the first decade of onchain, the interesting engineering was mostly about reading chains. Now it's about writing to them. And reading is not symmetric with writing. Engineers have learned, from years of production outages on traditional backends, that retry bugs, half-executed workflows, and lost state are runtime problems, not application problems. The expectation is that the framework handles them.
Writing onchain is harder than reading onchain
Goldsky started with the read path. Since 2022, we've shipped Subgraphs, Mirror, Turbo, and Edge, the indexing and data infrastructure that teams like Coinbase, Circle, Kraken, Anchorage, Privy, Polymarket, Phantom, and Uniswap use to read blockchain data.
Reading blockchains is now a solved problem in the boring sense. You can trust the infrastructure to be correct eventually, because eventual is the only consistency guarantee the read side needs.
Writing onchain is a different game, especially when money is the thing being moved. When you read a blockchain, the operation is replayable. If something goes wrong you just try again. When you write onchain, there is no undo.
That asymmetry (replayable vs. not replayable) shows up everywhere:
| Scenario | Reading onchain | Writing onchain |
|---|---|---|
| Retry safety | Run same query twice, get the same answer | Submit same transaction twice, move money twice |
| Reorg impact | Slightly stale data for a moment | Confirmed transaction disappears; your books reference something that didn’t happen |
| RPC failure | Brief delay | Nonce desyncs, entire pipeline stalls |
| Gas | Doesn't matter | Wallet runs dry, operation doesn't execute |
| Authorization | N/A; most blockchain data is public | Every write requires signature from a key you’re securing |
| Cost | Free/near-free per query | Every transaction costs gas; cost varies unpredictably with network congestion |
| Timing sensitivity | Delayed read returns slightly stale data | Delayed write can execute under different conditions than intended (price moved, liquidity shifted, window closed) |
| Ordering | Order-independent; queries can run in parallel | Nonce-dependent; transactions must be sequenced correctly or the entire pipeline stalls |
| Observability | Can reconstruct what the chain looked like at a given block | If you didn't log the executions, there's no way to reconstruct what happened |
| Reversibility | Reads don’t change state | Writes are permanent |
These failure modes don’t have a counterpart on the read side. The write path needs different guarantees: exactly-once settlement, durable state that survives process crashes, deterministic retries that know whether the thing actually happened, reorg-aware confirmation, and a complete audit trail of every external call the system made.
None of this comes out of the box. Every team building anything serious is rebuilding these guarantees from scratch. Usually in a hurry with a backlog of issues that look suspiciously alike across companies.
We built Compose because we spent three years watching the pattern repeat, and it was time.
Meet Goldsky Compose: the full onchain loop in one platform
Compose is our framework for the write path. Pair it with Goldsky’s read-side products (Edge RPC, and Subgraphs, Mirror, and Turbo for indexing) and you have the complete onchain operational stack in one place.
For teams building onchain financial products, the shared-platform benefit is operational. When you add a new chain, you add it once – Compose's wallets and execution sit on top of the same Edge RPC infrastructure powering your reads.
Everywhere else, teams assemble this loop from five or six vendors: an indexer, a webhook service, a workflow engine, an RPC provider, a wallet system, a tracing layer. The glue between those vendors is where most incidents happen. On Goldsky, those pieces come already connected.
Here's a first-party price feed, published every five minutes.
First-party price feed oracle
import { TaskContext } from "compose";
const ORACLE_CONTRACT = "0x...";
export async function main({ fetch, evm, logEvent }: TaskContext) {
const res = await fetch("https://data.example.com/v1/rates/eur-usd");
const { price, timestamp } = await res.json();
const wallet = await evm.wallet({ name: "price_publisher" });
const oracle = new evm.contracts.PriceFeed(ORACLE_CONTRACT, "base", wallet);
const tx = await oracle.update(price, timestamp);
await logEvent({
code: "PRICE_PUBLISHED",
message: `EUR/USD ${price} at ${timestamp}, tx ${tx.hash}`,
});
}name: "eur-usd-oracle"
tasks:
- path: "./src/tasks/publish-price.ts"
name: "publish_eur_usd"
triggers:
- type: "cron"
schedule: "*/5 * * * *"Continuous proof of reserves in forty lines
Price feeds are one pattern. Here's another that's become urgent as MiCA's July deadline approaches: a continuous proof-of-reserves reconciliation that compares your offchain reserve balance against your onchain token supply and surfaces drift automatically.
import type { TaskContext } from "compose";
const DRIFT_THRESHOLD = 0.001; // 0.1%
export async function main({ fetch, evm, env, logEvent }: TaskContext) {
// Pull current reserve balance from your bank API or custodian
const reserves = await fetch<{ balance: number }>(
`${env.CUSTODIAN_API}/reserves/usd`
);
// Read onchain token supply
const wallet = await evm.wallet({ name: "attestor" });
const token = new evm.contracts.StablecoinToken(
env.TOKEN_CONTRACT as `0x${string}`,
evm.chains.base,
wallet
);
const totalSupply = await token.totalSupply();
const supplyUsd = Number(totalSupply) / 1e6;
const drift = Math.abs(reserves.balance - supplyUsd) / supplyUsd;
// Log every check, whether it passes or not
await logEvent({
code: drift > DRIFT_THRESHOLD ? "RESERVE_DRIFT_ALERT" : "RESERVE_CHECK_OK",
message: `Reserves: $${reserves.balance.toLocaleString()} | Supply: $${supplyUsd.toLocaleString()} | Drift: ${(drift * 100).toFixed(4)}%`,
});
if (drift > DRIFT_THRESHOLD) {
const attestor = new evm.contracts.ReserveAttestor(
env.ATTESTOR_CONTRACT as `0x${string}`,
evm.chains.base,
wallet
);
await attestor.recordDrift(reserves.balance, supplyUsd, Date.now());
}
}name: "reserve-monitor"
tasks:
- name: "check_reserves"
path: "./src/tasks/check-reserves.ts"
triggers:
- type: "cron"
schedule: "*/10 * * * *"This works as a compliance artifact and not just a monitoring script because every run is traced regardless of outcome: pass, fail, drift, crash, custodian API down. Under MiCA and the GENIUS Act, that trace is the audit deliverable.
Under the hood
Both tasks above are only a few dozen lines. A lot more is happening underneath to make them safe in production.
- Durable execution and exactly-once guarantees: If a task fails mid-execution (e.g. network blip, provider timeout, crash between oracle read and the onchain write), Compose resumes from the last checkpoint and checks transaction status onchain before resubmitting. Successful calls are cached per run, so transactions are never re-sent. Retries use configurable exponential backoff. For workflows involving money, this prevents double-transactions.
- Full execution traces: Every external call the task makes (fetch, read, write) is logged with inputs and outputs to a dashboard your team can step through. The next time someone asks how your feed decided on a price, you can open it and show them. Every trace is an attestation: cryptographically verifiable, timestamped, and immutable.
- Task-to-task orchestration via
callTask: Tasks can trigger other tasks. - A cross-border payment can be a top-level orchestrator that calls
compliance_check, thenfx_quote, thenroute_select, thenbridge_execute, thensettlement_reconcile, thenaudit_log. - Each sub-task retries on its own terms, logs its own inputs and outputs, and can be replayed in isolation. This makes Compose safe for long-running financial workflows: when a payment fails at step 4, you debug step 4, retry step 4, and the rest of the workflow doesn't unwind.
- A cross-border payment can be a top-level orchestrator that calls
- Wallet infrastructure and gas:
evm.walletreturns a gas-sponsored smart wallet that Compose provisions and persists across runs. You don't need to fund it. If you'd rather bring your own EOA for signing (for owner-only contract methods, for instance), you can import a key through Compose's secrets store and the same code runs. - Persistent, versioned state:
collectionsstore state across runs and deployments, with every change tracked to the task that made it. If your reserve monitor needs to remember the last known balance to detect trends over time, that state is durable and versioned. - Type-safe contract interaction: Drop in an ABI file and get fully typed TypeScript classes with autocomplete and compile-time checks. No manual ABI encoding, topics parsing, or BigInt juggling.
- 100+ chains, already provisioned: RPCs, indexing, wallets, and gas for 100+ EVM chains and Solana, backed by Goldsky Edge’s multi-region RPC infrastructure. Adding a new chain to your workflow is a config change.
- TEE attestation without code changes: The same code that runs in the Compose cloud can be deployed to a Trusted Execution Environment for hardware-level attestations. This is available for enterprise deployments. Talk to us if it’s a requirement.
Our design philosophy is that if the durability layer is working well, users will never really think about it. Their app will consistently just work.
What Compose makes possible
The immediate change is that you ship faster. Months of backend engineering become a few TypeScript files.
But the more interesting shift is what becomes economically and operationally feasible.
Before Compose, a two-person team building a tokenized product had to manage a handful of services, unclear boundaries between smart contracts and their web app, and a pile of custom cronjobs for airdrops, auditing, and other onchain side effects. We know because we talked to these teams. Every change was approached with fear – if anything went wrong, debugging was a nightmare with so many possible failure points.
With Compose as the onchain execution layer:
- Products that required custom backends become weekend projects. A five-minute prediction market used to need a bespoke resolver service, a dedicated signer, a custom retry layer, and a tracing pipeline. Polymarket now launches a new asset type as a config.
- High-frequency workflows cost what low-frequency used to. A stablecoin issuer can continuously reconcile reserves against onchain supply instead of once a quarter, with every check timestamped and traced for the auditor. Per-run cost was never the blocker. The engineering cost of building reliable infrastructure to run it was.
- Close the reconciliation gap between your books and the chain. Every ops team running an onchain financial product has some version of the same problem: a human comparing a CSV from the chain against a CSV from the core banking system and investigating the deltas. Compose's traces and persistent state turn that into a workflow. The reconciliation runs on a schedule, diffs are flagged automatically, and the investigation starts with a full trace of what the task saw at the time of the mismatch.
- Multi-chain workflows that required dedicated infrastructure per chain become configuration. A cross-border payment pipeline that works across Base, Arbitrum, and Ethereum doesn't need three separate wallet setups, three gas funding pipelines, and three monitoring stacks. It's one Compose app with different chain targets in the YAML config.
- Unify workflows that cross vendor and counterparty boundaries. A tokenized fund subscription touches a transfer agent, a custodian, a pricing source, and the issuer's own books before a single token gets minted. Compose runs that entire sequence as one workflow so you’re not stitching it together at month-end.
These products were previously hard to ship because every team had to build and maintain the plumbing themselves, usually with a stack of infrastructure work before anyone could get to the actual user-facing product.
We think that custom build goes the way of the custom web server: something every previous era had to do and no one will miss.
What we don't handle yet
- A smart contract framework. Some platforms in this space include pre-made Solidity templates. We intentionally left that out. The teams we're building for tend to know exactly what they want onchain and are often building net-new experiences. What they need is the orchestration layer outside the smart contracts that ties everything to the real world.
- Sub-millisecond latency. Compose runs tasks in sandboxed environments. If you're building a latency-sensitive liquidation bot that needs to compete with MEV searchers, a colocated signer is still the right call. Compose is for workflows where reliability and correctness matter more than shaving microseconds.
- Every chain. Compose currently supports EVM chains with Solana support in progress. Check the docs for the current list.
- Arbitrary runtime environments. Tasks run in a sandboxed TypeScript runtime. You can import npm packages, but native modules and unrestricted network egress aren't supported. Deliberate tradeoff for traceability and security.
Start building with Compose
Compose is now available today. Give it a try here .
If you're crossing the offchain-to-onchain boundary at any serious scale (tokenized assets, stablecoin flows, first-party price feeds, prediction market reoslution, agentic commerce), read the docs or talk to our team .