azeth_get_due_agreements
Find payment agreements ready for execution across specified accounts. Returns actionable data for keeper bots to process due payments or service providers to collect owed funds.
Instructions
Find all payment agreements that are due for execution across one or more accounts.
Use this when: You are a keeper bot looking for agreements to execute, or a service provider checking which of your customers' payments are collectible.
Returns: Array of due agreements with payer account, agreement ID, and expected payout. Each entry can be passed directly to azeth_execute_agreement.
Note: This scans all agreements for the specified accounts. For large-scale keeper operations, consider filtering by specific accounts rather than scanning all.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chain | No | Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet"). | |
| accounts | No | Accounts to scan: addresses, names, "me", or "#N". Defaults to ["me"]. |
Implementation Reference
- src/tools/agreements.ts:810-913 (handler)The handler logic for 'azeth_get_due_agreements' that fetches agreement data, checks executability, calculates payouts, and returns a sorted list of due agreements.
async (args) => { let client; try { client = await createClient(args.chain); const chain = resolveChain(args.chain); // Resolve all accounts const accountInputs = args.accounts ?? ['me']; const resolvedAccounts: Array<{ address: `0x${string}`; name?: string }> = []; for (const input of accountInputs) { try { const resolved = await resolveAddress(input, client); resolvedAccounts.push({ address: resolved.address, name: resolved.name ?? resolved.resolvedFrom, }); } catch (resolveErr) { return handleError(resolveErr); } } let scannedAgreements = 0; const dueAgreements: Array<Record<string, unknown>> = []; for (const acct of resolvedAccounts) { // Get count from first getAgreementData call (avoids separate getAgreementCount RPC) let count: bigint; try { const firstData = await client.getAgreementData(0n, acct.address); count = firstData.count; } catch { continue; } for (let i = 0; i < Number(count); i++) { scannedAgreements++; // Single RPC: agreement + executability + isDue + nextExecutionTime let data; try { data = await client.getAgreementData(BigInt(i), acct.address); } catch { continue; } const { agreement, executable, isDue, nextExecutionTime: nextExecTime } = data; if (!agreement.active) continue; if (!executable || !isDue) continue; const decimals = tokenDecimals(agreement.token, chain); const tokenSymbol = resolveTokenSymbol(agreement.token, chain); // Estimate payout (pro-rata based on elapsed time) const nowSecs = BigInt(Math.floor(Date.now() / 1000)); const elapsed = agreement.lastExecuted === 0n ? agreement.interval // first execution: assume full interval : nowSecs - agreement.lastExecuted; const estimatedPayout = elapsed > 0n ? (agreement.amount * elapsed) / agreement.interval : agreement.amount; // Cap at 3x interval (max accrual multiplier) const cappedPayout = estimatedPayout > agreement.amount * 3n ? agreement.amount * 3n : estimatedPayout; // Calculate overdue from nextExecutionTime (already available, no extra RPC) let overdueBy: string | undefined; const diff = Math.floor(Date.now() / 1000) - Number(nextExecTime); if (diff > 0) { overdueBy = formatOverdue(diff); } // Payee name const payeeName = await lookupPayeeName(client, agreement.payee); dueAgreements.push({ account: acct.address, ...(acct.name ? { accountName: acct.name } : {}), agreementId: i.toString(), payee: agreement.payee, ...(payeeName ? { payeeName } : {}), tokenSymbol, estimatedPayout: formatUnits(cappedPayout, decimals), ...(overdueBy ? { overdueBy } : {}), }); } } // Sort by estimated payout descending (highest value first for keeper prioritization) dueAgreements.sort((a, b) => { const payoutA = parseFloat(a.estimatedPayout as string); const payoutB = parseFloat(b.estimatedPayout as string); return payoutB - payoutA; }); return success({ scannedAccounts: resolvedAccounts.length, scannedAgreements, dueAgreements, }); } catch (err) { return handleError(err); } finally { try { await client?.destroy(); } catch (e) { process.stderr.write(`[azeth-mcp] destroy error: ${e instanceof Error ? e.message : String(e)}\n`); } - src/tools/agreements.ts:789-809 (registration)Registration of 'azeth_get_due_agreements' tool with the MCP server, including the description and input schema.
server.registerTool( 'azeth_get_due_agreements', { description: [ 'Find all payment agreements that are due for execution across one or more accounts.', '', 'Use this when: You are a keeper bot looking for agreements to execute,', 'or a service provider checking which of your customers\' payments are collectible.', '', 'Returns: Array of due agreements with payer account, agreement ID, and expected payout.', 'Each entry can be passed directly to azeth_execute_agreement.', '', 'Note: This scans all agreements for the specified accounts. For large-scale keeper operations,', 'consider filtering by specific accounts rather than scanning all.', ].join('\n'), inputSchema: z.object({ chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'), accounts: z.array(z.string()).min(1).max(20).optional() .describe('Accounts to scan: addresses, names, "me", or "#N". Defaults to ["me"].'), }), },