Lamport is a neighborhood neobank where people sign up with an email — no wallet, no seed phrase — and the app provisions a custodial Solana wallet for each of them. They deposit, send money to other users with an on-chain proof link, set up a recurring payment, and watch balances update live. It’s a complete Rails 8 app running on devnet.
It’s the wallets-and-payments half of the custodial Wallet-per-User path through the Solana Developer Platform (SDP), where your app holds wallets for users instead of asking them to connect one. Its issuance counterpart is Kudos — same custody model, but for minting and burning a points token. Lamport is also the demo that drove the SDP gems into existence.
What it does
- Email signup → auto-provisioned custody wallet — no seed phrase, no wallet extension
- Deposits — devnet funding via a faucet, with a pre-funded treasury covering rate limits
- P2P transfers — send to another user, confirmed synchronously, with a Solana Explorer proof link
- Recurring payments — a “rent” schedule that fires real on-chain transfers each period
- Live balances — pushed to the browser the moment a transfer settles, no polling
- Fees sponsored — users pay no gas; a Kora fee-payer covers it
How it’s built
Lamport was hand-rolled against SDP first; the reusable parts were then extracted into two gems, and the demo was rebuilt on the published gems — deleting 1,522 lines of custom code. The whole money path runs through SDP:
# Email signup provisions a custody wallet per user (async, off the request).
class User < ApplicationRecord
include Solrengine::Sdp::WalletOwner
after_create_commit :provision_wallet!
end
# A P2P send is one engine call — SDP holds custody, signs, and Kora pays the fee.
transfer = Solrengine::Sdp::Transfer.execute!(
source: sender.sdp_wallet_id, destination: recipient.wallet_address,
amount: "0.1", memo: "rent"
)
transfer.status # "processing" → tracked to "confirmed" → "finalized"
| Lives in Rails (SQLite) | Lives on Solana (via SDP) |
|---|---|
| Users, transfer audit rows, recurrence schedules | A custodial wallet per user |
| Affordability checks, in-flight locks | Every transfer, signed custodially |
Session auth (has_secure_password) |
On-chain history (rendered verbatim) |
Two decisions worth calling out:
- Writes never retry. SDP has no idempotency key, so a re-sent transfer after a timeout could double-spend. A timeout is reconciled (match the memo against the transfer list) rather than re-sent.
- Recurrence is Rails-scheduled. On the SDP version Lamport targets, the
recurring-payments API is records-only — so Lamport schedules with Solid Queue
and a unique
(schedule, period)index; a duplicate tick makes zero SDP calls.
Because the app initiates every transfer, the job that settles it is the doorbell: it re-fetches from SDP and pushes the new balance to the browser live — no chain-WebSocket polling.
Running it
SDP is pre-mainnet and devnet-oriented, so there’s no hosted demo — it needs a running SDP instance plus a managed custody provider and a Kora fee-payer. The full source is on GitHub, the build story is on the blog, and the self-hosting field guide covers the infrastructure. See the Wallet-per-User docs for the prerequisites.
Born from a Solana Foundation SDP workshop at Superteam Thailand. Verified
end-to-end on devnet (126 tests) on the published solana-sdp and solrengine-sdp
gems — the first Ruby SDK for SDP.