Skip to content

Conversation

@youngkidwarrior
Copy link
Collaborator

Summary

Refactors the transfer workflow to use an intent-first write pattern where the workflow owns intent state, replacing the previous approach that relied on public.activity/temporal-trigger side effects.

Changes

Workflow (packages/workflows/src/transfer-workflow/)

  • workflow.ts: Creates/updates transfer_intents throughout the transfer lifecycle:

    • pending - Intent created at workflow start
    • submitted - Updated before sending userOp to chain
    • confirmed - Updated with tx_hash, block_num, event details
    • failed - Updated with error_message on failure
  • activities.ts: Added intent-based activities:

    • getChainIdActivity - Get chain ID for intent tracking
    • upsertTransferIntentActivity - Create/update transfer intent
    • updateTransferIntentActivity - Update intent status/data
    • upsertTransferReconciliationActivity - Link intent to on-chain event
  • supabase.ts: Added types and database functions:

    • TransferIntent, TransferIntentInsert, TransferIntentUpdate
    • TransferReconciliation, TransferReconciliationInsert
    • CRUD functions with upsert semantics for idempotent workflow restarts

Utilities (packages/workflows/src/utils/)

  • startWorkflow.ts: Extended to accept workflowIdOverride parameter
    • Allows using intent_id UUID as Temporal workflowId
    • Falls back to generated workflowId if not provided

API Layer (packages/api/src/routers/)

  • temporal.ts: Updated lookupInitializedWorkflow function:
    • First checks transfer_intents table (intent-first approach)
    • Falls back to activity table for backwards compatibility (legacy)
    • Requires status to be at least 'submitted' to consider workflow started

Deterministic On-Chain Identifiers

The reconciliation uses collision-invariant identifiers:

  • chain_id - Chain ID for multi-chain support
  • tx_hash - Transaction hash
  • log_idx - Log index within transaction

This ensures each on-chain event can only be linked to one intent via UNIQUE constraint.

Testing

  • All existing tests pass (27 tests, 3 test suites)
  • Race condition tests verify intent lifecycle behavior

This PR was generated with Warp.

Victor Ginelli and others added 4 commits December 31, 2025 02:37
…ration

Fixes issue where Supabase CLI commands fail outside of Tilt when
environment variables from .localnet.env are not loaded.

The supabase/config.toml uses env() placeholders for ports:
- api.port = "env(SUPABASE_API_PORT)"
- db.port = "env(SUPABASE_DB_PORT)"
- etc.

These require environment variables to be set, otherwise Supabase CLI
tries to parse literal strings as port numbers causing errors.

Solution:
- Update _with-env helper to load .localnet.env (from gen-env) and .env
- All supabase commands now use: dotenv -e ../.localnet.env -e ../.env -c --
- Priority: .localnet.env values override .env
- Prerequisite: Run `yarn gen-env local --name <workspace>` first

Changes:
- Update supabase/package.json _with-env helper
- Prefix all CLI commands (start, stop, reset, status, etc.) with _with-env

Benefits:
- Integrates with existing gen-env.ts workflow
- Workspace isolation via dynamic ports
- Works in any environment (Tilt, CLI, CI)

Success criteria:
✅ yarn supabase reset works without parsing errors
✅ Custom ports from gen-env are respected

Co-Authored-By: Warp <agent@warp.dev>
Fixes two critical race conditions that occurred when blockchain indexer
processes transfers before the Temporal workflow reaches 'confirmed' status:

1. **Notes disappearing**: User notes would be deleted before propagation
2. **Duplicate activities**: Two activities would appear for same transfer

Root causes:
- Temporal trigger used broad time-based matching to delete activities
- No direct FK reference between temporal transfers and pending activities
- Indexer could delete temporal activity before note propagation

Solution:
- Add activity_id column to temporal.send_account_transfers with FK
- Create trigger to insert pending activity when status='sent'
- Update temporal trigger to propagate notes and delete by exact activity_id
- Fix indexer trigger with enhanced matching (user_op_hash + fallback)

Changes:
- supabase/migrations/20251231_fix_temporal_transfer_race_conditions.sql
  Consolidated migration with all schema changes in atomic transaction
- supabase/schemas/temporal.sql
  Added activity_id column, new trigger, updated cleanup logic
- supabase/schemas/send_account_transfers.sql
  Enhanced indexer trigger to delete by activity_id with dual matching
- packages/workflows/src/transfer-workflow/race-conditions.test.ts
  Comprehensive test suite (10 tests) covering both race conditions
- supabase/tests/temporal_transfers_race_test.sql
  SQL integration test for race condition scenarios
- RACE_CONDITION_FIXES.md
  Complete documentation with test plan and monitoring queries

Test coverage:
✓ Note propagation (3 tests)
✓ Duplicate prevention (5 tests)
✓ Integration scenarios (2 tests)
All 25 workflow tests passing

Co-Authored-By: Warp <agent@warp.dev>
Implement optimistic navigation that triggers immediately after cache update
instead of waiting for temporal workflow to complete.

Changes:
- Navigate to chat or activity screen immediately after optimistic activity insertion
- Move navigation before await transfer() call for instant UX
- Keep background workflow for consolidation with temporal/final activity
- Existing rollback logic handles workflow failures

This builds on race condition fixes in add_activity_id_column_to_temporal_transfers
branch which ensure notes propagate correctly and prevent duplicate activities.

Benefits:
- Instant feedback to user after send action
- Temporal workflow consolidates optimistic activity in background
- Notes preserved through activity_id FK relationship
- Indexer can win race safely with dual matching logic

Co-Authored-By: Warp <agent@warp.dev>
…ites

- workflow.ts: Creates/updates transfer_intents throughout lifecycle
  (pending → submitted → confirmed/failed)
- activities.ts: Add intent-based activities (upsertTransferIntentActivity,
  updateTransferIntentActivity, upsertTransferReconciliationActivity)
- supabase.ts: Add types and DB functions for transfer_intents and
  transfer_reconciliations tables
- startWorkflow.ts: Add workflowIdOverride parameter for explicit
  Temporal workflowId (e.g., intent_id UUID)
- temporal.ts: Update lookupInitializedWorkflow to check transfer_intents
  first (intent-first), falling back to activity table (legacy)

The workflow now owns intent state instead of relying on
public.activity/temporal-trigger side effects. Uses deterministic
on-chain identifiers (chain_id, tx_hash, log_idx) for reconciliation.

Co-Authored-By: Warp <agent@warp.dev>
@github-actions
Copy link

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