Skip to content

feat: add Stellar local-key example (Classic + Soroban)#16

Merged
mihaic195 merged 16 commits into
mainfrom
feat/stellar-local-key-example
Jul 22, 2026
Merged

feat: add Stellar local-key example (Classic + Soroban)#16
mihaic195 merged 16 commits into
mainfrom
feat/stellar-local-key-example

Conversation

@mihaic195

Copy link
Copy Markdown
Contributor

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.

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.
@mihaic195 mihaic195 self-assigned this Jul 21, 2026
mihaic195 and others added 15 commits July 21, 2026 12:36
- 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
@mihaic195
mihaic195 marked this pull request as ready for review July 22, 2026 09:45
@mihaic195
mihaic195 merged commit 77da754 into main Jul 22, 2026
@mihaic195
mihaic195 deleted the feat/stellar-local-key-example branch July 22, 2026 10:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant