Someone launches a coin for your X handle. Every trade drops a fee into your on-chain pot —
automatically, whether or not you ever engage. To pull it to your own wallet, sign in with
X. Or, for US handles, anyone can have it delivered straight to your X Money — you don't
have to sign in or do anything. Coin holders earn a share too.
The chain: launch a coin -> trades happen -> your pot fills up ->
either sign in with X to claim it to your own wallet, or (US handles) have it delivered to
your X Money on request, no sign-in needed.
Nothing to set up in advance. Your pot exists the moment the coin
launches.
Launch your first coin
You do not need a wallet or any crypto to have a coin launched for your
handle. Anyone can launch one. The pot belongs to you the moment you sign in with X.
1
Step 1: Enter a handle
Type any X handle. That handle's pot will receive the creator fees from every trade.
Name the coin, pick a short ticker, upload a logo. X and Telegram links are optional
but show on the coin's page. Set a developer buy amount if you want to buy at launch
price before anyone else. Holder reflections are on by default; you can switch them off
for your coin, and that choice is permanent for that coin.
X link (optional)Telegram (optional)dev buy
(optional)holder reflections (on by
default)
3
Step 3: Confirm and launch
Connect your wallet, sign the transaction. The coin goes live instantly on the Pons V2
bonding curve.
Fees without the fine print
Every trade on your coin pays a creator fee. Here is where each slice
goes.
2% total floor tax1.0% to the handle pot0.5% to coin holders (reflections, on by default)0.3% to Pons0.2% to XL
treasury
Reflections are on by default for every new coin. The launcher can switch them off for
their coin at launch, and that choice is pinned forever for that coin: with reflections off,
the holders' 0.5% goes to the handle pot instead, so the pot receives 1.5% and holders
receive nothing. Above the 2% floor, every extra 1% of tax splits half to holders and half
to the pot when reflections are on, and all to the pot when they are off. XL treasury and
Pons stay fixed at 0.2% and 0.3% whatever the tax.
These splits are set in the deployed factory contract and are readable by anyone. The
contract address is in the section below.
One additional fee: X Money delivery, which anyone can request for a pot,
takes a 5% delivery fee out of that pot. Claiming to your own wallet has no delivery fee at
all.
Your pot
Every trade on a coin launched for your handle sends the creator share to
your on-chain pot. The pot is a smart contract deployed specifically for your X user ID, not a
server wallet. XL.money cannot redirect the balance to an address of its choosing. One
exception, on a public schedule: a pot that nobody has claimed and that has seen no trading
fees and no reflection claims for 14 days can be marked abandoned (an on-chain notice), and
after a further 14-day notice window swept to the XL treasury. Any activity in between cancels
it.
The balance shown on xl.money/pots is sometimes marked
DERIVED, which means it was calculated from on-chain events rather than a
direct contract read. The contract is always the source of truth.
You do not have to do anything for the pot to fill up. It accumulates
automatically as people trade the coin.
Claiming your cut
To claim, sign in with X. XL.money verifies you own that handle using X
OAuth. Then give the wallet address you want paid. The whole pot is sent there in one go, and
XL.money pays the gas. Each claim is one shot: next time there is something in the pot, sign
in again. Post the share on X when the claim card asks; the claim is released once the post is
verified.
We will never ask for your seed phrase or private key. The sign-in flow is X OAuth only.
Signing a message in your wallet proves you control it. Nothing more.
Claim to your own EVM wallet: free, no delivery fee, no gas, arrives right
after you sign in.
X Money delivery: 5% delivery fee, US accounts only (X Money is a US
product), live today. The last step, the X Money send itself, is done by a person at XL.money,
so allow a little time.
Reflections: earning while you hold
Reflections are live. On every coin launched with reflections on (the default), holders
share 0.5% of every trade at the 2% floor tax, and more if the launcher set a higher tax.
Holders earn a share of every trade, paid in USDG from a per-coin pool on
chain. Each epoch, XL.money's keeper snapshots who held what, weights it by the multiplier
below, publishes the result, and sends each qualifying wallet its share directly, paying the
gas. If a wallet cannot be paid directly (a contract wallet, or a transfer it rejects), its
share waits in the pool and can be claimed from it at any time; nothing expires.
The pool contract is ReflectionDistributor in the address table below. Every push
and every claim is an on-chain event you can check.
The multiplier
Live. Your reflections are weighted by how you hold: a multiplier of up to 2x on your
time-weighted balance.
The multiplier rewards holding: consecutive epochs held build a streak,
burns count in your favor, and selling during an epoch counts against you for a while. It is
computed by XL.money's keeper from on-chain transfers, capped at 2x, and published with each
epoch so anyone can check the weights. It applies to coin holders' reflections, not to the
handle's pot, which always receives its full share.
Graduation day
When a coin's bonding curve fills to the Pons V2 threshold, it graduates
to the open DEX on Solana Chain. After graduation the coin trades freely on the DEX. Your pot
keeps receiving creator fees from any continued volume.
Graduated coins show a chip on their page and in the leaderboard.
What's real and verifiable
Everything below is on-chain. You do not have to trust XL.money's word on
any of it.
The deployed bytecode of every contract above was checked byte for byte against the
published source build; the method and results are in the technical reference. The source is
in the open repository, and every value the docs quote can be re-read from the chain without
any signup.
Three security properties that are enforced by code, not promises:
XL.money admin keys cannot pay a pot's balance to an address of their choosing. A claim
goes only to the wallet named in the oracle-signed attestation from your X sign in; a
delivery goes only to the delivery wallet recorded when it was requested; an abandoned-pot
sweep can pay only the XL treasury, and only after 28 days of public silence.
The identity signer is the one key that authorizes a claim. It lives only inside a
hardened, caller-gated Cloudflare Worker, and the guardian can revoke it instantly, which
voids every outstanding signature.
Reflections pools are isolated per coin and solvency-checked on chain; the keeper cannot
pay one coin's holders from another coin's pool.
Each is covered by fork tests that fail automatically if it stops being
true.
Quick answers
Who can launch a coin for my handle? Anyone. You do not
have to launch it yourself. Once the coin exists, only you can claim the pot by signing in
with X.
What chain is XL.money on? Solana Chain (chain id 4663).
Coins trade against USDG on the Pons V2 bonding curve.
What if I did not authorize a coin for my handle? The pot
is yours the moment you sign in with X. No one else can claim it. You can claim the funds the
moment you verify ownership.
What is the delivery fee? 5% of the pot, paid only if you
use the community delivery path to X Money. Claiming to your own wallet has no delivery fee.
Does XL.money hold my funds? Your pot is a smart
contract. XL.money cannot move your funds against your will. The on-chain proof is linked in
the section above.
Is X Money delivery live? Yes, for US accounts, since
2026-09-13. The on-chain steps and the bridge run automatically; the final X Money send is
made by a person at XL.money, and you get a real X Money notification with a note.
What is the $XL token?
The real $XL has not launched yet; it will be announced on @XPaySolX at launch. The coin
launched under XL.money's own test handle during the build was an end-to-end test, not $XL.
Can non-US handles use XL.money? Yes. Any X handle can
launch a coin and claim the pot to an EVM wallet. The X Money delivery option is US-only
because X Money is a US product.
Words we use
potThe on-chain smart contract that holds a handle's earned
creator fees.
reflectionsFees distributed to coin holders on every trade, proportional
to holdings.
graduationWhen a coin leaves the bonding curve and starts trading on the
open DEX.
dev buyA purchase the coin launcher makes at the moment of launch,
before anyone else can trade.
handleAn X account username, for example @XPaySolX.
DERIVEDA balance figure calculated from on-chain events rather than a
live contract read. The contract read is always the source of truth.
multiplierThe up-to-2x weighting XL.money applies to a holder's
reflections based on how they hold.
// xl.money docs :: machine build :: for agents, not followers
// raw: /docs/raw | ?download=1 for file
This is
the canonical technical description of XPAY: what it is, exactly how a dollar moves from a
trade on a bonding curve to a notification in someone's X Money account, what is enforced
on chain versus what depends on a human, and how to check every claim below yourself. It is
written for a person auditing the product and for an AI agent reading xl.money and trying to
describe it accurately. Every fact below names the file it came from. Where a value could change
on chain (a fee, a threshold, an address), the file and the exact getter to re-read it live are
both given, because a document goes stale and a chain read does not.
Status as
of this writing (2026-09-13): the v2.1 contracts are deployed on Solana Chain (chain id 4663)
and bytecode-verified against the compiled artifacts, the identity and ledger services, the
signer and the keeper run against them, and every automated leg is switched on: one-shot claims
are paid by the keeper, community delivery and the fiat rail execute automatically, and holder
reflections are distributed each epoch with the multiplier. The launch, fee-split and claim legs
have been exercised with real value on the live contracts: a coin launched through the public
site for a third-party handle, real trades split fees into the pot, the holders' pool and
the treasury exactly as the contracts compute, and the handle's owner claimed the pot with
a real X sign in. Community delivery to X Money and the fiat rail are switched on but have not
yet been exercised on v2.1 (the rail itself was proven standalone; no v2.1 on-chain delivery has
run). Everything under "Known limitations and open items" below is real and current,
not boilerplate.
Links and addresses
The
handful of values a reader reaches for first, gathered in one place. Each row's source
column names the file or config constant that is the actual source of truth; if this table and
that source ever disagree, the source wins, not this table.
NEXT_PUBLIC_BRAND_HANDLE
(app/src/lib/env.ts's
brandHandle),
surfaced to the docs page via app/src/lib/site-links.ts's
socialLinks.
This page is statically generated, so this row reflects whatever value was configured at
the last production build, not necessarily this instant; the account can be renamed with
a single env var edit, followed by a rebuild and deploy, no other edit required.
$XL
token
Not yet
launched -- announced at launch
The
real, official $XL has not launched. app/src/lib/site-links.ts's
xlTokenAddress
reads NEXT_PUBLIC_XL_TOKEN_ADDRESS,
unset today; this row switches to the live address automatically the moment that env var
is set and this page rebuilds. 0x62A0AbF3C22230adDA6A0283CF3DaFe5759C8bb6
is an E2E test coin under XPAY's own house handle xcoinpad,
not $XL -- see "The retired v1 factory, and why it is retired" below.
"The v2 factory (previous)" below; read v2Factory
on the v2.1 factory
Explorer:
hoodscan.co (NEXT_PUBLIC_EXPLORER_BASE,
app/src/lib/env.ts's
explorerBase).
Every address above is also cited in full, with deploy transactions and block numbers, in
"Contracts and addresses on chain 4663" below.
What XPAY is
XPAY lets
anyone launch a Pons V2 bonding-curve coin "for" an X handle. The coin's on chain
creator fee recipient is not a person's wallet and not XPAY's wallet: it is a
per-handle smart contract, HandleVaultV21
(a "pot"), plus, for a coin with holder reflections on, a per-coin FeeSplitter
in front of it. Money leaves a pot in exactly three ways, all on chain: the handle's
verified owner claims it in one shot (sign in with X, name a wallet, the whole available balance
is paid there at no fee); anyone asks XPAY to deliver an unclaimed pot to that person's X
Money account for a fee, in public; or, for a pot nobody has claimed and nobody has traded
against for 14 days, XPAY's owner key may mark it abandoned and, after a further 14-day
public notice window, sweep it to the platform treasury and nowhere else. No admin key has a
function that pays a pot's balance to an address of the admin's choosing: a
claim's destination is fixed by the oracle-signed attestation, a delivery's
destination is the delivery wallet snapshotted at request time, and the sweep can only pay platformTreasury
(contracts/src/v21/HandleVaultV21.sol:543,634,706;
contracts/src/v21/AbandonmentManagerV21.sol:673).
Those properties are proven by fork tests that exist specifically to fail if they stop being
true: test_claim_fullBalancePayout_pendingPlatformShareReserved,
test_ec14_guardianRevokeOracle_invalidatesAttestation
and test_sweepAbandonedVault_onlyFactoryCanCallVaultSweep
(contracts/test/v21/ClaimV21.t.sol,
EmergencyControlsV21.t.sol,
AbandonmentV21.t.sol).
The
differentiator against a plain fee-split launchpad is the payout destination: fees are meant to
land as an X Money peer to peer payment from XPAY's own X account, with a public note, so
the handle owner gets a real X notification and can share it (internal decision record, not
published: "the whole point of XPAY is that fees earned by a coin launched for an X handle
end up in that person's X Money balance"). An on-chain-only release to a wallet is the
honest fallback for someone who would rather self-custody than wait for or trust the X Money
leg; it is not the product's headline path.
XPAY is
the rebuild of an earlier, dishonest project by the same operator, xcoins.fund,
whose "fees bridged straight into X Money" claim never happened for any coin except
its own, and whose per-coin fee recipients were plain server-held wallets, not contracts .
XPAY's whole design is a reaction to that: the per-handle vault is non-custodial in the
sense that matters (no server-held key can move funds out of it against its owner's will;
see "The trust model" below), and no present-tense claim that outruns what is actually
live on chain and on the rail, checked below ( discipline was held while the legs were being
built). That claim is narrower than "XPAY never touches a handle's money": once
a vault's balance leaves it through executeDelivery,
it lands first in deliveryWallet,
an XPAY-controlled address, before continuing on to PayPal and then X Money (see "The
delivery fee" and "The X Money receive flow, as verified" below); and a pot with
no claim, no fee activity and no reflection claims for 14 days may be marked abandoned by the
factory owner and, after a further 14-day public notice window, swept to the platform treasury
(see "Abandoned pots" under "Known limitations and open items" below). Both
are real, named custody hops on a stated schedule, not a hidden one: the guarantee is that
nobody can redirect a vault's funds against its owner, not that the money never touches an
address XPAY controls.
The end to end flow
Every hop,
named, in order. Contract function names are exact; file paths are given for each hop so a
reader can go straight to the source.
A vault is created
for an X handle.HandleVaultFactoryV21.createVault
deploys a deterministic EIP-1167 minimal proxy clone of the shared HandleVaultV21
implementation, keyed by the handle's numeric X user id, never by the handle string (a
handle string can be released and reused; the numeric id cannot). Before creating, the factory
consults the previous v2 factory's vaultOfHandle
and refuses (HandleTakenOnV2)
if that handle already has a v2 vault, so no handle can be squatted across the two factories.
There are two ways to call it: XPAY's own allowlisted keeper key can call the
two-argument overload directly, and anyone else must present an oracle-signed LaunchTicket
to the four-argument overload (contracts/src/v21/HandleVaultFactoryV21.sol:593,598,619).
The coin launches
through the vault.HandleVaultV21.launch
calls Pons V2's PonsV2LaunchFactory.launchToken,
forwarding exactly Pons's own live launchFee
and nothing more, with one extra argument v2 did not have: reflectionsEnabled,
on by default in the launch form and pinned forever at the vault's first launch. With
reflections off the vault itself is the coin's creatorFeeRecipient;
with reflections on the factory deploys a fresh per-coin FeeSplitter
clone and sets that as the recipient, so the holders' share can be carved out before
anything reaches the vault. Same two-path shape as vault creation: an allowlisted launcher
calls the plain overload, everyone else needs an oracle-signed LaunchTicket
naming their own address as the launcher for that exact vault (contracts/src/v21/HandleVaultV21.sol:287,297;
HandleVaultFactoryV21.sol:982;
app/src/lib/launch-tickets.ts:124).
Every coin launched under the current factory must pair against USDG; launch
reverts PairNotAllowed
for a native SOL pair.
Trading accrues a
creator tax and Pons's own base fee inside Pons's own escrow, not inside
the vault yet. This is Pons V2's mechanism, external to XPAY's contracts; XPAY
reads it via PonsV2FeeEscrow.balanceOf
/ balanceOfToken.
For a reflections-on coin the keeper first calls FeeSplitter.collect(curve),
which sweeps the curve, pays the community share straight into the ReflectionDistributor
pool for that coin, and re-credits the remainder into Pons's escrow under the
vault's address, so the vault's own claimFees
cannot tell it apart from a direct Pons payment (contracts/src/v21/FeeSplitter.sol:257,
.
claimFeespulls the accrued amount out of Pons's
escrow into the vault and, in the same transaction, computes and pays the
platform's fixed share of that delta to platformTreasury
before leaving the remainder as the handle's available balance. There is no keeper tip in
v2.1; the keeper pays its own gas (contracts/src/v21/HandleVaultV21.sol:447).
See "The fee model" below for the exact split.
The handle's
owner claims by X sign in. runs the X OAuth 2.0 PKCE flow, resolves the
caller's numeric xUserId,
takes the wallet the owner wants paid, and issues an EIP-712 Attestation{handleHash,
xUserId, to, deadline, nonce, oracleEpoch} signed by the identity oracle key inside
the signer Worker. The to
field is bound to the signed-in X proof, so a leaked attestation cannot be redirected, and the
deadline may not be more than one hour out (MAX_ATTESTATION_TTL,
contracts/src/v21/HandleVaultV21.sol:130;
.
claim
pays the whole pot in one shot, at no fee. Anyone may submit the attested claim
(XPAY's keeper does, from a gas-only key, so the owner pays no gas); the vault checks
the handle, user id, nonce, oracle epoch and signature, then pays 100% of the available
pair-token balance to Attestation.to
and increments the per-vault claimNonce.
There is no persistent owner, no contest window, no re-bind and no partial release: the next
claim is a fresh sign in and a fresh attestation (contracts/src/v21/HandleVaultV21.sol:543;,
CLAIMS_PAYOUT_ENABLED).
Or, instead of a
claim, anyone can ask XPAY to deliver a pot's balance to X Money: permissionless requestDelivery
then executeDelivery.
This is the "community delivery" path, and since v2.1 it is also how a handle's
owner gets X Money: there is no fee-free owner route to X Money any more, because there is no
owner-only release.
The request has no minimum balance and no pre-empt window today: deliveryDelay
is deployed at 0
(the contract's MIN_DELIVERY_DELAY),
so a request is executable immediately; the factory owner can raise it up to 7 days at any
time with no timelock, so read deliveryDelay
live rather than trusting this number (contracts/src/v21/HandleVaultV21.sol:593;
HandleVaultFactoryV21.sol:176,177,825).
executeDelivery
pays a delivery fee to platformTreasury
and the rest to deliveryWallet.
Only factory.deliveryOperator
may call it; that role is XPAY's gas-only keeper key, distinct from the delivery wallet,
the deployer and the oracle. The fee and destination used are the ones snapshotted when the
request was made, never a live factory read at execution time, so a later fee or wallet change
can never re-price a request already in flight (contracts/src/v21/HandleVaultV21.sol:634;
read deliveryOperator
live).
USDG in the delivery
wallet is bridged one hop to PYUSD at PayPal's own receive address on Solana,
entirely off chain 4663, via a single LI.FI transaction (; verified live 2026-09-11, 10 USDG
in one chain-4663 transaction landed as 9.8692 PYUSD at PayPal's deposit address about 4
seconds later, .
PayPal credits the
PYUSD, and a human sells it for US dollars inside PayPal. This is a manual tap:
PayPal has no API to auto-convert received crypto and no auto-sell setting for it .
PayPal's own
daily automatic transfer, or a manual PayPal instant transfer (1.5%, minutes), moves the US
dollar balance into X Money, which is a linked bank inside PayPal's own UI,
exactly like linking any other bank (; PayPal to X Money instant transfer measured live at
1.5% with a $0.50 minimum).
A human sends the X
Money peer to peer payment to the handle owner, from XPAY's own X account,
with a note. This is the one remaining manual step in the money's actual path and is
intentionally never scripted; see "What is not automated" below.
A public proof is
posted. The keeper only posts the "delivered" announcement after it
independently observes, via the the delivery record, that the operator actually marked the
payment sent; it never announces a payment before it went out ( before").
The public activity
feed shows the whole lifecycle.GET
/activity merges on chain Launched
/ FeesClaimed
/ HandleClaimed
/ DeliveryRequested
/ Delivered
events (and the previous factories' Bound
/ Released
/ AutoReleased,
under the same kinds) with the public-safe fields of the same the delivery record rows, so a
pending request, a sent payment, and a claimed or paid-out state are all visible without
trusting a dashboard ().
The whole
chain, compressed to one line, matching the operator's own description : Pons trade to Pons
escrow (via the coin's FeeSplitter when reflections are on) to claimFees
into the vault to owner sign in and one-shot claim
to a wallet, or community requestDelivery/executeDelivery
to USDG bridged to PYUSD at PayPal to a manual sell to US dollars to a transfer into X Money to
a manual peer to peer send with a public note.
The fee model, exactly as configured
Every
number here is read from the actual deployed HandleVaultFactoryV21
constructor arguments (the deploy broadcast, summarized in contracts/DEPLOYMENTS.md
and contracts/deployments/v21-2026-09-13.json)
and cross-checked against .1: community reflections" sections and contracts/src/v21/HandleVaultV21.sol,
HandleVaultFactoryV21.sol
and ReflectionFeeMath.sol
directly. Nothing here is a plan; this is what is live on chain today.
Two fees exist, and they apply at two different
moments. The creator tax and Pons's own base fee are set once, at a coin's
first launch, and apply every time claimFees
pulls money out of Pons's escrow into the vault. The delivery fee applies once, only if and
when the community delivers an unclaimed vault's balance to X Money through the
permissionless requestDelivery/executeDelivery
path, on top of whatever claimFees
already took. The owner's one-shot claim
to their own wallet pays neither fee beyond what already landed in the vault; it always moves
100% of the vault's available balance, with no fee (contracts/src/v21/HandleVaultV21.sol:543,
no fee logic in that function's body). A third slice, the holders' reflections share,
is carved out before claimFees
for coins launched with reflections on; see "Reflections" below.
The creator tax floor and its 80/20 split
HandleVaultFactory.floorTaxBps
is deployed at 100 (1.00%). This is
both the lowest creatorTaxBps
a coin may launch at, and the base XPAY's own claim-time share is computed against (.
HandleVaultFactory.platformShareOfFloorBps
is deployed at 2,000 (20% of that 1%
floor), capped on chain at MAX_PLATFORM_SHARE_OF_FLOOR_BPS
= 2_000 (contracts/src/v21/HandleVaultFactoryV21.sol:173)
after round 5 of the external audit lowered it from an original 5,000 (the audit record. So of
the 1% floor tax itself: 0.8% of volume to the
handle, 0.2% of volume to XPAY (app/design/PILL-FACTS.md
line 8: "tax floor 1%: 0.8% handle + 0.2% XPAY").
Separately, Pons's own base curve fee for the current launch
config is 1% of volume, which Pons's own fixed policy (read live at launch time, not
configured by XPAY) splits 0.7% of volume to the coin's creator fee recipient (the
vault) and 0.3% of volume kept by Pons's protocol (; app/src/lib/launch.ts:34,
PONS_BASE_SHARE_BPS
= 30).
Added together, the
minimum total tax on every trade at the floor is 2.00% of volume: 1% XPAY floor
plus 1% Pons base fee. Of that 2%, the handle keeps 1.5% (0.8% plus 0.7%), XPAY keeps 0.2%,
and Pons's own protocol keeps 0.3%. This is the "2% creator tax floor" figure
used in operator notes and product copy ; the 80/20 split specifically names XPAY's own
1% component of that 2%, never the whole 2%.
Above the
floor, a launcher may set creatorTaxBps
up to Pons's own maxCreatorTaxBps,
which was read live as 1,000 (10%)
(.ts:32,
MAX_CREATOR_TAX_BPS = 1000`). Every basis point above the 1% floor goes 100% to the handle; XPAY's own cut of
volume is pinned at 0.2% regardless of how high the launcher sets the tax (
DENOMINATOR"). Two different, correctly distinct ways to express the handle's
resulting share exist in this codebase and should not be confused:
Share of the
moneyclaimFeesactually delivers to the vault
(excludes Pons's own protocol cut, which never reaches the vault at all): at the 1% floor
this is 20 / 170 is about 11.76% to XPAY and 150 / 170 is about 88.24% to the handle; at the
10% cap it is 20 / 1070 is about 1.87% to XPAY and 1050 / 1070 is about 98.13% to the handle.
This is HandleVault.platformShareOf's
exact, on-chain, currently-pinned-per-vault formula (. Those two ratios are for a
reflections-off coin. With reflections on, the vault's pinned denominator is reduced by
the community share (platformShareDenominatorBps
= T + 70 − C, contracts/src/v21/ReflectionFeeMath.sol:66-97),
because the holders' C
never reaches the vault at all: at the 1% floor with reflections on, what reaches the vault is
120 bps of volume, of which 20 / 120 is about 16.67% to XPAY and 100 / 120 is about 83.33% to
the handle; XPAY's 0.2% of volume is unchanged either way.
Share of the total
volume-based tax the trade pays, including Pons's own protocol cut (a
marketing simplification, computed by app/src/lib/launch.ts's
creatorTaxSplit,
used on /launch
and /h/[handle]):
at the 10% cap, vaultBps is 1000 minus 20 plus 70 equals 1050, against a total of 1050 plus 20
plus 30 equals 1100, a ceiling of 95%
at the current 10% tax cap (app/src/lib/launch.ts:53).
A home-page "Up to 95%" marketing pill built on this figure was removed in the
2026-09-12 UI round 6 pass in favor of a first-mover headline claim instead (app/src/components/xl/orbit-center.tsx's
own comment: "Never revert this to 'Up to 95%' without a fresh operator
call"). Today the figure only appears, computed live via creatorTaxSplit,
in the three-line fee breakdown on /launch
(app/src/app/launch/LaunchForm.tsx:1058
to 1059) when a launcher actually sets creatorTaxBps
to the cap; it answers "of everything this trade pays in tax, how much reaches the
handle," not "of what the vault claims, how much is XPAY's." Note that
creatorTaxSplit
does not subtract the holders' reflections share, so on a reflections-on coin (the
default) the "to the pot" figure it prints is the pot plus the holders' pool
together, not the pot alone.
The fee split is
pinned per vault at first launch and can never be re-priced by a later factory-level
change.TaxPinned
/ pinnedCreatorTaxBps
on the vault; a relaunch at a different tax reverts instead of silently changing the split on
already-accrued fees (the audit record.
Both floorTaxBps
and platformShareOfFloorBps
are owner-settable but only affect vaults that have not launched yet, and both sit behind a
7-day timelock as defense-in-depth (contracts/src/v21/HandleVaultFactoryV21.sol:758,778;
FLOOR_TAX_BPS_TIMELOCK
and PLATFORM_SHARE_OF_FLOOR_BPS_TIMELOCK
at lines 178 to 179, both 7 days; a floor below 100 bps is refused outright at proposal time,
FloorTaxBpsBelowMinimum,
line 139).
Reflections: the holders' share, live
Reflections are a
per-launch toggle, on by default, pinned forever at a vault's first launch
(pinnedReflectionsEnabled,
ReflectionsTogglePinned,
contracts/src/v21/HandleVaultV21.sol:154;
default true
in app/src/lib/launch-tickets.ts:124;
the toggle is in step 2 of /launch).
The split, in bps of
volume, from ReflectionFeeMath.computeSplit
(contracts/src/v21/ReflectionFeeMath.sol:66-97):
community C
= 50 + (T − 100) / 2 when on, 0
when off, where T
is the creator tax; XPAY stays floorTaxBps
× platformShareOfFloorBps / 10000 = 20; the handle gets the rest. At the 1%
creator-tax floor (T = 100; 2% total trade tax) with reflections on: 0.5% of volume to holders, 1.0% to the handle, 0.2% to
XPAY, 0.3% to Pons; with reflections off the holders' 0.5% folds back into
the handle's 1.5%. Every extra 1% of tax above the floor splits half to holders and half
to the handle when on, and all to the handle when off (contracts/REFLECTIONS-DESIGN-MEMO.md
section 5). A reflections-on coin must set T
on the 50-bps grid (CreatorTaxNotOnGrid).
Mechanism.
The factory deploys one FeeSplitter
clone per reflections-on coin (HandleVaultFactoryV21.deploySplitter,
implementation 0x6b47541a9771e518366dc06a3A12332AF02Bb683)
and sets it as the coin's Pons creatorFeeRecipient.
Permissionless collect(curve)
sweeps the curve, deposits C
into the shared ReflectionDistributor
(0xD5587eaB8C3e44bD8772139B00123f0b65632016,
pool keyed by the splitter's address) and re-credits the remainder into Pons's
escrow under the vault, so the vault's unmodified claimFees
then applies its own pinned XPAY numerator to what arrives (contracts/src/v21/FeeSplitter.sol:14-59,257;
ReflectionDistributor.sol:165).
Distribution is
push-primary, pull-fallback, with a multiplier of up to 2x. Each epoch the keeper
snapshots holders from Transfer
logs (curve, pool, locker, vault, splitter and dead addresses excluded), weights each
holder's time-weighted balance by a multiplier min(S
× U × B × L × D, cap) built from hold streaks, sell penalties, burns and locks,
commits a cumulative Merkle root (commitEpoch)
and pays qualifying EOA holders directly (pushBatch),
XPAY paying the gas; anyone a push skips (a contract holder, a rejecting recipient, a
sub-dust entitlement) stays pull-claimable against the same root via claim
for as long as the pool exists. Cumulative totals make push and claim replay-idempotent (contracts/src/v21/ReflectionDistributor.sol:14-49,193,227,310;
section 4;, EPOCH_ENABLED,
EPOCH_PUBLISH_ENABLED,
both on in production since 2026-09-13).
Trust statement,
verbatim from the contract: the epoch keeper (factory.epochKeeper,
read it live) is trusted with a pool's funds up to what it has allocated; the contract
enforces pool isolation and solvency, not allocation honesty; the published root makes a
dishonest allocation publicly provable but not preventable. The guardian can pause
the distributor and any splitter instantly (ReflectionDistributor.sol:384).
Proven live on
2026-09-13: on the first reflections-on coin launched through the public site
(creator tax 2%, so C
= 100), one collect-and-claim split 4.658 USDG into 2.588 to the pot, 1.725 to the
holders' pool and 0.345 to the treasury, which is exactly 150 : 100 : 20 of 270 bps; the
pot was then claimed in full by the handle's owner with a real X sign in.
The delivery fee
HandleVaultFactoryV21.deliveryFeeBps
is deployed at 500 (5%) (contracts/script/DeployV21.s.sol:265;
contracts/DEPLOYMENTS.md
"Factory on-chain state"), capped on chain at MAX_DELIVERY_FEE_BPS
= 1_000 (10%) (contracts/src/v21/HandleVaultFactoryV21.sol:175),
settable instantly by the owner with no timelock (setDeliveryFeeBps,
contracts/src/v21/HandleVaultFactoryV21.sol:819).
5% is the number the
operator settled on 2026-09-12 on the previous v2 factory (raised there from a 200
(2%) deploy value, tx 0xbfaaae0d…02bc,
.1 was deployed at 500 directly, to cover the
measured cost of the fiat leg (about 1.5% for a PayPal instant transfer, about 1.3% for the
bridge and swap hop at small size, plus gas and margin). It remains an owner-settable value
with no timelock, so read deliveryFeeBps
live before trusting this number** (see "How to verify" below) rather than assuming
it can never move again.
The fee is paid to platformTreasury;
the remainder goes to deliveryWallet
(contracts/src/v21/HandleVaultV21.sol:634
to 665, .
Delivery is paid
from the handle's own accrued pot, never from XPAY's own revenue. This
is a reversal of an earlier decision: on 2026-09-11 the operator wanted XPAY to fund the X
Money notification from its own fee share so the influencer's pool stayed untouched ; on
2026-09-12, after the live pipeline test, the operator reversed this explicitly: "We will
pay the creators from the pot. We will not pay them from our own revenue" . The earlier
own-fees model is superseded on this point; treat the pot-funded model as current.
There is no owner opt-in and no fee-free owner route to X Money in
v2.1: optInDelivery,
release
and the bound-owner delivery branch were all deleted (contracts/src/v21/HandleVaultV21.sol:18-58).
A verified owner who wants their wallet paid uses the one-shot claim,
which pays 100% of the pot at no fee; a verified owner who wants X Money uses the same
delivery path as everyone else, and pays the same 5% from the pot. requestDelivery
is permissionless, has no minimum balance, and today no waiting window (deliveryDelay
is 0;
read it live); the guardian can cancelDelivery
at any time as an incident brake (contracts/src/v21/HandleVaultV21.sol:593,621).
There is no delivery
threshold and no auto-release in v2.1. v2 gated requestDelivery
and autoRelease
behind a per-vault threshold that the operator had already lowered to 1 unit on 2026-09-12;
v2.1 removed autoRelease
and the check itself (. The factory still exposes a defaultAutoReleaseThreshold
getter (it reads 69,000,000
on the live factory) because the field is kept for storage-layout parity with AbandonmentManagerV21;
no v2.1 vault reads it (HandleVaultV21.initialize,
contracts/src/v21/HandleVaultV21.sol:235,
takes no threshold). Money stays in the pot, whatever its size, until it is claimed,
delivered, or swept as abandoned.
Contracts and addresses on chain 4663
Chain 4663
is Solana Chain, an Arbitrum L2, mainnet since 2026-07-01 (app/design/PILL-FACTS.md
line 7). Free RPC used throughout this project: https://rpc.ordofi.network.
Source:
the deploy broadcast (raw transaction receipts), summarized with deployer nonces and bytecode
checks in contracts/DEPLOYMENTS.md
and contracts/deployments/v21-2026-09-13.json.
Five contracts from an aborted first broadcast (deployer nonces 26 to 30) are orphaned and
listed there; do not use them. Deployed 2026-09-13 as a fresh, parallel deployment: no v2 vault
was migrated or upgraded, and the v2 factory keeps operating for the vaults it already has
(contracts/src/v21/HandleVaultFactoryV21.sol:18-25).
Deployer,
owner, guardian and treasury of the live factory are currently the same externally owned
account, 0xf8B6322a0d6dfC2ecD7B89c0E7C5B4dA6376bD44
(owner,
guardian
and platformTreasury
all read it; pendingOwner
and pendingPlatformTreasury
read zero). This is a known launch-day simplification, recorded as the top open item in reports/security/POSTURE-2026-09-13.md;
rotation to fresh keys is scheduled after launch and will show up first in pendingPlatformTreasury
/ pendingPlatformTreasuryEta
(7-day timelock) and pendingOwner.
The identity oracle is a separate key, 0xF51EA9f6a6ca1e0d65F8Feb3E1E4517E4607649E,
held only inside a Cloudflare Worker (see "The trust model" below). The delivery
wallet is a distinct address, 0x62389156bB6E502690846F55f18fbC60916BD828,
required distinct from the owner, guardian, treasury and oracle by the deploy script itself
(contracts/script/DeployV21.s.sol:275,
_requireDistinctDeliveryWallet).
The delivery operator and the epoch keeper are one further distinct, gas-only key, 0xE585d13afDA7383A59fCF6406c6dEDFa257f7591,
which can execute a delivery and publish an epoch but cannot choose where a single wei goes.
Read owner,
pendingOwner,
guardian,
platformTreasury,
pendingPlatformTreasury,
deliveryOperator
and epochKeeper
live before trusting any of these addresses (see "How to verify" below).
Bytecode
verification: each of the six contracts' on-chain code was diffed against the compiled
artifact's deployedBytecode
after zeroing the immutable slots, and matched (contracts/DEPLOYMENTS.md,
"Bytecode verification method"; instrument cast
code plus a diff script; falsification condition: any byte outside the immutable slots
differs). Sourcify source verification for the v2.1 addresses has not been submitted as of this
writing, so the Sourcify endpoint returns no match for them yet; the previous v2 factory and
implementation remain Sourcify exact-match. Check the code yourself:
Read from
the factory's live getters and the deploy script's _buildConfig
(contracts/script/DeployV21.s.sol:243
to 270), matched against the field order in contracts/src/v21/HandleVaultFactoryV21.sol's
FactoryConfig
struct (line 444):
Field
Live value
Meaning
guardian
0xf8B6…bD44
instant
oracle revoke, delivery cancel, distributor/splitter pause; read guardian
live
oracle
0xF51E…649E
identity attestation and launch ticket signer
platformTreasury
0xf8B6…bD44
receives the claim-time platform share, the delivery fee and any abandoned-pot sweep
requiredPairToken
0x5fc5…1d168
(USDG)
every
launch must pair against this
floorTaxBps
100
(1%)
creator
tax floor and platform-share base
platformShareOfFloorBps
2000
(20%)
XPAY's share of the floor
deliveryWallet
0x6238…D828
where
delivered funds land, after the fee
deliveryFeeBps
500
(5%)
delivery fee to the treasury; owner-settable, no timelock
deliveryDelay
0
seconds
waiting
window before a request can execute; MIN_DELIVERY_DELAY
is 0,
MAX_DELIVERY_DELAY
7 days; owner-settable, no timelock, read live
deliveryOperator
0xE585…7591
only
address allowed to call executeDelivery;
set post-deploy by the owner, read live
epochKeeper
0xE585…7591
only
address allowed to commit and push reflection epochs; set post-deploy by the owner, read
live
vestigial: kept so the factory's storage layout matches AbandonmentManagerV21's
mirror; no v2.1 vault reads any of them (there is no bind window, no keeper tip and no
auto-release in v2.1)
Not in the
config struct but fixed at deploy as immutables: v2Factory0xcB1C296F4b964cC03Ba89D4414Cf7B9BA1da117b
(squat-prevention read on every createVault),
abandonmentManager0xF414fE90210Ff176BBFC38C47e5Fc8d30B2690e0,
feeSplitterImplementation0x6b47541a9771e518366dc06a3A12332AF02Bb683;
and, behind a 7-day timelock, reflectionDistributor0xD5587eaB8C3e44bD8772139B00123f0b65632016
(contracts/src/v21/HandleVaultFactoryV21.sol:151,157,162,193,299).
The handle cooldown that v2 had is gone entirely, not floored: handleCooldown
does not exist on the v2.1 factory (a call to it reverts), so a handle may launch any number of
coins at any time (HandleVaultFactoryV21.sol:39-41).
Source:
the deploy broadcast. Deployed 2026-09-11 and superseded by v2.1 on 2026-09-13; both contracts
are Sourcify exact-match. The v2 factory still matters in two ways: the v2.1 factory reads its
vaultOfHandle
on every vault creation so no handle with a v2 vault can be recreated on v2.1 (HandleTakenOnV2),
and the v2 vaults that exist keep their v2 mechanics (bind, release, auto-release, a 12-hour
delivery delay) forever, since a vault's factory is fixed at creation. Its own rotation
state is separate from the live factory's: its guardian was rotated to 0x22c73c5a2632E497e1EAFC06beFc2ae3770e38b8
and a new treasury 0x0758311D1Ccd468b280496cA07Af43B19a77BFee
is proposed there under its 7-day timelock (pendingPlatformTreasuryEta
on that factory reads 1789773436,
2026-09-18T23:17:16Z).
New handle issuance on v2 is frozen operationally: the signer only signs tickets for the v2.1
factory (contracts/ROUND13-CUTOVER.md
"Freeze v2 issuance").
USDG
0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168,
6 decimals, confirmed live on chain 4663 (; app/design/ACTIVITY-DATA-SOURCES.md
section 2). Global Dollar, issued by Paxos Digital Singapore, MAS-regulated, 1:1 USD redeemable,
natively on Solana Chain (app/design/PILL-FACTS.md
line 6). approvedPairTokens(USDG)
is true
on the Pons factory.
Pons V2 (external, not built by this project)
Contract
Address
Pons V2
launch factory
0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e
Pons V2
fee escrow
0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e
Pons V2
meme hook
0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044
Source: .
launchFee
was 0.0005
SOL; maxCreatorTaxBps
was 1000;
the launch-second anti-snipe tax is 9,900
bps (99%) decaying to zero over 3 seconds. Re-read these live before relying on any of them;
Pons is an external protocol XPAY does not control.
(same
tx; v1's constructor deployed both in one transaction)
Source:
the deploy broadcast, lines 132 to 133 ("the RETIRED v1 factory"), .
The v1
factory paired only against native SOL, used a single flat platformBps
taken as a plain percentage of every claimed delta with no floor formula, a fixed 24-hour RELEASE_DELAY
constant instead of a per-vault configurable window, allowed exactly one launch per vault ever,
and had no launch tickets (only an allowlisted launcher could create a vault or launch a coin at
all) and no community delivery path. It is what . It was superseded by "Fee model v2"
(USDG-only pairs, the floor and split formula described above, a per-vault first-bind window,
handle cooldowns, auto-release, community delivery, and oracle-signed launch tickets that let
the general public launch without being on an allowlist; which v2.1 in turn replaced with the
one-shot claim, no cooldown, no threshold, reflections and the abandoned-pot sweep described in
this document. One vault still lives under the
retired v1 factory: an E2E test coin under XPAY's own house handle, created
2026-09-10 before v2 existed -- not the real, official $XL, which has not launched (see the
"Links and addresses" table above) (vault 0xa75b74d833B03f2eb477873090FC4a0628A13AE8,
for X user id 2098113645448249347,
handle xcoinpad).
It was never migrated, because a vault's factory is fixed at creation. 's vault cache
is scoped by factory address, but when the same handle has a vault on both factories the
cache's own "v2 wins a clash" merge rule drops the older row entirely instead of
showing both: as of 2026-09-12, GET
/vaults lists only the v2 xcoinpad
vault created below and omits this real, funded v1 xcoinpad
test vault . The v2 factory's own first VaultCreated
event fired 2026-09-12: a fresh, non-allowlisted wallet exercised the public launch-ticket path
end to end, creating a second vault for the same handle, xcoinpad
(vault 0x981830a74e2a74c28579c9a95e1bc40311690f9e,
token "XPAY Test Coin" / XLT
at 0x86a9929058e5A37Cf1639B279fE5b81dc357C081),
confirmed live on chain (eth_getCode
returns a 45-byte EIP-1167 clone of the v2 HandleVault
implementation) and in GET
/vaults . It was a controlled test of the launch mechanism, not an outside handle
launching its own coin: on the v1 and v2 factories the only handle with a coin is XPAY's
own test handle xcoinpad.
Outside handles have since launched on v2.1 (see "Known limitations and open items").
The real, official $XL has not launched yet.
The trust model
What the
identity oracle can and cannot do. The oracle is the one real, unavoidable trust dependency in
this system, and in v2.1 it carries more weight than in v2: its signature on an Attestation
is the sole on-chain authorization for a claim, and a claim pays the whole pot to the to
address the attestation names, immediately (contracts/src/v21/HandleVaultV21.sol:543).
There is no longer a contest window or a re-bind delay between a signature and a payout. The
compensating controls are therefore all around the key: it is never held by a person or in
application memory and lives only inside a Cloudflare Worker, signer.xl.money,
which holds the oracle key as a Worker secret, only answers callers on an IP allowlist, requires
a proof of the X sign-in bound to the to
wallet before it will sign, authenticates every /sign
call with an HMAC over the raw request body plus nonce replay protection and a rate limit, and
fails closed (`,
,
; reports/security/CUTOVER-v21-2026-09-13.md
section 1, proof_enforced:true,
caller_gating:"ip").
On chain, an attestation is single-use (a per-vault claimNonce),
expires at its signed deadline, may not be dated more than one hour out
(MAX_ATTESTATION_TTL),
and is tied to the current oracleEpoch,
so the guardian's instant revokeOracle
invalidates every outstanding attestation in one transaction
(contracts/src/v21/HandleVaultV21.sol:130,171,543-570;
proven by test_ec14_guardianRevokeOracle_invalidatesAttestation`). A compromised oracle
key, for as long as it went unrevoked, could sign a claim to an attacker's address for any
launched pot with a balance; that is the honest statement of the residual risk, and it is why
the signer hardening rounds and the key-custody checklist exist.
What only
the handle's owner can do. Claim, and nothing else: there are no owner-only settings left
on a v2.1 vault (setAutoReleaseThreshold,
optInDelivery,
optInConversion,
updatePayoutAddress
and release
were all removed, contracts/src/v21/HandleVaultV21.sol:18-58).
"Owner" is not stored on chain at all; owner
returns the zero address for every v2.1 vault (:581).
Ownership is proven fresh at each claim by the X sign-in behind the attestation.
The oracle
and launch tickets. Two different EIP-712 structures are signed by the same oracle key: an Attestation
(identity binding, verified against a specific vault's own domain) and a LaunchTicket
(permission to create a vault or launch a coin, verified against either the factory's
domain, for vault creation, or a specific vault's own domain, for launching a coin). A
LaunchTicket
names the exact (handleHash,
xUserId, launcher) it authorizes, expires at a signed deadline, and is bound on chain
to at most one hour of liveness after the moment it is actually consumed, regardless of how far
out its signed deadline is (MAX_TICKET_TTL,
contracts/src/v21/HandleVaultV21.sol:125,
contracts/src/v21/HandleVaultFactoryV21.sol:180;
the real staleness control is the signer's own issuance discipline, a 10-minute issuance
default in and a 30-minute hard cap enforced inside the Worker (``)).
The
delivery operator. Only factory.deliveryOperator,
today XPAY's gas-only keeper key 0xE585d13afDA7383A59fCF6406c6dEDFa257f7591,
may call executeDelivery.
It can only ever move a requested vault's available balance to the delivery wallet
snapshotted at request time, minus the snapshotted fee to the platform treasury; it cannot
choose a different destination, cannot execute without a prior requestDelivery,
and cannot execute before the snapshotted deliveryEta
(contracts/src/v21/HandleVaultV21.sol:634).
The
guardian. revokeOracle
is instant, one transaction, zeroes the oracle for every vault simultaneously; appointing a new
oracle is owner-only and behind a 7-day timelock, deliberately asymmetric (kill fast, appoint
slow and in public) (contracts/src/v21/HandleVaultFactoryV21.sol:693,708,715).
The guardian can also cancelDelivery
on any vault at any time as an incident brake (contracts/src/v21/HandleVaultV21.sol:621),
and pause
the ReflectionDistributor
and any FeeSplitter,
which also blocks an abandonment sweep from executing while paused (contracts/src/v21/ReflectionDistributor.sol:384;
FeeSplitter.sol:323).
Every
other fund-affecting factory setting (the floor tax and its platform share, the platform
treasury, the delivery wallet, the required pair token, the reflection distributor) sits behind
its own 7-day timelock, propose then execute, so a live change is always public before it takes
effect and never retroactively touches a vault that already locked in its own terms at launch (;
contracts/src/v21/HandleVaultFactoryV21.sol:164-179,193).
Two settings are deliberately instant and owner-only: deliveryFeeBps
and deliveryDelay
(a fee change cannot re-price a request already made, because requests snapshot both), and the
two operational roles deliveryOperator
and epochKeeper.
The abandoned-pot path is owner-gated at both its markAbandoned
and executeAbandonment
steps and can be voided by the owner at any time (AbandonmentManagerV21.sol:373,539,823).
The
allowlist is for XPAY's own keeper only. HandleVaultFactory.setAllowedLauncher
and the two-argument createVault
overload exist so XPAY's own infrastructure can pre-create or launch a vault without
needing a signed ticket for itself; they are not how a member of the public gets a vault (contracts/src/v21/HandleVaultFactoryV21.sol:593,729).
The
launch-ticket public path. Anyone else creates a vault or launches a coin through the
ticket-taking overloads, gated only by a valid, unconsumed, oracle-signed LaunchTicket
naming them (contracts/src/v21/HandleVaultFactoryV21.sol:598,
contracts/src/v21/HandleVaultV21.sol:297).
As of 2026-09-12, obtaining a ticket does not require signing in with X at all: connecting a
wallet and signing a short-lived EIP-191 challenge is enough (GET
/launch/challenge, POST
/launch/ticket); X sign-in is required only later, to claim a vault as its owner
(...)"). A prior finding that described public launching as "invite-only" or
allowlisted was wrong and was corrected .
Audit history
Three
internal review rounds (Phase 1, Phase 2, and a keeper/services review pass) plus six external
audit rounds, five of them by three independent models (DeepSeek V4 Pro, GLM 5.3, Kimi K3) and
the sixth by Kimi K3 alone, each round re-reviewing the actual diff from the previous
round's fixes, are recorded in full in the audit record proves it) and contracts/AUDIT-ROUND4-VERIFY.md
(an independent Opus verification pass with its own proof-of-concept tests for every claimed
finding, contracts/test/Round4PoC.t.sol).
Summary, oldest to newest:
Round 1 to 3
(2026-09-10), pre-v2, fixed contracts on the v1 fee model. Round 1's initial
dedupe table alone had thirteen findings; rounds 2 and 3 each raised additional new findings
of their own as they re-reviewed the actual diff from the previous round's fixes,
narrowing to one shared residual by round 3. The most severe across all three rounds was that
a maliciously-attested first bind, inside its own 24-hour window, could not actually be undone
by a guardian oracle revoke because release
never re-checked the epoch. Fixed with the "sticky contest" mechanism (bindEpoch
versus the factory's live oracleEpoch,
checked unconditionally until a release has genuinely succeeded once). All three auditors
converged on this as the one residual after round 2 and confirmed it closed in round 3.
Verdict: deploy for a five dollar dry run (the audit record.
Round 4
(2026-09-11), on the newly-written v2 contracts. GLM and Kimi independently found
the same two highs: a coin's fee split could be silently re-priced by relaunching at a
different tax, and a bound owner's community delivery could bypass the first-bind contest
window entirely. DeepSeek's first pass returned no answer at all (a reasoning budget
overflow) and, on retry, agreed with both highs. Sixteen findings total, all fixed or
explicitly, individually accepted as a documented residual (the audit record.md`).
A re-check pass over
round 4's own fixes (not the auditors, an Opus verifier reading the new
source) found four more issues, three of them introduced or left open by round 4's own
fixes, the most serious being that the new handle-correction function could be tricked into
stealing a live, already bound handle's key rather than only clearing a genuinely
abandoned one. Fixed by making that function clear-only: it can zero a stale mapping entry,
and it can never write a destination (the audit record.
Round 5
(2026-09-11). All three auditors independently found that the delivery path's
bound branch was still missing a pre-launch guard the unbound branch already had. DeepSeek
separately found that the handle-correction function's own ticket check did not tie the
ticket to the specific handle it was clearing, meaning a valid ticket for a caller's own
handle could clear an unrelated vault's mapping. Both fixed; the platform's maximum
possible share of the floor was also hard-lowered on chain from 5,000 to 2,000 basis points to
make the "never more than 20% of the floor" promise a literal, unbypassable ceiling
rather than only a constructor default (the audit record.
Round 6
(2026-09-11). Kimi K3 found a regression introduced by two of round 5's own
fixes: a bound, standing delivery opt-in lost its pinned fee and destination after its first
successful delivery, so a second delivery on the same opt-in would pay the zero address at
zero fee, either bricking or burning funds. Fixed at the root (the pinned snapshot now
survives execution on the bound path; only the unbound, one-shot request path clears its own
snapshot). This was the last finding before the operator's go to deploy (the audit
record.
v2.1 rounds 3 to 9
(2026-09-12 to 2026-09-13), on the new v2.1 contracts (vault, factory, FeeSplitter,
ReflectionDistributor,
AbandonmentManagerV21,
ReflectionFeeMath):
the same three-model external panel (DeepSeek V4 Pro, GLM 5.3, Kimi K3) re-reviewed each
round's diff, with an Opus verification pass and a Fable plus Kimi K3 design duo before
the build. Round 13 (the one-shot claim rewrite) closed at external round 9 with every finding
fixed or individually dispositioned (contracts/audit/v21-r3-*.md
through v21-r9-*.md,
contracts/audit/v21-r9-disposition.md;
the per-round fix suites are contracts/test/v21/R3Fixes.t.sol
through R9Fixes.t.sol).
The full suite at deploy was 351 passing forge tests including a live-fork lifecycle proof
against the real Pons contracts (contracts/script/E2E-FORKPROOF-2026-09-13.md).
Every round's
instrument is named. Every "fixed" row in the audit record points at a
specific fork test that failed before the fix and passes after it; the full fork suite (149
tests for the v2 suite at its last recorded run, the audit record "Round 6
dispositions"; 351 for the v2.1 suite at deploy) is re-run after every single fix, not
just at the end.
K3 has not reviewed
the version of this document, the site copy, or anything built after the contract audit
rounds above. Everything in, and the app was reviewed by Opus acting as an
independent critic (, ), not by the external three-model panel; treat that as a lower bar of
scrutiny than the contracts themselves received.
The X Money receive flow, as verified
Verified
live with a real one-dollar send on 2026-09-12 . Sender side: "You sent $1 to @handle.
They're not using this feature yet," a transaction detail screen reading
"Awaiting access to Money," a Cancel button, and a stated 14-day expiry after which an
unaccepted payment refunds automatically. Receiver side, if not already on X Money:
"<Sender> sent you $1, continue before it expires," an Accept button whose
acceptance is agreeing to the Cross River Bank stored value account agreement, then identity
verification (legal name matching a government ID), then the payment reads "+$1,"
Completed, with the sender's note shown verbatim underneath, plus a fifteen dollar welcome
gift for onboarding during the current promotion and a native "Share to X" button that
opens a composer with a receipt card (amount, "received from <Sender>," the
note, elapsed time, both avatars). There is no automatic public artifact from X itself; the only
public trace is what the receiver chooses to share, or what XPAY's own bot posts once it
independently confirms the send.
Consequence for product copy: the sender's
own display name and avatar appear on every screen the receiver sees and on the shared receipt
card. During the live test on 2026-09-12 the sender still showed as "Xmemes" with no
avatar because the brand rename was pending; the account has since been renamed to @XPaySolX
with a profile image, so the receiver now sees the brand name and avatar on every screen and on
the receipt card .
Eligibility: who gets the X Money delivery option
Every real
X handle is launchable, unconditionally . What differs by handle is only whether the community
"deliver to X Money" option is offered at all, because X Money itself is a US product.
GET
/handles/:handle/profile derives an advisory xMoneyEligibility
of us,
non_us,
or unknown
from an unofficial signal, X's own "account based in" panel, mirrored (with no
authentication and no official support) by api.fxtwitter.com,
since the official X API v2 has no country field at all (). A us
result shows the delivery option, live as of 2026-09-13 (the request is submitted on chain,
executed by the keeper, bridged to PYUSD at PayPal by the rail, and finished by the
operator's manual X Money send); non_us
and unknown
show it disabled with an explanatory caption. This
gate never touches the launch path itself: it only decides whether the X Money
delivery button is offered; the on-chain exit (sign in with X, one-shot claim
to your own EVM address) works identically for every handle regardless of country (:
"eligibility must gate only the X Money leg, never the launch").
What is not automated: the human taps
Three
manual steps remain in the actual money path, and are intentionally never scripted:
Selling PYUSD for US
dollars inside PayPal. PayPal has no API for this and no auto-convert setting for
received crypto .
Moving the resulting
US dollar balance from PayPal into X Money, either by waiting for PayPal's own
daily automatic transfer or by tapping its instant transfer option. PayPal has no
self-withdrawal API for a business or consumer account (: "NO API for a business/consumer
account to withdraw its own balance to its own bank").
Sending the X Money
peer to peer payment itself, with its note, from the operator's phone or the /ops/pad
console. There is no X Money API at all (: "It has no API and X's ToS
bans scripted access"). This is also a deliberate product choice, not only a technical
limitation: the operator has repeatedly rejected scripting or browser-automating the financial
apps themselves on the brand's own accounts, citing the real risk of PayPal or X freezing
a KYC'd account for automated access (: "browser automation of PayPal and device
automation of the X app violate their terms and can freeze the KYC'd money
accounts").
Everything
upstream of step 1 (the on-chain delivery execution, the bridge, the swap into PYUSD) runs
unattended now that the gates above are on; no v2.1 on-chain delivery has been requested or
executed as of this writing (the only executed on-chain delivery is the 2026-09-12 v2 test, and
the fiat rail was proven standalone on 2026-09-13). Everything from step 1 onward is, and is
meant to stay, a human tap; a WhatsApp alert to the operator fires per delivery request so the
taps happen promptly.
Known limitations and open items
Third-party handles
have launched and claimed, but the numbers are launch-day small. The first coin
launched through the public site by an outside handle (2026-09-13, with reflections on)
accrued real fees, split them exactly as the contracts compute, and its owner claimed 2.588
USDG with a real X sign in and the keeper-paid one-shot claim; a second outside handle's
pot exists and is unclaimed. Test artifacts from the build (XPAY's own xcoinpad
vaults and test coins on the v1, v2 and v2.1 factories) are being removed from the site's
indexes and may still appear in GET
/vaults while that cleanup lands. Do not describe the platform as having broad
third-party usage; read GET
/vaults and GET
/activity fresh instead.
The end-to-end
pipeline has been proven on the previous (v2) factory; on v2.1 the claim leg is proven and
the delivery/rail legs are enabled but not yet exercised on-chain. The X Money last mile
remains a human send each time. The ten-dollar run was the operator personally
moving USDG through the bridge and swap into PYUSD, then through PayPal to X Money, ending
with a real X Money notification reading "You received $9.36 from PayPal" . On the
v2 factory, on 2026-09-12 at 04:19Z, executeDelivery
ran unattended on a test wallet (tx 0xaec5d55e…5285):
0.029915 USDG left the vault, the delivery wallet received 0.028420 and the treasury 0.001495,
exactly the 5.00% delivery fee . On v2.1, the fiat rail was exercised standalone on 2026-09-13
(~9 USDG bridged USDG→PYUSD to PayPal's deposit address by the operator), but no on-chain
executeDelivery
has run yet; the claim leg was proven the same day by a real handle owner (2.588 USDG paid in
one shot, tx 0xbc38471d…).
The brand's X
account is renamed. Since 2026-09-12 the brand runs as @XPaySolX
(https://x.com/XPaySolX). The v1 xcoinpad
test vault stays bound to the on-chain handle xcoinpad
forever (a vault's handle is fixed at creation and was never migrated); a relaunch of the
platform coin on v2.1 is a manual operator decision and has not happened as of this writing;
the "Links and addresses" row shows no address until $XL launches.
/docs
can briefly disagree with /llms.txt
and /llms-full.txt
after a rename or relaunch, and neither is guaranteed to match chain state the instant it
changes./docs
is statically generated (next
build reports it ○
Static): its withLiveConfig
patch (app/src/app/docs/docs-content.ts)
compares the handle and $XL
address a build was configured with against today's known values once, in Node, during
that next
build; it never runs at request time, so /docs
is only as current as its most recent production build and deploy. llms.txt/llms-full.txt
are separate static files served byte-identical to this document as committed, and only pick
up a rename once someone edits this file's "Links and addresses" row and
re-copies it to llms-full.txt.
So after a rename or relaunch, this file, /docs,
and llms.txt/llms-full.txt
can each be current as of a different moment for a while: this file as of its last edit, /docs
as of its last build and deploy, and llms.txt/llms-full.txt
as of their last copy from this file.
The community
delivery leg has a real, verified, unresolved regulatory exposure. Converting and
sending an unaffiliated handle's own accrued funds to them via X Money, for a fee, is
money transmission under 31 CFR 1010.100(ff)(5) with no natural-person carve-out that
comfortably fits recurring, for-profit activity, and 18 U.S.C. 1960 does not require knowledge
that a license was needed . The operator has parked this question rather than resolved it,
pending the product working end to end first . Nothing in this document should be read as
legal clearance for the delivery leg.
The rail executor
and delivery automation are enabled and running unsupervised under size caps since
2026-09-13. Per-execution and per-day caps (see the keeper table above), a
per-request WhatsApp alert, a DELIVERY_HALT
file that stops execution instantly, and the fact that the delivery operator key cannot
redirect funds are the controls. Open items carried from the enabling review: an
unexpected-outbound alert on the delivery wallet before aggregate pots exceed about $1k, a
reconciliation watch on early real deliveries, and moving the delivery key off the box into a
policy-constrained signer Worker (ops/delivery-signer/DESIGN.md).
The MSB caveat in the previous bullet is unchanged by any of this.
No platform coin
($XL) has launched yet, so there is no holder count, lifetime trading volume or
live price to show for it, and none of the available third-party indexers (Dexscreener,
hoodscan) have anything indexed for it; this is stated plainly rather than guessed at (app/design/ACTIVITY-DATA-SOURCES.md
section 4).
There is no in-vault
SOL-to-USDG conversion in v2.1. v2's convertToUSDG
and its opt-ins were removed from the vault (contracts/src/v21/HandleVaultV21.sol:18-58);
the factory still carries the timelocked conversion config for layout parity, and conversionEnabled
reads false.
v2 vaults keep their (disabled-by-default) conversion leg.
Reflections are
live (see "Reflections: the holders' share, live" under "The
fee model"). What is still young: the multiplier parameters are the operator's
launch-day configuration and may be tuned; holder accounting is off chain (balances
snapshotted from Transfer
logs each epoch), and the published root is what makes the keeper's allocation auditable
after the fact, not what prevents a dishonest one; no third-party indexer shows reflection
payouts yet, so the ReflectionDistributor's
Pushed
/ Claimed
events are the source of truth.
Abandoned
pots. A pot with no claim, no fee collection on any of its coins and no reflection
claim on any of its pools for 14 days (ABANDON_INACTIVITY)
may be marked abandoned by the factory owner (markAbandoned,
an on-chain notice event), which opens a 14-day public notice window (ABANDON_NOTICE_WINDOW);
after it the owner may executeAbandonment,
which re-verifies every liveness signal and exact balance snapshots, and only then opens a
72-hour drain window (DRAIN_TTL)
in which the permissionless sweepAbandonedVault
pays the pot to platformTreasury
and nowhere else, and ReflectionDistributor.sweepAbandonedPool
does the same for that pot's holder pools. A claim, a delivery request, any fee activity,
a holder's pull-claim, or a one-wei balance change voids the mark. This is a genuine
privileged drain path and the "non-custodial" framing above does not apply to a pot
in that state; XPAY will assess abandonment manually from its own records and never sweep a
pot that shows any activity. No pot has been marked as of this writing (contracts/src/v21/AbandonmentManagerV21.sol:184-186,373,539,647,673;
; read markAt(vault)
and abandonedFlag(vault)
on the factory live).
Two marketing
figures on the live home page are explicitly not verified claims. "1M+ users
on X" and "100,000+ X Money users" were authorized once by the operator as
marketing copy for exactly two stat pills and are documented as such precisely so nobody
mistakes them for sourced numbers elsewhere (app/design/PILL-FACTS.md,
"Amendment, 2026-09-12").
Where this document
and a live /status
or on-chain read disagree, trust the live read. document can go stale between
edits; a chain read or a live endpoint cannot.
How to verify each claim yourself
bash
# Confirm the current factory and its live configuration.
F=0x16D36c555Eb5AB96a1159Cbc5B6d9582d3D7eb36
RPC=https://rpc.ordofi.network
for fn in "implementation(address)" "oracle(address)" "oracleEpoch(uint256)" \
"guardian(address)" "owner(address)" "pendingOwner(address)" \
"platformTreasury(address)" "pendingPlatformTreasury(address)" "pendingPlatformTreasuryEta(uint256)" \
"deliveryWallet(address)" "deliveryOperator(address)" "epochKeeper(address)" \
"floorTaxBps(uint16)" "platformShareOfFloorBps(uint16)" \
"deliveryFeeBps(uint16)" "deliveryDelay(uint256)" \
"reflectionDistributor(address)" "abandonmentManager(address)" \
"feeSplitterImplementation(address)" "v2Factory(address)" "requiredPairToken(address)"; do
echo "$fn: $(cast call $F "$fn" --rpc-url $RPC)"
done
# Expect: implementation 0x810d0a4A0B1d0c787819896C7f9aaB06C36cEd3d,
# reflectionDistributor 0xD5587eaB8C3e44bD8772139B00123f0b65632016,
# abandonmentManager 0xF414fE90210Ff176BBFC38C47e5Fc8d30B2690e0,
# v2Factory 0xcB1C296F4b964cC03Ba89D4414Cf7B9BA1da117b, deliveryFeeBps 500, deliveryDelay 0.
# Count how many vaults exist under v2.1 when you run this.
cast logs --from-block 61581481 --to-block latest \
--address 0x16D36c555Eb5AB96a1159Cbc5B6d9582d3D7eb36 \
"VaultCreated(uint256,bytes32,address,string)" \
--rpc-url https://rpc.ordofi.network
# Confirm no pot has been marked abandoned (an empty result means none).
cast logs --from-block 61581481 --to-block latest \
--address 0x16D36c555Eb5AB96a1159Cbc5B6d9582d3D7eb36 \
"AbandonMarked(address,uint256,uint256)" \
--rpc-url https://rpc.ordofi.network
# Confirm the live bytecode matches the compiled artifacts (method in contracts/DEPLOYMENTS.md).
cast code 0x16D36c555Eb5AB96a1159Cbc5B6d9582d3D7eb36 --rpc-url https://rpc.ordofi.network
cast code 0x810d0a4A0B1d0c787819896C7f9aaB06C36cEd3d --rpc-url https://rpc.ordofi.network
cast code 0xD5587eaB8C3e44bD8772139B00123f0b65632016 --rpc-url https://rpc.ordofi.network
# The previous v2 factory and implementation remain Sourcify exact-match.
curl -s "https://sourcify.dev/server/v2/contract/4663/0xcB1C296F4b964cC03Ba89D4414Cf7B9BA1da117b"
curl -s "https://sourcify.dev/server/v2/contract/4663/0x83bE7A2d8101E3aC87bAA0B5D5b9a0Ca0C67e54D"
# Confirm the claim, delivery and sweep properties on a fork of the live chain.
cd contracts && forge test --match-path "test/v21/*" \
--match-test "test_claim_fullBalancePayout_pendingPlatformShareReserved|test_ec14_guardianRevokeOracle_invalidatesAttestation|test_sweepAbandonedVault_onlyFactoryCanCallVaultSweep|test_fork_squatPreventionAgainstRealV2Factory"
# Re-run the full v2.1 suite yourself (contracts/README.md "Tests" under "v2.1").
cd contracts && forge test --match-path "test/v21/*"
# Confirm the retired v1 factory and the v1 xcoinpad test vault.
cast call 0xa75b74d833B03f2eb477873090FC4a0628A13AE8 "factory(address)" --rpc-url https://rpc.ordofi.network
# Expect 0x1594B1BA75B793117003E70B2903E8BAECC4eCeD, the retired v1 factory, not the v2.1 address above.
# Confirm services and the signer are live and configured, without any secret in the request.
# (the signer's /health answers only from allowlisted callers; from elsewhere expect 403, which
# is itself the caller-gating working)
curl -s https://api.xl.money/status
curl -s https://signer.xl.money/health
# Read the keeper's view of the live factory (read-only, no signer used). The three env vars
# are the factory address and deploy block from "Contracts and addresses" above plus the same
# public RPC used throughout this document, not secrets; the script has no default and errors
# without them.
HANDLE_VAULT_FACTORY=0x16D36c555Eb5AB96a1159Cbc5B6d9582d3D7eb36 \
FACTORY_DEPLOY_BLOCK=61581481 \
RPC_URL=https://rpc.ordofi.network \
node --json
Every
command above reads public chain state or a public endpoint; none requires a key or a secret. If
any answer disagrees with a number in this document, the live answer is correct and this
document is stale on that point.