Skip to main content

Argus Built-in Reducers

This guide explains the built-in reducers available in Argus and how to use them to efficiently index and query Cardano blockchain data for your applications.


Introduction

Argus provides a comprehensive collection of pre-built reducers that handle common blockchain data indexing needs. These reducers save significant development time by handling the complex logic of parsing and transforming raw blockchain data. They offer production-tested reliability, optimized performance through efficient database schemas, maintenance-free updates that adapt to protocol changes, and standardized models for consistent access patterns across projects.

While you can always create custom reducers for specialized needs, built-in reducers provide robust, ready-to-use solutions for common indexing patterns. These reducers fall into two main categories:


Available Reducers

General Reducers

These reducers handle fundamental blockchain data structures that are useful for most applications:

ReducerDescription
BlockBySlotReducerIndexes blocks by their slot number for fast lookups
TxBySlotReducerIndexes transactions chronologically by slot for historical analysis
UtxoByAddressReducerTracks UTXOs for specific addresses to monitor balances and activity
OutputBySlotReducerIndexes transaction outputs by slot for historical output analysis
BalanceByAddressReducerTracks address balances for both ADA and native assets
Why Use General Reducers

General reducers provide the foundation for most blockchain applications. They handle common tasks like finding transactions, tracking UTXOs, and monitoring address balances, saving you from implementing these patterns from scratch.

DApp Reducers

These reducers are tailored for specific Cardano DApps and protocols:

SundaePriceByTokenReducer
Tracks token prices and swaps on SundaeSwap DEX

MinswapPriceByTokenReducer
Monitors prices on Minswap DEX pools

JpgPriceByTokenReducer
Tracks NFT sales on JPG Store marketplace

SplashPriceByTokenReducer
Tracks token prices on Splash DEX

Why DApp-Specific Reducers Matter

DApp-specific reducers handle the complex logic of parsing specialized transaction patterns used by popular Cardano protocols. This provides several benefits:

  • Immediate integration with established ecosystems
  • Protocol-aware data models designed for specific use cases
  • Optimized queries for common DApp operations
  • Automatic updates when protocols evolve

Instead of building custom parsing logic for each DApp, you can use these reducers as building blocks for advanced applications like price aggregators, trading tools, and DeFi dashboards.


Setting Up Reducers

Follow these steps to add reducers to your Argus project:

1. Create Your Database Context

Your DbContext should inherit from CardanoDbContext:

public class MyCardanoDbContext : CardanoDbContext
{
public MyCardanoDbContext(DbContextOptions<MyCardanoDbContext> options,
IConfiguration configuration)
: base(options, configuration)
{
}
}

2. Register the Indexer and Reducers

In your Program.cs, register the Cardano indexer and reducers:

// Add Cardano indexer with your database context
builder.Services.AddCardanoIndexer<MyCardanoDbContext>(builder.Configuration);

// Add specific reducers
builder.Services.AddReducers<MyCardanoDbContext, IReducerModel>([
typeof(BlockBySlotReducer<>),
typeof(TxBySlotReducer<>),
typeof(UtxoByAddressReducer<>),
typeof(OutputBySlotReducer<>),
typeof(BalanceByAddressReducer<>)
// Add other reducers as needed
]);

3. Configure Connection Settings

Configure your application in appsettings.json:

{
"ConnectionStrings": {
"CardanoContext": "Host=localhost;Database=argus-db;Username=postgres;Password=yourpassword;Port=5432;",
"CardanoContextSchema": "public"
},
"CardanoNodeConnection": {
"ConnectionType": "UnixSocket", // or "gRPC"
"UnixSocket": {
"Path": "/path/to/node.socket"
},
"gRPC": {
"Endpoint": "http://localhost:50051",
"ApiKey": "your-api-key"
},
"NetworkMagic": 2, // 1 for mainnet, 2 for preview, 1097911063 for preprod
"MaxRollbackSlots": 1000,
"RollbackBuffer": 10
},
"Sync": {
"Dashboard": {
"TuiMode": true,
"RefreshInterval": 5000,
"DisplayType": "Full"
},
"State": {
"ReducerStateSyncInterval": 5000
}
}
}

4. Create and Apply Migrations

Set up your database with Entity Framework migrations:

# Add migration
dotnet ef migrations add InitialMigration

# Apply migration
dotnet ef database update

When you run your application, Argus will automatically connect to the Cardano node, begin indexing blockchain data, process it through your registered reducers, and store the results in your database.


General Reducer Models

BlockBySlot

Indexes blocks by their slot number for quick lookup.

PropertyTypeDescription
SlotulongBlock slot number (primary key)
HashstringBlock hash as hex string
BlockTimeDateTimeOffsetTimestamp of block creation
SizeintSize of the block in bytes
EpochNoulongEpoch number
EpochSlotulongSlot within the epoch

Example query:

using (var context = await _dbContextFactory.CreateDbContextAsync())
{
var block = await context.BlocksBySlot
.FirstOrDefaultAsync(b => b.Slot == 12345678);

Console.WriteLine($"Block Hash: {block.Hash}");
Console.WriteLine($"Block Time: {block.BlockTime}");
}
Block Lookup Use Cases

This reducer is particularly useful for:

  • Building block explorers
  • Implementing time-based queries
  • Converting slot numbers to timestamps
  • Tracking chain reorganizations

TxBySlot

Indexes transactions chronologically by slot.

PropertyTypeDescription
SlotulongBlock slot number (part of composite key)
HashstringTransaction hash as hex string (part of composite key)
IndexulongTransaction index within the block (part of composite key)
RawCborbyte[]Raw CBOR bytes of the transaction

Example query:

using (var context = await _dbContextFactory.CreateDbContextAsync())
{
var recentTxs = await context.TxBySlot
.Where(tx => tx.Slot >= startSlot && tx.Slot <= endSlot)
.OrderBy(tx => tx.Slot)
.ThenBy(tx => tx.Index)
.ToListAsync();

foreach (var tx in recentTxs)
{
Console.WriteLine($"Tx Hash: {tx.Hash} at Slot: {tx.Slot}");
}
}
Transaction Indexing Considerations

When using the TxBySlot reducer:

  • Consider the query patterns your application needs most
  • For time-based queries, filter by slot range
  • For specific transaction lookups, query by hash
  • The raw CBOR data lets you decode transaction details when needed
  • Use in combination with other reducers for more advanced queries

This reducer provides the chronological backbone for transaction history, while other reducers can index specific transaction aspects.

OutputBySlot

Tracks transaction outputs by slot for historical analysis.

PropertyTypeDescription
SlotulongBlock slot number (part of composite key)
IdstringTransaction ID (part of composite key)
IndexulongOutput index within the transaction (part of composite key)
AddressstringBech32 address
RawCborbyte[]Raw CBOR bytes of the output
AmountValueValue of the output (transient, not stored)
DatumDatumDatum attached to the output (transient, not stored)

Example query:

using (var context = await _dbContextFactory.CreateDbContextAsync())
{
var outputs = await context.OutputBySlot
.Where(o => o.Slot == targetSlot)
.ToListAsync();

foreach (var output in outputs)
{
Console.WriteLine($"Output: {output.Id}#{output.Index} to {output.Address}");
}
}
Transient Properties

Note that Amount and Datum are transient properties populated when querying but not stored in the database. These are parsed from the RawCbor field when needed, so you can access them in your code but can't filter by them in LINQ queries.

BalanceByAddress

Monitors address balances for both ADA and native assets.

PropertyTypeDescription
AddressstringBech32 address (primary key)
AssetsstringJSON representation of assets
LovelaceulongADA amount in lovelace
UpdatedAtSlotulongSlot when balance was last updated

Example query:

using (var context = await _dbContextFactory.CreateDbContextAsync())
{
var balance = await context.BalanceByAddress
.FirstOrDefaultAsync(b => b.Address == "addr1...");

Console.WriteLine($"Balance: {balance.Lovelace / 1_000_000.0} ₳");
Console.WriteLine($"Last updated at slot: {balance.UpdatedAtSlot}");

// Parse assets JSON if needed
var assets = JsonSerializer.Deserialize<Dictionary<string, ulong>>(balance.Assets);
foreach (var (assetId, amount) in assets)
{
Console.WriteLine($"Asset: {assetId}, Amount: {amount}");
}
}
Balance Tracking Applications

This reducer is essential for:

  • Wallet applications tracking user balances
  • Analytics dashboards monitoring large addresses
  • DeFi applications tracking liquidity positions
  • Trading tools monitoring asset movements

DApp Reducer Models

SundaePriceByToken

Tracks token prices and swaps on SundaeSwap DEX.

On-chain Datum Structure

SundaeSwap uses a pool datum to track liquidity information. In Chrysalis, this is represented by the following C# class:

[CborConverter(typeof(ConstrConverter))]
[CborOptions(Index = 0)]
public record SundaeSwapLiquidityPool(
[CborIndex(0)]
CborBytes Identifier,

[CborIndex(1)]
AssetClassTuple Assets,

[CborIndex(2)]
CborUlong CirculatingLp,

[CborIndex(3)]
CborUlong BidFeesPer10Thousand,

[CborIndex(4)]
CborUlong AskFeesPer10Thousand,

[CborIndex(5)]
Option<MultisigScript> FeeManager,

[CborIndex(6)]
CborUlong MarketOpen,

[CborIndex(7)]
CborUlong ProtocolFees
) : CborBase;

The SundaeSwap reducer detects swaps by analyzing transactions interacting with SundaeSwap pools, calculating the exchange rates based on the input and output values.

Database Model

PropertyTypeDescription
SlotulongBlock slot number (part of composite key)
TxHashstringTransaction hash (part of composite key)
TxIndexulongTransaction index (part of composite key)
TokenXSubjectstringFirst token asset ID (part of composite key)
TokenYSubjectstringSecond token asset ID (part of composite key)
TokenXQuantityulongQuantity of first token
TokenYQuantityulongQuantity of second token
TokenXPerTokenYdecimalExchange rate (TokenX per TokenY)
TokenYPerTokenXdecimalExchange rate (TokenY per TokenX)
TimestampDateTimeOffsetTime of the transaction

Example query:

// Query SundaeSwap price for ADA/HOSKY pair
var tokenPrices = await context.PriceByToken
.Where(p =>
(p.TokenXSubject == "lovelace" && p.TokenYSubject == "a0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235.484f534b59") ||
(p.TokenXSubject == "a0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235.484f534b59" && p.TokenYSubject == "lovelace")
)
.OrderByDescending(p => p.Slot)
.Take(10)
.ToListAsync();

// Access latest price
var latestPrice = tokenPrices.FirstOrDefault();
if (latestPrice.TokenXSubject == "lovelace")
{
Console.WriteLine($"Latest price: {latestPrice.TokenXPerTokenY} ADA per HOSKY");
}
else
{
Console.WriteLine($"Latest price: {latestPrice.TokenYPerTokenX} ADA per HOSKY");
}
SundaeSwap DEX Insights

The SundaePriceByToken reducer:

  • Detects swaps executed on SundaeSwap DEX
  • Calculates precise exchange rates for each trading pair
  • Tracks swap volumes and liquidity movements
  • Supports both ADA and token-to-token pairs
  • Provides timestamps for time-based analysis

This data can power price charts, trading volume analytics, and arbitrage tools specifically for the SundaeSwap ecosystem.

MinswapPriceByToken

Monitors prices on Minswap DEX pools.

On-chain Datum Structure

Minswap uses a different pool model. In Chrysalis, the pool datum is represented by:

[CborConverter(typeof(ConstrConverter))]
[CborOptions(Index = 0)]
public record MinswapLiquidityPool(
[CborIndex(0)]
Inline<Credential> StakeCredential,

[CborIndex(1)]
AssetClass AssetX,

[CborIndex(2)]
AssetClass AssetY,

[CborIndex(3)]
CborUlong TotalLiquidity,

[CborIndex(4)]
CborUlong ReserveA,

[CborIndex(5)]
CborUlong ReserveB,

[CborIndex(6)]
CborUlong BaseFeeAnumerator,

[CborIndex(7)]
CborUlong BaseFeeBNumerator,

[CborIndex(8)]
Option<CborUlong> FeeSharingNumeratorOpt,

[CborIndex(9)]
Bool AllowDynamicFee
) : CborBase;

The Minswap reducer tracks swap events by monitoring transactions that interact with Minswap pool addresses, identifying the tokens being exchanged and calculating the effective exchange rate.

Database Model

PropertyTypeDescription
SlotulongBlock slot number (part of composite key)
TxHashstringTransaction hash (part of composite key)
TxIndexulongTransaction index (part of composite key)
TokenXSubjectstringFirst token asset ID (part of composite key)
TokenYSubjectstringSecond token asset ID (part of composite key)
TokenXQuantityulongQuantity of first token
TokenYQuantityulongQuantity of second token
TokenXPerTokenYdecimalExchange rate (TokenX per TokenY)
TokenYPerTokenXdecimalExchange rate (TokenY per TokenX)
PoolIdstringMinswap pool identifier
TimestampDateTimeOffsetTime of the transaction

Example query:

// Calculate price change over time for a specific pool
var poolPrices = await context.PriceByToken
.Where(p => p.PoolId == "pool1...")
.OrderByDescending(p => p.Slot)
.Take(100)
.ToListAsync();

if (poolPrices.Count >= 2)
{
var newest = poolPrices.First();
var oldest = poolPrices.Last();
var priceChange = newest.TokenXPerTokenY - oldest.TokenXPerTokenY;
var percentChange = (priceChange / oldest.TokenXPerTokenY) * 100;

Console.WriteLine($"Price change over period: {percentChange:F2}%");
}
Minswap vs SundaeSwap Reducers

Note that the Minswap reducer includes an additional PoolId field not present in the SundaeSwap reducer. This reflects the architectural differences between the two DEXs, with Minswap using explicit pool contracts with unique identifiers.

JpgPriceByToken

Tracks NFT sales on JPG Store marketplace.

On-chain Datum Structure

JPG Store uses a listing datum for its NFT marketplace. In Chrysalis, this is defined as:

[CborConverter(typeof(ConstrConverter))]
[CborOptions(Index = 0)]
public record Listing(
[CborIndex(0)]
CborIndefList<ListingPayout> Payouts,

[CborIndex(1)]
CborBytes OwnerPkh
) : CborBase;

// Listing payout structure
[CborConverter(typeof(ConstrConverter))]
[CborOptions(Index = 1)]
public record ListingPayout(
[CborIndex(0)]
Address Address,

[CborIndex(1)]
CborUlong Amount
) : CborBase;

JPG Store uses both datum-based listings and a recognizable transaction pattern for purchases:

The JPG Store reducer analyzes these transaction patterns to extract NFT sale information, focusing on single-asset transfers with payment flows typical of marketplace transactions.

Database Model

PropertyTypeDescription
SlotulongBlock slot number (part of composite key)
TxHashstringTransaction hash (part of composite key)
TxIndexulongTransaction index (part of composite key)
SubjectstringNFT asset ID (part of composite key)
PriceInLovelaceulongPrice paid in lovelace
BuyerAddressstringAddress of the buyer
SellerAddressstringAddress of the seller
TimestampDateTimeOffsetTime of the transaction

Example query:

// Analyze NFT collection sales statistics
var collectionSales = await context.PriceByToken
.Where(p => p.Subject.StartsWith("d894897411707efa755a76deb66d26dfd50593f2e70863e1661e98a0."))
.OrderByDescending(p => p.Timestamp)
.Take(20)
.ToListAsync();

// Calculate floor price
var floorPrice = collectionSales.Min(s => s.PriceInLovelace) / 1_000_000.0;
Console.WriteLine($"Collection floor price: {floorPrice} ₳");

// Calculate average sale price
var avgPrice = collectionSales.Average(s => s.PriceInLovelace) / 1_000_000.0;
Console.WriteLine($"Average sale price: {avgPrice} ₳");
NFT Market Analysis

This reducer is perfect for:

  • Tracking floor prices for collections
  • Analyzing NFT market trends
  • Monitoring specific assets or collections
  • Building rarity-based pricing models
  • Identifying whale buyers and sellers

SplashPriceByToken

Tracks token prices on the Splash DEX.

On-chain Datum Structure

Splash is a DEX on Cardano. In Chrysalis, the pool datum is defined as:

[CborConverter(typeof(ConstrConverter))]
[CborOptions(Index = 0)]
public record SplashLiquidityPool(
[CborIndex(0)]
AssetClass PoolNft,

[CborIndex(1)]
AssetClass AssetX,

[CborIndex(2)]
AssetClass AssetY,

[CborIndex(3)]
AssetClass AssetLq,

[CborIndex(4)]
CborUlong Fee1,

[CborIndex(5)]
CborUlong Fee2,

[CborIndex(6)]
CborUlong Fee3,

[CborIndex(7)]
CborUlong Fee4,

[CborIndex(8)]
CborMaybeIndefList<Inline<Credential>> Verification,

[CborIndex(9)]
CborUlong MarketOpen,

[CborIndex(10)]
CborBytes Last
) : CborBase;

The Splash reducer works by detecting swap events in transactions that interact with Splash protocol addresses, computing the effective exchange rates from the input and output differences.

Database Model

PropertyTypeDescription
SlotulongBlock slot number (part of composite key)
TxHashstringTransaction hash (part of composite key)
TxIndexulongTransaction index (part of composite key)
TokenXSubjectstringFirst token asset ID (part of composite key)
TokenYSubjectstringSecond token asset ID (part of composite key)
TokenXQuantityulongQuantity of first token
TokenYQuantityulongQuantity of second token
TokenXPerTokenYdecimalExchange rate (TokenX per TokenY)
TokenYPerTokenXdecimalExchange rate (TokenY per TokenX)
TimestampDateTimeOffsetTime of the transaction

Example query:

// Find latest price for a specific token pair
var latestPrice = await context.PriceByToken
.Where(p =>
(p.TokenXSubject == "lovelace" && p.TokenYSubject == "targetTokenId") ||
(p.TokenXSubject == "targetTokenId" && p.TokenYSubject == "lovelace")
)
.OrderByDescending(p => p.Slot)
.FirstOrDefaultAsync();

if (latestPrice != null)
{
if (latestPrice.TokenXSubject == "lovelace")
{
Console.WriteLine($"Latest price: {latestPrice.TokenXPerTokenY} ADA per token");
}
else
{
Console.WriteLine($"Latest price: {latestPrice.TokenYPerTokenX} ADA per token");
}
}
Combining DEX Reducers

A powerful approach is to use multiple DEX reducers together to:

  1. Compare prices across different exchanges
  2. Calculate arbitrage opportunities
  3. Create aggregated price feeds
  4. Track liquidity migration between protocols
  5. Build comprehensive trading dashboards

Each reducer provides protocol-specific data, but combining them gives you a complete view of the Cardano DeFi ecosystem.


Argus's built-in reducers provide a powerful foundation for building blockchain applications that need to index and query Cardano data efficiently. By combining general-purpose reducers for blockchain primitives with specialized DApp reducers for protocol-specific data, you can quickly build sophisticated applications without having to implement complex transaction parsing logic. As the Cardano ecosystem continues to evolve, Argus reducers will be updated to support new protocols and data structures, ensuring your applications stay current with the latest developments in the blockchain.