Defining Sort Rules

Overview

DAO Treasury’s sorting system provides a flexible and extensible way to classify transactions. Sort rules can be defined either by specifying a set of transaction attributes to match or by supplying a custom matching function. The system is built around three main components:

  1. Sort Rule Classes and Factories – Predefined sort rule classes (such as RevenueSortRule, ExpenseSortRule, etc.) are specialized tools that associate a provided transaction group identifier (txgroup) with a fixed category (e.g. “Revenue”, “Expenses”, “Other Income”). Internally, these classes register rules under their composite key fullname (e.g., “Revenue:Sales:Ice Cream”) for matching purposes.

  2. Custom Matching via SortRuleFactory – Factories (provided by functions like revenue(), expense(), etc. in dao_treasury.sorting.factory) allow registration of custom synchronous or asynchronous matching functions.

  3. External YAML-Based Sort Rules – Declarative rules defined in YAML files can be loaded at runtime by passing the directory path to the CLI via the –sort-rules flag.

This document details how to define and register your own sort rules, with real-world examples.

1. Defining Sort Rules with Predefined Classes

DAO Treasury provides specialized rule classes in dao_treasury.sorting.rule to cover common transaction types:

  • RevenueSortRule: Intended for inbound transactions representing revenue. - When instantiated with a txgroup like “Sales”, it registers the rule internally using a composite key such as “Revenue:Sales”, signaling that the rule is for revenue transactions.

  • CostOfRevenueSortRule: Designed for outbound transactions representing cost of revenue. - The rule is registered with a composite key like “Cost of Revenue:<txgroup>”.

  • ExpenseSortRule: For outbound transactions representing expenses. - A rule created with txgroup “Office” is registered under “Expenses:Office”.

  • OtherIncomeSortRule: For inbound transactions representing other income. - The composite key is formed by associating “Other Income” with the provided txgroup.

  • OtherExpenseSortRule: For outbound transactions representing other expenses. - The rule is similarly registered with an “Other Expenses” prefix.

  • IgnoreSortRule: For transactions that should be omitted from certain reports. - The rule is registered using a composite key prefixed with “Ignore:”.

Example (static attribute matching):

from dao_treasury.sorting.rule import RevenueSortRule

# Create a revenue rule that matches inbound transactions where the token symbol
# is 'DAI' and the to_address is '0xAbC1230000000000000000000000000000000000'
# The rule is registered under a composite key "Revenue:Sales"
# Once defined, all transactions matching this rule will be sorted to group
# "Revenue:Sales" in the db
rule = RevenueSortRule(
    txgroup='Sales',
    symbol='DAI',
    to_address='0xAbC1230000000000000000000000000000000000'
)

2. Registering Custom Matchers Using SortRuleFactory

For more dynamic rules, DAO Treasury offers a decorator interface through dao_treasury.sorting.factory. Factory functions such as revenue, expense, other_income, etc., return a SortRuleFactory preconfigured with the appropriate rule type. These factories support two patterns:

A. Decorator form:

from dao_treasury.sorting.factory import revenue

@revenue("Token Sales", networks=[1, 3])
async def match_large_sales(tx):
    return tx.value_usd > 1000

Note

Real-world Yearn example: Vault fees rule using @fees decorator: https://github.com/BobTheBuidler/yearn-treasury/blob/master/yearn_treasury/rules/revenue/vaults.py

In this form, the factory registers the custom matching function via the rule’s func parameter. The network specification determines if the rule is instantiated (typically, the current CHAINID must be among the allowed networks).

B. Static attribute matching:

from dao_treasury.sorting.factory import ignore

ignore("Dust").match(symbol="WETH", from_address="0xAAA...")

3. External YAML-Based Sort Rules

Define rules in YAML under category subfolders:

rules/
├── revenue/
│   ├── match_on_hash.yml
│   └── match_on_to_address.yml
├── other_income/
├── other_expense/
└── ignore/

Example YAML mapping:

1:
  DonationReceived:
    - 0xabc123...

Real-world YAML examples: - Other Expense matcher:

Enable via CLI:

dao-treasury --sort-rules ./rules …

Additionally, YAML definitions may include fields such as from_address, to_address, or full transaction hash—these values are validated under the hood by the library’s string matcher classes.

4. Matching Logic in Sort Rules

Sort rules extend the base class (_SortRule) which defines the matching behavior:

  • If specific transaction attributes (like symbol, to_address, etc.) are provided, these are gathered into a cached list (stored in the internal _match_all dictionary) and used for direct attribute comparisons.

  • If a custom matching function (func) is provided, that function is invoked on the transaction. It can be either synchronous (returning a boolean) or asynchronous (awaitable).

The matching process is encapsulated in the match method of the rule classes, ensuring that only one matching strategy (either attribute-based or function-based) is used for a given rule.

Conclusion

Defining custom sort rules involves:

  • Using predefined rule classes for standard categories.

  • Registering dynamic rules via decorator factories.

  • Declaring rules in YAML for external, file-based configuration.

This modular design enables DAO Treasury users to flexibly adapt its sorting strategy to the evolving requirements of their DAO.

For details, see: