@gnosisflow/payment-intent-sdk
v0.1.0
Published
TypeScript SDK for Payment Intent Standard (PIS) — encode, hash, verify
Readme
@gnosisflow/payment-intent-sdk
TypeScript SDK for Payment Intent Standard (PIS) — encode off-chain payloads, hash them, and verify via on-chain registry (PIS-1) or EIP-712 signatures (PIS-2).
Install
From the monorepo root:
npm install
npm run build --workspace=@gnosisflow/payment-intent-sdkIn another project (after publish):
npm install @gnosisflow/payment-intent-sdk viemQuick start
import {
encodePaymentIntent,
hashPayload,
verifyOnChain,
signPaymentIntent,
verifyEip712,
verifyPaymentIntentV1,
verifyPaymentIntentV2,
} from "@gnosisflow/payment-intent-sdk";
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
const intent = {
invoiceId: "inv-001",
recipient: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
amount: 1_000_000n,
chainId: 1,
expiresAt: 1893456000n,
};PIS-1 — on-chain registry
Recipient registers keccak256(protobuf payload) on PaymentIntentRegistry. Wallet verifies before sending.
// 1. Encode off-chain payload (protobuf)
const payload = encodePaymentIntent(intent);
// 2. Hash for on-chain key
const payloadHash = hashPayload(payload); // keccak256 by default
// 3. Issuer: registry.createIntent(payloadHash, expiresAt) — on-chain tx
// 4. Wallet: verify
const client = createPublicClient({ chain: mainnet, transport: http() });
const { payloadHash, status } = await verifyPaymentIntentV1(
client,
"0xRegistryAddress...",
payload
);
if (status.verified) {
console.log("✓ Verified Payment Intent", status.issuer);
}Lower-level API:
import { getIntent, verifyOnChain, paymentIntentRegistryAbi } from "@gnosisflow/payment-intent-sdk";
const intentMeta = await getIntent(client, registryAddress, payloadHash);
const status = await verifyOnChain(client, registryAddress, payloadHash);PIS-2 — EIP-712 (0 gas to create)
Recipient signs the intent off-chain. Wallet verifies signature locally.
import { privateKeyToAccount } from "viem/accounts";
const issuer = privateKeyToAccount("0x...");
// Sign (0 gas)
const signature = await signPaymentIntent(issuer, intent);
// Verify
const { status } = await verifyPaymentIntentV2(intent, signature, issuer.address);
if (status.verified) {
console.log("✓ Verified Payment Intent", status.method); // "eip712"
}EIP-712 domain (matches on-chain PaymentIntentEIP712 library):
| Field | Value |
|-------|-------|
| name | PaymentIntentRegistry |
| version | 1 |
| chainId | intent chain |
| verifyingContract | 0x0000000000000000000000000000000000000000 |
API reference
Encoding
| Function | Description |
|----------|-------------|
| encodePaymentIntent(intent) | PaymentIntent → protobuf Uint8Array |
| decodePaymentIntent(payload) | protobuf → PaymentIntent |
| hashPayload(payload, algorithm?) | keccak256 (default) or sha256 |
PIS-1 (registry)
| Function | Description |
|----------|-------------|
| paymentIntentRegistryAbi | viem ABI for IPaymentIntentRegistry |
| getRegistryContract(client, address) | typed contract instance |
| getIntent(client, registry, hash) | read intent metadata |
| verifyOnChain(client, registry, hash) | wallet verification flow |
| verifyPaymentIntentV1(client, registry, payload) | encode + hash + verify |
PIS-2 (EIP-712)
| Function | Description |
|----------|-------------|
| buildEip712Domain(chainId) | EIP-712 domain object |
| buildTypedData(intent) | full typed data for signing |
| signPaymentIntent(signer, intent) | sign with WalletClient or LocalAccount |
| verifyEip712(intent, signature, expectedIssuer?) | recover signer + checks |
| verifyPaymentIntentV2(intent, signature, expectedIssuer?) | convenience wrapper |
Types
interface PaymentIntent {
invoiceId: string;
recipient: Address;
token: Address;
amount: bigint;
chainId: number;
expiresAt: bigint;
}
type VerificationStatus =
| { verified: true; method: "registry" | "eip712"; issuer: Address }
| { verified: false; reason: "not_found" | "revoked" | "expired" | "invalid_signature" | "issuer_mismatch" };Payload format
Protobuf schema: proto/payment_intent.proto
invoice_id, recipient (20 bytes), token (20 bytes),
amount (uint64), chain_id (uint32), expires_at (uint64)Development
# from repo root
npm run build --workspace=@gnosisflow/payment-intent-sdk
npm run test --workspace=@gnosisflow/payment-intent-sdkRelated
- Root README
- PIS specification
- Solidity interfaces:
contracts/interfaces/
License
MIT
