Implement BlockchainService to Bridge Backend and Stellar Smart Contract
#301 建立於 2026年3月18日
倉庫指標
- Star
- (2 star)
- PR 合併指標
- (PR 指標待抓取)
描述
Description:
The BlockchainService currently returns a placeholder string and does nothing. This service is the critical bridge between the NestJS backend and the deployed Soroban smart contract on Stellar testnet. Without it, the blockchain layer is completely disconnected from all game logic despite the backend being fully functional. The backend already has everything needed to trigger blockchain calls, stellarWallet exists on the User entity, puzzle completions are processed through ProgressService, XP is tracked on the User entity, and streaks are managed through StreaksService. This issue wires all of that to the contract.
Current Behavior:
@Injectable()
export class BlockchainService {
getHello(): string {
return 'Hello from Blockchain Service';
}
}
The service is injected nowhere and does nothing.
Expected Behavior:
BlockchainService connects to the Stellar testnet and exposes methods that the rest of the backend can call after game events occur. It acts as the oracle that submits verified scores to the contract — meaning the contract never receives unverified data directly from a player.
Architecture Context - Important to Read:
The backend is the source of truth for all game logic. Postgres handles gameplay state. The smart contract is the verification and reward layer - it receives confirmed results from the backend, not from players directly. This is also what satisfies Issue 4 in the contracts repo (oracle role), where submit_puzzle should only be callable by a trusted address - that trusted address is the backend wallet controlled by this service.
Requirements:
Setup:
- Install and configure the Stellar SDK (
@stellar/stellar-sdk) in the backend - Store the backend's Stellar keypair (the oracle wallet) in environment variables — never hardcode keys
- Add the contract ID to environment variables
- Connect to the Stellar Soroban RPC testnet endpoint
Methods to implement inside BlockchainService:
registerPlayerOnChain(stellarWallet: string, username: string, iqLevel: number)
- Called after a user completes onboarding and has a
stellarWalletattached to their account - Invokes
register_playeron the contract using the player's wallet address
submitPuzzleOnChain(stellarWallet: string, puzzleId: string, category: string, score: number)
- Called by the backend after
ProgressServiceverifies a correct puzzle answer - Invokes
submit_puzzleon the contract signed by the oracle wallet (not the player) - Should be non-blocking - a failure here should log and not crash the game flow
getPlayerOnChain(stellarWallet: string)
- Fetches the player's on-chain profile for display or verification purposes
- Invokes
get_playeron the contract
syncXpMilestone(stellarWallet: string)
- Called when a user's XP crosses a level threshold (detectable from the existing
getXpNeededForNextLevel()method on the User entity) - Can trigger token rewards or badge minting in future - for now it should record the milestone on-chain
Integration points, where to call BlockchainService:
- In
ProgressCalculationProvider(or equivalent) after a puzzle answer is confirmed correct, callsubmitPuzzleOnChain - In the user onboarding flow after
stellarWalletis linked, callregisterPlayerOnChain - XP milestone sync can be called inside the same flow that updates
user.level
Error Handling:
- All blockchain calls must be wrapped in try/catch
- Failures must be logged but must not block the main game response
- Consider a retry queue (Redis is already available in the project) for failed on-chain submissions
Environment Variables to Add:
STELLAR_SECRET_KEY= # Oracle wallet secret key
STELLAR_CONTRACT_ID= # Deployed contract address
STELLAR_RPC_URL= # Soroban testnet RPC endpoint
STELLAR_NETWORK_PASSPHRASE= # Testnet passphrase
These must also be documented in the backend .env.example file.
### Acceptance Criteria:
- [ ] BlockchainService connects to Stellar testnet successfully
- [ ] registerPlayerOnChain correctly invokes the contract's register_player function
- [ ] submitPuzzleOnChain is signed by the oracle wallet, not the player
- [ ] submitPuzzleOnChain is called automatically after a correct puzzle answer is processed
- [ ] Blockchain failures do not interrupt normal gameplay responses
- [ ] Stellar credentials are stored in environment variables only
- [ ] New environment variables are documented in .env.example
- [ ] At least one integration test covering a successful on-chain submission is included