Quick Start
Get up and running with MARC Protocol in 4 steps.
Prerequisites
- * Node.js 18+
- * An Ethereum wallet with Sepolia ETH (for gas)
- * Sepolia USDC (use the MockUSDC faucet or mint from contract)
- * An RPC endpoint (Infura, Alchemy, etc.)
Step 1: Install the SDK
npm install marc-protocol-sdk ethers@6The SDK includes TypeScript types, the payment handler, Express middleware, facilitator server, and ERC-8004/8183 helpers.
Step 2: Wrap USDC into cUSDC
Before making encrypted payments, wrap your plaintext USDC into encrypted cUSDC. This is a one-time operation per funding.
wrap.tstypescript
import { ethers } from "ethers";
const USDC = "0xc89e913676B034f8b38E49f7508803d1cDEC9F4f";
const CUSDC = "0xE944754aa70d4924dc5d8E57774CDf21Df5e592D";
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
// 1. Approve USDC spending
const usdc = new ethers.Contract(USDC, [
"function approve(address, uint256) returns (bool)"
], wallet);
await usdc.approve(CUSDC, 10_000_000n); // 10 USDC
// 2. Wrap USDC into encrypted cUSDC
const cusdc = new ethers.Contract(CUSDC, [
"function wrap(address to, uint256 amount)"
], wallet);
await cusdc.wrap(wallet.address, 10_000_000n); // 10 USDC
// Net: 9.99 cUSDC (0.1% fee deducted)Step 3: Make an Encrypted Payment
Use fheFetch to automatically handle the HTTP 402 payment flow. When the server responds 402, the SDK encrypts the payment amount, sends two transactions (transfer + record), and retries the request.
pay.tstypescript
import { fheFetch } from "marc-protocol-sdk";
import { createInstance } from "@zama-fhe/relayer-sdk";
// Initialize FHE instance
const fhevm = await createInstance({
gatewayUrl: "https://gateway.zama.ai",
chainId: 11155111,
});
// Make request — 402 is handled automatically
const response = await fheFetch("https://api.example.com/premium", {
tokenAddress: "0xE944754aa70d4924dc5d8E57774CDf21Df5e592D",
verifierAddress: "0x4503A7aee235aBD10e6064BBa8E14235fdF041f4",
rpcUrl: process.env.RPC_URL!,
signer: wallet,
fhevmInstance: fhevm,
});
console.log(await response.json());What happens under the hood:
- Client sends GET request to server
- Server responds
402withFhePaymentRequiredbody - SDK encrypts amount using
fhevmjs - TX 1:
cUSDC.confidentialTransfer(to, encAmount, proof) - TX 2:
verifier.recordPayment(payer, server, nonce, minPrice) - Client retries with
Paymentheader containing tx hashes - Server verifies events on-chain and responds
200
Step 4: Verify on the Server Side
Use fhePaywall Express middleware to gate your API behind encrypted payments.
server.tstypescript
import express from "express";
import { fhePaywall } from "marc-protocol-sdk";
const app = express();
app.get("/api/premium",
fhePaywall({
price: "1000000", // 1 USDC (6 decimals)
asset: "USDC",
tokenAddress: "0xE944754aa70d4924dc5d8E57774CDf21Df5e592D",
verifierAddress: "0x4503A7aee235aBD10e6064BBa8E14235fdF041f4",
recipientAddress: "0xYOUR_WALLET",
rpcUrl: process.env.RPC_URL!,
}),
(req, res) => {
res.json({
data: "premium content",
paidBy: req.paymentInfo?.from,
});
}
);
app.listen(3000);