Current collected state Current
Observed since 20 Oct, 22:15 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
@0takuc0mrade/swiftrail-sdk
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.
Declarations describe requirements; they are not a resolved dependency graph.
Source distribution
{
"integrity": "sha512-d8CCN5k+rBn3AW5faVeKAyGZVcyucRBPPoMS16tuGeJdWgti7M42yLbdK8gax/OcCmywHfPNDZn12C2Hy5a8uw==",
"shasum": "ffbedd0ec86f986adf105b5042807bf89e5073de",
"tarball": "https://registry.npmjs.org/@0takuc0mrade/swiftrail-sdk/-/swiftrail-sdk-0.1.1.tgz",
"fileCount": 6,
"unpackedSize": 471564,
"signatures": [
{
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U",
"sig": "MEUCIBni+ndtSxw+OGqJU88bHDXW32FguC0Pn78cZVjQNBsRAiEAnyd+GtcTSBjo69bnTLYl2uy8LhPOQd/fQZ0sykyRb+8="
}
]
}Observed since 20 Oct, 22:15 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:3aabdfa40efb4ce18f36c8a8e9434fe736afff30c52a58c4a667c3322e02daf1