@caplane-network/sdk
v0.1.0
Published
Caplane consumer SDK — buy scarce on-chain capabilities in ~10 lines. No coordinator, no gas required.
Downloads
54
Maintainers
Readme
@caplane-network/sdk
Buy scarce on-chain capabilities in ~10 lines. No coordinator, no ETH required on the consumer side.
Caplane is a serverless, facilitator-free primitive: one atomic on-chain transaction settles payment + capability grant. The consumer only signs — the permissionless relayer broadcasts and earns a fee.
Install
npm i @caplane-network/sdk viemQuick start — buy an Allocation slot on Robinhood testnet
import { connect } from "@caplane-network/sdk";
import { privateKeyToAccount } from "viem/accounts";
// Your key stays in env — never hardcoded.
const account = privateKeyToAccount(process.env.CONSUMER_PK as `0x${string}`);
// Fund first (testnet only): POST https://faucet.caplane.xyz/faucet
// { address: account.address, chainId: 46630 }
const client = await connect({
chainId: 46630, // Robinhood testnet
relayerUrl: "https://relayer.caplane.xyz", // hosted permissionless relayer
});
const result = await client.buyAllocation({
account,
vaultId: 1n, // Allocation vault 1 (cap 100, price 5 TUSDC)
feeBps: 50, // 0.5% relayer fee
});
console.log(result.txHash); // on-chain settlement tx
console.log(result.slotTokenId); // your ERC-721 allocation slot
console.log(result.explorerUrl); // https://explorer.testnet.chain.robinhood.com/tx/...Testnet funding
The consumer is gas-poor (it only signs). Fund TUSDC from the hosted faucet:
curl -X POST https://faucet.caplane.xyz/faucet \
-H "content-type: application/json" \
-d '{"address":"0xYOUR_ADDRESS","chainId":46630}'Returns 10 TUSDC (10_000_000 in 6-decimal units). Rate-limited to once per 24h per address.
Generic broker (any capability)
import { connect, encodeAllocationParams } from "@caplane-network/sdk";
const client = await connect({ chainId: 46630, relayerUrl: "https://relayer.caplane.xyz" });
const result = await client.broker({
account,
adapter: "0x76C4F7d48Bba5f4fa36C47D9B34ba3b5Ea46FE43", // AllocationAdapter
params: encodeAllocationParams(1n),
amount: 5_000_000n, // 5 TUSDC (6 decimals)
feeBps: 50,
});
console.log(result.receipt); // raw adapter receipt (abi-encoded)Low-level signing
For advanced use — build, sign, and submit manually:
import { connect, buildOrder, signOrder, submit, encodeAllocationParams } from "@caplane-network/sdk";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.CONSUMER_PK as `0x${string}`);
const client = await connect({ chainId: 46630, relayerUrl: "https://relayer.caplane.xyz" });
const order = buildOrder({
consumer: account.address,
adapter: client.registry.contracts.AllocationAdapter!,
params: encodeAllocationParams(1n),
payToken: client.registry.usdc.verifyingContract,
amount: 5_000_000n,
feeBps: 50,
payee: account.address,
});
const now = BigInt(Math.floor(Date.now() / 1000));
const sig = await signOrder(account, client.domain, order, client.brokerAddress, 0n, now + 3600n);
const res = await submit("https://relayer.caplane.xyz", order, sig, 46630);How it works
- The consumer builds an
Order(adapter + params + amount + feeBps + payee). nonce = keccak256(abi.encode(order))is computed off-chain.- The consumer signs an EIP-3009
TransferWithAuthorizationwithnonce = orderHash. - Any relayer calls
CapabilityBroker.brokerCapability(order, sig):- Settles USDC via
transferWithAuthorization(the USDC contract verifies the sig). - Calls
adapter.grant(consumer, params)atomically. - Pays the provider
amount - feeand the relayerfeeBps.
- Settles USDC via
- If the adapter cannot grant (sold out, etc.), the whole tx reverts — no payment leaks.
The relayer earns feeBps — that's what makes it permissionless and incentivized.
API reference
connect(opts)
interface ConnectOptions {
chainId: number; // 46630 = Robinhood testnet, 421614 = Arbitrum Sepolia
relayerUrl: string; // "https://relayer.caplane.xyz" or self-hosted
deploymentsDir?: string; // override (else env CAPLANE_DEPLOYMENTS_DIR, else baked config)
publicClient?: PublicClient; // inject viem PublicClient (for tests)
fetchImpl?: FetchLike; // inject fetch (for tests)
}Returns a CaplaneClient with buyAllocation(), broker(), registry, domain, brokerAddress.
Key exports
| Export | Purpose |
|--------|---------|
| connect | Main entry point — loads registry, returns a key-free client |
| buyAllocation | (via client) buy an Allocation slot end-to-end |
| broker | (via client) buy any capability |
| buildOrder | Assemble an Order with a random nonce salt |
| orderHash | keccak256(abi.encode(order)) — the EIP-3009 nonce |
| encodeOrder | abi.encode(order) — byte-for-byte identical to Solidity |
| signOrder | Sign the EIP-3009 authorization (nonce = orderHash) |
| submit | POST to a relayer's /broker endpoint |
| encodeAllocationParams | abi.encode(uint256 vaultId) |
| decodeAllocationReceipt | Decode the minted slotId from a grant receipt |
| loadChain | Load a chain registry (baked or live filesystem) |
| resolveCapability | Resolve a capability name to chain + adapter |
| BAKED_CHAINS | The baked-in chain registries (no filesystem needed) |
Supported chains
| Chain | chainId | Use case | |-------|---------|----------| | Robinhood testnet | 46630 | Allocation slots (guaranteed floor) | | Arbitrum Sepolia | 421614 | Timeboost express lane + Attested allocation |
Security model
- The consumer only signs — it needs zero ETH.
- The relayer cannot steal:
nonce = orderHashties the USDC authorization to the exact order. Any tamper changes the hash andtransferWithAuthorizationreverts. - The relayer earns only the declared
feeBps. It cannot increase its own fee. - Private keys stay in your environment — the SDK never logs or transmits them.
License
MIT
