Wallets and key custody
What the Solana master wallet pays for, how per-user deposit addresses are created, why they are not derived from the master wallet, and what a backup has to contain to be a backup.
Solana custody follows Ecosystem's model with one significant difference, and that difference changes what your backups have to cover. Read Master wallets and the vault first — the vault, the passphrase trade-off and the two ways to lose every wallet on an install are described there and are not repeated here.
The master wallet is a fee payer
One Solana master wallet per install, created from Admin → Ecosystem → Wallets →
Master Wallets. The keypair is generated by the Solana service, encrypted with
the vault key immediately, and stored in ecosystem_master_wallet.data. That
encrypted blob is the only persisted copy; nothing is written to a plaintext
file, deliberately.
On Solana it does four jobs, and holding customer funds is not one of them.
- Pays the network fee on every SPL token withdrawal. The tokens move out of the customer's own address; the master wallet signs only as fee payer.
- Pays associated-token-account rent. Before an SPL transfer the platform ensures both the sender and the recipient have an associated token account for that mint, creating either if it is absent. Creating one costs rent-exempt lamports, and the master wallet pays.
- Deploys SPL mints. A token created from the admin panel is a mint whose mint authority is the master wallet, with no freeze authority.
- Mints the initial supply, as a background job, into the nominated holder's associated token account.
Native SOL withdrawals do not involve it at all. That produces a very specific failure shape worth memorising:
Native SOL withdrawals keep working — they are signed by the customer's own address and the fee comes out of the balance being sent. Every SPL token withdrawal fails. So does mint deployment, and so does the background initial mint, which deletes the token row it was minting for.
The symptom is "USDC withdrawals are broken but SOL is fine", which does not look like a funding problem until you know this.
Disabling the Solana master wallet is a real lever: the diagnostics treat a disabled wallet exactly like a missing one and downgrade withdrawal readiness to failed. Use it to stop signing on Solana if you believe the key is compromised.
Per-user deposit addresses
Every ECO wallet whose currency exists as an active Solana token gets an entry in its address map:
{
"SOL": {
"address": "9xQe…",
"network": "mainnet",
"balance": 0
}
}The network value comes from SOLANA_NETWORK, defaulting to mainnet. It is
a label recorded at generation time. It does not select a cluster and nothing
re-reads it, so if it disagrees with SOL_NETWORK the record is simply wrong.
Addresses are created lazily, the first time a user opens the deposit page for that currency. Ecosystem finds or creates the ECO wallet, looks up every active token with that currency symbol, and derives an address for any chain that does not have a usable one yet. An entry that exists but has no address is regenerated in place on the next fetch — that repair is the design working, not a fault.
Alongside the address map, a wallet_data row is written per wallet and chain,
holding the encrypted key material. For native SOL that row's currency is SOL;
for an SPL token it is the token's currency. Native withdrawals read the row
matched on currency SOL and chain SOL; SPL withdrawals read the row for that
wallet on chain SOL.
Solana addresses are not derived from the master wallet
This is the difference that matters.
On EVM chains, each user's deposit address is derived from the master wallet's HD material at the next unused index — which is why losing an EVM master wallet loses the ability to spend from every address under it.
On Solana, each address generation produces a completely independent
keypair: a fresh BIP39 mnemonic, an ed25519 derivation at m/44'/501'/0'/0',
and a keypair from that seed. The mnemonic, the derivation path, the public key
and the secret key are encrypted together and written to that user's
wallet_data row. The index column on Solana rows is always 0 and is not a
derivation index.
Two consequences, and they pull in opposite directions:
Losing the Solana master wallet does not strand user funds. Every user key is self-contained. You lose SPL withdrawals and mint deployment until you create and fund a new master wallet, but no customer address becomes unspendable.
There is no seed from which user addresses can be regenerated. On EVM you
could, in principle, re-derive an address from the master wallet's mnemonic. On
Solana every address exists in exactly one place: its own encrypted
wallet_data row. A wallet_data row lost from a restore is a customer address
you can never sign for again.
The wallet_data blobs are encrypted with the vault key, which is
ENCRYPTED_ENCRYPTION_KEY in .env, itself encrypted under
ENCRYPTION_KEY_PASSPHRASE. Restore the database without those and you have
several thousand rows of ciphertext and no way to read any of them.
Back up .env somewhere your database backup does not live, and keep the
passphrase somewhere else again.
There are no custodial contracts on Solana
Ecosystem deploys shared custodial contracts for EVM tokens that cannot delegate
their own fee. Solana is on the non-custodial chain list, so this never applies:
every Solana deposit — native or SPL, whatever the token's contractType says —
goes to the user's own address.
If a Solana token row is mis-flagged NO_PERMIT, the deposit path ignores the
flag and uses the per-user address anyway rather than looking for a custodial
contract that cannot exist. You will not see "All custodial wallets are
currently in use" on this chain.
The practical version: on Solana there is exactly one address per user per currency, it belongs to that user, and the platform holds its key.
Address validation on withdrawal
A withdrawal destination is validated by constructing a Solana public key from
it. Anything that is not a valid base58 Solana address is rejected with 400 and
the message Invalid Solana address: … before any balance is touched.
Note what that does not check: whether the destination is an exchange deposit address expecting a memo, whether it is a token account rather than a wallet address, or whether the recipient can receive the mint you are sending. A syntactically valid address that is the wrong kind of account is accepted, signed and broadcast.
Related
- Deposits — what happens at those addresses
- Withdrawals and fees — which key signs what
- Ecosystem: Master wallets and the vault — the vault, the passphrase and the recovery story
- Ecosystem: Deposit wallets and custody — the platform-wide custody model