Skip to content

Conversation

@Majormaxx
Copy link

@Majormaxx Majormaxx commented Oct 16, 2025

Module-II: Round-based Payouts Implementation

Summary

This PR implements Module-II: Round-based Payouts for the CollabBerry Token Payment Automation system, delivering the complete workflow for preview, propose, sign, and execute functionality for round-based stablecoin and recognition token distributions via Gnosis Safe multisig.

Additionally, this PR includes completion of Module-I (Admin Configuration) features to ensure a clean build with zero TypeScript errors and full compliance with both modules' acceptance criteria.


Module-II Acceptance Criteria

Core Requirements

Requirement Status Implementation
Preview endpoint - List incomplete rounds with recipient details, totals, and preflight checks Complete GET /api/v1/payouts/preview?roundId=
Propose endpoint - Create Safe transaction proposals with chunking logic Complete POST /api/v1/payouts/propose
Status endpoint - Poll transaction status and execution state Complete GET /api/v1/payouts/status?roundId=
Safe SDK integration - Protocol Kit + API Kit for transaction creation and signing Complete Safe SDK v5 with dynamic imports
Joi validation - Request validation for all payout endpoints Complete Schemas for preview, propose, status
Chunking logic - Batch recipients with gas estimation and recursive splitting Complete Implemented in PayoutService
Accurate decimal conversion - Proper token decimal handling for stablecoin and recognition tokens Complete Uses ethers.parseUnits() with token decimals
TP minting permission check - Verify Safe has MINTER_ROLE before minting TeamPoints Complete Validated in preview and propose
Class-based router pattern - Dependency injection with InversifyJS Complete PayoutRouter follows established pattern
Zero TypeScript errors - Clean build Complete 0 compilation errors

Module-I Completion

To achieve a clean build and ensure Module-II has a solid foundation, this PR also completes missing Module-I features:

Implemented Features

Feature Status Implementation
getSupportedChains endpoint Complete GET /api/v1/organization/configuration/chains
validateSafeConfig endpoint Complete POST /api/v1/organization/configuration/validate
Comprehensive validation metadata Complete Returns Safe info (owners, threshold) and token info (name, symbol, decimals, balance, roles)
Safe SDK v3 → v5 migration Complete Updated organization-configuration.service.ts and safe.service.ts
User.walletAddress → address Complete Fixed 14 instances across controllers
Ethers v5 → v6 API Complete Updated to ethers.parseUnits()

Technical Implementation

Architecture Decisions

  1. Safe SDK v5 Integration

    • Used dynamic imports (await import()) to avoid TypeScript namespace errors
    • Implemented SafeTransactionService as a wrapper for Protocol Kit and API Kit
    • Supports transaction creation, proposal submission, and status polling
  2. Joi Validation Pattern

    • Created validation schemas for all payout endpoints
    • Applied validation in controller before service layer
    • Returns clear error messages with field-level constraints
  3. Class-based Router Pattern

    • Converted PayoutRouter to use InversifyJS dependency injection
    • Follows established pattern from UserRouter, OrgRouter, RoundsRouter
    • Enables testability and maintainability
  4. Entity Definite Assignment

    • Added ! assertions to all payout entity properties
    • Ensures TypeORM compatibility with strict TypeScript settings
  5. Chunking Logic

    • Implements recursive gas estimation and splitting
    • Stores chunks as tx_proposals with partIndex/partCount
    • Supports up to N recipients per batch with gas cap enforcement

Key Files Modified

Module-II Implementation:

  • src/services/safe/safe-transaction.service.ts - Safe SDK wrapper (new)
  • src/services/payout.service.ts - Payout business logic with Safe integration
  • src/controllers/payout.controller.ts - Payout endpoints with Joi validation
  • src/routers/payout.router.ts - Class-based router with dependency injection
  • src/entities/payout/*.ts - Payout, TxProposal, PayoutRecipient entities
  • src/models/payout/*.ts - Joi validation schemas (new)
  • src/inversify.config.ts - Added Module-II service bindings
  • src/inversify.types.ts - Added Module-II type symbols

Module-I Completion:

  • src/services/org/organization-configuration.service.ts - Added getSupportedChains() and validateSafeConfig() methods, migrated to Safe SDK v5
  • src/controllers/organization-configuration.controller.ts - Implemented validation and chains endpoints
  • src/services/safe.service.ts - Migrated to Safe SDK v5
  • src/controllers/organization.controller.ts - Fixed User.walletAddressUser.address (7 instances)
  • src/controllers/rounds.controller.ts - Fixed User.walletAddressUser.address (7 instances), fixed editAssessment and addTokenMintTx methods
  • src/controllers/user.controller.ts - Fixed User.walletAddressUser.address (2 instances)
  • src/models/rounds/addTokenMintTx.model.ts - Created missing Joi schema (new)

Testing & Verification

Build Verification

npx tsc --noEmit
# Result: 0 errors

Commits by Category

Module-II Core Features:

  • c7cddb3 - build: add Safe API Kit dependency for transaction service integration
  • b9e4816 - feat: implement Safe transaction service wrapper for multisig operations
  • 1c8e5d1 - feat: add Joi validation schemas for payout endpoints
  • fe542a0 - feat: apply Joi validation to payout controller endpoints
  • 1586f21 - feat: integrate Safe SDK for transaction creation and status polling
  • a7ce3be - refactor: convert payout router to class-based dependency injection pattern
  • 2ca4e9a - fix: resolve TypeScript compilation errors in Module-II implementation

Module-I Completion:

  • 7aa2b87 - fix: resolve Module-I TypeScript errors to enable clean build
  • d8566fb - feat: implement getSupportedChains and validateSafeConfig methods

Earlier Foundation Work:

  • baba04c - Added database entities for payout recipients, transactions, and Safe proposal tracking
  • ac1f9b1 - Integrated payout management routes for rounds listing, preview, proposals, and status tracking
  • d62b876 - Added rounds listing and preview APIs with validation warnings
  • d6614b1 - Added rounds listing API with incomplete round filtering
  • 5634c9a - Added dependency injection configuration for Module II services

API Endpoints

Module-II Endpoints

GET /api/v1/payouts/rounds?orgId={orgId}

List incomplete rounds for an organization.

Response:

{
  "rounds": [
    {
      "id": "uuid",
      "name": "Round 1",
      "status": "INCOMPLETE",
      "participantCount": 10
    }
  ]
}

GET /api/v1/payouts/preview?roundId={roundId}

Preview payout details with preflight checks.

Response:

{
  "recipients": [
    {
      "address": "0x...",
      "stablecoinAmount": "100.00",
      "recognitionTokenAmount": "50.00"
    }
  ],
  "totals": {
    "stablecoin": "1000.00",
    "recognitionToken": "500.00"
  },
  "warnings": ["Safe has insufficient stablecoin balance"],
  "chunkPlan": {
    "totalChunks": 2,
    "recipientsPerChunk": [50, 25]
  }
}

POST /api/v1/payouts/propose

Create Safe transaction proposals for a round.

Request:

{
  "roundId": "uuid",
  "signerPrivateKey": "optional-for-auto-sign"
}

Response:

{
  "proposals": [
    {
      "safeTxHash": "0x...",
      "partIndex": 1,
      "partCount": 2,
      "status": "PROPOSED"
    }
  ]
}

GET /api/v1/payouts/status?roundId={roundId}

Poll transaction status for a round.

Response:

{
  "proposals": [
    {
      "safeTxHash": "0x...",
      "status": "EXECUTED",
      "confirmations": 2,
      "threshold": 2,
      "executedAt": "2025-10-16T12:00:00Z"
    }
  ]
}

Module-I Endpoints

GET /api/v1/organization/configuration/chains

Get supported blockchain networks.

Response:

{
  "chains": [
    {
      "chainId": 42161,
      "name": "Arbitrum One",
      "rpcUrl": "https://arb1.arbitrum.io/rpc"
    }
  ]
}

POST /api/v1/organization/configuration/validate

Validate Safe configuration without saving.

Request:

{
  "safeAddress": "0x...",
  "safeChainId": 42161,
  "stablecoinAddress": "0x...",
  "stablecoinDecimals": 6,
  "recognitionTokenAddress": "0x...",
  "recognitionTokenDecimals": 18,
  "recognitionTokenMode": "MINT"
}

Response:

{
  "isValid": true,
  "errors": [],
  "warnings": ["Safe has low stablecoin balance"],
  "safeInfo": {
    "owners": ["0x...", "0x..."],
    "threshold": 2
  },
  "tokenInfo": {
    "stablecoin": {
      "name": "USD Coin",
      "symbol": "USDC",
      "decimals": 6
    },
    "recognition": {
      "name": "TeamPoints",
      "symbol": "TP",
      "decimals": 18,
      "hasMintingRole": true
    }
  }
}

Dependencies Added

{
  "@safe-global/api-kit": "^2.4.6",
  "@safe-global/protocol-kit": "^5.2.17"
}

Breaking Changes

None. This PR is additive and completes existing functionality.


Reviewer Notes

  • All changes follow existing code patterns and conventions
  • Minimal file modifications to reduce review burden
  • Zero TypeScript compilation errors
  • Module-I completion ensures clean foundation for Module-II
  • Safe SDK v5 migration uses dynamic imports to avoid type conflicts
  • Joi validation provides consistent error handling across all endpoints
  • Class-based router pattern enables dependency injection and testability

Majormaxx and others added 27 commits September 27, 2025 01:03
- Added validateMintingPermission method to check MINTER_ROLE, ADMIN_ROLE, and owner patterns
- Enhanced recognition token validation for MINT mode with comprehensive role checking
- Added support for TeamPoints contracts using ADMIN_ROLE instead of MINTER_ROLE
- Added detailed error messages and logging for debugging minting permission issues
- Fixed linting issues in organization and user controllers
Add comprehensive validation endpoint that returns detailed metadata:
- Safe info: owners list and threshold
- Token info: name, symbol, decimals for both stablecoin and recognition token
- Minting role status for mint mode
- Token balance for transfer mode

Extract validation logic into separate method to support validation endpoint without persisting changes.
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.

2 participants