feat: add Stellar local-key example (Classic + Soroban)#16
Merged
Conversation
Add an example suite for deploying and managing Stellar tokens via the Token Tool public Stellar API (/api/v1), signing locally with a private key. Shared helper (common/stellarApi.ts) wraps the non-custodial build -> sign -> submit -> poll lifecycle, the response envelope, retryable error classification, and the submit/poll contract (only `failed` permits re-signing; never re-sign on `pending`). Classic (SEP-based, Horizon): - createAsset - full flags -> trustline -> issuance sequence, optional lock - mintAsset - mint additional supply - distributeAsset - chunked distribution (<= 99 recipients/tx) - manageAsset - authorize/freeze/unfreeze, clawback, lock-issuer - readAsset - asset info + holder list Soroban (SEP-41 contract): - deployToken - single-tx contract deploy - manageToken - mint/burn/pause/blacklist/change-owner/balance-limit - distributeToken - chunked distribution (<= 20 recipients/tx) Plus createWallet.ts, secret_key.example, and a README. Real key files are already gitignored via the *_key suffix.
- unwrap: fall back to HTTP status when an error response is not a JSON envelope (infra-layer 429/503/502), so ApiError.status stays usable for retryability instead of throwing an opaque SyntaxError. - buildSignSubmit: throw on an unresolved `pending` (poll timeout), not just `failed`, so a multi-step sequence never builds a dependent step against state that has not landed. deployToken hardened the same way. - createAsset: fail loudly when LOCK_SUPPLY is combined with compliance flags instead of silently leaving supply unlocked. - createAsset: drop the dead `asset` variable.
- deployToken: use the shared buildSignSubmit helper instead of hand-inlining build/sign/submit/poll. Removes ~25 lines and the duplicated pending/failed re-sign guard that previously had to be kept in sync with stellarApi.ts. - loadSecretKey: read `fs` via a module-level import instead of taking it as a parameter; drops the leaky `fs` arg and its `import fs` from all 7 callers.
Remove comments that only restated adjacent code or duplicated documentation already present elsewhere: - deployToken: drop the inline lifecycle comment (duplicated buildSignSubmit's JSDoc and the file header) and the "Optional document URI" field restatement. - manageAsset: collapse the "Choose one" block that re-listed the ManageAction union declared directly above it. - createAsset: drop the issuance-step lock caveat already stated by the explicit guard and the LOCK_SUPPLY config comment.
Add explorerAccountUrl and explorerAssetUrl helpers (stellar.expert) and surface them in the create/deploy output: - createAsset prints the issued asset link plus issuer/distribution account links alongside the issuance tx. - deployToken prints the admin account link alongside the deploy tx.
Introduce local-key/stellar/config.ts holding the shared account/asset identifiers (CHAIN_ID, CODE, ISSUER_ADDRESS, DISTRIBUTION_ADDRESS, SUPPLY, ADMIN_ADDRESS, TOKEN_ADDRESS). Every script imports them so the values are edited once for an end-to-end run; operation-specific knobs (flags, recipients, manage action, mint amount) stay in each script. Add getDeployedContractAddress: after a SEP-41 deploy confirms, read the new contract's address (C...) from the deploy transaction's return value via Soroban RPC (overridable with SOROBAN_RPC_URL). deployToken now prints the contract address and its explorer link instead of pointing at the explorer. Add explorerContractUrl helper and update the README for the config workflow.
Add addTrustline.ts: establishes a trustline from a holder account to the Classic asset so it can receive distributions. Reuses the create/trustline build endpoint (which builds a changeTrust for whatever account is passed as the source), signed by the holder. A trustline needs the holder's own signature and reserve, so this only works for accounts you control (e.g. test recipients). The README notes that in production each holder self-serves via a wallet or the Token Tool UI. Add accountAddress() helper to derive a G... address from a secret key.
Replace the programmatic addTrustline.ts (which required the holder's secret key - unrealistic, the distributor never holds it) with a manageAssetUrl helper that returns the Token Tool manage-token page for a Classic asset. There the holder connects their own wallet and adds the trustline themselves. distributeAsset.ts prints the link for the asset being distributed so it can be shared with any recipient that doesn't trust the asset yet. README updated.
Align manageAssetUrl with the Token Tool app's canonical trustlineInviteLink: append ?networkName=<chainId> so the manage page preselects the right network (networkGuard reads router.query.networkName).
Before distributing an issued asset, probe each recipient's trustline via /classic/account/held-assets and split out the ones that don't trust it yet: report them with the manage-page invite link and skip them, then distribute to the eligible recipients. Mirrors the Token Tool app's airdrop flow, which excludes no-trustline holders the same way, and turns a mid-run build failure into an upfront, actionable list. Add heldAssets() and hasClassicTrustline() read helpers.
Holder-targeted manage actions (authorization, clawback) require the holder to already trust the asset. The API already probes this and rejects the build naming any holder without a trustline, so manageAsset does not pre-skip holders (silently dropping a compliance target is undesirable). It documents the requirement and prints the manage-page invite link for holder-targeted actions so the operator can send it to holders that need a trustline.
Check each Classic distribute destination with StrKey up front and name any malformed address, instead of surfacing an opaque 400 from the first held-assets probe (Promise.all hides which recipient is invalid). Add isValidAccount helper.
SEP-41 distribution runs through a distribution contract that moves the signer's tokens via transfer_from, which needs a prior allowance. distributeToken.ts now sends a one-time approval (POST /soroban/distribute/approve/build) before the distribute chunks. README updated. The approve endpoint is being added to the public v1 API separately.
The v1 API dropped the trailing /build from every endpoint route, so update all path strings accordingly (e.g. /soroban/distribute/approve/build -> /soroban/distribute/approve, /classic/create/flags/build -> /classic/create/flags). Doc wording updated to say "build endpoints" without implying a /build path. Verified against the live local API: new paths 200, old /build paths 404.
Extract the repeated network/token identifiers in the local-key/evm, fireblocks/evm, and metaco/evm examples into a per-group config.ts, so they are edited once instead of per script. Operation-specific knobs stay in the individual scripts. Also gitignore the tsc build output (dist/).
refactor: shared config.ts for EVM examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add an example suite for deploying and managing Stellar tokens via the Token Tool public Stellar API (/api/v1), signing locally with a private key.
Shared helper (common/stellarApi.ts) wraps the non-custodial build -> sign -> submit -> poll lifecycle, the response envelope, retryable error classification, and the submit/poll contract (only
failedpermits re-signing; never re-sign onpending).Classic (SEP-based, Horizon):
Soroban (SEP-41 contract):
Plus createWallet.ts, secret_key.example, and a README. Real key files are already gitignored via the *_key suffix.