Skip to content

feat: add Starknet blockchain integration#3570

Draft
TaprootFreak wants to merge 8 commits intodevelopfrom
feat/starknet-integration
Draft

feat: add Starknet blockchain integration#3570
TaprootFreak wants to merge 8 commits intodevelopfrom
feat/starknet-integration

Conversation

@TaprootFreak
Copy link
Copy Markdown
Collaborator

@TaprootFreak TaprootFreak commented Apr 10, 2026

Summary

Add Starknet (L2 ZK-STARK rollup on Ethereum) as new non-EVM blockchain with native account abstraction. This PR covers the infrastructure layer — blockchain client, service, address detection, signature verification, explorer URLs, and all enum/registry mappings.

Starknet is NOT EVM-compatible. It uses its own Cairo VM, stark-curve ECDSA signatures, felt-based addresses (32 bytes), and native account abstraction (every account is a smart contract). The integration follows the same pattern as Solana/Monero (custom BlockchainClient implementation).

New files

  • src/integration/blockchain/starknet/starknet-client.ts — RPC client (balance, send, tx queries, signature verification)
  • src/integration/blockchain/starknet/services/starknet.service.ts — NestJS service wrapper
  • src/integration/blockchain/starknet/starknet.module.ts — NestJS module
  • src/integration/blockchain/starknet/starknet.util.ts — Wei/amount conversions (18 decimals for STRK and ETH)
  • src/integration/blockchain/starknet/dto/starknet.dto.ts — Transaction/token DTOs
  • scripts/deploy-starknet-account.ts — Key generation + OZ account contract deployment

Modified files (infrastructure)

  • blockchain.enum.tsSTARKNET = 'Starknet'
  • blockchain.module.ts — import/export StarknetModule
  • blockchain-registry.service.ts — register StarknetService/StarknetClient in types and switch
  • blockchain.util.ts — explorer URL (voyager.online), tx/address/token paths
  • config.ts — address format (0x[0-9a-fA-F]{50,64}), signature format, env vars, blockchain config block
  • crypto.service.ts — address detection, signature verification (on-chain via account contract), payment request, UserAddressType.STARKNET
  • user.enum.tsSTARKNET = 'Starknet' in UserAddressType

Modified files (enum mappings)

  • 7 exchange services + exchange.test.tsStarknet: undefined
  • ref-reward.service.tsStarknet: undefined in PayoutLimits

Modified files (business layer)

  • blockchain.adapter.ts — add STARKNET to token client balance update (prevents crash in liquidity management)
  • payment-link-fee.service.ts — add STARKNET with fee 0
  • payment-balance.service.ts — add STARKNET to chainsWithoutPaymentBalance
  • payment-request.mapper.ts — add STARKNET to payment link payment case

Environment variables

  • STARKNET_GATEWAY_URL — RPC endpoint (Alchemy, Infura, etc.)
  • STARKNET_WALLET_ADDRESS — deployed account contract address
  • STARKNET_WALLET_PRIVATE_KEY — stark-curve private key

Dependencies

  • starknet ^9.4.2 (starknet.js SDK)

Account deployment (before first use)

Starknet has native account abstraction — every account is a smart contract. Before the wallet can send transactions, the account contract must be deployed once:

# 1. Generate key pair
npx ts-node scripts/deploy-starknet-account.ts --generate
# 2. Set env vars from output
# 3. Fund the address with STRK (~0.001 STRK for gas)
# 4. Deploy
npx ts-node scripts/deploy-starknet-account.ts --deploy

Uses OpenZeppelin Account v0.8.1 class hash (verified against starknet.js docs).

Open items (follow-up PRs)

Payout/Payin/DEX strategies (not needed until assets are activated)

Strategy files are required per blockchain for payout, payin, and DEX operations. These are only triggered when Starknet assets exist in the database AND are enabled for buy/sell. Without DB assets, no code path reaches these strategies.

Needed files (follow same pattern as Solana strategies):

  • payout/strategies/payout/impl/starknet-coin.strategy.ts
  • payout/strategies/payout/impl/starknet-token.strategy.ts
  • payout/strategies/prepare/impl/starknet.strategy.ts
  • payin/strategies/register/impl/starknet.strategy.ts
  • payin/strategies/send/impl/starknet-coin.strategy.ts
  • payin/strategies/send/impl/starknet-token.strategy.ts
  • dex/strategies/check-liquidity/impl/starknet-coin.strategy.ts
  • dex/strategies/check-liquidity/impl/starknet-token.strategy.ts
  • dex/strategies/purchase-liquidity/impl/starknet-coin.strategy.ts
  • dex/strategies/purchase-liquidity/impl/starknet-token.strategy.ts
  • dex/strategies/sell-liquidity/impl/starknet-coin.strategy.ts
  • dex/strategies/sell-liquidity/impl/starknet-token.strategy.ts
  • dex/strategies/supplementary/impl/starknet.strategy.ts

Plus corresponding strategy registry tests.

Deposit address generation (Account Abstraction complexity)

deposit.service.ts needs a createStarknetDeposits() method. Unlike EVM/Solana where addresses are derived from a seed, Starknet requires deploying an account contract for each deposit address. This needs a custom implementation:

  1. Derive key pair from seed + index
  2. Compute counterfactual address
  3. Fund and deploy account contract

This is architecturally different from all other blockchains and warrants its own PR.

Balance/Transaction API endpoints

blockchain-balance.service.ts and blockchain-transaction.service.ts need Starknet cases for the external REST API. Currently returns BadRequestException("Blockchain Starknet is not supported for balance queries") — acceptable for initial rollout.

Payment Link full support

Starknet is in chainsWithoutPaymentBalance, so payment links won't be generated. To enable:

  1. Add STARKNET to PaymentLinkBlockchains array
  2. Implement deposit address generation (see above)
  3. Add STARKNET to payment-activation.service.ts switch
  4. Add STARKNET to VerifiedTxIdBlockchains or UnverifiedTxIdBlockchains in payment-link enums
  5. Remove from chainsWithoutPaymentBalance

Exchange network mappings

All exchange services have Starknet: undefined. When exchanges add Starknet withdrawal support, update with the correct network name (e.g. Starknet: 'STRK' for Binance).

Database

Create Starknet assets in the database:

  • STRK coin (type: Coin, decimals: 18)
  • ETH bridged token (type: Token, chainId: 0x049d...dc7, decimals: 18)
  • Optional: USDC, USDT, and other tokens

Test plan

  • TypeScript compilation passes (0 errors)
  • Build succeeds (npm run build)
  • ESLint clean
  • Prettier clean
  • Account deployment script tested on Sepolia testnet
  • STRK/ETH balance queries verified against mainnet
  • Token transfer flow tested end-to-end

Add full Starknet support as a non-EVM blockchain with native account
abstraction. Includes client, service, module, DTOs, deployment script,
config entries, explorer URLs, and exchange service mappings.

Env vars: STARKNET_GATEWAY_URL, STARKNET_WALLET_ADDRESS,
STARKNET_WALLET_PRIVATE_KEY
Starkscan permanently redirects to Voyager (301). Replace explorer URL.
Use isSuccess() type guard instead of as-any casts on receipt types.
Add address detection, signature verification, payment request,
and UserAddressType for Starknet in CryptoService. Signature
verification uses on-chain verifyMessageInStarknet via the
account contract.
- BlockchainAdapter: add STARKNET to token client balance update
- PaymentLinkFeeService: add STARKNET with fee 0
- PaymentBalanceService: add STARKNET to chainsWithoutPaymentBalance
- PaymentRequestMapper: add STARKNET to payment link payment case
- Remove unused HttpService injection (starknet SDK handles HTTP)
- Remove unused StarknetTokenDto
- Remove unnecessary SharedModule import
- Fix verifySignature: hash message with starknetKeccak before verify
- Reduce local variables in getTokenBalance/getTokenBalances
- Align section comments with codebase convention (UPPERCASE)
- Reorder methods: balances, transactions, send, verification, fees
Account creation is now conditional — only instantiated when
STARKNET_WALLET_ADDRESS and STARKNET_WALLET_PRIVATE_KEY are set.
Write operations throw a clear error instead of crashing on startup.
Contract.call('balanceOf') returns { balance: bigint }, not a raw
value. Destructure the named field instead of calling toString()
on the object (which produced "[object Object]" → NaN).

Verified with integration tests against Starknet mainnet:
15/15 tests pass (balance, decimals, receipts, tx queries, hashing).
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