Plinth
Devnet

Build on Plinth

Sell or buy data per request with x402, settle x402 payments for your own API, and drive loans from code. Everything here pays and settles in USDC on Solana Devnet.

01 / Overview

What you can build

Plinth is a USDC lending pool on Solana backed by stock tokens. Around it sit three tools for software: a paid data API, a payment facilitator for other services, and a JavaScript SDK with a CLI. Payments use x402, the HTTP 402 payment standard: a request without payment gets the price, and a signed USDC transfer for that price gets the response.

  1. 01Paid data

    Pool conditions, loan quotes, borrower reports, and loans at risk, for a few cents in USDC per request.

  2. 02Facilitator

    Take x402 payments for your own API without running a wallet server or holding SOL. (not enabled on this site yet)

  3. 03SDK and CLI

    Quote, borrow, repay, deposit, and pay x402 endpoints from an agent or a script.

02 / Paid data

Buy pool data over x402

  1. 01

    Ask

    Call an endpoint without paying. It answers 402 Payment Required with the price in the PAYMENT-REQUIRED header (x402 v2); the body repeats it in the v1 format. The requirement names the server's fee payer in extra.feePayer.

  2. 02

    Sign

    Build a USDC TransferChecked for exactly that price to the payee's token account, with the server as fee payer, and sign it. Signing sends nothing by itself.

  3. 03

    Pay and receive

    Send the request again with the signed transaction in PAYMENT-SIGNATURE (or X-PAYMENT for v1). The server checks it, adds its fee-payer signature, sends it, and answers with the data and the settlement in PAYMENT-RESPONSE (X-PAYMENT-RESPONSE for v1). The USDC goes straight to the payee; the server only pays the network fee.

GET /api/x402/pool

0.01 USDC. Pool conditions: funds, idle USDC, utilization, safety fund, limits, and every listed stock with its price and loan limits.

GET /api/x402/quote

0.01 USDC. Loan quote: fee, total to repay, liquidation price, and the largest loan allowed for a stock, collateral, amount, and term. Parameters: asset, collateral, amount, days, borrower (optional).

GET /api/x402/borrower/{address}

0.03 USDC. Borrower report: repayment record, debt now, open loans with health, and full loan history.

GET /api/x402/risk

0.05 USDC. Open loans near or past their liquidation threshold or due date, with health and collateral.

The free catalog at GET /api/x402 lists every resource, its price, the network, the USDC mint, the fee payer, and where payments go. You are never charged for a request that fails: bad parameters answer 400, an unknown borrower 404, and an unreadable chain 503, all before a payment is asked for.

If settling takes more than a few seconds, the server answers 402 with "pending": true and a Retry-After header. Send the same signed payment again after that many seconds: once it has settled, the data is served for it without a second charge. A signed payment is only used for the endpoint it was sent to.

With the official x402 client

Any standard x402 v2 client for Solana can pay, including the official @x402/fetch with @x402/svm. Allow USDC on this network in its spend controls, with a cap per payment:

import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactSvmScheme } from "@x402/svm/exact/client";

// signer: a @solana/kit TransactionSigner (e.g. a keypair loaded from a file; never hard-code a key)
const pay = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "solana:*", client: new ExactSvmScheme(signer) }],
  spendControls: { allowedAssets: [{ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", asset: "7k8RSp2TLduCfVJKyMsjMWHCoc3cYQQJrJm6pF44B5c7", maxAmountPerPayment: "100000" }] }, // at most 0.1 USDC per call
});
const res = await pay("https://www.plinthcity.com/api/x402/pool");
03 / Paying from an agent

Pay any x402 endpoint, and borrow the gap

The Plinth CLI and SDK include an x402 payer. It pays any x402 endpoint in USDC on this network, including other sites. If the agent's USDC balance is short, it can borrow the difference from this pool against a stock token the agent holds (at least the pool's minimum loan, locked with a buffer above the loan limit), then pay, and keep the rest for the next calls. The agent needs a little SOL only to borrow and repay; x402 payments themselves are paid for by the server's fee payer.

npx plinth x402-catalog --url https://www.plinthcity.com
npx plinth pay --url https://www.plinthcity.com/api/x402/pool --max-price 0.05
npx plinth pay --url https://www.plinthcity.com/api/x402/pool --max-price 0.05 --max-borrow 5 --asset NVDAx --days 7 --yes

Without --yes the command only previews: the price, your balance, and any loan it would take. --max-price and --max-borrow are hard limits. A loan taken to pay follows every normal rule: a fixed fee, a due date, and liquidation of all its collateral if it is not repaid. x402 never opens a loan on its own, never repays, and never gives Plinth control of the agent's wallet: every payment and every loan is signed by the agent.

04 / Facilitator

Take x402 payments for your own API (not enabled on this site yet)

A facilitator checks each payment, adds its fee-payer signature, and sends the transfer for you. With Plinth's, you run no wallet server and hold no SOL: the USDC moves from the payer straight to your wallet, and the facilitator pays the network fee. It never signs a transaction in which its own key would move funds.

  1. 01

    Register

    On the Merchants page, connect the Solana wallet that should be paid and sign one message. You receive an API key, shown once.

  2. 02

    Connect your server

    Point your x402 server's facilitator at https://www.plinthcity.com/api/facilitator with the key in an Authorization: Bearer header. Its fee payer is listed at GET /supported.

  3. 03

    Get paid

    Each settle is free up to a daily number; after that a small fixed fee comes from a prepaid USDC balance you top up from the same wallet. A settle that is refused or fails costs nothing.

import { HTTPFacilitatorClient, x402ResourceServer } from "@x402/core/server";
import { ExactSvmScheme } from "@x402/svm/exact/server";

const facilitator = new HTTPFacilitatorClient({
  url: "https://www.plinthcity.com/api/facilitator",
  createAuthHeaders: async () => ({
    verify: { Authorization: `Bearer ${process.env.FACILITATOR_KEY}` },
    settle: { Authorization: `Bearer ${process.env.FACILITATOR_KEY}` },
    supported: {},
  }),
});
const server = new x402ResourceServer(facilitator).register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme());
// price in USDC atomic units (6 decimals): 20000 = 0.02 USDC; extra.feePayer comes from GET /supported
const requirements = await server.buildPaymentRequirements({
  scheme: "exact", network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", payTo: YOUR_WALLET,
  price: { amount: "20000", asset: "7k8RSp2TLduCfVJKyMsjMWHCoc3cYQQJrJm6pF44B5c7" },
});

POST /verify, POST /settle

The standard x402 facilitator calls. Your key only settles payments to your own wallet.

GET /supported

Networks, x402 versions, and the fee payer that signs as the transaction's fee payer.

GET /health

The current terms: free settles, the fee, daily limits, the smallest payment, and the fee payer's SOL.

GET /merchants, /settlements, /discovery/resources

Public lists of merchants, recent settlements, and paid resources seen on this facilitator.

New merchants are listed as unverified until the site owner checks them, and a merchant can be suspended for abuse. The facilitator has had an internal review and tests against malicious payments only, not an independent audit.

05 / SDK and CLI

Drive the pool from code

The JavaScript SDK (@solana/kit) and the plinth CLI cover every pool action: read the market, quote, borrow, add collateral, repay, deposit, withdraw, liquidate, and pay x402 endpoints. Every action that moves money has a preview first, and the CLI sends nothing without --yes.

Install

npm install @plinth/sdk     # JavaScript SDK (Node.js 20+); Solana API at @plinth/sdk/solana
npx plinth --help                # the CLI, no install needed

The Solana Devnet deployment ships with the SDK, so reading needs only SOLANA_CLUSTER (and optionally SOLANA_RPC_URL): try npx plinth doctor or npx plinth pool. To send transactions, set SOLANA_KEYPAIR to a keypair file (solana-keygen format), and ALLOW_MAINNET=true on mainnet.

Borrow and repay

import { LendingClient, loadSolanaDeployment, loadKeypairSigner, parseUsd, parseToken } from "@plinth/sdk/solana";

const signer = await loadKeypairSigner(process.env.SOLANA_KEYPAIR); // a keypair file; never hard-code a key
const pool = new LendingClient({ rpcUrl: "https://api.devnet.solana.com", deployment: loadSolanaDeployment("devnet"), signer });

const q = await pool.quote({ asset: "NVDAx", collateral: parseToken("2", 6), amount: parseUsd("50"), days: 7, borrower: signer.address });
if (q.reasons.length === 0) {
  const { loanId } = await pool.borrow({ asset: "NVDAx", collateral: parseToken("2", 6), amount: parseUsd("50"), days: 7 });
  const loan = await pool.loan(loanId); // health, due date, whether it can be liquidated
  await pool.repay(loanId);
}
// a new address starts with a 3 USDC limit that grows with loans held 7+ days and repaid in full

Pay x402, borrowing only if needed

import { X402Payer, parseUsd } from "@plinth/sdk/solana";

const payer = new X402Payer({ client: pool, maxPrice: parseUsd("0.05"), maxBorrow: parseUsd("5"), collateral: "NVDAx", days: 7 });
const preview = await payer.preview(url); // price, balance, any loan it would take; nothing is signed
const { response, paid, borrowed, loanId } = await payer.pay(url);

Main methods: poolStats, assets, quote, borrowLimit, borrow, loan, loans, addCollateral, repay, previewDeposit/deposit, positionsOf, previewWithdraw/withdraw, previewLiquidate/liquidate, and record for an address's repayment history.

06 / Limits and safety

Before an agent spends or borrows

Cap the price per request and the loan size, restrict which collateral an agent may use, keep a buffer above the loan limit, and keep enough USDC to repay. If prices or pool data cannot be verified, stop opening loans and ask for a human review. A loan can be liquidated after a price drop, or once the due date and its one-day grace period have passed; the liquidator takes all of the collateral.

The pool program has not been audited and can still be upgraded by its upgrade authority. Read the risks before using real funds.

Developers | Plinth