Keyrxng

Payment Portal: Sub-second load performance

Ubiquity · 2024 · Cut load time from ~12.5s to sub-1s by consolidating providers, batching calls, pruning assets, and rendering optimistically.

So what?
<1 s initial render
Role
Product Engineer
Year
2024
Stack
TypeScript, ethers.js, esbuild, GitHub Actions
Read narrative
<1 s initial render

Problem

The payment portal took ~12.5s to become usable, issuing ~40 requests and repeating provider detection logic, causing long delays and UI flicker.

Approach

System diagram

flowchart LR
  User[User loads page] --> RPC[Optimal RPC provider selection]
  RPC --> Static[Static permit data rendering]
  Static --> Async[Async treasury data fetching]
  Async --> Updates[Dynamic balance/allowance updates]
  Updates --> Ready[Fully loaded claim interface]

Outcome

Proof

Code excerpt — optimal provider selection (trimmed)

export async function getOptimalProvider(networkId: number) {
  if (networkId === 31337)
    return new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545", { name: "http://127.0.0.1:8545", chainId: 31337, ensAddress: "" });
  const promises = networkRpcs[networkId].map(async (baseURL: string) => {
    try {
      const startTime = performance.now();
      // … measure and pick fastest

Code excerpt — optimistic render + async treasury

renderDetailsFields([
  { name: "From", value: `${permit.owner}` },
  { name: "Balance", value: "Loading..." },
  { name: "Allowance", value: "Loading..." },
]);
table.setAttribute(`data-claim`, "ok");
table.setAttribute(`data-contract-loaded`, "true");
const { balance, allowance } = await fetchTreasury(permit.permit.permitted.token, permit.owner, provider);

Performance notes

Before: ~40 requests → 14–18; ~6–7s → 1.8–3s; 8.2MB → 3.9MB. Sub-1s UI via optimistic render.

CI waits and funding sequencing (trimmed)

- name: Wait for Anvil
  run: |
    for i in {1..30}; do curl -s http://localhost:8545 && break; sleep 1; done || exit 1
- name: Fund test accounts
  run: yarn test:fund

References