Skip to content

Latest commit

 

History

History
394 lines (310 loc) · 13.4 KB

File metadata and controls

394 lines (310 loc) · 13.4 KB

Polkadot Multisig Integration - Implementation Summary

This document summarizes the Polkadot multisig integration for GrantFlow's milestone-based grant payments.

🚀 Setup Status

✅ COMPLETE:

  • dedot library for Polkadot blockchain interactions
  • @luno-kit/react for wallet connection and management
  • @luno-kit/ui for wallet UI components
  • Child bounty payout system implemented
  • Multisig approval workflow implemented
  • Integration tests passing (59 tests)

Polkadot Stack:

  • dedot (v0.16.0) - Type-safe Substrate/Polkadot client with LegacyClient
  • @luno-kit/react (v0.0.8) - React hooks for wallet connection (useApi, useConnect, useSigner)
  • @luno-kit/ui (v0.0.8) - Pre-built wallet selector components

Current Status: ✅ Infrastructure Complete | ⏳ Testnet Validation Pending

✅ What Was Built

1. Database Schema Extensions

New Tables:

  • milestone_approvals - Tracks on-chain multisig execution process
  • multisig_signatures - Individual blockchain signatures from committee members

Enhanced Tables:

  • groups.settings.multisig - Committee multisig wallet configuration
  • Relationship: multisig_signatures.reviewIdreviews.id (links off-chain decision to on-chain execution)

2. Polkadot Client Setup

Files: /src/lib/polkadot/

  • client.ts - WebSocket connection to Polkadot network (Paseo testnet / mainnet)
  • multisig.ts - Core multisig functions:
    • initiateMultisigApproval() - First signatory publishes + votes
    • approveMultisigCall() - Intermediate signatories approve
    • approveOrExecuteMultisigCall() - Smart approval that executes when quorum is hit
    • createBatchedPaymentCall() - Atomic transfer + remark
  • child-bounty.ts - Child bounty payout functions:
    • getNextChildBountyId() - Query next available child bounty ID
    • getParentBounty() - Query parent bounty info including curator
    • getParentBountyCurator() - Get curator address for a parent bounty
    • createChildBountyBundle() - Create 5-call atomic bundle for payout
    • createPayoutCall() - Main entry point for milestone payouts

3. Database Write Operations

File: /src/lib/db/writes/milestone-approvals.ts

  • createMilestoneApproval() - Record multisig initiation
  • createMultisigSignature() - Record individual signatures
  • getMilestoneApprovalWithVotes() - Fetch approval with all signatures
  • completeMilestoneApproval() - Mark milestone complete after execution
  • hasUserVoted() - Check if signatory already signed

4. Server Actions

File: /src/app/(dashboard)/dashboard/submissions/multisig-actions.ts

  • initiateMultisigApproval - Start multisig approval process
  • castMultisigVote - Record intermediate signatures
  • finalizeMultisigApproval - Execute final transaction and complete milestone
  • getMilestoneApprovalStatus - Query current approval state

5. Wallet Provider

File: /src/components/providers/polkadot-provider.tsx

  • React context for Polkadot wallet connections
  • Supports: Polkadot.js, Talisman, SubWallet, Nova
  • Auto-reconnection from localStorage
  • Account selection and signer management

🔄 Two Workflow Patterns

Committees can choose between two approval workflows:

Merged Workflow (Decision + Execution Combined)

Review Approval → Blockchain Signature → Threshold Met → Payment Executes
  • Use case: High-trust committees, fast execution
  • Process: Single action approves AND signs blockchain transaction
  • Link: multisig_signatures.reviewId points to reviews.id

Separated Workflow (Two-Phase Process)

Phase 1: Review Approval → Quorum → Mark "Approved Pending Payment"
Phase 2: Initiate Payment → Signatures → Threshold Met → Payment Executes
  • Use case: High-value grants, regulated environments
  • Process: Discuss/approve first, then separate payment authorization
  • Link: Signatures in phase 2 have reviewId = null

📊 Configuration

Committee admins configure multisig in group settings:

interface MultisigConfig {
  multisigAddress: string              // Committee's multisig wallet
  signatories: string[]                // Member wallet addresses
  threshold: number                    // Required signatures (e.g., 2 of 3)
  approvalWorkflow: 'merged' | 'separated'  // Which pattern to use
  requireAllSignatories: boolean       // Force all to sign vs. threshold
  votingTimeoutBlocks: number          // Expiry time
  automaticExecution: boolean          // Auto-execute on threshold
  network: 'polkadot' | 'kusama' | 'paseo'  // Which chain
  
  // Child Bounty Configuration (required for payouts)
  parentBountyId: number               // Parent bounty ID on-chain
  curatorProxyAddress: string          // Curator for child bounties
}

Child Bounty Payouts

All milestone payouts use the childBounties pallet, which provides:

  • Proper on-chain indexing by Subscan/Subsquare
  • Atomic 5-call bundle (add → propose curator → accept → award → claim)
  • Automatic curator fetching from chain in the UI

🔐 Security & Architecture

First-Signatory-Votes Pattern:

  • First committee member calls asMulti with full call data
  • Transaction is published on-chain AND counts as first approval
  • Deposit is locked from initiator (~20 tokens)
  • Subsequent signatories use approveAsMulti (only need call hash)
  • Final signatory provides full call data again to execute

Atomic Execution:

  • Uses utility.batchAll() to combine transfer + remark
  • All-or-nothing: both succeed or both fail
  • On-chain record of milestone completion

Type Safety:

  • All schemas use Zod validation
  • Drizzle ORM type inference
  • No any types in codebase

🚀 What's Next

✅ Completed

  • MilestoneVotingPanel - Show approval progress, signature status
  • PolkadotWalletSelector - Connect Polkadot wallet extensions (in header)
  • MultisigConfigForm - Configure multisig settings in committee management
  • SignatoryVoteList - Visual progress of multisig approvals
  • PolkadotProvider - React context for wallet management
  • Integration with reviewer submission view (shows voting panel for approved milestones)
  • Database schema and server actions complete
  • Workflow pattern support (merged/separated)
  • Committee management UI for multisig configuration

⏳ Priority 1: Test Blockchain Transactions

  • Install dedot and @luno-kit packages
  • Configure Polkadot client with LegacyClient from dedot
  • Implement multisig functions in src/lib/polkadot/multisig.ts:
    • initiateMultisigApproval()
    • approveOrExecuteMultisigCall()
    • finalizeMultisigCall()
  • Implement child bounty functions in src/lib/polkadot/child-bounty.ts
  • Test actual blockchain transactions on Paseo testnet

Priority 2: Testing & Validation

  • Create test multisig wallet on Paseo with 2 signatories
  • Test merged workflow end-to-end with real wallet signatures
  • Test separated workflow end-to-end
  • Validate transaction cost calculations
  • Error handling for failed transactions
  • Edge cases: expired approvals, rejected signatures, insufficient balance

Priority 3: Advanced Features

  • Query pending multisigs from blockchain (queryPendingMultisigs)
  • Cancel/replace pending approvals
  • Batch multiple milestone payments
  • Historical signature analytics
  • Gas estimation before transaction
  • Notification system for signature requests

📦 Dependencies & Setup

Dependencies

The project uses the following Polkadot-related packages:

{
  "dependencies": {
    "dedot": "^0.16.0",           // Polkadot client and utilities
    "@luno-kit/react": "^0.0.8",  // React hooks for wallet connection
    "@luno-kit/ui": "^0.0.8"      // UI components for wallet selection
  },
  "devDependencies": {
    "@dedot/chaintypes": "^0.171.0"  // Type definitions for chains
  }
}

Polkadot Client Setup

The client is configured in /src/lib/polkadot/lunokit.ts:

import { createConfig } from '@luno-kit/react'
import { polkadot, kusama, paseo } from '@luno-kit/react/chains'
import { talisman, subwallet, nova, polkadotJs } from '@luno-kit/react/connectors'

export const config = createConfig({
  chains: [polkadot, kusama, paseo],
  connectors: [talisman(), subwallet(), nova(), polkadotJs()],
})

Using the Client in Components

import { useApi, useSigner, useConnect } from '@luno-kit/react'
import type { LegacyClient } from 'dedot'

function MyComponent() {
  const { api: client } = useApi('paseo')
  const signer = useSigner()
  const { connect, isConnected } = useConnect()
  
  // Use client for queries
  const balance = await client.query.system.account(address)
  
  // Use client for transactions
  const tx = client.tx.multisig.asMulti(...)
  await tx.signAndSend(signer)
}

Resources:

🗄️ Database Migration

Run migration to create new tables:

pnpm db:push

This will create:

  • milestone_approvals table
  • multisig_signatures table
  • New enum types: approval_status, signature_type

🧪 Testing Checklist

Before production:

  • Test wallet connections (all supported extensions)
  • Test merged workflow end-to-end on testnet
  • Test separated workflow end-to-end on testnet
  • Verify transaction costs and gas estimates
  • Test threshold edge cases (2/3, 3/5, unanimous)
  • Test rejection/cancellation flows
  • Verify proper error handling and rollback
  • Load test with multiple concurrent approvals

📚 Documentation

See detailed documentation:

🎯 Key Design Decisions

  1. Separate tables for reviews vs signatures - Reviews are off-chain decisions, signatures are on-chain cryptographic proofs
  2. Optional reviewId link - Connects off-chain vote to on-chain signature in merged workflow
  3. Workflow configuration - Committees choose their own pattern based on trust/requirements
  4. First-signatory-votes - Follows Polkadot best practice of publishing + voting in one transaction
  5. Batched calls - Atomic transfer + remark ensures data integrity

🔗 Integration Points

Existing Code Modified:

  • ❌ None yet (all new code, no modifications to existing)

Existing Code Will Need Updates:

  • src/components/milestone-completion-form.tsx - Add multisig option
  • src/components/milestone-status.tsx - Show blockchain status
  • src/app/(dashboard)/dashboard/committees/[id]/manage/page.tsx - Multisig config UI
  • src/components/review/milestone-review-dialog.tsx - Integrate signature flow

💡 Usage Example (Pseudocode)

Merged Workflow

// Committee member approves milestone
async function approveMilestone() {
  // 1. Create review (off-chain)
  const review = await submitReview({ vote: 'approve' })
  
  // 2. Connect wallet and sign (on-chain)
  const { selectedSigner } = usePolkadot()
  
  // 3. Initiate or join multisig
  if (!existingApproval) {
    await initiateMultisigApproval({
      approvalWorkflow: 'merged',
      reviewId: review.id
    })
  } else {
    await castMultisigVote({
      signatureType: 'signed',
      reviewId: review.id
    })
  }
}

Separated Workflow

// Phase 1: Approve (off-chain)
async function approveMilestone() {
  await submitReview({ vote: 'approve' })
  // Quorum check happens automatically
}

// Phase 2: Execute payment (on-chain)
async function executePayment() {
  const { selectedSigner } = usePolkadot()
  
  await initiateMultisigApproval({
    approvalWorkflow: 'separated',
    reviewId: null // No review link in separated mode
  })
}

📝 Summary

✅ Complete:

  • Database schema (milestone_approvals, multisig_signatures tables)
  • Server actions (initiate, vote, finalize)
  • React components (MilestoneVotingPanel, MultisigConfigForm, WalletSelector)
  • LunoKit Provider context and wallet integration
  • UI integration in reviewer submission view
  • Committee management configuration (with parentBountyId and curatorProxyAddress)
  • Both workflow patterns (merged/separated) supported
  • Seed data with multisig-enabled committee
  • dedot client for blockchain interactions
  • @luno-kit hooks for wallet connection
  • Child bounty payout system
  • Integration tests (59 tests passing)

⏳ Pending:

  • Test on Paseo testnet with real multisig wallet and parent bounty
  • Full end-to-end testing of child bounty payout flow

🛠️ Development Guide

Testing Database Setup

# Reset and seed database with multisig configuration
pnpm db:reset

Test Locally

  • Login as reviewer1@test.com (password: reviewer123)
  • Navigate to Infrastructure Committee
  • View an approved milestone
  • Connect Polkadot wallet (ensure you have Paseo testnet tokens)
  • Sign the multisig transaction

Monitor Transactions

Running Tests

# Run all tests
pnpm test

# Run with watch mode
pnpm test:watch

# Run specific test file
pnpm test -- child-bounty.test.ts