Skip to content

13x54n/Maple

Repository files navigation

Maple

Off-chain gateway for an x402-style credit layer: for each request Maple evaluates a borrower (Solana address) and a target paid URL, runs credit → policy → repayment (stub engines today), optionally GETs the seller with Maple’s wallet after policy allows, and can record a receipt on Solana when enabled.

Layer Role
HTTP Bun.serveGET/POST /maple with headers (see below).
x402 @x402/fetch + @x402/svm; Maple can pay sellers using MAPLE_SVM_X402_PRIVATE_KEY. Each /maple call gets a Maple-generated payment id for the payment-identifier extension when the seller advertises it.
On-chain Anchor program maple records Receipt PDAs (buyer + resource hash + nonce per purchase).
  • Runtime: Bun — use bun instead of npm for installs and scripts.
  • Config: copy .env.example.env (Bun loads .env automatically; .env* is gitignored).

Prerequisites

  • Bun
  • Solana CLI (solana, solana-keygen)
  • Anchor (anchor, Rust, cargo-build-sbf) — only needed to build or deploy the on-chain program

Install and build

bun install
bun run build          # anchor build + bun test
bun run build:anchor   # only the Solana program + IDL

Artifacts:

  • Program binary: target/deploy/maple.so
  • IDL: target/idl/maple.json — a copy used by TypeScript lives at src/idl/maple.json (regenerate with anchor build and copy if you change the program interface).

Program ID: FVJ5CDuy9NTpzUwj9XdBdEFfM7aHFsMv3FPRC9GsrP8E (see programs/maple/src/lib.rs / Anchor.toml). If you generate a new program keypair, run anchor keys sync and redeploy.


Deploy the on-chain program (contracts)

Deployments use the Solana CLI default wallet (solana config get → keypair path) and must have enough SOL on the target cluster for rent and fees.

1. Point the CLI at the cluster (example: devnet)

solana config set --url devnet
solana balance          # fund if needed: solana airdrop 2

2. Build

cd /path/to/maple
anchor build

3. Deploy the program

anchor deploy --provider.cluster devnet

Use mainnet-beta or localnet instead of devnet when appropriate; Anchor.toml lists the same program id for localnet and devnet.

4. Upgrade the on-chain IDL (when it already exists)

The first deploy creates an IDL account. Later deploys often fail at the “create IDL” step with an error like account already in use — the program binary still upgrades, but you must upgrade the IDL separately:

anchor idl upgrade -f target/idl/maple.json \
  FVJ5CDuy9NTpzUwj9XdBdEFfM7aHFsMv3FPRC9GsrP8E \
  --provider.cluster devnet

5. Verify

solana program show FVJ5CDuy9NTpzUwj9XdBdEFfM7aHFsMv3FPRC9GsrP8E --url devnet

Checklist for the gateway when testing receipts against devnet: set MAPLE_RPC_URL to the same cluster (e.g. https://api.devnet.solana.com) and ensure MAPLE_AUTHORITY_KEYPAIR is the wallet that pays rent for new receipt accounts (see .env.example).


Configuration (environment variables)

Variable Purpose
PORT Gateway port (default 3040, or next free in a small range).
MAPLE_DEFAULT_ESTIMATED_CENTS Default spend estimate for policy when the header is omitted.
MAPLE_MAX_TX_CENTS Policy cap per request (stub policy engine).
MAPLE_SOLANA_CLUSTER devnet / mainnet-beta / testnet — logged at startup; used with MAPLE_RPC_URL for a mismatch warning (default devnet).
MAPLE_POLICY_ALLOW_ALL Set true / 1 / yes to always allow policy (skips estimate vs MAPLE_MAX_TX_CENTS); use for local/dev only.
MAPLE_CHAIN_ENABLED Set true / 1 to submit RecordReceipt transactions.
MAPLE_RPC_URL Solana HTTP RPC (must match where the program is deployed).
MAPLE_SVM_RPC_URL Optional RPC for @x402/svm client; defaults to MAPLE_RPC_URL when set.
MAPLE_AUTHORITY_KEYPAIR JSON array or base58 secret; signs and pays for on-chain receipts.
MAPLE_SVM_X402_PRIVATE_KEY Base58 64-byte secret; Maple payer for x402 to seller URLs. Fund USDC on the same cluster as the seller.
MAPLE_X402_FETCH Set 0 / false to skip HTTP to the seller (pipeline only).

See .env.example for a template.


Run the gateway

bun run dev

Listen address: http://127.0.0.1:<PORT> (default 3040). Use bun run start without hot reload.

HTTP API

Method Path Purpose
GET / JSON list of routes
GET /health Liveness ({ "ok": true })
GET or POST /maple Run the pipeline; requires headers below

/maple headers

Defined in src/constants.ts:

  • x-maple-buyer-address — Solana public key (base58) of the borrower (credit / policy identity). Not the key that pays the seller.
  • x-maple-request-url — Full URL of the paid resource (e.g. seller GET /demo).
  • x-maple-estimated-cents — Optional; spend estimate for policy (defaults from env or 100).

Missing required headers → 400 with error: "missing_headers".

Response shape

JSON includes pipeline fields (trace, credit, policy, repayment, chain) plus:

  • paymentId — Maple-generated id (prefix maple_) for x402 payment-identifier when the seller supports it.
  • sellerFetch — Present when policy allows and x402 fetch runs: HTTP result or skipped with a reason.

Maple payer and paid GET

When policy allows and MAPLE_SVM_X402_PRIVATE_KEY is set (and MAPLE_X402_FETCH is not disabled), Maple performs a paid GET to x-maple-request-url using @x402/fetch. Omit the key or set MAPLE_X402_FETCH=false for credit/policy only without calling the seller.

On-chain receipt

With MAPLE_CHAIN_ENABLED=true and a valid MAPLE_AUTHORITY_KEYPAIR, Maple submits a RecordReceipt instruction after the pipeline. Receipt accounts use a per-request nonce so repeat purchases to the same URL do not collide.

Example: Maple + local seller (devnet)

  1. Seller: cd examples/seller-express-svm, configure .env, bun run dev.
  2. Maple: configure .env (payer + optional chain), bun run dev from repo root.
  3. Request:
curl -s http://127.0.0.1:3040/maple \
  -H 'content-type: application/json' \
  -H 'x-maple-buyer-address: So11111111111111111111111111111111111111112' \
  -H 'x-maple-request-url: http://127.0.0.1:4021/demo' \
  -H 'x-maple-estimated-cents: 1'

Local x402 seller example

cd examples/seller-express-svm
cp env.example .env
# Set SVM_PAY_TO to your devnet receiving pubkey
bun run dev

Point x-maple-request-url at your seller base URL and path. More background: docs/SELLER.md, docs/BUYERS.md.

Buyer examples (x402 vs Maple)

Example What it does
examples/buyer-x402-direct x402 only: @x402/fetch + SVM_PRIVATE_KEY — pays the seller directly (no Maple gateway).
examples/buyer-maple Maple gateway: @maple/buyerPOST /maple with x-maple-* headers; Maple runs credit/policy and may paid-fetch the seller with MAPLE_SVM_X402_PRIVATE_KEY.

Further reading

  • AGENTS.md — product goal and conventions
  • docs/SELLER.md, docs/BUYERS.md — x402 notes
  • x402 payment-identifier extension — idempotency semantics for paid retries

About

On/Off-Chain, Credit Reporting Agent built for Solana Ecosystem.

Resources

Stars

Watchers

Forks

Contributors

Languages