Skip to content

rework refund approvals#47

Merged
MaryammAli merged 3 commits into
BlockDash-Studios:mainfrom
Noble-Devbase:feat/Rework-refund-approvls-into-a-state-machine
Jun 21, 2026
Merged

rework refund approvals#47
MaryammAli merged 3 commits into
BlockDash-Studios:mainfrom
Noble-Devbase:feat/Rework-refund-approvls-into-a-state-machine

Conversation

@Noble-Devbase

Copy link
Copy Markdown
Contributor

closes #3

📌 Description

Critical Operational Gap - The refund approval process currently updates the database status to approved but does not execute or track the actual on-chain contract refund operation. This creates a dangerous disconnect where operators believe refunds have been processed while escrow funds remain locked on-chain. Users may be told refunds are complete when they haven't been executed, leading to trust and compliance issues.

🧩 Changes

1. Refund State Machine (refunds.types.ts, refunds.service.ts)

Introduced explicit states with clear transitions:

enum RefundState {
  PENDING = 'pending',           // Initial state, awaiting approval
  APPROVED = 'approved',         // Approved but not yet submitted
  SUBMITTED = 'submitted',       // Contract tx submitted, awaiting confirmation
  CONFIRMED = 'confirmed',       // On-chain confirmed (terminal success)
  FAILED = 'failed',             // Failed with reason (terminal failure)
  REJECTED = 'rejected'          // Rejected by operator (terminal)
}

State Transitions:

PENDING → APPROVED (approval)
PENDING → REJECTED (rejection)
APPROVED → SUBMITTED (submission)
SUBMITTED → CONFIRMED (confirmation)
SUBMITTED → FAILED (failure)
APPROVED → FAILED (submission failure)
FAILED → SUBMITTED (retry)

2. On-Chain Execution (refunds.service.ts, soroban-rpc.service.ts)

  • Wired approve() to trigger contract refund function via Soroban RPC
  • Added idempotency keys to prevent duplicate submissions
  • Store transaction hash, contract ID, network, and failure reason
  • Implement retry logic with exponential backoff

3. Database Schema (supabase/migrations/*refund*)

Added columns for tracking on-chain operations:

ALTER TABLE refunds ADD COLUMN onchain_state TEXT;
ALTER TABLE refunds ADD COLUMN transaction_hash TEXT;
ALTER TABLE refunds ADD COLUMN contract_id TEXT;
ALTER TABLE refunds ADD COLUMN network TEXT;
ALTER TABLE refunds ADD COLUMN failure_reason TEXT;
ALTER TABLE refunds ADD COLUMN retry_count INTEGER DEFAULT 0;
ALTER TABLE refunds ADD COLUMN last_attempt_at TIMESTAMP;
ALTER TABLE refunds ADD COLUMN idempotency_key TEXT UNIQUE;
ALTER TABLE refunds ADD COLUMN metadata JSONB;

4. Idempotency & Retry Logic

  • Generate idempotency key: refund_{userId}_{timestamp}_{nonce}
  • Store key before submission to prevent duplicates
  • Implement retry queue for failed submissions (SQS/Bull/Redis)
  • Exponential backoff: 1s, 2s, 4s, 8s, 30s, 60s (max 6 attempts)

5. API Enhancements (refunds.controller.ts)

Response now includes:

{
  "id": "ref_123",
  "state": "submitted",
  "transactionHash": "0xabc...",
  "contractId": "C...",
  "network": "mainnet",
  "failureReason": null,
  "retryable": true,
  "retryCount": 1,
  "updatedAt": "2024-01-01T00:00:00Z"
}

6. Background Job Processor

  • New job type: process_refund_submission
  • Queue failed submissions for retry
  • Webhook notifications on state changes
  • Admin alerts for repeated failures

7. Error Handling (common/soroban-errors/*)

Enhanced error categorization:

  • SorobanTimeoutError → mark as FAILED, retry later
  • SorobanInsufficientBalanceError → FAILED (terminal)
  • SorobanInvalidContractError → FAILED (terminal)
  • SorobanNetworkError → retry with backoff

🔍 Why This Matters

Before After
❌ Operators think refunds are processed ✅ Clear state visibility
❌ Funds stuck on-chain indefinitely ✅ Automated on-chain execution
❌ No audit trail for on-chain operations ✅ Full transaction tracking
❌ Duplicate approvals cause double refunds ✅ Idempotency prevents duplicates
❌ No retry mechanism for failures ✅ Automatic retry with backoff
❌ No visibility into failed operations ✅ Clear failure reasons & alerts

📁 Files Changed

app/backend/src/refunds/refunds.service.ts          - Core state machine + on-chain execution
app/backend/src/refunds/refunds.controller.ts       - Enhanced API responses
app/backend/src/refunds/refunds.types.ts            - New state enum + types
app/backend/src/refunds/refunds.worker.ts           - Background job processor (NEW)
app/backend/src/transactions/soroban-rpc.service.ts - Contract interaction enhancements
app/backend/src/common/soroban-errors/             - Error categorization
app/backend/supabase/migrations/20240101_refund_tracking.sql - Schema changes
app/backend/src/refunds/__tests__/refunds.service.test.ts - Comprehensive tests
docs/REFUND_STATE_MACHINE.md                        - Documentation (NEW)

@MaryammAli MaryammAli left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM
thanks for ur contribution @Noble-Devbase

@MaryammAli MaryammAli merged commit ca6e636 into BlockDash-Studios:main Jun 21, 2026
2 checks passed
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.

Rework refund approvals into a state machine with on-chain settlement

2 participants