Ir al contenido

Filtrado de Transacciones

Con el lanzamiento de Indexer gRPC v2, introducimos la función de filtrado de transacciones. El filtrado de transacciones te permite procesar selectivamente transacciones de la blockchain de Aptos basándote en criterios específicos. Esto es particularmente útil cuando construyes indexadores o servicios que solo necesitan procesar un subconjunto de todas las transacciones, como:

  • Rastrear interacciones específicas de contratos inteligentes
  • Monitorear actividad de billeteras para ciertas direcciones
  • Indexar eventos de módulos particulares
  • Procesar solo transacciones exitosas

El código fuente se puede encontrar en aptos-core.

Los filtros de transacciones se aplican incluyéndolos en la solicitud gRPC al stream de transacciones.

El sistema de filtrado está definido en aptos/indexer/v1/filter.proto:

message BooleanTransactionFilter {
oneof filter {
APIFilter api_filter = 1;
LogicalAndFilters logical_and = 2;
LogicalOrFilters logical_or = 3;
BooleanTransactionFilter logical_not = 4;
}
}
message APIFilter {
oneof filter {
TransactionRootFilter transaction_root_filter = 1;
UserTransactionFilter user_transaction_filter = 2;
EventFilter event_filter = 3;
}
}

Los filtros se proporcionan como un parámetro opcional en el mensaje GetTransactionsRequest:

message GetTransactionsRequest {
// Requerido; versión inicial del stream actual.
optional uint64 starting_version = 1;
// Opcional; número de transacciones a retornar en el stream actual.
optional uint64 transactions_count = 2;
// Opcional; número de transacciones en cada lote de respuesta.
optional uint64 batch_size = 3;
// Opcional; si se proporciona, solo se incluyen las transacciones que coincidan con el filtro.
optional BooleanTransactionFilter transaction_filter = 4;
}

Ejemplo:

Podemos utilizar el filtro de transacciones para obtener todas las transacciones de usuario desde el endpoint gRPC de Geomi:

Ventana de terminal
grpcurl \
-d '{"transaction_filter":{"api_filter":{"transaction_root_filter":{"transaction_type":"TRANSACTION_TYPE_USER"}}}}' \
-max-msg-sz 30000000 \
-H "authorization:Bearer <api_key>" \
grpc.mainnet.aptoslabs.com:443 \
aptos.indexer.v1.RawData/GetTransactions

Puntos Clave:

  • El campo transaction_filter es opcional - puedes transmitir todas las transacciones omitiéndolo
  • Cuando se proporciona, solo se retornarán las transacciones que coincidan con los criterios del filtro
  • El filtro se aplica del lado del servidor, reduciendo el ancho de banda y la sobrecarga de procesamiento para los clientes
  • Los filtros se validan antes de aplicarse; los filtros inválidos resultarán en una respuesta de error

El sistema de filtros de transacciones usa un enfoque declarativo donde especificas qué quieres coincidir usando filtros, y luego los combinas usando lógica booleana (AND, OR, NOT). Los filtros pueden definirse en:

  • Código Rust usando patrones de construcción
  • JSON para configuración basada en API
  • YAML para archivos de configuración

Hay tres tipos principales de filtros que puedes usar:

Filtra transacciones basándose en propiedades de nivel superior de la transacción.

Campos Disponibles:

  • success (boolean): Si la transacción tuvo éxito o falló
  • txn_type (enum): El tipo de transacción (User, Genesis, BlockMetadata, StateCheckpoint, Validator, BlockEpilogue)

Ejemplo:

{
"type": "TransactionRootFilter",
"txn_type": "User",
"success": true
}

Filtra transacciones enviadas por usuarios basándose en el remitente y detalles de la función de entrada.

Campos Disponibles:

  • sender (string): La dirección de cuenta que envió la transacción
  • payload: Filtrar por la función de entrada que se llama
    • function: Detalles de la función de entrada
      • address (string): Dirección del contrato
      • module (string): Nombre del módulo
      • function (string): Nombre de la función

Ejemplo:

{
"type": "UserTransactionFilter",
"sender": "0x1",
"payload": {
"function": {
"address": "0x1",
"module": "coin",
"function": "transfer"
}
}
}

Filtra transacciones basándose en los eventos que emiten.

Campos Disponibles:

  • struct_type: Filtrar por el tipo de struct Move del evento
    • address (string): Dirección del contrato
    • module (string): Nombre del módulo
    • name (string): Nombre del struct
  • data_substring_filter (string): Filtrar eventos por una subcadena en sus datos

Ejemplo 1 - Filtrar por tipo de struct:

{
"type": "EventFilter",
"struct_type": {
"address": "0x1",
"module": "coin",
"name": "CoinDeposit"
}
}

Ejemplo 2 - Filtrar por subcadena de datos:

{
"type": "EventFilter",
"data_substring_filter": "transfer"
}

Ejemplo 3 - Combinar tipo de struct y subcadena de datos:

{
"type": "EventFilter",
"struct_type": {
"address": "0x1",
"module": "coin"
},
"data_substring_filter": "0xabc123"
}

Los filtros pueden combinarse usando operadores lógicos para crear consultas complejas:

Coincide con transacciones que satisfacen todos los filtros especificados.

{
"and": [
{
"type": "TransactionRootFilter",
"success": true
},
{
"type": "EventFilter",
"struct_type": {
"address": "0x1",
"module": "coin",
"name": "CoinDeposit"
}
}
]
}

Coincide con transacciones que satisfacen cualquiera de los filtros especificados.

{
"or": [
{
"type": "UserTransactionFilter",
"sender": "0xabc..."
},
{
"type": "UserTransactionFilter",
"sender": "0xdef..."
}
]
}

Coincide con transacciones que no satisfacen el filtro especificado.

{
"not": {
"type": "TransactionRootFilter",
"success": false
}
}

Filtrar Transacciones de Transferencia de Monedas

Sección titulada «Filtrar Transacciones de Transferencia de Monedas»

Coincidir con todas las transacciones de transferencia de monedas exitosas:

{
"and": [
{
"type": "TransactionRootFilter",
"success": true
},
{
"type": "UserTransactionFilter",
"payload": {
"function": {
"address": "0x1",
"module": "coin",
"function": "transfer"
}
}
}
]
}

Rastrear todas las transacciones de una billetera específica:

{
"type": "UserTransactionFilter",
"sender": "0x806b27f3d7824a1d78c4291b6d0371aa693437f9eb3393c6440519c0ffaa627f"
}

Rastrear transacciones de múltiples billeteras:

{
"or": [
{
"type": "UserTransactionFilter",
"sender": "0xabc..."
},
{
"type": "UserTransactionFilter",
"sender": "0xdef..."
}
]
}

Rastrear eventos de acuñación de NFT de una colección específica:

{
"type": "EventFilter",
"struct_type": {
"address": "0x4",
"module": "aptos_token",
"name": "MintTokenEvent"
}
}

Filtrar Interacciones con Contratos Inteligentes

Sección titulada «Filtrar Interacciones con Contratos Inteligentes»

Rastrear todas las interacciones con un módulo de contrato inteligente específico:

{
"type": "EventFilter",
"struct_type": {
"address": "0x123abc...",
"module": "my_defi_module"
}
}

Rastrear eventos de intercambio exitosos de múltiples protocolos DEX:

{
"and": [
{
"type": "TransactionRootFilter",
"success": true
},
{
"or": [
{
"type": "EventFilter",
"struct_type": {
"address": "0xdex1",
"module": "swap",
"name": "SwapEvent"
}
},
{
"type": "EventFilter",
"struct_type": {
"address": "0xdex2",
"module": "pool",
"name": "TradeEvent"
}
}
]
}
]
}

Obtener todas las transacciones de usuario excepto las fallidas:

{
"and": [
{
"type": "UserTransactionFilter",
"sender": "0xabc..."
},
{
"type": "TransactionRootFilter",
"success": true
}
]
}

Los filtros también pueden expresarse en formato YAML, que a menudo es más legible para archivos de configuración:

and:
- or:
- type: TransactionRootFilter
success: true
- type: UserTransactionFilter
sender: '0x1'
- type: EventFilter
struct_type:
address: '0x1'
module: coin
name: CoinDeposit

Si estás construyendo con Rust, puedes usar el crate aptos-transaction-filter con patrones de construcción:

use aptos_transaction_filter::{TransactionRootFilterBuilder, BooleanTransactionFilter};
// Crear un filtro para transacciones exitosas
let filter = TransactionRootFilterBuilder::default()
.success(true)
.build()
.unwrap();
let boolean_filter = BooleanTransactionFilter::from(filter);
use aptos_transaction_filter::{EventFilterBuilder, MoveStructTagFilterBuilder};
let filter = EventFilterBuilder::default()
.struct_type(
MoveStructTagFilterBuilder::default()
.address("0x1")
.module("coin")
.name("CoinDeposit")
.build()
.unwrap()
)
.build()
.unwrap();
use aptos_transaction_filter::{UserTransactionFilterBuilder, EntryFunctionFilterBuilder, UserTransactionPayloadFilterBuilder};
let filter = UserTransactionFilterBuilder::default()
.sender("0x1")
.payload(
UserTransactionPayloadFilterBuilder::default()
.function(
EntryFunctionFilterBuilder::default()
.address("0x1")
.module("coin")
.function("transfer")
.build()
.unwrap()
)
.build()
.unwrap()
)
.build()
.unwrap();
use aptos_transaction_filter::BooleanTransactionFilter;
// Crear filtros individuales
let success_filter = TransactionRootFilterBuilder::default()
.success(true)
.build()
.unwrap();
let sender_filter = UserTransactionFilterBuilder::default()
.sender("0x1")
.build()
.unwrap();
let event_filter = EventFilterBuilder::default()
.struct_type(
MoveStructTagFilterBuilder::default()
.address("0x1")
.module("coin")
.build()
.unwrap()
)
.build()
.unwrap();
// Combinar con operadores lógicos
let combined = BooleanTransactionFilter::from(success_filter)
.or(sender_filter)
.and(event_filter);
// Usar el filtro
if combined.matches(&transaction) {
// Procesar transacción
}
// Serializar a JSON
let json = serde_json::to_string_pretty(&filter).unwrap();
// Serializar a YAML
let yaml = serde_yaml::to_string(&filter).unwrap();
// Deserializar desde JSON
let filter: BooleanTransactionFilter = serde_json::from_str(&json).unwrap();

El sistema de filtros de transacciones está optimizado para procesamiento de alto rendimiento:

  1. Pasada Única: Los filtros procesan cada transacción solo una vez
  2. Mínimas Asignaciones: Los filtros evitan clones y copias innecesarias
  3. Salida Temprana: Los filtros hacen cortocircuito tan pronto como se encuentra una no-coincidencia
  4. Caché de Direcciones: La estandarización de direcciones se almacena en caché para mejorar el rendimiento

Para mejor rendimiento:

  • Usa filtros específicos cuando sea posible (ej. filtrar por dirección en lugar de todas las transacciones)
  • Coloca filtros más restrictivos primero en operaciones AND
  • Considera el volumen de transacciones en mainnet al diseñar filtros