Demonstrates jobs where the buyer's funded amount is split: part of it pays
the seller (the budget), and part of it is forwarded to a separate on-chain
destination on submission. Same event-driven entry lifecycle as
basic/. The buyer uses createJobFromOffering with
an offering where requiredFunds is true (the SDK selects the fund-transfer
hook on-chain).
Both buyer and seller include:
- Header comments describing lifecycle, evaluator modes (self-eval via
evaluatorAddress: buyerAddress), and restart safety (hydrated sessions replaybudget.set/job.submitted). promptYesNoon the buyer before creating a new job when in-flight client sessions exist (avoids stacking jobs after Ctrl+C).- Full system branches:
budget.set,job.submitted,job.completed(transcript viasession.toContext()),job.rejected,job.expired. - SIGINT / SIGTERM shutdown on the buyer; seller stops cleanly on signal.
- Seller reject-with-detail (
sendMessagethenreject) for malformed or unsupported requirements.
- Register the seller with at least one offering where
requiredFundsis true. - Optionally set
FUND_TRANSFER_OFFERING_NAMEto select that offering by name; otherwise the firstrequiredFundsoffering is used. - Default requirement shape (
FUND_TRANSFER_DEMO=plain):{ description, forwardUsdc }. The seller readsforwardUsdc(ortransferUsdc) for the forward slice.
Set FUND_TRANSFER_DEMO to swap, open, or close when your registry
offering is named swap_token, open_position, or close_position
respectively. The buyer sends the JSON from jobTypes.ts.
The seller parses the requirement and calls setBudgetWithFundRequest using the
rules in seller.ts.
buyer seller
───── ──────
createJobFromOffering() (requiredFunds → fund-transfer job + requirement)
│ ▶ job.created ──────────────────────▶ case "job.created"
│ setBudgetWithFundRequest(...)
│ ◀────────── budget.set ◀──────────
case "budget.set"
session.fetchJob(); session.fund()
│ ──────────▶ job.funded ───────────▶ case "job.funded"
│ session.submit(...)
│ ◀────────── job.submitted ◀────────
case "job.submitted"
session.complete()
case "job.completed" → transcript, stop
| File | Role |
|---|---|
buyer.ts |
getAgentByWalletAddress, createJobFromOffering, fund, evaluate |
seller.ts |
Registry match, structured samples, documentation example string; setBudgetWithFundRequest |
jobTypes.ts |
Structured requirement types, parsers, and sample bodies |
Same .env as basic/ (BUYER_*, SELLER_*). Start
seller first, then buyer:
npx tsx src/examples/fund-transfer/seller.ts
npx tsx src/examples/fund-transfer/buyer.ts| Variable | Default | Meaning |
|---|---|---|
FUND_TRANSFER_DEMO |
plain |
plain | swap | open | close — requirement shape; structured values require a matching offering name |
FUND_TRANSFER_OFFERING_NAME |
(first requiredFunds) |
Select offering by registry name |
FUND_TRANSFER_DESTINATION |
seller wallet | Forward USDC recipient |
FUND_TRANSFER_DEFAULT_FORWARD_USDC |
0.022 |
Default forward slice when not set on the requirement |
FUND_TRANSFER_STRUCTURED_FORWARD_USDC |
0.022 |
Forward slice for swap / open structured samples (no registry row) |
FUND_TRANSFER_EXAMPLE_BUDGET_USDC |
0.1 |
Budget when job.description matches the documentation example string |
FUND_TRANSFER_EXAMPLE_FORWARD_USDC |
0.022 |
Forward slice for that case |
FUND_TRANSFER_EXAMPLE_JOB_DESCRIPTION |
(see jobTypes.ts) |
Override which job.description value the seller treats as the doc example |
FUND_TRANSFER_CLOSE_BUDGET_USDC |
0.02 |
Budget for close_position structured sample |
const jobId = await buyer.createJobFromOffering(
chainId,
offering, // offering.requiredFunds === true
agent.walletAddress,
requirementData,
{ evaluatorAddress: buyerAddress }
);
await session.setBudgetWithFundRequest(
AssetToken.usdc(budget, session.chainId),
AssetToken.usdc(transfer, session.chainId),
destinationAddress
);For createFundTransferJob (manual job creation without the registry), see
the main README — Fund Transfer Jobs.
setBudgetis invalid when the job usesFundTransferHook— usesetBudgetWithFundRequestonly (seeJobSession.setBudget).- See the main README — Fund Transfer Jobs.