Protocol

Docs

Paidline is a public marketplace with a payment checker. You list work on Creditcoin. Buyers pay USDC on Ethereum. The contract is the only thing that may say the listing is paid.

Live contract

0x4fB6aB16B3CEf1853DF4247b245E4de139108339

Verified on Blockscout. Inherits ASCBase. Replay is keyed on Attestcoin query id.

What it is

Not a bridge. Not a credit score. Not a company that reports a payment. You publish a listing. Anyone may pay the exact USDC. A proof of that Ethereum transfer is submitted on Creditcoin. If token, destination, and amount match, the listing is paid and locked credit releases to the buyer.

Two open invoices cannot share the same chain, token, destination, and amount. One payment can only mean one invoice. There is no buyer list. Locked Creditcoin releases to the wallet that sent the matching transfer. First payment claims it.

How it works

  1. 01

    Issue

    The invoice is written on Creditcoin: the work, amount, destination, expiry. The work stays off-chain and locked until paid. Creditcoin may be locked against the listing.

  2. 02

    Pay

    The buyer sends USDC on Ethereum. Paidline never receives that USDC.

  3. 03

    Prove

    Anyone may submit the Ethereum transaction. Attestcoin says whether it is in the chain. The submitter does not get to assert that it is.

  4. 04

    Match

    Right token, right address, exact amount, open invoice, unused hash. Anything else is rejected.

  5. 05

    Stamp

    isPaid becomes true. InvoicePaid is emitted. Locked Creditcoin releases. Checkout and GET /api/gate/:id serve the work. Same fact.

Attestcoin

Settlement is impossible without a verified proof. Paidline inherits ASCBase. execute() checks Merkle inclusion and continuity at precompile 0x0FD2. queryId is the leaf: chain, block, tx index — not a string the submitter typed. Receipt status must be 1. Transfer logs are scanned for the exact token, destination, and amount. Replay is keyed on that query id and the proved body, so a fake hash cannot relabel a payment.

The relayer fetches a proof and submits. It has no opinion. Other contracts never talk to Attestcoin. They call isPaid.

The stack

Paidline does not invent a new proof system. It sits on three pieces that already exist.

  • Ethereum

    The buyer sends USDC here. Sepolia in this demo. Paidline never receives that transfer.

  • Attestcoin

    The Creditcoin native prover. It says whether an Ethereum transaction is in the chain, with a Merkle proof and continuity. A relayer submits that proof. The submitter does not get to assert the fact.

  • Creditcoin

    The invoice, isPaid, and the optional locked balance live here. CC3 testnet in this demo. Other contracts call this chain, not Ethereum.

  • Paidline

    The checker. Matches token, destination, exact amount, expiry, unused hash. Emits InvoicePaid. Releases locked Creditcoin to the paying wallet.

Who uses it

  • A seller on Creditcoin

    They invoice in USDC. The buyer pays on Ethereum the way they already pay. The desk shows pending, then paid, with the hash.

  • A buyer who will not open a new chain

    They get a number and an amount. They send USDC. They do not operate the proof.

  • An agent calling an API

    The endpoint returns 402 until the invoice is paid. After the stamp, the same request returns the work. x402 with your checker, not someone else’s.

  • Another contract

    It calls isPaid or listens for InvoicePaid. It does not integrate Attestcoin. Paidline is the checker.

  • A marketplace that will not hold funds

    The board is the marketplace. Listings are public. First matching USDC claims the lock. No auction contract. No admin wallet.

  • A protocol gating work

    Mint, unlock, flip a role — after isPaid is true. The payment is the fact. Your contract is the consequence.

isPaid

Other contracts ask this. Unknown invoices return false. True only after a matching remote payment has been verified and local value released.

Solidity

interface IPaidline {
    function isPaid(uint256 invoiceId) external view returns (bool);
    event InvoicePaid(
        uint256 indexed invoiceId,
        bytes32 indexed sourceTxHash,
        address indexed releasedTo,
        uint256 localAmount
    );
}

contract Example {
    IPaidline public immutable paidline;

    constructor(IPaidline checker) {
        paidline = checker;
    }

    function claim(uint256 invoiceId) external {
        require(paidline.isPaid(invoiceId), "unpaid");
        // release the work
    }
}

0x4fB6aB16B3CEf1853DF4247b245E4de139108339

x402

Drop this in front of a resource. Unpaid invoices return 402 with a pay URL. Paid invoices return 200 and the work. Retry after the stamp. No new Solidity.

TypeScript

const res = await fetch(`${origin}/api/gate/${invoiceId}`)
if (res.status === 402) {
  const { pay } = await res.json()
  // send the buyer or agent to pay, then retry
}
if (res.status === 200) {
  const { work } = await res.json()
  // work.kind is "link" or "text"; work.body is the payload
}

Try the gate

Contract

Public testnet. Demo value is not market value. You stay in control of the wallet. Paidline does not hold the USDC.