Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
chainNoTarget chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").
accountsNoAccounts to scan: addresses, names, "me", or "#N". Defaults to ["me"].

Implementation Reference

  • 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`); }
  • 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"].'),
        }),
      },

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/azeth-protocol/mcp-azeth'

If you have feedback or need assistance with the MCP directory API, please join our Discord server