Current collected state Current
Observed since 20 Oct, 22:15 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
SwiftRail gasless checkout SDK for Arbitrum
This guide shows how to integrate the checkout SDK into your own app (React, Vite, Next.js).
Published npm package:
@0takuc0mrade/swiftrail-sdkhttps://www.npmjs.com/package/@0takuc0mrade/swiftrail-sdknpm i @0takuc0mrade/swiftrail-sdk
connect_wallet)421614)eth_signTypedData_v4)POST /submit-payment)http://127.0.0.1:3000)window.ethereum, MetaMask)From repo root:
wasm-pack build sdk --target web --out-dir ./frontend/pkg
For another app/repo, set --out-dir to a folder inside that app and import from that path.
import init, { ArbitrumCheckout } from "@0takuc0mrade/swiftrail-sdk";
await init();
const sdk = new ArbitrumCheckout(
"https://your-relayer.example.com", // relayer URL
"0xYourPaymentContractAddress", // payment contract
"0xYourUsdcAddress" // USDC token
);
const user = await sdk.connect_wallet();
const merchant = "0xMerchantAddress";
const amount = "20000000"; // 20 USDC in base units (6 decimals)
const resultJson = await sdk.checkout(user, merchant, amount);
const result = JSON.parse(resultJson);
import { useEffect, useState } from "react";
import init, { ArbitrumCheckout } from "@0takuc0mrade/swiftrail-sdk";
export default function CheckoutWidget() {
const [sdk, setSdk] = useState(null);
const [user, setUser] = useState("");
const [status, setStatus] = useState("");
useEffect(() => {
let mounted = true;
(async () => {
await init();
if (!mounted) return;
setSdk(
new ArbitrumCheckout(
"https://your-relayer.example.com",
"0xYourPaymentContractAddress",
"0xYourUsdcAddress"
)
);
})().catch((e) => setStatus(String(e)));
return () => {
mounted = false;
};
}, []);
async function connect() {
if (!sdk) return;
try {
const addr = await sdk.connect_wallet();
setUser(String(addr));
setStatus(`Connected: ${String(addr)}`);
} catch (e) {
setStatus(`Connect failed: ${String(e)}`);
}
}
async function pay() {
if (!sdk || !user) return;
try {
const res = await sdk.checkout(user, "0xMerchantAddress", "20000000");
const parsed = JSON.parse(String(res));
setStatus(`Payment tx: ${parsed.payment_tx_hash}`);
} catch (e) {
setStatus(`Payment failed: ${String(e)}`);
}
}
return (
<div>
<button onClick={connect}>Connect</button>
<button onClick={pay}>Pay</button>
<pre>{status}</pre>
</div>
);
}
Use the same React example above. For plain Vite JS:
import init, { ArbitrumCheckout } from "@0takuc0mrade/swiftrail-sdk";
const status = document.querySelector("#status");
const connectBtn = document.querySelector("#connect");
const payBtn = document.querySelector("#pay");
await init();
const sdk = new ArbitrumCheckout(
"https://your-relayer.example.com",
"0xYourPaymentContractAddress",
"0xYourUsdcAddress"
);
let user = "";
connectBtn.onclick = async () => {
try {
user = String(await sdk.connect_wallet());
status.textContent = `Connected: ${user}`;
} catch (e) {
status.textContent = `Connect failed: ${String(e)}`;
}
};
payBtn.onclick = async () => {
try {
const res = await sdk.checkout(user, "0xMerchantAddress", "20000000");
const parsed = JSON.parse(String(res));
status.textContent = `Payment tx: ${parsed.payment_tx_hash}`;
} catch (e) {
status.textContent = `Payment failed: ${String(e)}`;
}
};
Create a client component (important):
"use client";
import { useEffect, useState } from "react";
import init, { ArbitrumCheckout } from "@0takuc0mrade/swiftrail-sdk";
export default function CheckoutClient() {
const [sdk, setSdk] = useState<ArbitrumCheckout | null>(null);
const [user, setUser] = useState("");
const [status, setStatus] = useState("");
useEffect(() => {
(async () => {
await init();
setSdk(
new ArbitrumCheckout(
"https://your-relayer.example.com",
"0xYourPaymentContractAddress",
"0xYourUsdcAddress"
)
);
})().catch((e) => setStatus(String(e)));
}, []);
async function connect() {
if (!sdk) return;
try {
setUser(String(await sdk.connect_wallet()));
} catch (e) {
setStatus(String(e));
}
}
async function pay() {
if (!sdk || !user) return;
try {
const raw = await sdk.checkout(user, "0xMerchantAddress", "20000000");
const parsed = JSON.parse(String(raw));
setStatus(`Payment tx: ${parsed.payment_tx_hash}`);
} catch (e) {
setStatus(`Payment failed: ${String(e)}`);
}
}
return (
<div>
<button onClick={connect}>Connect</button>
<button onClick={pay}>Pay</button>
<p>{status}</p>
</div>
);
}
"20") instead of base units ("20000000").window.ethereum).JSON.parse.On success:
payment_tx_hashsettlement_tx_hashgross_amountfee_amountnet_amountfee_bpsfee_model_notesDownload counts and popularity will appear here as the collector receives them.
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 22:15 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:c6194156558ead21026eb92e4edf2997eadf6424000ec02f1b1f7bacbc49e273