Skip to content

wallets: remove @stellar/stellar-sdk dependency#1688

Merged
albertoelias-crossmint merged 5 commits intomainfrom
devin/1773964898-remove-stellar-sdk
Mar 20, 2026
Merged

wallets: remove @stellar/stellar-sdk dependency#1688
albertoelias-crossmint merged 5 commits intomainfrom
devin/1773964898-remove-stellar-sdk

Conversation

@albertoelias-crossmint
Copy link
Collaborator

@albertoelias-crossmint albertoelias-crossmint commented Mar 20, 2026

Description

Removes the @stellar/stellar-sdk dependency from @crossmint/wallets-sdk by replacing its two usages with lightweight alternatives already available in the codebase:

  • ed25519 key derivation & signingtweetnacl (already a dependency, used by SolanaServerSigner)
  • Stellar StrKey public key encoding → local util ported from open-signer/shared/cryptography/src/primitives/encoding/strkey.ts

The Stellar SDK was only used in two files (stellar-server-signer.ts and derive-server-signer.ts) for three operations: Keypair.fromRawEd25519Seed(), keypair.publicKey(), and keypair.sign(). This PR replaces all three with a new packages/wallets/src/utils/stellar.ts utility module.

Key things for reviewers to verify:

  • The encodeStellarPublicKey implementation (CRC16-XModem checksum + base32 encoding) produces addresses identical to the Stellar SDK. The existing test suite validates the format (/^G[A-Z0-9]+$/) and passes, but there are no hardcoded test vectors comparing against a known SDK-generated address. Consider verifying with e.g. Keypair.fromRawEd25519Seed(Buffer.from(new Uint8Array(32).fill(1))).publicKey()"GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGCHWLC8V2GCEF4OZMPOPBT".
  • tweetnacl.sign.detached produces signatures compatible with what the Stellar SDK's Keypair.sign produced (both are standard ed25519).
  • No other package in the monorepo imports from @stellar/stellar-sdk (grep confirmed only these two files).

Updates since last revision

  • Fixed duplicate abitype key in package.json (introduced when the leftover blank line was removed via a GitHub suggestion that replaced it with abitype instead of deleting the line)

Test plan

  • All 235 existing tests in @crossmint/wallets-sdk pass, including StellarServerSigner tests (address format, signing, transaction delegation) and deriveServerSignerAddress tests for Stellar chain
  • Lint passes (pnpm lint)

Package updates

  • Removed @stellar/stellar-sdk (v14.0.0-rc.3) from packages/wallets/package.json
  • Changeset added (@crossmint/wallets-sdk: patch)

Link to Devin session: https://crossmint.devinenterprise.com/sessions/32a29a66027548e39212f6d15a5a8104
Requested by: @albertoelias-crossmint


Open with Devin

devin-ai-integration bot and others added 3 commits March 20, 2026 00:03
Replace Stellar SDK usage with tweetnacl (already a dep) for ed25519
operations and a local StrKey encoder ported from open-signer.

Co-Authored-By: Alberto Elias <alberto.elias@paella.dev>
Co-Authored-By: Alberto Elias <alberto.elias@paella.dev>
Co-Authored-By: Alberto Elias <alberto.elias@paella.dev>
@changeset-bot
Copy link

changeset-bot bot commented Mar 20, 2026

🦋 Changeset detected

Latest commit: 6d025e5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
@crossmint/wallets-sdk Patch
expo-demo Patch
@crossmint/client-sdk-react-base Patch
@crossmint/client-sdk-react-native-ui Patch
@crossmint/client-sdk-react-ui Patch
@crossmint/auth-ssr-nextjs-demo Patch
@crossmint/client-sdk-nextjs-starter Patch
@crossmint/wallets-quickstart-devkit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@devin-ai-integration
Copy link
Contributor

Original prompt from Alberto Elias

in the main and wallets-v1 branch of the crossmint-sdk, we're using the stellar sdk to derive a private key for the server signer. We want to do this without using the stellar sdk and remove that dependency entirely. look at other libraries or open-signer to see if we have an existing implementation for this and if not, figure out how to do it in a util function

You only need to look in the following repos: Crossmint/crossmint-sdk, Crossmint/open-signer

@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 20, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/wallets/src/utils/stellar.ts
Line: 74-85

Comment:
**Missing deterministic test vectors for `encodeStellarPublicKey`**

The PR description explicitly acknowledges this gap: the test suite only validates the output format (`/^G[A-Z0-9]+$/`) but never compares the output against a known-good address generated by the Stellar SDK. This is a meaningful risk for a custom re-implementation of a cryptographic encoding function (CRC16-XModem + base32 + StrKey version byte).

A subtle byte-ordering bug, off-by-one in the CRC, or wrong version byte would produce a Stellar-looking string that still passes the regex test yet would be a different (invalid) address than the real SDK would generate — silently causing all server-signer-derived Stellar addresses to be wrong in production.

Please add at least one hardcoded test vector that compares output against a pre-computed Stellar SDK address, e.g.:

```typescript
// In server-signers.test.ts (or a new stellar.test.ts)
import { ed25519KeypairFromSeed, encodeStellarPublicKey } from "../../utils/stellar";

it("produces an address identical to the Stellar SDK for a known seed", () => {
    // Seed: 32 bytes of 0x01
    const seed = new Uint8Array(32).fill(1);
    const { publicKey } = ed25519KeypairFromSeed(seed);
    const address = encodeStellarPublicKey(publicKey);
    // Replace with the known-good value produced by:
    // Keypair.fromRawEd25519Seed(Buffer.from(seed)).publicKey()
    expect(address).toBe("GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGCHWLC8V2GCEF4OZMPOPBT");
});
```

Similarly, the `StellarServerSigner.signMessage` test only checks that the result is decodable as base64 — unlike the Solana test which actually verifies the signature with `nacl.sign.detached.verify`. It would be valuable to verify the Stellar signature is cryptographically valid against the public key.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/wallets/package.json
Line: 32

Comment:
**Leftover blank line**

Removing the `@stellar/stellar-sdk` entry left an extra blank line between `@solana/web3.js` and `abitype`.

```suggestion
        "abitype": "1.0.8",
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "chore: add changeset..."

greptile-apps[bot]

This comment was marked as resolved.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

🔥 Smoke Test Results

Status: Passed

Statistics

  • Total Tests: 5
  • Passed: 5 ✅
  • Failed: 0
  • Skipped: 0
  • Duration: 5.05 min

✅ All smoke tests passed!

All critical flows are working correctly.


This is a non-blocking smoke test. Full regression tests run separately.

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 6 additional findings in Devin Review.

Open in Devin Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 20, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/wallets/src/utils/stellar.ts
Line: 65-67

Comment:
**Stellar StrKey should not include RFC 4648 `=` padding**

The Stellar StrKey spec and SDK strip base32 padding characters — Stellar addresses never include `=`. For ed25519 public keys the payload is always 35 bytes (1 version + 32 key + 2 CRC), which encodes to exactly 56 chars (`35 × 8 / 5 = 56`, no remainder), so this code path is currently unreachable and the output is correct.

However, if `encodeStellarPublicKey` is ever called with a non-32-byte key (or if this `base32Encode` helper is reused for other Stellar key types such as seed keys or hash types), the `=` padding would be appended and produce an address that is incompatible with the Stellar protocol. Removing the padding loop makes this a correct Stellar-specific base32 encoder regardless of input size.

```suggestion
    return output;
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "Apply suggestion fro..."

greptile-apps[bot]

This comment was marked as resolved.

Co-Authored-By: Alberto Elias <alberto.elias@paella.dev>
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 20, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/wallets/src/signers/server/server-signers.test.ts
Line: 125-132

Comment:
**Missing cryptographic signature verification in Stellar test**

The Stellar signing test only confirms the result is decodable as base64, but does not verify the signature is actually valid against the public key. The Solana signing test (line 104) does this correctly:

```ts
expect(nacl.sign.detached.verify(messageBytes, sigBytes, pubkey)).toBe(true);
```

An equivalent check here would confirm that the `secretKey` stored in `StellarServerSigner` genuinely corresponds to the derived public key and that `ed25519Sign` produces a verifiable signature. Since `ed25519KeypairFromSeed` is now directly importable, this is straightforward to add:

```ts
import { ed25519KeypairFromSeed } from "../../utils/stellar";

it("signs a base64-encoded message", async () => {
    const messageBytes = Buffer.from([1, 2, 3, 4]);
    const message = messageBytes.toString("base64");
    const result = await signer.signMessage(message);
    expect(result.signature).toBeDefined();

    const sigBytes = Buffer.from(result.signature, "base64");
    const { publicKey } = ed25519KeypairFromSeed(config.derivedKeyBytes);
    expect(nacl.sign.detached.verify(messageBytes, sigBytes, publicKey)).toBe(true);
});
```

Without this, the test would still pass even if the signing function returned a completely wrong or random byte sequence that happened to be base64-decodable.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "fix: remove duplicat..."

@albertoelias-crossmint albertoelias-crossmint merged commit 732eeb0 into main Mar 20, 2026
3 checks passed
@albertoelias-crossmint albertoelias-crossmint deleted the devin/1773964898-remove-stellar-sdk branch March 20, 2026 16:19
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.

3 participants