dextorosystem handoff
As-is handoff document

The dextoro system, explained.

This page explains everything you are buying: the mobile app your users trade in, the dashboard you run it from, and the backend that ties it together. The default view is plain English. Flip the Simple / Technical switch at the top right for the engineer-level detail (file names, functions, settings).

Sold as-is
You get

The mobile app

The iOS and Android trading app on the App Store. Users sign in with email, get a wallet, and buy and sell Solana tokens.

You get

The admin dashboard

A private website where you curate tokens, set the live app version, manage staff, and watch system health.

You get

The backend

One Supabase project: the database, the server functions that run trades, login, and the live data the app shows.

Not included in the sale

The old "launchpad" product (token launch site, market-data, staking, seedbot, edge-cache) is shut down and is not part of this sale. The dextoro.com domain is also excluded, the app currently leans on it for links, so you will stand up your own domain (covered under Known issues and Operations).

Everything here was checked against the live code on 2026-06-08. Where older internal docs disagreed with the code, the code wins.

If you just took over

First steps, in order

A do-this-in-order checklist for a new owner. It pulls together actions that also appear in Operations and Known issues, so you have one path to follow. Plain action per line; turn on Technical for the exact settings and files.

  1. Transfer the App Store app to your own Apple account.

    The app, its store listing and its id move to your Apple team. The app keeps the same name and id.

    Technical
    • Transfer in App Store Connect. Bundle com.dextoro.app and App Store id 6739891028 stay the same.
    • Apple Team id changes from 65F2J667K9 to yours. Your new Team id then has to go into the domain link-verification file (AASA) and the next build.
  2. Take over (or replace) the iosdextoro@gmail.com Google account.

    This one email is the hub for three things, so secure it before anything else: the admin dashboard seed login, the BirdEye account, and the Google Cloud project that holds the Google Play credential.

    Technical
    • Seed super_admin in admin_users = iosdextoro@gmail.com.
    • Same email owns the BirdEye account and Google Cloud project dextoro-635a3 (the Play service account).
    • Either take ownership of the mailbox, or migrate each service to your own account one by one.
  3. Issue your own key at every outside service, swap it in, then revoke the old keys.

    For each service in the Services section, create your own account/key, paste it into the backend or the build settings, confirm things still work, then revoke the previous key last.

    Technical
    • Services: Turnkey, Alchemy, Helius, BirdEye, Jupiter, OneSignal, Upstash, Sentry, MoonPay.
    • Backend keys go in Supabase secrets; baked-in ones go in EAS (see Appendix). Redeploy functions after Supabase changes; rebuild the app after EAS changes.
    • Revoke old keys only after the new ones are confirmed live.
  4. Rotate the leaked Google Play key and purge it from git history.

    The Play upload credential is committed in the app repo's history, so anyone with the repo has it. Rotate it and scrub it out.

    Technical
    • service-account-file.json is tracked and appears in 2 historical commits (project dextoro-635a3).
    • Rotate the key in Google Cloud (required regardless), purge the file from history with git-filter-repo or BFG, and store the new key as an EAS secret, not in the repo.
  5. Upgrade BirdEye to a paid plan and rotate its key.

    This restores the price charts, which are degraded today because the free quota is maxed out.

    Technical
    • Upgrade the BirdEye plan, then rotate BIRDEYE_API_KEY (it is baked into the binary in dead code), updating both the Supabase secret and the EAS value.
  6. Set a paid Jupiter key.

    Trades work on the free public router today; a paid key raises the limits so trading stays smooth under load.

    Technical
    • Set JUPITER_SWAP_API_URL=api.jup.ag/swap/v1 + JUPITER_SWAP_API_KEY (EAS) for the app swap path, and JUPITER_API_KEY (Supabase) for the token list/prices.
  7. Change the seed super-admin password and keep super_admin to yourself.

    Reset the dashboard login password and make sure you are the only super admin. Invite staff at lower roles.

    Technical
    • Reset the password for the seed login (iosdextoro@gmail.com) or create your own super admin and demote/remove the seed.
    • Invite staff as manager or viewer from User Management (super_admin only).
  8. Fund the fee payer wallet with SOL.

    This is the wallet that pays network fees for users. If it runs empty, trades start failing, so keep some SOL in it.

    Technical
    • Send SOL to the fee payer wallet (pubkey is in EXPO_FEE_PAYER_PUBKEY / the dashboard; private key is the Supabase secret FEE_PAYER_PRIVATE_KEY).
  9. Stand up your own domain and ship a new build to repoint to it.

    Because dextoro.com is not included, set up your own domain with the link-verification files and the legal and landing pages, then build and submit a new app version that points at it.

    Technical
    • Serve /.well-known/apple-app-site-association (with your Apple Team id), /.well-known/assetlinks.json, the /applinks landing page, the /moonpay return, and real /terms and /privacy pages.
    • Repoint mainly 3 files: constants/urlConstant.ts, app.json, hooks/hooksWithContext/useReviewFlow.tsx; set up your own Branch app and MoonPay return URL. Full steps: DOMAIN_MIGRATION_RUNBOOK.md.
  10. Confirm on-chain that trading fees land in your treasury wallet.

    After your build is live, make a small test trade and check that the fee actually arrives at your treasury wallet. Remember the dashboard treasury field is display-only, so do not trust it alone.

    Technical
    • The destination is the baked-in TREASURY_WALLET EAS value (project scope wins; watch the account-scope duplicate, see Operations). Verify the fee transfer on a block explorer, not just in the dashboard.

Each item is expanded in Operations and Known issues. The treasury "ghost control" trap and the duplicate TREASURY_WALLET landmine are explained in full under Operations.

Big picture

How the pieces fit together

Two apps talk to one backend. The backend talks out to a handful of specialist services. Click any box for what it is and how to manage it.

Tip: click a box to open its details. Turn on Technical (top right) to see file paths, function names and settings inside each box and each step.
Users (traders)
phones
Owner & staff
web browser
use
everything goes through
which calls
Wallet & chain
Data & RPC
Engagement & monitoring
Hosting & release
The two apps

What users touch, and what you run it from

The backend

Supabase: the brain of the system

One Supabase project (ref ehauakpcvvuoocxjytwb) holds the database, runs the server functions that build and submit trades, handles login, stores avatars, and pushes live updates to the app. You manage it from the Supabase dashboard.

Server functions (the things the apps call)

These are small server programs ("edge functions"). Plain purpose on the left. Turn on Technical for which are login-protected and what they call.

FunctionWhat it doesNotes

The main database tables

TableHolds

Live updates (Realtime)

When something changes a user's balance (a deposit, a trade), the backend pushes it to that user's app instantly, so balances and the activity list update without a manual refresh. Implemented with Supabase Realtime listening to INSERTs on the activities table filtered by wallet (hooks/useRealtimeBalance.ts), kept warm by a 60s pg_cron job. Live price ticking on the home and token screens is a separate, older mechanism, see Known issues.

Outside services

The specialist services dextoro relies on

Each of these is a separate company with its own account and dashboard. On takeover you generally create your own account, issue your own keys, and point the system at them. Each card says what it does, where its dashboard is, and how you manage it.

Key flows

How the system actually works, step by step

Four common journeys, click through each step. The plain story is the default. Turn on Technical to see the exact function and file behind every step.

Operations / runbook

Running dextoro day to day

Where the money goes: the treasury wallet

Every buy or sell charges a small platform fee. That fee is sent to your treasury wallet, a Solana wallet you control. Fees pile up there automatically as people trade. The admin Infrastructure page shows the treasury balance and total fees collected.

Trap: changing the treasury in the dashboard does nothing today

The fee destination is baked into the app when it is built (it comes from a build setting called TREASURY_WALLET). The treasury field you see in the admin Settings page, and the matching admin_settings.treasury_wallet value, are display and record only right now. The app does not read them when it builds a trade. The server-side enforcement that was designed to make the dashboard the source of truth has not shipped yet.

So if a non-technical owner edits the treasury in the dashboard, it will look like it saved, but fees will keep going to the old wallet. The only way to actually change where fees land today is: update the TREASURY_WALLET build setting in EAS and ship a new app build (then confirm on-chain). See "Shipping an update" below.

Landmine for your engineer: there are two TREASURY_WALLET settings

In EAS there is a TREASURY_WALLET at project scope and a second one at account scope (shared, marked secret). Project scope wins by precedence, so today fees go where the project value points. But if anyone deletes or recreates the project-scope variable, the account-scope value silently takes over and fees can quietly route to a different wallet. This does not show up anywhere in the code, it lives only in EAS. After any EAS change or rebuild, check both variables and verify the on-chain fee destination.

The fee payer (gasless trading)

Solana charges a tiny network fee for every action, and setting up a new token in a wallet costs a small refundable deposit. The fee payer is a wallet that quietly covers those costs and co-signs every trade, so your users can trade without first buying SOL for "gas". It needs to stay funded with SOL.

Who can do what (admin roles)

RoleCan do
super_adminEverything: tokens, tags, app version, settings (treasury / fees), and staff/user management.
managerCurate tokens and tags. Cannot manage staff, settings, or the live version.
viewerRead-only. Look, but no changes.

Enforced both in the UI and in the database via the admin_role() function and row-level security on the admin_* tables. Staff are invited from the User Management page (super_admin only), which calls the admin-users function. The seed super admin is iosdextoro@gmail.com.

Shipping an update

WhatHowSpeed
Mobile appBuild with EAS, submit to the App Store and Google Play, wait for review. Bump version + build number in app.json first (appVersionSource=local, so it is manual). One build per day, batch changes in.Hours, not instant (Apple review ~2 to 6h)
Admin dashboardPush to the main branch. Cloudflare rebuilds and deploys it automatically.A couple of minutes
Backend functionsDeploy the function. supabase functions deploy <name> (reads verify_jwt from config.toml). Database changes are applied by hand (no migration chain). Secrets are set in the Supabase Dashboard, then redeploy.Seconds

Monitoring: what healthy looks like

Where the secrets live

No secret values are in this document. Here is where each kind lives and how to rotate it. Full list in the Appendix.

Cross-reference: a fuller owner guide for the dashboard already exists in the admin repo at dextoro-app-admin/docs/OWNER_GUIDE.md.

Known issues

As-is items, in the open

This is an as-is sale, so here is the honest list. Your engineer will find these anyway. None of them stop the core flows (login, trade, deposit) from working today.

ItemWhat it meansWhat to do
New-coin buy can fail A buy of a brand new token can fail in the fee / token-account setup path inside the trade function. It is not fully diagnosed. Diagnose this before redeploying the trade function (for example before adding the referral validator). Lives in the transaction edge function's ATA / fee path.
BirdEye over its limit The prices/charts provider is over its free monthly quota, so charts and some prices are degraded until it resets or you upgrade. Upgrade BirdEye to a paid plan, then rotate the key. Key is baked into the binary in dead client code; rotate after upgrade. Over-cap caused a chart crash (guard shipped).
dextoro.com is excluded The app's links (referrals, share links, deep links, the card-purchase return, legal/support links) point at dextoro.com, which is not part of the sale. Stand up your own domain and serve the link-verification files and pages, set up your own Branch and MoonPay return URL, then ship a new build. Repoint is mainly 3 files: constants/urlConstant.ts, app.json, hooks/hooksWithContext/useReviewFlow.tsx; plus AASA (your Apple Team ID) + assetlinks + landing/legal pages. A runbook exists: DOMAIN_MIGRATION_RUNBOOK.md.
Google Play key is in git history The Google Play upload credential (service-account-file.json) is committed into the app repo and is present in its git history. Whoever gets the repo gets a live credential. Before close: purge it from git history and rotate the key in Google Cloud. Project dextoro-635a3; file is tracked and appears in 2 historical commits. Replace with an EAS secret going forward.
Swaps run on the free price router Trades use Jupiter's free public endpoint, which has lower rate limits. Trades work, but under heavy load this can throttle. Buy a Jupiter plan and set the paid URL + key. App falls back to lite-api.jup.ag/swap/v1 because the env URL is intentionally mis-set; set JUPITER_SWAP_API_URL=api.jup.ag/swap/v1 + key (common/index.ts).
Live price ticking is partial Balances and deposits update live, but the constantly-moving prices on the home and token screens relied on old servers that are no longer running. Decide a host for live prices (Supabase Realtime or a small worker) and wire it up. Legacy EXPO_TOKEN_WEBSOCKET_SERVER / EXPO_BACKEND_WEBSOCKET_SERVER (AWS) referenced in useTokenList.tsx / useActivity.ts are offline; Supabase Realtime covers balance/deposit.
Referral push not built Deposit notifications work. The "you earned a referral reward" push was intentionally left for the buyer to add. Build it on the existing push sender when ready. Deferred along with the trade function redeploy because of the new-coin buy issue above.
Old Firebase push half-removed Push moved to OneSignal. Leftover Firebase plumbing is dormant but not fully deleted. Finish removing the old Firebase pieces on a routine build. The push-token function is a stub returning empty.
Version number mismatch (cosmetic) The app's two version files disagree (app.json says 2.5.4, package.json says 2.5.2). Harmless, but tidy it. Align them on the next build. App Store version is driven by app.json (2.5.4 / build 162 in source).
Glossary

Plain definitions

WalletAn account on the blockchain that holds a user's crypto. dextoro creates one per user automatically.
Self-custodyThe user controls their own wallet keys. dextoro cannot freeze or take their funds.
Treasury walletThe wallet you control where dextoro's trading fees are collected.
Fee payer / relayerA wallet that pays the blockchain's tiny network fees for users so they can trade without holding SOL for gas.
RPCThe connection used to read from and send to the blockchain. Alchemy is the main one here.
WebhookA service calling your backend the moment something happens, for example Helius pinging you when a deposit lands.
Edge functionA small server program that runs on demand. dextoro's live in Supabase.
Swap / routeTrading one token for another. The "route" is the best path to do it at the best price (found by Jupiter).
SlippageHow much price movement a user accepts between asking and the trade completing.
Mint / mint addressA token's unique blockchain id. Like a product code for a coin.
ATA (token account)A small sub-account a wallet needs before it can hold a given token. Creating one costs a tiny refundable deposit, paid by the fee payer.
Deep link / universal linkA link that opens the app directly (or the store if it is not installed). Used for referrals and sharing.
OTA update"Over the air", a small app update delivered without an App Store review. Limited to certain changes.
RLS"Row-level security", database rules that decide which user or admin can see or change which rows.
OTPThe one-time code emailed to a user to log in.
USDC / SOLUSDC is a US-dollar stablecoin. SOL is Solana's native coin, used for fees.
Appendix

Reference

Public identifiers (safe to share)

IdentifierValueNote
App Store id6739891028Stays with the app on transfer
iOS bundlecom.dextoro.appUnchanged on transfer
Android packagecom.dextoro.pro
Supabase projectehauakpcvvuoocxjytwbURL: ehauakpcvvuoocxjytwb.supabase.co
Branch link domainsdextoro.app.link (+ alternates)Branch-owned, not dextoro.com
Intercom app idnplqj2mhSupport chat (public id)
Sentryorg ios-dextoro / project dextoro-appCrash monitoring
Apple Team id65F2J667K9Changes to the buyer's team on transfer
Treasury walletconfirm in EAS / dashboardLive value lives in the EAS build setting, not in the code (committed value is a placeholder)
Fee payer walletconfirm in EAS / dashboardPublic key in EAS; private key in Supabase secrets

Secrets inventory (names only, never values)

SecretLives inFor
TURNKEY_API_PRIVATE_KEY / _PUBLIC_KEY / _ORGANIZATION_IDSupabase secretsWallets + login
FEE_PAYER_PRIVATE_KEYSupabase secretsCo-signing + gas (the relayer wallet)
ALCHEMY_API_KEYSupabase secrets + EASMain chain connection
HELIUS_API_KEY / HELIUS_WEBHOOK_ID / HELIUS_WEBHOOK_SECRETSupabase secretsDeposit watcher + its auth
BIRDEYE_API_KEYSupabase secretsPrices + charts (rotate after upgrade)
JUPITER_API_KEY / JUPITER_SWAP_API_KEYSupabase secrets / EASToken data + paid swap routing
ONESIGNAL_REST_API_KEYSupabase secretsSending push (App ID is public)
UPSTASH_REDIS_REST_URL / _TOKENSupabase secretsThe fast cache
SENTRY_API_TOKENSupabase secretsHealth page reads crash stats
SUPABASE_SERVICE_ROLE_KEY / JWT_SECRETSupabase secretsInternal admin access + session signing
TREASURY_WALLET / FEE_PAYER_PUBKEYEAS build env (baked into app)Fee destination + relayer pubkey
MOON_PAY_API_KEY, SUPABASE_URL, SUPABASE_ANON_KEY, etc.EAS build envApp runtime config
VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEYCloudflare Pages build envAdmin site config (both public)
Google Play service accountCurrently in the repo (move to EAS secret)Play submission, see Known issues

Rotation rule of thumb: on takeover, issue your own key at each service, replace the value in Supabase (or EAS for the baked-in ones), redeploy or rebuild, then revoke the old key.