-
Notifications
You must be signed in to change notification settings - Fork 18
feat(send-earn): add standalone CLI for revenue collection automation #2491
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
0xBigBoss
wants to merge
27
commits into
dev
Choose a base branch
from
bb/send-earn-automation
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cb648ba to
2b346a2
Compare
Implement automated claiming of MORPHO and WELL token rewards from Merkl distribution system for Send Earn vaults (SEND-172). - Add MerklDistributor contract to wagmi config with claim function ABI - Add send_earn_reward_claims table schema with indexes and RLS policies - Create Temporal workflow with read (retryable) and write (non-retryable) activities: - getActiveVaultsActivity: query vaults from send_earn_create table - fetchClaimableRewardsActivity: call Merkl API with rate limiting/retry - executeClaimActivity: batch claim with individual fallback on failure - recordClaimsActivity: insert claim records to database - Add configurable minimum claim thresholds for gas efficiency - Include design documentation and unit tests 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Redesign architecture based on clarification that MORPHO/WELL tokens are protocol revenue for Send Foundation, not yield for depositors. Key changes: - Two-step flow: harvest (Merkl→vault) + sweep (vault→revenue safe) - Terminology: claim/rewards → harvest/sweep/revenue throughout - Safety check: verify vault.collections() == REVENUE_SAFE before sweep - Track both steps in separate DB tables (harvest + sweep) - Monthly schedule + manual trigger - Dry run mode for simulation Destination: Send Foundation revenue safe (0x65049C4B8e970F5bcCDAE8E141AA06346833CeC4) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…sweep
Refactor rewards claiming workflow to revenue collection with two-step flow:
1. Harvest: Claim MORPHO/WELL rewards from Merkl to vault addresses
2. Sweep: Call SendEarn.collect() to transfer tokens to revenue safe
Key changes:
- Rename rewards-claim-workflow → revenue-collection-workflow
- Add sweepToRevenueActivity with safety check (verify collections == REVENUE_SAFE)
- Add getVaultBalancesActivity for dry run mode
- Add DryRunData type with harvestableFromMerkl, currentVaultBalances, expectedRevenue
- Add RevenueError with step discriminator ('harvest' | 'sweep')
- Add Temporal schedule (monthly 1st at 6 AM UTC)
- Add manual API trigger at /api/cron/send-earn-revenue
- Add database tables: send_earn_revenue_harvest, send_earn_revenue_sweep
- Bump workflows version to 0.0.9
Safety: sweepToRevenueActivity verifies vault.collections() matches REVENUE_SAFE
before executing sweep to prevent sending tokens to wrong address.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add integration test infrastructure using @morpho-org/test with Vitest: - Setup forked Base mainnet using Anvil for realistic contract interactions - Test token balance reading (MORPHO, WELL) - Test ERC20 storage manipulation for test setup - Test Merkl distributor contract accessibility - Test account impersonation for collector New scripts: - yarn test:anvil - Run fork integration tests - yarn test:anvil:watch - Watch mode for development Dependencies added: - @morpho-org/test ^2.6.6 - Vitest fixtures for Anvil fork tests - vitest ^2.1.8 - Test runner (Jest kept for existing unit tests) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Replace Jest with Vitest for faster ESM-native testing - Add separate configs for unit tests (vitest.config.ts) and anvil fork tests (vitest.anvil.config.ts) - Update test imports from @jest/globals to vitest (jest.* → vi.*) - Remove Jest dependencies (@jest/globals, jest, ts-jest, nyc) - Add @vitest/coverage-v8 for coverage support - Add vitest.setup.ts with nock configuration for unit tests - Anvil tests run without nock to allow real network access Test commands: - yarn test: unit tests - yarn test:anvil: anvil fork integration tests - yarn test:all: all tests 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Update README.md to document Vitest test configurations - Remove jest.config.ts from tsconfig.json includes - Delete obsolete docs/todo-fix-workflows-jest-paths.md Addresses review ISSUE-3. Co-Authored-By: Claude <noreply@anthropic.com>
- Add SEND_EARN_VAULT address and MERKL_DISTRIBUTOR_ABI to test setup - Add mock SendEarn vault bytecode for deploying test contracts - Add tests that deploy mock vault and call collections() on-chain - Add tests for Merkl claim flow (claimed amounts, revert behavior) - Verify safety check correctly passes/fails based on collections address These tests exercise the actual on-chain call paths used by sweepToRevenueActivity, catching regressions that pure logic tests would miss. Addresses review ISSUE-1, ISSUE-2, ISSUE-4. Co-Authored-By: Claude <noreply@anthropic.com>
Add AbortController with configurable timeout to prevent fetch calls from hanging indefinitely. Timeout is controlled via MERKL_API_TIMEOUT_MS environment variable (default: 30 seconds). - Add merklApiTimeoutMs to RevenueConfig interface - Wrap fetch calls with AbortController signal - Properly cleanup timeout in finally block Addresses review ISSUE-5. Co-Authored-By: Claude <noreply@anthropic.com>
…lection Implements the @my/send-earn package with: - CLI commands: dry-run, harvest, sweep - Library exports for workflows integration - Merkl API client with rate limiting and retries - Vault operations (balance reading, harvest, sweep) - Output formatters (table, json, csv, markdown) - Database queries using pg (node-postgres) The package provides a reusable foundation for the revenue collection workflow and can be used as a standalone CLI tool. Includes specification document defining the package API and success criteria. Co-Authored-By: Claude <noreply@anthropic.com>
…bility Address review feedback from iteration 1: - Add @my/send-earn as workspace dependency in @my/workflows - Import shared types (REVENUE_ADDRESSES, VaultRevenue, etc.) from @my/send-earn - Fix harvest fail-fast: batch failures now throw instead of falling back to individual claims (stale proofs require fresh data, not retry) - Fix Anvil test port collision: add pkill cleanup and maxConcurrency=1 All verifications pass: typecheck, lint, unit tests, anvil tests. Co-Authored-By: Claude <noreply@anthropic.com>
…ctions Complete the refactor to use @my/send-earn library: - Activities now delegate to library functions: - getActiveVaultsActivity → getActiveVaults - fetchHarvestableRevenueActivity → fetchHarvestableRevenue - getVaultBalancesActivity → getVaultBalances - harvestRevenueActivity → executeHarvest - sweepToRevenueActivity → executeSweep - Config updated to use createConfig from @my/send-earn with SUPABASE_DB_URL and BASE_RPC_URL env vars - Types re-exported from @my/send-earn, keeping only workflow-specific HarvestResult and SweepResult types local - Tests updated to mock @my/send-earn functions instead of local impls All verifications pass: typecheck, lint, 35 unit tests, 15 anvil tests. Co-Authored-By: Claude <noreply@anthropic.com>
- Remove execSync pkill that could kill unrelated Anvil processes - @morpho-org/test manages Anvil lifecycle; added comment for manual cleanup - Document Foundry and native build tool prerequisites in README Co-Authored-By: Claude <noreply@anthropic.com>
- Align zx version in @my/send-earn to ^8.1.2 (matches monorepo) - Update vitest in @0xsend/webauthn-authenticator to ^2.1.8 (fixes check-deps) - Move REVENUE_ADDRESSES to types.ts to prevent pg module import in workflow bundle (Temporal requires deterministic code - pg uses Node.js modules) - Document Python setuptools requirement for Python 3.12+ in README Co-Authored-By: Claude <noreply@anthropic.com>
- Add yarn resolution for better-sqlite3@11.5.0 in root package.json - @snaplet/snapshot pins 8.5.0 which has C++ compilation issues with Node.js 22 - The newer version includes prebuilt binaries and fixes for Node.js 22 Co-Authored-By: Claude <noreply@anthropic.com>
When --vault flag is specified, use the provided vault addresses directly instead of fetching from database. This enables CLI testing without a database connection. Tested against local Anvil fork: - dry-run with all output formats (table, json, csv, markdown) - harvest and sweep commands - Error handling for missing env vars Co-Authored-By: Claude <noreply@anthropic.com>
Addresses review feedback ISSUE-1: - Normalize vault addresses to lowercase for consistent comparison - Deduplicate using Set to prevent double-counting in dry-run or duplicate claims/sweeps from repeated --vault flags Co-Authored-By: Claude <noreply@anthropic.com>
The Merkl v4 API returns an array structure with rewards nested inside chain data, not an object keyed by token address. This caused the CLI to show 0 rewards when querying production vaults. - Update MerklRewardsResponse type to match v4 API structure - Add MerklTokenInfo and MerklV4Reward types for proper typing - Iterate rewards array and match by token.address field - Tested against production: Platform Vault shows 74.54 MORPHO + 21,176.40 WELL Co-Authored-By: Claude <noreply@anthropic.com>
Add CLI commands and functions to distribute performance fee shares from SendEarnAffiliate contracts: - `fees-dry-run`: Display pending fee shares for affiliate contracts - `distribute-fees`: Execute fee distribution via pay() on affiliates Key changes: - New types for fee distribution tracking (shares, not assets) - `getFeeRecipientInfo()` identifies affiliate vs direct recipients - `executeFeeDistribution()` calls pay() on affiliate contracts - All output formats (table/json/csv/markdown) include fee share data - `dry-run` now shows fee shares alongside Merkl rewards Per spec: non-Revenue Safe contracts are classified as affiliates; affiliate metadata (platformVault, payVault) is optional. Co-Authored-By: Claude <noreply@anthropic.com>
Add TVL (Total Value Locked) tracking specification: - VaultTVL and TVLResult interfaces - getVaultsTVL() implementation details - CLI command example (send-earn tvl) - Output format examples - Use cases: analytics, revenue projections, monitoring, reporting Co-Authored-By: Claude <noreply@anthropic.com>
- Add VaultTVL and TVLResult interfaces to types.ts - Implement getVaultsTVL() in vaults.ts using ERC4626 totalAssets(), totalSupply(), and VAULT() getter - Add tvl() high-level function to revenue.ts - Export new types and functions from index.ts - Add send-earn tvl CLI command with all output formats (table, json, csv, markdown) - Format USDC amounts with commas and 2 decimal places Co-Authored-By: Claude <noreply@anthropic.com>
- Fix table output to use box-drawing characters matching spec format - Add UnderlyingVaultType discriminator (Morpho, Moonwell, Unknown) - Add detectVaultType() to identify Morpho vaults via MORPHO() function - Update all output formats (table, json, csv, markdown) to show vault type - Table now displays: Vault | TVL (USDC) | Underlying (Morpho/Moonwell) Co-Authored-By: Claude <noreply@anthropic.com>
Title row was 66 chars (pad to 64 + 2 borders) but borders are 67 chars. Fixed by padding title content to 65 chars. Co-Authored-By: Claude <noreply@anthropic.com>
Spec shows continuous top border for title spanning full width, with column separators only appearing on the row below the title. Co-Authored-By: Claude <noreply@anthropic.com>
Type was renamed to MerklV4Reward in @my/send-earn but wasn't used. Co-Authored-By: Claude <noreply@anthropic.com>
The Dockerfile explicitly lists all package.json files for yarn install. Co-Authored-By: Claude <noreply@anthropic.com>
Add tables to track the two-step revenue collection process: - send_earn_reward_claims: legacy reward tracking - send_earn_revenue_harvest: Merkl → vault claims - send_earn_revenue_sweep: vault → revenue safe transfers Includes summary views for analytics and appropriate RLS policies. Co-Authored-By: Claude <noreply@anthropic.com>
2b346a2 to
7cdecbd
Compare
Replace @jest/globals imports with vitest for consistency with the rest of the workflows package which migrated to Vitest. Co-Authored-By: Claude <noreply@anthropic.com>
|
Vercel Unique URL: https://sendapp-4xytjcwl9-0xsend.vercel.app |
Playwright Report
Summary
Suites✅ account-sendtag-add.onboarded.spec.ts (5/5 passed)can visit add sendtags page
can add a pending tag
cannot add an invalid tag name
cannot add more than 5 tags
cannot confirm a tag without paying
❌ account-sendtag-checkout.onboarded.spec.ts (1/3 passed)can confirm a tag
can refer a tag
can refer multiple tags in separate transactions
✅ account-settings-backup.onboarded.spec.ts (2/2 passed)can backup account
can remove a signer
✅ account.logged-in.spec.ts (2/2 passed)can visit account page
can update profile
❌ activity.onboarded.spec.ts (0/1 passed)can visit activity page and see correct activity feed
✅ contacts.onboarded.spec.ts (0/0 passed)✅ earn.onboarded.spec.ts (0/0 passed)❌ home.onboarded.spec.ts (0/1 passed)can visit token detail page
✅ leaderboard.logged-in.spec.ts (0/0 passed)can visit leaderboard page
❌ onboarding.logged-in.spec.ts (0/1 passed)can visit onboarding page
✅ profile-external-address.anon.spec.ts (0/0 passed)✅ profile-external-address.onboarded.spec.ts (0/0 passed)❌ profile.anon.spec.ts (1/2 passed)anon user can visit public profile
anon user cannot visit private profile
❌ profile.logged-in.spec.ts (0/1 passed)logged in user needs onboarding before visiting profile
✅ profile.onboarded.spec.ts (4/4 passed)can visit other user profile and send by tag
can visit my own profile
can visit private profile
can view activities between another profile
❌ send-token-upgrade.onboarded.spec.ts (0/1 passed)can upgrade their Send Token V0 to Send Token V1
❌ send.onboarded.spec.ts (0/13 passed)can send USDC starting from profile page
can send USDC using tag starting from home page
can send USDC using sendid starting from home page
can send USDC using address starting from home page
can send SEND starting from profile page
can send SEND using tag starting from home page
can send SEND using sendid starting from home page
can send SEND using address starting from home page
can send ETH starting from profile page
can send ETH using tag starting from home page
can send ETH using sendid starting from home page
can send ETH using address starting from home page
cannot send below minimum amount for SEND token
❌ sendtag-happy-path.onboarded.spec.ts (0/1 passed)sendtag complete happy path - create, confirm, and change main tag
sendtag main tag succession - auto-assigns new main when current is deleted
✅ sign-in.anon.spec.ts (3/3 passed)redirect on sign-in
redirect to send page on sign-in with recipient params
old user can login using phone number
✅ sign-up.anon.spec.ts (2/2 passed)can sign up
country code is selected based on geoip
❌ swap.onboarded.spec.ts (2/5 passed)can swap USDC for SEND
can swap USDC for ETH
can refresh swap form and preserve filled data
can't access form page without accepting risk dialog
can't access summary page without filling swap form
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
@my/send-earnCLI package for automating Send Earn vault revenue collection@my/workflowsto delegate activities to@my/send-earnlibrary functionsFeatures
CLI Commands
send-earn dry-run- Preview harvestable revenue and vault balancessend-earn harvest- Claim MORPHO/WELL rewards from Merkl distributorsend-earn sweep- Transfer tokens from vaults to revenue safesend-earn distribute-fees- Process affiliate contract fee distributionssend-earn tvl- Display total value locked across all vaultsOutput Formats
All commands support
--formatflag:table(default),json,csv,markdownTechnical Changes
packages/send-earn/with full library + CLITest plan
yarn typecheckpassesyarn biome:checkpassesyarn workspace @my/send-earn test)yarn workspace @my/send-earn test:anvil)bun packages/send-earn/bin/send-earn.ts --help)🤖 Generated with Claude Code