name: sweep-sablier-flow
description: Check whether the wallet in repo-root dotenvx .env has recipient-owned Sablier Flow streams with assets due for withdrawal. Use when Codex needs to inspect Sablier streams broadly or Sablier Flow specifically, query the Flow Hyperindex GraphQL endpoint, confirm exact withdrawable balances onchain with cast, prepare zero-value per-contract batch(bytes[]) withdrawals, simulate them, and wait for user confirmation before broadcasting.
Sweep Sablier Flow
Overview
Inspect recipient-owned Sablier Flow streams for the wallet defined in the repo-root dotenvx .env. Find candidate Flow streams via the indexer, confirm exact due funds with batched onchain withdrawableAmountOf reads through Multicall3.aggregate3, then prepare one zero-value batch(bytes[]) transaction per contract and simulate each transaction before any confirmation prompt.
Keep this workflow separate from unwrap, bridge, and consolidate-stables.
Use $evm-chains to resolve chain name, chain ID, default public RPC, and RouteMesh support for every discovered chainId before any onchain step or output formatting. RouteMesh routing and ROUTEMESH_API_KEY sourcing are delegated to the globally installed $evm-chains skill; if that skill is unavailable, tell the user to install it with npx skills add evm-chains before proceeding. Use cli-cast only for the cast commands. Read ../../references/sablier-stream-discovery.md before querying the endpoint. Use only the shared sections plus the Flow-specific query and diagnostic material.
Workflow
1. Load the wallet identity
Load ETH_PRIVATE_KEY, ADDRESS, and any other repo-local secrets from the repo-root dotenvx .env. Run secret-dependent commands through a dotenvx-run subshell, such as dotenvx run --quiet -- sh -c '...'; do not source .env directly because it is encrypted.
Use ADDRESS as the wallet identity for GraphQL queries. If ADDRESS is missing, empty, or otherwise unusable, derive it from ETH_PRIVATE_KEY with cast wallet address and continue.
Normalize ADDRESS to lowercase before sending it as a GraphQL variable.
2. Query the Flow indexer
Run the Flow candidate queries (the FlowStream entity) from the shared reference against Sablier's unified Envio streams indexer at https://indexer.hyperindex.xyz/53b7e25/v1/graphql. The legacy standalone Flow endpoint (.../3b4ea6b/...) is decommissioned (HTTP 404).
Exclude testnet chain ids in the GraphQL where clause. Default chain denylist: 11155111 (Sepolia) and 84532 (Base Sepolia).
For Flow candidate discovery, use:
chainId: { _nin: [11155111, 84532] }
Treat the indexer results as candidate discovery only.
If the candidate queries return zero rows, do not conclude that the wallet has no Flow streams. Run the Flow zero-result diagnostic counts from the shared reference before answering.
Use those diagnostics only to explain the zero-result case. Do not merge non-candidate history into the actionable output.
3. Confirm exact withdrawable amounts onchain
Use Multicall3 at 0xcA11bde05977b3631167028862bE2a173976CA11.
Group candidate rows by chainId. For each chain:
- Resolve the chain with
$evm-chainsand use theRPC_URLit returns.$evm-chainsowns RouteMesh selection and globalROUTEMESH_API_KEYaccess; otherwise it falls back to public RPCs. - Serialize that chain's candidate rows as a JSON array and pipe them to
../../scripts/sablier-multicall3-withdrawable.sh --rpc-url "$RPC_URL". - Use the helper output as the onchain source of truth. It preserves the original candidate fields and adds
multicallSuccess,multicallReturnData,withdrawableRaw, andwithdrawableNonZero. - Filter actionable rows from the helper output with
withdrawableNonZero = true.
Treat batch-level RPC failures as hard errors for that chain. Treat per-subcall failures as best-effort only: keep successful results, omit failed candidates from actionable output, and only mention failed candidates if the user asks for debugging details.
4. Prepare per-contract withdrawal transactions
After filtering to actionable rows, keep the workflow chain-local. For each chain with actionable rows:
- Resolve the chain with
$evm-chainsand use theRPC_URLit returns.$evm-chainsowns RouteMesh selection and globalROUTEMESH_API_KEYaccess; otherwise it falls back to public RPCs. - Pipe that chain's actionable rows to
../../scripts/sablier-prepare-withdraw-txs.sh --protocol flow --recipient "$ADDRESS" --rpc-url "$RPC_URL" --from "$ADDRESS". - Treat the helper output as the source of truth for prepared transactions. It emits one transaction per Sablier contract with
txData,txValueWei,callKind,castSendCommand,simulationOk,simulationError, andgasEstimate. - Expect every prepared transaction to use
txValueWei = 0andcallKind = batch(bytes[]). - The helper must encode one inner
withdrawMax(uint256,address)call per stream, withto = ADDRESS.
5. Simulate every prepared transaction
The helper must preflight every prepared transaction with both:
cast call "$TX_TO" --data "$TX_DATA" --value 0 --from "$ADDRESS" --rpc-url "$RPC_URL"cast rpc eth_estimateGas --raw "[{\"from\":\"$ADDRESS\",\"to\":\"$TX_TO\",\"data\":\"$TX_DATA\",\"value\":\"0x0\"}]" --rpc-url "$RPC_URL"
Do not treat a transaction as ready unless both commands succeed.
If any prepared transaction fails simulation:
- report the blocked contract batch
- include the simulation error and raw calldata
- stop before asking for confirmation
- do not submit any transaction
6. Report actionable results and transaction previews
Report streams whose onchain withdrawableAmountOf is greater than zero, then append the prepared transaction preview.
Default behavior: return the findings as a Markdown table. Only use another format if the user explicitly asks for one.
Start with a one-line summary that includes the normalized wallet address and the number of actionable streams found.
Findings columns:
- Stream Type
- Chain
- Sablier Contract
- Stream ID
- Withdrawable Amount
Findings formatting rules:
- Use one table for all actionable rows.
- Sort rows by chain id ascending, then asset symbol ascending, then raw withdrawable amount descending.
- Render the chain column as
<name> (<chainId>)when$evm-chainsresolves thatchainId; otherwise render the numericchainId. - Render the human withdrawable amount as a decimal scaled by
asset.decimals, with no scientific notation, followed by a space and the asset symbol. - Strip trailing zeroes from the fractional part.
- If more than two non-zero digits remain in the fractional part, truncate after the second non-zero fractional digit. Do not round.
- Use code spans for full addresses and stream ids.
- Keep intermediate candidate rows out of the final answer unless the user asks for debugging details.
Prepared transaction formatting rules:
- Use one prepared-transactions table for all contract batches.
- Include these columns: Chain, Sablier Contract, Call, Streams, Value, Simulation, Gas Estimate.
- Render
Valueas0 wei. - Mark
SimulationasreadywhensimulationOk = true; otherwise mark it asblocked. - Under the table, include the raw calldata and exact zero-value
cast sendcommand for each contract batch. - If any batch is blocked, say that broadcast is not safe yet and do not ask for confirmation.
- If all batches are ready, ask once for explicit confirmation before any send step.
If there are no non-zero results, say No withdrawable Sablier Flow streams found for \
If the candidate queries return zero before the onchain step, tailor the answer to the Flow diagnostics:
- If recipient history exists on supported chains, say the wallet has Sablier Flow stream history, but none of it matched the candidate predicates under the current filters.
- Only say no recipient-owned Sablier Flow streams were found when both the candidate counts and the recipient history counts are zero.
7. Broadcast only after confirmation
If and only if every prepared transaction is ready and the user explicitly confirms:
- broadcast the prepared transactions sequentially with
cast send - use the exact prepared calldata and
--value 0 - confirm each submitted transaction with
cast receipt - stop on the first broadcast failure or failed receipt and report which contract batches already succeeded
Guardrails
- Do not inspect Lockup within this skill.
- Do not mix this workflow into
unwrap,bridge, orconsolidate-stables. - Do not assume GraphQL
FlowStreamrows exposewithdrawableAmountOf; they do not. - Do not treat candidate rows as exact balances without the onchain Multicall3 confirmation.
- Do not include Sepolia (
11155111) or Base Sepolia (84532) in default candidate discovery; exclude them in the GraphQL query. - Do not infer chain labels or RPC URLs from ad hoc mappings; resolve them through
$evm-chains. - Do not report
no streamsbased only on zero candidate rows; run the Flow zero-result diagnostics first. - Do not fall back to one direct
cast callper failed subcall unless the skill is explicitly changed again. - Do not parse the pretty-printed tuple output from
cast call; use the shared helper or the raw-hex decode path from the shared reference. - Do not pretend Flow exposes
withdrawMultiple; usebatch(bytes[])with innerwithdrawMax(uint256,address)calls. - Do not batch across different chains or different contracts.
- Do not treat a prepared transaction as ready if either the simulation call or the gas estimate failed.
- Do not broadcast any withdrawal until the user explicitly confirms.