stellagate-core
v0.1.1
Published
Check a Stellar destination before you send: memo requirements, trustlines, Stellar Asset Contract holdability and contract-account compatibility.
Maintainers
Readme
Stellagate
Check a Stellar destination before you send, not after.
Stellagate tells a wallet, before anything is signed, whether a destination needs a memo, whether it can hold the asset, and whether the sending account can reach it at all. It is built for the payments that go missing today: exchange deposits without a memo, withdrawals to wallets without a trustline, and passkey smart wallets (C-addresses) paying exchanges that only understand memos.
The problem
A Stellar address does not say what the receiver needs.
- Exchanges pool deposits in one account and credit them by memo. Without it, the funds arrive and are never credited. The SDK team's own description: "a deposit credited to a pooled exchange account with no memo, which means lost funds and a support ticket" (js-stellar-sdk#1607).
- Memos are still required by many services. Stellar's docs, September 2026: memos "remain the required path for many exchanges, anchors, and wallets" (stellar-docs#2769).
- Smart wallets cannot attach a memo, because Soroban transactions do not carry one. The muxed-ID transfer that replaces it (CAP-67) "is not yet widely supported in exchanges for deposit" (freighter-mobile#492).
- Receiving a classic asset needs a trustline and 0.5 XLM of reserve. Withdrawals to a wallet without one fail.
There are now four address forms (G, M, C, and name*domain federation) and a memo field
that works for some of them. SEP-29 lets an account flag "memo required", but only helps if
the account sets it and the sending wallet checks it. Most exchanges never set it: of the
107 accounts StellarExpert's directory tags memo-required, only 14 carry the SEP-29 flag on
chain. Binance, Kraken, Bitstamp, Coinbase Deposits and Crypto.com are among the 93 that do
not (survey of 2026-10-02, reproducible with node scripts/survey-memo-required.mjs).
A wallet that relies on SEP-29 alone misses 87% of them.
What Stellagate checks
npm install github:StellaGate/stellagateimport { check } from '@stellagate/core'
const verdict = await check({
to: 'GABFQIK63R2NETJM7T673EAMZN4RJLLGP3OFUEJU5SZVTGWUKULZJNL6', // what the user pasted
from: 'CB...', // optional: the sending account, G or C
memo: undefined, // optional: a memo the user already entered
asset: 'native', // optional: 'native' or CODE:ISSUER, for the holdability check
amount: '250', // optional: checked against the receiver's trustline limit
network: 'public',
})
verdict.status // 'block'
verdict.entry // { name: 'Binance Deposits', domain: 'binance.com', tags: ['exchange', 'memo-required'] }
verdict.reasons // [{ code: 'contract_sender_cannot_memo', severity: 'block',
// message: 'Binance Deposits requires a memo (public directory), and a
// contract account cannot attach one.',
// fix: 'Send from a classic (G...) account with the memo, ...' }]- Parse G, M, C and federation addresses into an account plus memo or muxed ID, and refuse a pasted secret key.
- Memo required? From the account's SEP-29 data entry and the StellarExpert directory, later also Stellagate's own exchange records.
- Flagged? Blocks accounts the directory marks malicious or unsafe.
- Can this sender reach it? Flags a contract account paying a memo-required account, or trying to attach a memo at all, and gives the route that works.
- Can it hold the asset? A classic account needs a trustline the issuer has authorised,
with room under its limit for the amount. A contract address holds classic assets through
the asset's Stellar Asset Contract, so that contract must be deployed, the destination
contract must exist, and an issuer that requires approval must have approved it. Contract
checks read Stellar RPC: testnet's public endpoint by default, and on mainnet whatever
endpoint you pass with
contracts: rpcContractSource(url).
Stellagate never signs, submits or holds funds.
Try it without installing anything at stellagate.namite.xyz. The check runs in your browser.
Status
| Part | Status | |---|---| | Problem research and evidence | Done, see docs/PRD.md | | Address parsing (G, M, C, federation, secret-key refusal) | Done, tested | | Memo-required check: SEP-29 and public directory | Done, tested, verified against mainnet | | Malicious and unsafe account flags | Done, tested | | Contract-account sender compatibility | Done, tested | | Trustline and SAC holdability check | Done, tested, exercised on testnet | | Public check page, with terms, privacy and cookie pages | Live at stellagate.namite.xyz and stellagate.github.io/stellagate | | Exchange records with deposit evidence | Planned | | npm release | Planned, not yet published | | Upstream proposal to js-stellar-sdk | Planned |
Evidence
scripts/survey-memo-required.mjschecks every account the StellarExpert directory tags memo-required against Horizon. On 2026-10-02: 107 accounts, 14 with SEP-29 set.scripts/testnet-evidence.mjscreates fresh testnet accounts, runs each check, then makes the real payment anyway. Everyblockbelow matches a payment the network refused, and everyokmatches one it accepted, with two exceptions. The two memoblockrows were not sent, because the network accepts a payment with no memo and the exchange never credits it. And in the last row, a token transfer to a contract address with nothing deployed succeeds, and the tokens sit at that address until someone deploys a contract there. The network will not stop that payment, so Stellagate does.- The contract destination is a deployed contract (the KYC asset's own Stellar Asset Contract) standing in for a smart wallet.
- Testnet is reset from time to time, which breaks these links. The script reproduces the whole run in about a minute.
Run on testnet, 2026-10-03.
Setup:
| Step | Transaction | |---|---| | Exchange sets SEP-29 memo_required | acccf23a | | Approval-required issuer sets AUTH_REQUIRED and AUTH_REVOCABLE | 8b27a270 | | Receiver adds STG trustline | a427a390 | | Receiver adds STG trustline with limit 100 | 492c08d1 | | Receiver adds KYC trustline (not yet approved) | 48b900d0 | | Sender adds STG trustline | 9bc4c934 | | Issuer pays sender 1000 STG | e6df3799 | | Deploy the STG Stellar Asset Contract | f3b99f4b | | Deploy the KYC Stellar Asset Contract | bacb5a3b | | Issuer authorises the KYC trustline | 3dbb801d | | Deploy the NEW Stellar Asset Contract | 1d728180 |
Checks, then the real payment:
| Scenario | Destination | Verdict | Reasons | Payment attempted | Network result |
|---|---|---|---|---|---|
| SEP-29 account, no memo | GBJ2...TMIE | block | memo_required | none | |
| SEP-29 account, memo given | GBJ2...TMIE | ok | none | 0413d601 | success |
| SEP-29 account, contract sender | GBJ2...TMIE | block | contract_sender_cannot_memo | none | |
| No trustline | GDEY...4ZTU | block | no_trustline | d7f807ca | tx_failed, op_no_trust |
| Trustline present | GALN...M45E | ok | none | 03c26f4e | success |
| Over the trustline limit | GD7W...EK4D | block | trustline_limit_exceeded | b88d4025 | tx_failed, op_line_full |
| Within the trustline limit | GD7W...EK4D | ok | none | ddfb5be3 | success |
| Trustline not authorised by issuer | GDLA...CXBX | block | trustline_not_authorized | 21a37b87 | tx_failed, op_not_authorized |
| Trustline authorised by issuer | GDLA...CXBX | ok | none | f4852528 | success |
| Account does not exist, STG | GDHF...JR3E | block | destination_unfunded | e7ab5249 | tx_failed, op_no_destination |
| Contract, asset contract not deployed | CC2B...SN4C | block | sac_not_deployed | not submitted | rejected in simulation: HostError: Error(Storage, MissingValue) |
| Contract, asset contract deployed | CC2B...SN4C | ok | none | 49922faf | success |
| Contract, STG via its asset contract | CC2B...SN4C | ok | none | 3c4a6f90 | success |
| Contract, approval-required asset, not approved | CC2B...SN4C | block | sac_balance_not_authorized | not submitted | rejected in simulation: HostError: Error(Contract, #11) |
| Contract address with nothing deployed | CDSC...HENS | block | destination_contract_missing | 301a734c | success |
Development
npm install
npm test # unit tests, offline
npm run build
node scripts/testnet-evidence.mjs # rerun the testnet evidence, about a minuteThe check page lives in site/. It imports the library source directly and runs every
check in the visitor's browser, so there is no server.
cd site && npm install
npm run dev # http://localhost:5173
[email protected] npm run buildThe build refuses to run without a contact address, because the privacy policy names one.
Set contactEmail in site/site.config.json or the CONTACT_EMAIL variable. BASE_PATH
sets the path the site is served under (/stellagate/ on GitHub Pages, / elsewhere).
Data sources
Horizon and Stellar RPC, SEP-1 stellar.toml files, SEP-2 federation servers, and the
StellarExpert directory API. All public. Exchange behaviour is established by small test
deposits from the maintainer's own accounts and recorded with the transaction hash.
Maintainer
@jadonamite, an open-source contributor to Stellar projects since January 2026, working on payment infrastructure, Soroban contracts and SDKs.
Contributing
Issues are scoped so each one can be picked up independently. See CONTRIBUTING.md.
License
Apache-2.0. See LICENSE.

