Skip to content

Token & Stablecoin payout automation via Gnosis Safe ( detailed ) #99

@polux0

Description

@polux0

CollabBerry - Token Payments via SAFE Multisig (Spec)

Scope (what this delivers)

  • Admin configuration (Module A) - Set Safe, stablecoin, and recognition mode (TeamPoints mint or DAO token transfer).
  • Payouts (Module II) - Workflow: Preview → Propose → Sign → Execute for round-based distributions (stablecoin + TP/DAO).
  • Status & History (Module III) - Show chunked proposals per payout, Safe links, execution states, and Export CSV.
  • Manual payouts via Safe (Module IV) - Ad-hoc transfers/mints with the same Safe flow, tracked & exportable.

Core primitives used

  • Safe Protocol Kit - Build batched MultiSend transactions via createTransaction([...MetaTransactionData]). Supports arrays of calls.

  • Safe API Kit / Transaction Service - Propose, collect confirmations, and track execution status.

  • OpenZeppelin ERC-20 & AccessControl - Always read decimals() (do not assume 18); manage roles (hasRole, grantRole) for TP minting.


Module A - Admin Configuration (Safe & Tokens) [Recap]

  • UI: Chain selector, Safe address, Stablecoin address, Recognition mode (TeamPoints mint | DAO token).
  • Backend validation:
    • Safe is a contract; (optionally) sanity-check via Safe Transaction Service.
    • Token is ERC-20; fetch and cache decimals().
    • If TeamPoints (mint): Safe must have MINTER_ROLE. Enforce using AccessControl.
  • Why: Ensures correctness for batched payouts using Safe.

Module II - Payouts (Preview → Propose → Sign → Execute)

Frontend Flow

  1. Rounds list - Incomplete payoff rounds.
  2. Preview - Table: members, stable & recognition amounts, totals, warnings (balance & role checks).
  3. Actions - Buttons for “Create fiat transaction” and “Create recognition transaction.” Optional “Create both.”
  4. Post-propose - Show “Pending in Safe” link; status auto-updates to Executed/Failed.

API Endpoints

  • GET /payouts/rounds?orgId= → List incomplete rounds.
  • GET /payouts/preview?roundId= → Recipient list (human + base-unit), totals, preflight checks (balance, role), and chunk plan.
  • POST /payouts/propose { roundId, tokenType } → Build batched calls, create Safe transaction, propose it, store proposal, return Safe link.
  • GET /payouts/status?roundId= → Poll Transaction Service for statuses; update records accordingly.

Chunking logic (dev one-liner)

“Build a batched Safe MultiSend of up to N recipients, estimateGas; if gas or calldata exceeds cap, split and re-estimate recursively until ≤ cap; store chunks as tx_proposals with partIndex/partCount.”

Server-side Validations

  • Wallet address validation and dedupe.
  • Convert amounts using real decimals().
  • Balance checks for transfers; TP mint requires Safe to hold MINTER_ROLE.

Module III - Status & History (+ Export)

UI

  • List payouts with status (Pending / Executed / Partial / Failed).
  • Details: show tx_proposals (tokenType, chunk info, attempt, status, Safe link, timestamps), and recipients snapshot.
  • Retry on failed proposals: creates new slot with attempt+1 and links back to original.
  • Export CSV of each payout.

Export API

Returns `text/csv` with columns:
round_id, payout_type, token_type, part_index, part_count, attempt,
safe_tx_hash, status, proposed_at, executed_at, explorer_url,
wallet_address, amount_human, amount_base_units, recipient_status, error

Why: Combines on-chain Safe metadata and DB snapshots for full audit trace.
Module IV - Manual Payouts via Safe (Enabled) Approach: Allow manual
payouts (stablecoin & TP mint) via Safe flow.

Requirements: Safe must have MINTER_ROLE to mint TP.

Identical validations, chunking, proposals, status, and exports as
Module II.

Why via Safe: Maintains multisig governance and trace.

Data Model (Final)

payouts


-   id (uuid pk),
-   organization_id (fk),
-   round_id (nullable),
-   status (draft\|proposed\|executed\|partial\|failed),
-   total_stable_payout,
-   total_recognition_payout,
-   timestamps


tx_proposals


-   id (uuid pk),
-   payout_id (fk),
-   payout_type ('round'\|'manual'),
-   token_type ('stablecoin'\|'recognition'),
-   part_index (int),
-   part_count (int),
-   attempt (int, default 1),
-   retry_of_tx_proposal_id (uuid, nullable),
-   safeTxHash,
-   status (proposed\|executed\|failed\|canceled),
-   proposed_at,
-   executed_at,
-   payload_json,
-   explorer_url

Mirrors Safe TS for status.

payout_recipients


-   id,
-   payout_id,
-   round_compensation_id (nullable),
-   user_id,
-   wallet_address_snapshot,
-   token_type,
-   token_address_snapshot,
-   token_decimals_snapshot,
-   amount_human,
-   amount_base_units,
-   tx_proposal_id,
-   part_index,
-   part_count,
-   attempt (int, default 1),
-   status (pending\|proposed\|executed\|failed\|skipped),
-   error,
-   timestamps

Immutable per-recipient snapshot.

Milestones & Deliverables

  • Module A - Admin Config setup & validation.
  • Module II - Round-based payouts via Safe: Preview, Propose, Sign,
    Execute.
  • Module III - Status tracking, retry, and CSV export.
  • Module IV - Manual payouts (stable & TP) via Safe.

Global Acceptance Criteria

  • All proposals created with Safe SDKs and signed via Safe UI.
  • Accurate token decimal conversion.
  • TP minting only when Safe holds rights.
  • Chunking logic enforced.
  • CSV exports fully traceable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions