diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..ebfe7db
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,7 @@
+
+
+ 10
+ enable
+ disable
+
+
diff --git a/SapientFi.Cosmos.sln.DotSettings b/SapientFi.Cosmos.sln.DotSettings
index babbf5c..cd92545 100644
--- a/SapientFi.Cosmos.sln.DotSettings
+++ b/SapientFi.Cosmos.sln.DotSettings
@@ -2,6 +2,16 @@
NEVER
NEVER
NEVER
+ True
+ True
+ True
+ True
+ True
+ True
True
True
+ True
+ True
+ True
+ True
True
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index f92bc25..9834a0a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,7 +2,7 @@ version: '3.9'
services:
db:
- image: timescale/timescaledb:2.5.0-pg12
+ image: timescale/timescaledb:2.10.0-pg15
command: ["postgres", "-c", "log_statement=all", "-c", "max_locks_per_transaction=4096"]
shm_size: 1g
ports:
@@ -16,7 +16,7 @@ services:
- .docker/postgres/data:/var/lib/postgresql/data
rabbitmq:
- image: masstransit/rabbitmq:3.9
+ image: rabbitmq:3.11-management
environment:
# default user + pass is "guest"
RABBITMQ_ERLANG_COOKIE: ${RABBITMQ_ERLANG_COOKIE}
@@ -32,7 +32,7 @@ services:
- "35433:6379"
logging:
- image: datalust/seq:5.1
+ image: datalust/seq:latest
ports:
- "35435:80"
environment:
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 0000000..c086269
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,19 @@
+# Configuration
+
+Keys marked with :exclamation: should be set when deploying.
+
+## General
+
+| Key | Type | Default | Example | Description |
+|----------------------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|-------------|
+| **General** | | | | |
+| `SAPIENTFI_SERVICE_ROLES_ENABLED` | comma-separated-list | `API, BACKGROUND_WORKER` | `Foo, Bar, Baz` | |
+| `SAPIENTFI_DB_CONNECTION_STRING` | string | `User ID=sapientfi_indexer_user;Password=sapientfi_indexer_user_pass;Host=localhost;Port=35432;Database=sapientfi_indexer;Pooling=true;Minimum Pool Size=10;Maximum Pool Size=100;` | | |
+| `SAPIENTFI_DB_RUN_MIGRATIONS_ON_BOOT` | bool | `true` | | |
+| `SAPIENTFI_NR_ELEMENTS_DEPOSITS_PR_WALLET` | int | `11` | | |
+| `SAPIENTFI_API_ALLOWED_CORS_ORIGINS` | string | `http://localhost:3000` | | |
+| `SAPIENTFI_TRIGGER_GATEWAY_POOL_FULL_RESYNC` | bool | `false` | | |
+| `SAPIENTFI_TRIGGER_MINE_STAKING_FULL_RESYNC` | bool | `false` | | |
+| `SAPIENTFI_TRIGGER_MINE_BUYBACK_FULL_RESYNC` | bool | `false` | | |
+| `SAPIENTFI_MESSAGE_TRANSPORT_URI` | string | `amqp://guest:guest@localhost:5672` | | |
+| `Kujira_TransactionListener_DoEnable` | bool | `true` | | |
\ No newline at end of file
diff --git a/src/Infrastructure/Cosmos/BusMessages/IRawCosmosTransactionAvailableAnnouncement.cs b/src/Infrastructure/Cosmos/BusMessages/IRawCosmosTransactionAvailableAnnouncement.cs
new file mode 100644
index 0000000..fa3ab40
--- /dev/null
+++ b/src/Infrastructure/Cosmos/BusMessages/IRawCosmosTransactionAvailableAnnouncement.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace SapientFi.Infrastructure.Cosmos.BusMessages;
+
+public interface IRawCosmosTransactionAvailableAnnouncement
+{
+ public string TransactionHash { get; init; }
+ ///
+ /// Primary key id in the underlying database
+ ///
+ public long RawEntityId { get; init; }
+
+ ///
+ /// Timestamp in the entity for it's creation date.
+ ///
+ public DateTimeOffset CreatedAt { get; init; }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Cosmos/CosmosFactory.cs b/src/Infrastructure/Cosmos/CosmosFactory.cs
new file mode 100644
index 0000000..1148cc9
--- /dev/null
+++ b/src/Infrastructure/Cosmos/CosmosFactory.cs
@@ -0,0 +1,31 @@
+using System.Text.Json;
+using SapientFi.Infrastructure.Cosmos.Storage;
+using SapientFi.Kernel.IdGeneration;
+using TerraDotnet;
+using TerraDotnet.TerraLcd.Messages;
+
+namespace SapientFi.Infrastructure.Cosmos;
+
+public class CosmosFactory
+ : ICosmosFactory
+ where TRawTransactionEntity : ICosmosRawTransactionEntity, new()
+{
+ protected readonly IdProvider IdProvider;
+
+ public CosmosFactory(IdProvider idProvider)
+ {
+ IdProvider = idProvider;
+ }
+
+ public virtual TRawTransactionEntity NewRawEntity(LcdTxResponse lcdTransaction)
+ {
+ return new()
+ {
+ Id = IdProvider.Snowflake(),
+ Height = lcdTransaction.HeightAsInt,
+ CreatedAt = lcdTransaction.CreatedAt,
+ TxHash = lcdTransaction.TransactionHash,
+ RawTx = JsonSerializer.Serialize(lcdTransaction, TerraJsonSerializerOptions.GetThem())
+ };
+ }
+}
diff --git a/src/Infrastructure/Cosmos/CosmosMarker.cs b/src/Infrastructure/Cosmos/CosmosMarker.cs
new file mode 100644
index 0000000..6f4da62
--- /dev/null
+++ b/src/Infrastructure/Cosmos/CosmosMarker.cs
@@ -0,0 +1,3 @@
+namespace SapientFi.Infrastructure.Cosmos;
+
+public abstract record CosmosMarker;
diff --git a/src/Infrastructure/Cosmos/ICosmosFactory.cs b/src/Infrastructure/Cosmos/ICosmosFactory.cs
new file mode 100644
index 0000000..3566f45
--- /dev/null
+++ b/src/Infrastructure/Cosmos/ICosmosFactory.cs
@@ -0,0 +1,10 @@
+using SapientFi.Infrastructure.Cosmos.Storage;
+using TerraDotnet.TerraLcd.Messages;
+
+namespace SapientFi.Infrastructure.Cosmos;
+
+public interface ICosmosFactory
+ where TRawTransactionEntity : ICosmosRawTransactionEntity, new()
+{
+ public TRawTransactionEntity NewRawEntity(LcdTxResponse lcdTransaction);
+}
diff --git a/src/Infrastructure/Cosmos/Indexers/Delegations/CosmosDelegationIndexer.cs b/src/Infrastructure/Cosmos/Indexers/Delegations/CosmosDelegationIndexer.cs
new file mode 100644
index 0000000..36b0034
--- /dev/null
+++ b/src/Infrastructure/Cosmos/Indexers/Delegations/CosmosDelegationIndexer.cs
@@ -0,0 +1,205 @@
+using System;
+using System.Text.Json;
+using System.Threading.Tasks;
+using MassTransit;
+using Microsoft.Extensions.Logging;
+using SapientFi.Infrastructure.Cosmos.BusMessages;
+using SapientFi.Infrastructure.Cosmos.Indexers.Delegations.Storage;
+using SapientFi.Infrastructure.Cosmos.Storage;
+using SapientFi.Infrastructure.Indexing;
+using SapientFi.Kernel.IdGeneration;
+using TerraDotnet;
+using TerraDotnet.TerraFcd.Messages;
+using TerraDotnet.TerraLcd.Messages;
+
+namespace SapientFi.Infrastructure.Cosmos.Indexers.Delegations;
+
+public abstract class
+ CosmosDelegationIndexer<
+ TRawTransactionAvailableAnnouncement,
+ TValidatorDelegationLedgerEntity,
+ TRawTransactionEntity>
+ : IIndexer
+ where TRawTransactionAvailableAnnouncement : class, IRawCosmosTransactionAvailableAnnouncement
+ where TValidatorDelegationLedgerEntity : ICosmosValidatorDelegationLedgerEntity, new()
+ where TRawTransactionEntity : ICosmosRawTransactionEntity, new()
+{
+ protected readonly
+ ILogger> Logger;
+ protected readonly CosmosDelegationsRepository Repository;
+ protected readonly CosmosRawRepository RawRepository;
+ protected readonly IdProvider IdProvider;
+
+ protected CosmosDelegationIndexer(
+ ILogger> logger,
+ CosmosDelegationsRepository repository,
+ CosmosRawRepository rawRepository,
+ IdProvider idProvider
+ )
+ {
+ Logger = logger;
+ Repository = repository;
+ RawRepository = rawRepository;
+ IdProvider = idProvider;
+ }
+
+ public abstract string NameOfBlockChain { get; }
+ public abstract string Id { get; }
+ public abstract string DisplayName { get; }
+
+ public virtual async Task Consume(ConsumeContext context)
+ {
+ await DefaultConsume(context);
+ }
+
+ protected async Task DefaultConsume(ConsumeContext context)
+ {
+ var rawTransaction = await RawRepository.GetByIdAndCreateOrDefaultAsync(
+ context.Message.RawEntityId,
+ context.Message.CreatedAt,
+ context.CancellationToken
+ );
+ if (rawTransaction is null)
+ {
+ // TODO write to a custom metric in order to be able to monitor missing raw transactions
+ Logger.LogWarning(
+ "({BlockChain}) Unable to find raw transaction with id={RawEntityId}",
+ NameOfBlockChain,
+ context.Message.RawEntityId
+ );
+ // throw for retry!
+ throw new Exception($"Unable to find raw transaction with id={context.Message.RawEntityId}")
+ }
+
+ var txHash = rawTransaction.TxHash;
+
+ // TODO: deal with re-entrance/re-processing i.e. make sure we do not make duplicate entries if we process the same TX multiple times
+
+ var tx = JsonSerializer.Deserialize(rawTransaction.RawTx);
+ if (tx == default)
+ {
+ Logger.LogWarning(
+ "({BlockChain}) Could not deserialize transaction with hash={TxHash}",
+ NameOfBlockChain,
+ txHash
+ );
+ }
+
+ foreach (var txMessage in tx!.Transaction.Body.Messages)
+ {
+ var parseSuccessful = TerraMessageParser.TryParse(txMessage, out IMsg? message);
+ if (parseSuccessful)
+ {
+ switch (message)
+ {
+ case CosmosUndelegateMessage undelegateMsg:
+ {
+ if (undelegateMsg.Amount == default)
+ {
+ Logger.LogWarning(
+ "({BlockChain}) Missing amount on undelegate message (txHash={TxHash})",
+ NameOfBlockChain,
+ txHash
+ );
+ break;
+ }
+
+ var undelegateEntity = new TValidatorDelegationLedgerEntity
+ {
+ Id = IdProvider.Snowflake(),
+ TxHash = txHash,
+ At = rawTransaction.CreatedAt,
+ DelegatorAddress = undelegateMsg.DelegatorAddress,
+ ValidatorAddress = undelegateMsg.ValidatorAddress,
+ Amount = -long.Parse(undelegateMsg.Amount.Amount), // subtracts amount
+ Denominator = undelegateMsg.Amount.Denominator
+ };
+
+ await Repository.SaveAsync(undelegateEntity, context.CancellationToken);
+ break;
+ }
+
+ case CosmosDelegateMessage delegateMsg:
+ {
+ if (delegateMsg.Amount == default)
+ {
+ Logger.LogWarning(
+ "({BlockChain}) Missing amount on delegate message (txHash={TxHash})",
+ NameOfBlockChain,
+ txHash
+ );
+ break;
+ }
+
+ var delegateEntity = new TValidatorDelegationLedgerEntity
+ {
+ Id = IdProvider.Snowflake(),
+ TxHash = txHash,
+ At = rawTransaction.CreatedAt,
+ DelegatorAddress = delegateMsg.DelegatorAddress,
+ ValidatorAddress = delegateMsg.ValidatorAddress,
+ Amount = long.Parse(delegateMsg.Amount.Amount), // adds amount
+ Denominator = delegateMsg.Amount.Denominator
+ };
+
+ await Repository.SaveAsync(delegateEntity, context.CancellationToken);
+ break;
+ }
+
+ case CosmosRedelegateMessage redelegateMsg:
+ {
+ if (redelegateMsg.Amount == default)
+ {
+ Logger.LogWarning(
+ "({BlockChain}) Missing amount on redelegate message (txHash={TxHash})",
+ NameOfBlockChain,
+ txHash
+ );
+ break;
+ }
+
+ var entities = new[]
+ {
+ // first a ledger entry that removes the delegation
+ // from the original validator
+ new TValidatorDelegationLedgerEntity
+ {
+ Id = IdProvider.Snowflake(),
+ TxHash = txHash,
+ At = rawTransaction.CreatedAt,
+ DelegatorAddress = redelegateMsg.DelegatorAddress,
+ ValidatorAddress = redelegateMsg.ValidatorSourceAddress,
+ Amount = -long.Parse(redelegateMsg.Amount.Amount), // subtracts amount
+ Denominator = redelegateMsg.Amount.Denominator
+ },
+ // then a ledger entry adding the delegation to the new validator
+ new TValidatorDelegationLedgerEntity
+ {
+ Id = IdProvider.Snowflake(),
+ TxHash = txHash,
+ At = rawTransaction.CreatedAt,
+ DelegatorAddress = redelegateMsg.DelegatorAddress,
+ ValidatorAddress = redelegateMsg.ValidatorDestinationAddress,
+ Amount = long.Parse(redelegateMsg.Amount.Amount), // adds amount
+ Denominator = redelegateMsg.Amount.Denominator
+ }
+ };
+
+ await Repository.SaveAllAsync(entities, context.CancellationToken);
+ break;
+ }
+
+ default:
+ // we do not care about this message, so leave it be :)
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Infrastructure/Cosmos/Indexers/Delegations/Storage/CosmosDelegationsRepository.cs b/src/Infrastructure/Cosmos/Indexers/Delegations/Storage/CosmosDelegationsRepository.cs
new file mode 100644
index 0000000..ab65575
--- /dev/null
+++ b/src/Infrastructure/Cosmos/Indexers/Delegations/Storage/CosmosDelegationsRepository.cs
@@ -0,0 +1,76 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Data;
+using System.Threading;
+using System.Threading.Tasks;
+using ServiceStack.Data;
+using ServiceStack.OrmLite;
+
+namespace SapientFi.Infrastructure.Cosmos.Indexers.Delegations.Storage;
+
+public abstract class CosmosDelegationsRepository
+ where TValidatorDelegationLedgerEntity : ICosmosValidatorDelegationLedgerEntity
+{
+ protected readonly IDbConnectionFactory DbFactory;
+
+ protected CosmosDelegationsRepository(IDbConnectionFactory dbFactory)
+ {
+ DbFactory = dbFactory;
+ }
+
+ public async Task SaveAsync(
+ TValidatorDelegationLedgerEntity entity,
+ CancellationToken cancellationToken = default
+ )
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(cancellationToken);
+ await db.SaveAsync(entity, token: cancellationToken);
+ }
+
+ public async Task SaveAllAsync(
+ IEnumerable entities,
+ CancellationToken cancellationToken = default
+ )
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(cancellationToken);
+ await db.SaveAllAsync(entities, token: cancellationToken);
+ }
+
+ public async Task SingleAsync(SqlExpression sql, CancellationToken ct = default)
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(ct);
+ return await db.SingleAsync(sql, ct);
+ }
+
+ public async Task> SelectAsync(
+ SqlExpression sql,
+ CancellationToken ct = default
+ )
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(ct);
+ return await db.SelectAsync(sql, ct);
+ }
+
+ public async Task SingleByIdAsync(T2 id, CancellationToken ct = default)
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(ct);
+ return await db.SingleByIdAsync(id, ct);
+ }
+
+ public async Task> SelectByIds(IEnumerable ids, CancellationToken ct = default)
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(ct);
+ return await db.SelectByIdsAsync(ids, ct);
+ }
+
+ public async Task ScalarAsync(SqlExpression sql, CancellationToken ct = default)
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(ct);
+ return await db.ScalarAsync(sql, ct);
+ }
+
+ public async Task GetDbConnectionAsync(CancellationToken cancellationToken = default)
+ {
+ return await DbFactory.OpenDbConnectionAsync(cancellationToken);
+ }
+}
diff --git a/src/Infrastructure/Cosmos/Indexers/Delegations/Storage/ICosmosValidatorDelegationLedgerEntity.cs b/src/Infrastructure/Cosmos/Indexers/Delegations/Storage/ICosmosValidatorDelegationLedgerEntity.cs
new file mode 100644
index 0000000..eed7f36
--- /dev/null
+++ b/src/Infrastructure/Cosmos/Indexers/Delegations/Storage/ICosmosValidatorDelegationLedgerEntity.cs
@@ -0,0 +1,38 @@
+using System;
+using ServiceStack.DataAnnotations;
+
+namespace SapientFi.Infrastructure.Cosmos.Indexers.Delegations.Storage;
+
+///
+/// Represents an entry in a "ledger" of delegated
+/// governance tokens for a specific validator.
+///
+/// This means that when tokens are re-delegated
+/// i.e. moved from validator A to validator B,
+/// we have 2 entries for the same action (but with
+/// inverted signs), as the tokens are leaving A
+/// and going into B.
+///
+public interface ICosmosValidatorDelegationLedgerEntity
+{
+ public long Id { get; init; }
+
+ public DateTimeOffset At { get; init; }
+
+ ///
+ /// The TxHash of the transaction that this delegation came from
+ ///
+ public string TxHash { get; init; }
+
+ public string ValidatorAddress { get; init; }
+
+ public string DelegatorAddress { get; init; }
+
+ ///
+ /// Positive = delegation to validator
+ /// Negative = delegation away from validator
+ ///
+ public long Amount { get; init; }
+
+ public string Denominator { get; init; }
+}
diff --git a/src/Infrastructure/Cosmos/Storage/CosmosRawRepository.cs b/src/Infrastructure/Cosmos/Storage/CosmosRawRepository.cs
new file mode 100644
index 0000000..b61a0f3
--- /dev/null
+++ b/src/Infrastructure/Cosmos/Storage/CosmosRawRepository.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Threading;
+using System.Threading.Tasks;
+using ServiceStack;
+using ServiceStack.Data;
+using ServiceStack.OrmLite;
+
+namespace SapientFi.Infrastructure.Cosmos.Storage;
+
+public abstract class CosmosRawRepository
+ where TRawTransactionEntity : ICosmosRawTransactionEntity
+{
+ protected readonly IDbConnectionFactory DbFactory;
+
+ protected CosmosRawRepository(IDbConnectionFactory dbFactory)
+ {
+ DbFactory = dbFactory;
+ }
+
+ public async Task SaveRawTransactionAsync(
+ TRawTransactionEntity entity,
+ CancellationToken cancellationToken = default
+ )
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(cancellationToken);
+ await db.SaveAsync(entity, token: cancellationToken);
+ }
+
+ public async Task GetLatestSeenBlockHeightAsync(CancellationToken cancellationToken = default)
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(cancellationToken);
+ return await db.ScalarAsync(
+ x => Sql.Max(x.Height),
+ _ => true,
+ cancellationToken
+ );
+ }
+
+ public async Task GetByIdOrDefaultAsync(
+ long id,
+ CancellationToken cancellationToken = default
+ )
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(cancellationToken);
+ return await db.SingleByIdAsync(id, cancellationToken);
+ }
+
+ public async Task GetByIdAndCreateOrDefaultAsync(
+ long id,
+ DateTimeOffset createdAt,
+ CancellationToken cancellationToken = default
+ )
+ {
+ using IDbConnection? db = await DbFactory.OpenDbConnectionAsync(cancellationToken);
+ var results =
+ await db.SelectAsync(
+ q => q.Id == id
+ && q.CreatedAt >= createdAt,
+ token: cancellationToken);
+
+ return results.IsEmpty() ? default : results[0];
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Cosmos/Storage/ICosmosRawTransactionEntity.cs b/src/Infrastructure/Cosmos/Storage/ICosmosRawTransactionEntity.cs
new file mode 100644
index 0000000..7130262
--- /dev/null
+++ b/src/Infrastructure/Cosmos/Storage/ICosmosRawTransactionEntity.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace SapientFi.Infrastructure.Cosmos.Storage;
+
+public interface ICosmosRawTransactionEntity
+{
+ public long Id { get; init; }
+
+ public DateTimeOffset CreatedAt { get; init; }
+
+ public string TxHash { get; init; }
+
+ public string RawTx { get; init; }
+
+ public int Height { get; init; }
+}
diff --git a/src/Infrastructure/Cosmos/TransactionListener/CosmosTransactionListenerConfig.cs b/src/Infrastructure/Cosmos/TransactionListener/CosmosTransactionListenerConfig.cs
new file mode 100644
index 0000000..7aa0abf
--- /dev/null
+++ b/src/Infrastructure/Cosmos/TransactionListener/CosmosTransactionListenerConfig.cs
@@ -0,0 +1,17 @@
+using Microsoft.Extensions.Configuration;
+
+namespace SapientFi.Infrastructure.Cosmos.TransactionListener;
+
+public abstract class CosmosTransactionListenerConfig
+{
+ protected readonly IConfiguration Raw;
+
+ protected CosmosTransactionListenerConfig(IConfiguration raw)
+ {
+ Raw = raw;
+ }
+
+ public abstract bool DoEnable();
+
+ public abstract string LcdUri();
+}
diff --git a/src/Infrastructure/Cosmos/TransactionListener/CosmosTransactionListenerHostedService.cs b/src/Infrastructure/Cosmos/TransactionListener/CosmosTransactionListenerHostedService.cs
new file mode 100644
index 0000000..3d8dc30
--- /dev/null
+++ b/src/Infrastructure/Cosmos/TransactionListener/CosmosTransactionListenerHostedService.cs
@@ -0,0 +1,104 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MassTransit;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Npgsql;
+using SapientFi.Infrastructure.Cosmos.BusMessages;
+using SapientFi.Infrastructure.Cosmos.Storage;
+using TerraDotnet;
+using TerraDotnet.TerraLcd.Messages;
+// ReSharper disable MemberCanBePrivate.Global
+
+namespace SapientFi.Infrastructure.Cosmos.TransactionListener;
+
+public abstract class
+ CosmosTransactionListenerHostedService
+ : IHostedService
+ where TMarker : CosmosMarker
+ where TRawTransactionEntity : ICosmosRawTransactionEntity, new()
+ where TRawTransactionAvailableAnnouncement : IRawCosmosTransactionAvailableAnnouncement, new()
+{
+ protected readonly
+ ILogger> Logger;
+ protected readonly CosmosTransactionEnumerator TransactionEnumerator;
+ protected readonly CosmosRawRepository RawRepository;
+ protected readonly CosmosFactory Factory;
+ protected readonly IBus MassTransitBus;
+
+ protected CancellationTokenSource? TokenSource;
+ protected abstract string NameOfBlockChain { get; }
+
+
+ protected CosmosTransactionListenerHostedService(
+ ILogger> logger,
+ CosmosTransactionEnumerator transactionEnumerator,
+ CosmosRawRepository rawRepository,
+ CosmosFactory factory,
+ IBus massTransitBus
+ )
+ {
+ Logger = logger;
+ TransactionEnumerator = transactionEnumerator;
+ RawRepository = rawRepository;
+ Factory = factory;
+ MassTransitBus = massTransitBus;
+ }
+
+ public virtual async Task StartAsync(CancellationToken cancellationToken)
+ {
+ Logger.LogInformation("Starting {BlockChain} transaction listener", NameOfBlockChain);
+ TokenSource = new CancellationTokenSource();
+
+ int latestSeenBlock = await RawRepository.GetLatestSeenBlockHeightAsync(cancellationToken);
+ IAsyncEnumerable enumeration =
+ TransactionEnumerator.EnumerateTransactionsAsync(latestSeenBlock, cancellationToken);
+
+ var thread = new Thread(async () =>
+ {
+ await foreach (LcdTxResponse lcdTransaction in enumeration.WithCancellation(cancellationToken))
+ {
+ Logger.LogDebug("Got new raw transaction {TxHash} with height={TxHeight}",
+ lcdTransaction.TransactionHash,
+ lcdTransaction.HeightAsInt
+ );
+ TRawTransactionEntity entity = Factory.NewRawEntity(lcdTransaction);
+
+ try
+ {
+ await RawRepository.SaveRawTransactionAsync(entity, cancellationToken);
+
+ await MassTransitBus.Publish(
+ new TRawTransactionAvailableAnnouncement
+ {
+ TransactionHash = entity.TxHash,
+ RawEntityId = entity.Id,
+ CreatedAt = entity.CreatedAt,
+ }, cancellationToken);
+ }
+ catch (PostgresException e) when (e.SqlState == PostgresErrorCodes.UniqueViolation)
+ {
+ // we already have that transaction, so do nothing else
+ }
+ }
+ }
+ );
+
+ Logger.LogInformation("Starting worker thread for {BlockChain}'s transaction listener", NameOfBlockChain);
+ thread.Start();
+ }
+
+ public virtual Task StopAsync(CancellationToken cancellationToken)
+ {
+ Logger.LogInformation("Stopping {BlockChain} transactions listener", NameOfBlockChain);
+ TokenSource?.Cancel();
+ return Task.CompletedTask;
+ }
+}
diff --git a/src/Infrastructure/DAL/Migrations/M_20220624_1049_from_scratch.cs b/src/Infrastructure/DAL/Migrations/M_20220624_1049_from_scratch.cs
new file mode 100644
index 0000000..7f7f89b
--- /dev/null
+++ b/src/Infrastructure/DAL/Migrations/M_20220624_1049_from_scratch.cs
@@ -0,0 +1,91 @@
+using RapidCore.Migration;
+using RapidCore.PostgreSql.Migration;
+using ServiceStack.OrmLite.Dapper;
+
+namespace SapientFi.Infrastructure.Migrations;
+
+public class M_20220624_1049_from_scratch: MigrationBase
+{
+ protected override void ConfigureUpgrade(IMigrationBuilder builder)
+ {
+ var ctx = ContextAs();
+ var connection = ctx.ConnectionProvider.Default();
+
+ /* *********************************************************************************
+ */
+ builder.Step("enable timescaledb", () =>
+ {
+ connection.Execute("CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;");
+ });
+
+
+ /* *********************************************************************************
+ */
+ builder.Step("create raw terra2 transaction table", () =>
+ {
+ connection.Execute(@"
+create table terra2_raw_transaction_entity
+(
+ id bigint not null,
+ raw_tx jsonb,
+ created_at timestamp with time zone not null,
+ tx_hash text,
+ height integer not null,
+
+ constraint terra2_raw_transaction_entity_pkey
+ primary key (id, created_at),
+
+ constraint terra2_raw_transaction_entity_uniqueness
+ unique (tx_hash, created_at)
+);
+
+create index idx_terra2rawtransactionentity_height
+ on terra2_raw_transaction_entity (height);
+
+SELECT create_hypertable('terra2_raw_transaction_entity', 'created_at', chunk_time_interval => INTERVAL '1 day');
+");
+ });
+
+ // TODO Why is amount using 38 digits numeric precision? It seems rather excessive, none of the
+ // built-in types are even that big. Ain't uluna represented as a uint64? That's only 20 digits.
+ // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types
+ // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types
+
+ /* *********************************************************************************
+ */
+ builder.Step("create terra2 delegations table", () =>
+ {
+ connection.Execute(@"
+create table terra2_validator_delegation_ledger_entity
+(
+ id bigint not null,
+ at timestamp with time zone not null,
+ tx_hash text,
+ validator_address text,
+ delegator_address text,
+ amount numeric(38, 6) not null,
+ denominator varchar,
+
+ constraint terra2_validator_delegation_ledger_entity_pkey
+ primary key (id, at)
+);
+
+create index idx_terra2validatordelegationledgerentity_txhash
+ on terra2_validator_delegation_ledger_entity (tx_hash);
+
+create index idx_terra2validatordelegationledgerentity_valaddr
+ on terra2_validator_delegation_ledger_entity (validator_address);
+
+create index idx_terra2validatordelegationledgerentity_deladdr
+ on terra2_validator_delegation_ledger_entity (delegator_address);
+
+SELECT create_hypertable('terra2_validator_delegation_ledger_entity', 'at', chunk_time_interval => INTERVAL '1 day');
+");
+ });
+ }
+
+ protected override void ConfigureDowngrade(IMigrationBuilder builder)
+ {
+ throw new System.NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/DAL/Migrations/M_20221006_1242_add_kujira.cs b/src/Infrastructure/DAL/Migrations/M_20221006_1242_add_kujira.cs
new file mode 100644
index 0000000..12efdd2
--- /dev/null
+++ b/src/Infrastructure/DAL/Migrations/M_20221006_1242_add_kujira.cs
@@ -0,0 +1,83 @@
+using RapidCore.Migration;
+using RapidCore.PostgreSql.Migration;
+using ServiceStack.OrmLite.Dapper;
+
+namespace SapientFi.Infrastructure.Migrations;
+
+public class M_20221006_1242_add_kujira: MigrationBase
+{
+ protected override void ConfigureUpgrade(IMigrationBuilder builder)
+ {
+ var ctx = ContextAs();
+ var connection = ctx.ConnectionProvider.Default();
+
+ /* *********************************************************************************
+ */
+ builder.Step("create raw kujira transaction table", () =>
+ {
+ connection.Execute(@"
+create table kujira_raw_transaction_entity
+(
+ id bigint not null,
+ raw_tx jsonb,
+ created_at timestamp with time zone not null,
+ tx_hash text,
+ height integer not null,
+
+ constraint kujira_raw_transaction_entity_pkey
+ primary key (id, created_at),
+
+ constraint kujira_raw_transaction_entity_uniqueness
+ unique (tx_hash, created_at)
+);
+
+create index idx_kujirarawtransactionentity_height
+ on kujira_raw_transaction_entity (height);
+
+SELECT create_hypertable('kujira_raw_transaction_entity', 'created_at', chunk_time_interval => INTERVAL '7 days');
+");
+ });
+
+ // TODO Why is amount using 38 digits numeric precision? It seems rather excessive, none of the
+ // built-in types are even that big. Ain't uluna represented as a uint64? That's only 20 digits.
+ // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types
+ // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types
+
+ /* *********************************************************************************
+ */
+ builder.Step("create kujira delegations table", () =>
+ {
+ connection.Execute(@"
+create table kujira_validator_delegation_ledger_entity
+(
+ id bigint not null,
+ at timestamp with time zone not null,
+ tx_hash text,
+ validator_address text,
+ delegator_address text,
+ amount numeric(38, 6) not null,
+ denominator varchar,
+
+ constraint kujira_validator_delegation_ledger_entity_pkey
+ primary key (id, at)
+);
+
+create index idx_kujiravalidatordelegationledgerentity_txhash
+ on kujira_validator_delegation_ledger_entity (tx_hash);
+
+create index idx_kujiravalidatordelegationledgerentity_valaddr
+ on kujira_validator_delegation_ledger_entity (validator_address);
+
+create index idx_kujiravalidatordelegationledgerentity_deladdr
+ on kujira_validator_delegation_ledger_entity (delegator_address);
+
+SELECT create_hypertable('kujira_validator_delegation_ledger_entity', 'at', chunk_time_interval => INTERVAL '7 days');
+");
+ });
+ }
+
+ protected override void ConfigureDowngrade(IMigrationBuilder builder)
+ {
+ throw new System.NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/DAL/Migrations/M_202304270909_index_id.cs b/src/Infrastructure/DAL/Migrations/M_202304270909_index_id.cs
new file mode 100644
index 0000000..73a49c9
--- /dev/null
+++ b/src/Infrastructure/DAL/Migrations/M_202304270909_index_id.cs
@@ -0,0 +1,44 @@
+using RapidCore.Migration;
+using RapidCore.PostgreSql.Migration;
+using ServiceStack.OrmLite.Dapper;
+
+namespace SapientFi.Infrastructure.Migrations;
+
+public class M_202304270909_index_id: MigrationBase
+{
+ protected override void ConfigureUpgrade(IMigrationBuilder builder)
+ {
+ var ctx = ContextAs();
+ var connection = ctx.ConnectionProvider.Default();
+
+ /* *********************************************************************************
+ */
+ builder.Step("create index on id in terra raw", () =>
+ {
+ connection.Execute(@"
+create index terra2_raw_transaction_entity_id_index
+ on public.terra2_raw_transaction_entity (id);
+");
+ });
+
+ // TODO Why is amount using 38 digits numeric precision? It seems rather excessive, none of the
+ // built-in types are even that big. Ain't uluna represented as a uint64? That's only 20 digits.
+ // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types
+ // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types
+
+ /* *********************************************************************************
+ */
+ builder.Step("create index on id on kujira raw", () =>
+ {
+ connection.Execute(@"
+create index kujira_raw_transaction_entity_id_index
+ on public.kujira_raw_transaction_entity (id);
+");
+ });
+ }
+
+ protected override void ConfigureDowngrade(IMigrationBuilder builder)
+ {
+ throw new System.NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/DAL/Queriers/Terra2StakingQuerier.cs b/src/Infrastructure/DAL/Queriers/Terra2StakingQuerier.cs
new file mode 100644
index 0000000..9d468c2
--- /dev/null
+++ b/src/Infrastructure/DAL/Queriers/Terra2StakingQuerier.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using SapientFi.Infrastructure.Terra2.Indexers.Delegations.Storage;
+using ServiceStack.OrmLite;
+
+namespace SapientFi.Infrastructure.DAL.Queriers;
+
+public class Terra2StakingQuerier
+{
+ private readonly Terra2DelegationsRepository _repository;
+
+ public Terra2StakingQuerier(Terra2DelegationsRepository repository)
+ {
+ _repository = repository;
+ }
+
+ public async Task> TotalStakingCurrentlyAtSpecificValidatorsAsync(
+ IEnumerable validatorAddresses,
+ CancellationToken cancellationToken = default)
+ {
+ using var db = await _repository.GetDbConnectionAsync(cancellationToken);
+ var result = await db.SqlListAsync<(string, long)>(db.From()
+ .Select(delegation =>
+ new {
+ ValidatorAddr = delegation.ValidatorAddress,
+ Sum = Sql.Sum(delegation.Amount)
+ })
+ .GroupBy(delegation => delegation.ValidatorAddress)
+ .Where(delegation => Sql.In(delegation.ValidatorAddress, validatorAddresses)),
+ cancellationToken
+ );
+
+ return result;
+ }
+
+ public async Task> TotalStakingAtGivenTimeAtSpecificValidatorsAsync(
+ IEnumerable validatorAddresses,
+ DateTime dateTime,
+ CancellationToken cancellationToken = default
+ )
+ {
+ using var db = await _repository.GetDbConnectionAsync(cancellationToken);
+
+ var result = await db.SqlListAsync<(string, long)>(db.From()
+ .Select(delegation =>
+ new {
+ ValidatorAddr = delegation.ValidatorAddress,
+ Sum = Sql.Sum(delegation.Amount)
+ })
+ .GroupBy(delegation => delegation.ValidatorAddress)
+ .Where(delegation => Sql.In(delegation.ValidatorAddress, validatorAddresses))
+ .And(validator => validator.At <= dateTime),
+ cancellationToken
+ );
+
+ return result;
+ }
+
+ public async Task> DeltaStakingInTimePeriodAtSpecificValidators(
+ IEnumerable validatorAddresses,
+ DateTime from,
+ DateTime to,
+ CancellationToken cancellationToken = default)
+ {
+
+ using var db = await _repository.GetDbConnectionAsync(cancellationToken);
+
+ var result = await db.SqlListAsync<(string, long)>(db.From()
+ .Select(delegation =>
+ new {
+ ValidatorAddr = delegation.ValidatorAddress,
+ Sum = Sql.Sum(delegation.Amount)
+ })
+ .GroupBy(delegation => delegation.ValidatorAddress)
+ .Where(delegation => Sql.In(delegation.ValidatorAddress, validatorAddresses))
+ .And(validator => validator.At >= from && validator.At <= to),
+ cancellationToken
+ );
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/DAL/Repositories/ExchangeMarketRepository.cs b/src/Infrastructure/DAL/Repositories/ExchangeMarketRepository.cs
index f793fe5..70cf1ae 100644
--- a/src/Infrastructure/DAL/Repositories/ExchangeMarketRepository.cs
+++ b/src/Infrastructure/DAL/Repositories/ExchangeMarketRepository.cs
@@ -1,8 +1,6 @@
+//TODO do we still need this?
using Microsoft.Extensions.Logging;
-using SapientFi.Kernel;
-using SapientFi.Kernel.DAL.Entities.Exchanges;
using ServiceStack.Data;
-using ServiceStack.OrmLite;
namespace SapientFi.Infrastructure.DAL.Repositories;
@@ -19,24 +17,4 @@ IDbConnectionFactory dbConnectionFactory
_logger = logger;
_dbConnectionFactory = dbConnectionFactory;
}
-
- public async Task GetLatestRateAsync(
- string market,
- Exchange exchange,
- CancellationToken cancellationToken
- )
- {
- _logger.LogDebug("querying db for rates on market {Market} from exchange {Exchange}", market, exchange);
-
- using var db = await _dbConnectionFactory.OpenDbConnectionAsync(token: cancellationToken);
-
- var rate = await db.SingleAsync(
- db.From()
- .Where(q => q.Market == market && q.Exchange == exchange)
- .OrderByDescending(q => q.CloseTime)
- .Take(1),
- token: cancellationToken);
-
- return rate;
- }
}
\ No newline at end of file
diff --git a/src/Infrastructure/Hosting/BackgroundWorkers/IScopedBackgroundWorker.cs b/src/Infrastructure/Hosting/BackgroundWorkers/IScopedBackgroundWorker.cs
index 4d1f170..587220a 100644
--- a/src/Infrastructure/Hosting/BackgroundWorkers/IScopedBackgroundWorker.cs
+++ b/src/Infrastructure/Hosting/BackgroundWorkers/IScopedBackgroundWorker.cs
@@ -1,3 +1,6 @@
+using System.Threading;
+using System.Threading.Tasks;
+
namespace SapientFi.Infrastructure.Hosting.BackgroundWorkers;
public interface IScopedBackgroundServiceWorker
diff --git a/src/Infrastructure/Hosting/BackgroundWorkers/ScopedBackgroundWorker.cs b/src/Infrastructure/Hosting/BackgroundWorkers/ScopedBackgroundWorker.cs
index 993f71a..818b099 100644
--- a/src/Infrastructure/Hosting/BackgroundWorkers/ScopedBackgroundWorker.cs
+++ b/src/Infrastructure/Hosting/BackgroundWorkers/ScopedBackgroundWorker.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
diff --git a/src/Infrastructure/Indexing/IIndexer.cs b/src/Infrastructure/Indexing/IIndexer.cs
new file mode 100644
index 0000000..9a41b75
--- /dev/null
+++ b/src/Infrastructure/Indexing/IIndexer.cs
@@ -0,0 +1,10 @@
+using MassTransit;
+
+namespace SapientFi.Infrastructure.Indexing;
+
+public interface IIndexer : IConsumer where TAnnouncement : class
+{
+ string Id { get; }
+
+ string DisplayName { get; }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj
index 3eece57..9ca34c3 100644
--- a/src/Infrastructure/Infrastructure.csproj
+++ b/src/Infrastructure/Infrastructure.csproj
@@ -2,8 +2,6 @@
net6.0
- enable
- enable
SapientFi.Infrastructure
SapientFi.Infrastructure
@@ -27,10 +25,4 @@
-
-
-
-
-
-
diff --git a/src/Infrastructure/InfrastructureAssemblyMarker.cs b/src/Infrastructure/InfrastructureAssemblyMarker.cs
new file mode 100644
index 0000000..9d9079c
--- /dev/null
+++ b/src/Infrastructure/InfrastructureAssemblyMarker.cs
@@ -0,0 +1,5 @@
+namespace SapientFi.Infrastructure;
+
+public class InfrastructureAssemblyMarker
+{
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Kujira/BusMessages/RawKujiraTransactionAvailableAnnouncement.cs b/src/Infrastructure/Kujira/BusMessages/RawKujiraTransactionAvailableAnnouncement.cs
new file mode 100644
index 0000000..30121f4
--- /dev/null
+++ b/src/Infrastructure/Kujira/BusMessages/RawKujiraTransactionAvailableAnnouncement.cs
@@ -0,0 +1,12 @@
+using System;
+using SapientFi.Infrastructure.Cosmos.BusMessages;
+
+namespace SapientFi.Infrastructure.Kujira.BusMessages;
+
+public record RawKujiraTransactionAvailableAnnouncement : IRawCosmosTransactionAvailableAnnouncement
+{
+ public string TransactionHash { get; init; } = string.Empty;
+ public long RawEntityId { get; init; }
+
+ public DateTimeOffset CreatedAt { get; init; }
+}
diff --git a/src/Infrastructure/Kujira/Indexers/Delegations/KujiraDelegationIndexer.cs b/src/Infrastructure/Kujira/Indexers/Delegations/KujiraDelegationIndexer.cs
new file mode 100644
index 0000000..c1f1cb2
--- /dev/null
+++ b/src/Infrastructure/Kujira/Indexers/Delegations/KujiraDelegationIndexer.cs
@@ -0,0 +1,30 @@
+using Microsoft.Extensions.Logging;
+using SapientFi.Infrastructure.Cosmos.Indexers.Delegations;
+using SapientFi.Infrastructure.Indexing;
+using SapientFi.Infrastructure.Kujira.BusMessages;
+using SapientFi.Infrastructure.Kujira.Indexers.Delegations.Storage;
+using SapientFi.Infrastructure.Kujira.Storage;
+using SapientFi.Kernel.IdGeneration;
+
+namespace SapientFi.Infrastructure.Kujira.Indexers.Delegations;
+
+public class KujiraDelegationIndexer
+ : CosmosDelegationIndexer<
+ RawKujiraTransactionAvailableAnnouncement,
+ KujiraValidatorDelegationLedgerEntity,
+ KujiraRawTransactionEntity>,
+ IIndexer
+{
+ public KujiraDelegationIndexer(
+ ILogger logger,
+ KujiraDelegationsRepository repository,
+ KujiraRawRepository rawRepository,
+ IdProvider idProvider
+ ) : base(logger, repository, rawRepository, idProvider)
+ {
+ }
+
+ public override string NameOfBlockChain => "Kujira";
+ public override string Id => "kujira_delegations";
+ public override string DisplayName => "Kujira Delegations";
+}
diff --git a/src/Infrastructure/Kujira/Indexers/Delegations/KujiraDelegationsServiceCollectionExtensions.cs b/src/Infrastructure/Kujira/Indexers/Delegations/KujiraDelegationsServiceCollectionExtensions.cs
new file mode 100644
index 0000000..ce59691
--- /dev/null
+++ b/src/Infrastructure/Kujira/Indexers/Delegations/KujiraDelegationsServiceCollectionExtensions.cs
@@ -0,0 +1,15 @@
+using Microsoft.Extensions.DependencyInjection;
+using SapientFi.Infrastructure.Kujira.Indexers.Delegations.Storage;
+
+namespace SapientFi.Infrastructure.Kujira.Indexers.Delegations;
+
+public static class KujiraDelegationsServiceCollectionExtensions
+{
+ public static IServiceCollection AddKujiraDelegationsIndexer(this IServiceCollection services)
+ {
+ services.AddTransient();
+ services.AddTransient();
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Kujira/Indexers/Delegations/Storage/KujiraDelegationsRepository.cs b/src/Infrastructure/Kujira/Indexers/Delegations/Storage/KujiraDelegationsRepository.cs
new file mode 100644
index 0000000..dc9c54f
--- /dev/null
+++ b/src/Infrastructure/Kujira/Indexers/Delegations/Storage/KujiraDelegationsRepository.cs
@@ -0,0 +1,11 @@
+using SapientFi.Infrastructure.Cosmos.Indexers.Delegations.Storage;
+using ServiceStack.Data;
+
+namespace SapientFi.Infrastructure.Kujira.Indexers.Delegations.Storage;
+
+public class KujiraDelegationsRepository : CosmosDelegationsRepository
+{
+ public KujiraDelegationsRepository(IDbConnectionFactory dbFactory) : base(dbFactory)
+ {
+ }
+}
diff --git a/src/Infrastructure/Kujira/Indexers/Delegations/Storage/KujiraValidatorDelegationLedgerEntity.cs b/src/Infrastructure/Kujira/Indexers/Delegations/Storage/KujiraValidatorDelegationLedgerEntity.cs
new file mode 100644
index 0000000..0d36f0f
--- /dev/null
+++ b/src/Infrastructure/Kujira/Indexers/Delegations/Storage/KujiraValidatorDelegationLedgerEntity.cs
@@ -0,0 +1,43 @@
+using System;
+using SapientFi.Infrastructure.Cosmos.Indexers.Delegations.Storage;
+using ServiceStack.DataAnnotations;
+
+namespace SapientFi.Infrastructure.Kujira.Indexers.Delegations.Storage;
+
+///
+/// Represents an entry in a "ledger" of delegated
+/// governance tokens for a specific validator.
+///
+/// This means that when tokens are re-delegated
+/// i.e. moved from validator A to validator B,
+/// we have 2 entries for the same action (but with
+/// inverted signs), as the tokens are leaving A
+/// and going into B.
+///
+public record KujiraValidatorDelegationLedgerEntity : ICosmosValidatorDelegationLedgerEntity
+{
+ public long Id { get; init; }
+
+ public DateTimeOffset At { get; init; }
+
+ ///
+ /// The TxHash of the transaction that this delegation
+ /// came from
+ ///
+ [Index]
+ public string TxHash { get; init; } = string.Empty;
+
+ [Index]
+ public string ValidatorAddress { get; init; } = string.Empty;
+
+ [Index]
+ public string DelegatorAddress { get; init; } = string.Empty;
+
+ ///
+ /// Positive = delegation to validator
+ /// Negative = delegation away from validator
+ ///
+ public long Amount { get; init; }
+
+ public string Denominator { get; init; } = string.Empty;
+}
diff --git a/src/Infrastructure/Kujira/KujiraMarker.cs b/src/Infrastructure/Kujira/KujiraMarker.cs
new file mode 100644
index 0000000..472c601
--- /dev/null
+++ b/src/Infrastructure/Kujira/KujiraMarker.cs
@@ -0,0 +1,5 @@
+using SapientFi.Infrastructure.Cosmos;
+
+namespace SapientFi.Infrastructure.Kujira;
+
+public record KujiraMarker : CosmosMarker;
diff --git a/src/Infrastructure/Kujira/KujiraServiceCollectionExtensions.cs b/src/Infrastructure/Kujira/KujiraServiceCollectionExtensions.cs
new file mode 100644
index 0000000..5d3571f
--- /dev/null
+++ b/src/Infrastructure/Kujira/KujiraServiceCollectionExtensions.cs
@@ -0,0 +1,32 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using SapientFi.Infrastructure.Cosmos;
+using SapientFi.Infrastructure.Kujira.Indexers.Delegations;
+using SapientFi.Infrastructure.Kujira.Storage;
+using SapientFi.Infrastructure.Kujira.TransactionListener;
+using TerraDotnet;
+
+namespace SapientFi.Infrastructure.Kujira;
+
+public static class KujiraServiceCollectionExtensions
+{
+ public static IServiceCollection AddKujiraStack(this IServiceCollection services, IConfiguration rawConfig)
+ {
+ var config = new KujiraTransactionListenerConfig(rawConfig);
+
+ services.AddSingleton(config);
+ services.AddTransient();
+ services.AddKujiraDelegationsIndexer();
+ services.AddTransient();
+ services.AddTransient>();
+ services.AddTransient();
+ services.AddCosmosDotnet(config.LcdUri());
+
+ if (config.DoEnable())
+ {
+ services.AddHostedService();
+ }
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Kujira/Storage/KujiraRawRepository.cs b/src/Infrastructure/Kujira/Storage/KujiraRawRepository.cs
new file mode 100644
index 0000000..ab40add
--- /dev/null
+++ b/src/Infrastructure/Kujira/Storage/KujiraRawRepository.cs
@@ -0,0 +1,11 @@
+using SapientFi.Infrastructure.Cosmos.Storage;
+using ServiceStack.Data;
+
+namespace SapientFi.Infrastructure.Kujira.Storage;
+
+public class KujiraRawRepository : CosmosRawRepository
+{
+ public KujiraRawRepository(IDbConnectionFactory dbFactory) : base(dbFactory)
+ {
+ }
+}
diff --git a/src/Infrastructure/Kujira/Storage/KujiraRawTransactionEntity.cs b/src/Infrastructure/Kujira/Storage/KujiraRawTransactionEntity.cs
new file mode 100644
index 0000000..6b0d1ee
--- /dev/null
+++ b/src/Infrastructure/Kujira/Storage/KujiraRawTransactionEntity.cs
@@ -0,0 +1,22 @@
+using System;
+using SapientFi.Infrastructure.Cosmos.Storage;
+using ServiceStack.DataAnnotations;
+
+namespace SapientFi.Infrastructure.Kujira.Storage;
+
+public class KujiraRawTransactionEntity : ICosmosRawTransactionEntity
+{
+ [PrimaryKey]
+ public long Id { get; init; }
+
+ public DateTimeOffset CreatedAt { get; init; }
+
+ [Unique]
+ public string TxHash { get; init; } = string.Empty;
+
+ [PgSqlJsonB]
+ public string RawTx { get; init; } = string.Empty;
+
+ [Index]
+ public int Height { get; init; }
+}
diff --git a/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionEnumerator.cs b/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionEnumerator.cs
new file mode 100644
index 0000000..7cba4d4
--- /dev/null
+++ b/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionEnumerator.cs
@@ -0,0 +1,23 @@
+using Microsoft.Extensions.Logging;
+using TerraDotnet;
+using TerraDotnet.TerraLcd;
+
+namespace SapientFi.Infrastructure.Kujira.TransactionListener;
+
+public class KujiraTransactionEnumerator : CosmosTransactionEnumerator
+{
+ public KujiraTransactionEnumerator(
+ ILogger> logger,
+ ICosmosLcdApiClient cosmosClient
+ ) : base(logger,
+ cosmosClient,
+ new()
+ {
+ SecondsPerBlock = 6,
+ WindowBlockWidth = 2000,
+ PaginationLimit = 200
+ }
+ )
+ {
+ }
+}
diff --git a/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionListenerConfig.cs b/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionListenerConfig.cs
new file mode 100644
index 0000000..d824eb1
--- /dev/null
+++ b/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionListenerConfig.cs
@@ -0,0 +1,17 @@
+using Microsoft.Extensions.Configuration;
+using RapidCore.Configuration;
+using SapientFi.Infrastructure.Cosmos.TransactionListener;
+
+namespace SapientFi.Infrastructure.Kujira.TransactionListener;
+
+public class KujiraTransactionListenerConfig : CosmosTransactionListenerConfig
+{
+ public KujiraTransactionListenerConfig(IConfiguration raw) : base(raw)
+ {
+ }
+
+ public override bool DoEnable() => Raw.Get("KUJIRA__TRANSACTION_LISTENER__DO_ENABLE", true);
+
+ public override string LcdUri() =>
+ Raw.Get("KUJIRA__TRANSACTION_LISTENER__LCD_URI", "https://lcd.kaiyo.kujira.setten.io");
+}
diff --git a/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionListenerHostedService.cs b/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionListenerHostedService.cs
new file mode 100644
index 0000000..33fdd31
--- /dev/null
+++ b/src/Infrastructure/Kujira/TransactionListener/KujiraTransactionListenerHostedService.cs
@@ -0,0 +1,28 @@
+using MassTransit;
+using Microsoft.Extensions.Logging;
+using SapientFi.Infrastructure.Cosmos;
+using SapientFi.Infrastructure.Cosmos.TransactionListener;
+using SapientFi.Infrastructure.Kujira.BusMessages;
+using SapientFi.Infrastructure.Kujira.Storage;
+using TerraDotnet;
+
+namespace SapientFi.Infrastructure.Kujira.TransactionListener;
+
+public class KujiraTransactionListenerHostedService
+ : CosmosTransactionListenerHostedService<
+ KujiraMarker,
+ KujiraRawTransactionEntity,
+ RawKujiraTransactionAvailableAnnouncement>
+{
+ public KujiraTransactionListenerHostedService(
+ ILogger logger,
+ KujiraTransactionEnumerator transactionEnumerator,
+ KujiraRawRepository rawRepository,
+ CosmosFactory factory,
+ IBus massTransitBus
+ ) : base(logger, transactionEnumerator, rawRepository, factory, massTransitBus)
+ {
+ }
+
+ protected override string NameOfBlockChain => "Kujira";
+}
diff --git a/src/Infrastructure/Oracles/ExchangeRates/Terra/LowLevel/ITerraMoneyExchangeRateApiClient.cs b/src/Infrastructure/Oracles/ExchangeRates/Terra/LowLevel/ITerraMoneyExchangeRateApiClient.cs
index 7f6da48..52be008 100644
--- a/src/Infrastructure/Oracles/ExchangeRates/Terra/LowLevel/ITerraMoneyExchangeRateApiClient.cs
+++ b/src/Infrastructure/Oracles/ExchangeRates/Terra/LowLevel/ITerraMoneyExchangeRateApiClient.cs
@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
using Refit;
using TerraDotnet;
diff --git a/src/Infrastructure/Oracles/ExchangeRates/Terra/TerraExchangeRateOracle.cs b/src/Infrastructure/Oracles/ExchangeRates/Terra/TerraExchangeRateOracle.cs
index b6c5764..1bf3b86 100644
--- a/src/Infrastructure/Oracles/ExchangeRates/Terra/TerraExchangeRateOracle.cs
+++ b/src/Infrastructure/Oracles/ExchangeRates/Terra/TerraExchangeRateOracle.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
using SapientFi.Infrastructure.Oracles.ExchangeRates.Terra.LowLevel;
using ServiceStack;
using ServiceStack.Data;
diff --git a/src/Infrastructure/Terra2/BusMessages/RawTerra2TransactionAvailableAnnouncement.cs b/src/Infrastructure/Terra2/BusMessages/RawTerra2TransactionAvailableAnnouncement.cs
new file mode 100644
index 0000000..ac60f62
--- /dev/null
+++ b/src/Infrastructure/Terra2/BusMessages/RawTerra2TransactionAvailableAnnouncement.cs
@@ -0,0 +1,11 @@
+using System;
+using SapientFi.Infrastructure.Cosmos.BusMessages;
+
+namespace SapientFi.Infrastructure.Terra2.BusMessages;
+
+public class RawTerra2TransactionAvailableAnnouncement : IRawCosmosTransactionAvailableAnnouncement
+{
+ public string TransactionHash { get; init; } = string.Empty;
+ public long RawEntityId { get; init; }
+ public DateTimeOffset CreatedAt { get; init; }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/Indexers/Delegations/Storage/Terra2DelegationsRepository.cs b/src/Infrastructure/Terra2/Indexers/Delegations/Storage/Terra2DelegationsRepository.cs
new file mode 100644
index 0000000..0a87358
--- /dev/null
+++ b/src/Infrastructure/Terra2/Indexers/Delegations/Storage/Terra2DelegationsRepository.cs
@@ -0,0 +1,11 @@
+using SapientFi.Infrastructure.Cosmos.Indexers.Delegations.Storage;
+using ServiceStack.Data;
+
+namespace SapientFi.Infrastructure.Terra2.Indexers.Delegations.Storage;
+
+public class Terra2DelegationsRepository : CosmosDelegationsRepository
+{
+ public Terra2DelegationsRepository(IDbConnectionFactory dbFactory) : base(dbFactory)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/Indexers/Delegations/Storage/Terra2ValidatorDelegationLedgerEntity.cs b/src/Infrastructure/Terra2/Indexers/Delegations/Storage/Terra2ValidatorDelegationLedgerEntity.cs
new file mode 100644
index 0000000..5a9a099
--- /dev/null
+++ b/src/Infrastructure/Terra2/Indexers/Delegations/Storage/Terra2ValidatorDelegationLedgerEntity.cs
@@ -0,0 +1,43 @@
+using System;
+using SapientFi.Infrastructure.Cosmos.Indexers.Delegations.Storage;
+using ServiceStack.DataAnnotations;
+
+namespace SapientFi.Infrastructure.Terra2.Indexers.Delegations.Storage;
+
+///
+/// Represents an entry in a "ledger" of delegated
+/// governance tokens for a specific validator.
+///
+/// This means that when tokens are re-delegated
+/// i.e. moved from validator A to validator B,
+/// we have 2 entries for the same action (but with
+/// inverted signs), as the tokens are leaving A
+/// and going into B.
+///
+public record Terra2ValidatorDelegationLedgerEntity : ICosmosValidatorDelegationLedgerEntity
+{
+ public long Id { get; init; }
+
+ public DateTimeOffset At { get; init; }
+
+ ///
+ /// The TxHash of the transaction that this delegation
+ /// came from
+ ///
+ [Index]
+ public string TxHash { get; init; } = string.Empty;
+
+ [Index]
+ public string ValidatorAddress { get; init; } = string.Empty;
+
+ [Index]
+ public string DelegatorAddress { get; init; } = string.Empty;
+
+ ///
+ /// Positive = delegation to validator
+ /// Negative = delegation away from validator
+ ///
+ public long Amount { get; init; }
+
+ public string Denominator { get; init; } = string.Empty;
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/Indexers/Delegations/Terra2DelegationIndexer.cs b/src/Infrastructure/Terra2/Indexers/Delegations/Terra2DelegationIndexer.cs
new file mode 100644
index 0000000..74ae28b
--- /dev/null
+++ b/src/Infrastructure/Terra2/Indexers/Delegations/Terra2DelegationIndexer.cs
@@ -0,0 +1,29 @@
+using Microsoft.Extensions.Logging;
+using SapientFi.Infrastructure.Cosmos.Indexers.Delegations;
+using SapientFi.Infrastructure.Indexing;
+using SapientFi.Infrastructure.Terra2.BusMessages;
+using SapientFi.Infrastructure.Terra2.Indexers.Delegations.Storage;
+using SapientFi.Infrastructure.Terra2.Storage;
+using SapientFi.Kernel.IdGeneration;
+
+namespace SapientFi.Infrastructure.Terra2.Indexers.Delegations;
+
+public class Terra2DelegationIndexer
+ : CosmosDelegationIndexer<
+ RawTerra2TransactionAvailableAnnouncement,
+ Terra2ValidatorDelegationLedgerEntity,
+ Terra2RawTransactionEntity>,
+ IIndexer
+{
+ public Terra2DelegationIndexer(
+ ILogger logger,
+ Terra2DelegationsRepository repository,
+ Terra2RawRepository rawRepository,
+ IdProvider idProvider
+ ) : base(logger, repository, rawRepository, idProvider)
+ {
+ }
+ public override string NameOfBlockChain => "Terra2";
+ public override string Id => "terra2_delegations";
+ public override string DisplayName => "Terra2 Delegations";
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/Indexers/Delegations/Terra2DelegationsServiceCollectionExtensions.cs b/src/Infrastructure/Terra2/Indexers/Delegations/Terra2DelegationsServiceCollectionExtensions.cs
new file mode 100644
index 0000000..15a15ea
--- /dev/null
+++ b/src/Infrastructure/Terra2/Indexers/Delegations/Terra2DelegationsServiceCollectionExtensions.cs
@@ -0,0 +1,15 @@
+using Microsoft.Extensions.DependencyInjection;
+using SapientFi.Infrastructure.Terra2.Indexers.Delegations.Storage;
+
+namespace SapientFi.Infrastructure.Terra2.Indexers.Delegations;
+
+public static class Terra2DelegationsServiceCollectionExtensions
+{
+ public static IServiceCollection AddTerra2DelegationsIndexer(this IServiceCollection services)
+ {
+ services.AddTransient();
+ services.AddTransient();
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/Storage/Terra2RawRepository.cs b/src/Infrastructure/Terra2/Storage/Terra2RawRepository.cs
new file mode 100644
index 0000000..2cdcae1
--- /dev/null
+++ b/src/Infrastructure/Terra2/Storage/Terra2RawRepository.cs
@@ -0,0 +1,11 @@
+using SapientFi.Infrastructure.Cosmos.Storage;
+using ServiceStack.Data;
+
+namespace SapientFi.Infrastructure.Terra2.Storage;
+
+public class Terra2RawRepository : CosmosRawRepository
+{
+ public Terra2RawRepository(IDbConnectionFactory dbFactory) : base(dbFactory)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/Storage/Terra2RawTransactionEntity.cs b/src/Infrastructure/Terra2/Storage/Terra2RawTransactionEntity.cs
new file mode 100644
index 0000000..a498051
--- /dev/null
+++ b/src/Infrastructure/Terra2/Storage/Terra2RawTransactionEntity.cs
@@ -0,0 +1,22 @@
+using System;
+using SapientFi.Infrastructure.Cosmos.Storage;
+using ServiceStack.DataAnnotations;
+
+namespace SapientFi.Infrastructure.Terra2.Storage;
+
+public class Terra2RawTransactionEntity : ICosmosRawTransactionEntity
+{
+ [PrimaryKey]
+ public long Id { get; init; }
+
+ public DateTimeOffset CreatedAt { get; init; }
+
+ [Unique]
+ public string TxHash { get; init; } = string.Empty;
+
+ [PgSqlJsonB]
+ public string RawTx { get; init; } = string.Empty;
+
+ [Index]
+ public int Height { get; init; }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/Terra2Marker.cs b/src/Infrastructure/Terra2/Terra2Marker.cs
new file mode 100644
index 0000000..a2f0497
--- /dev/null
+++ b/src/Infrastructure/Terra2/Terra2Marker.cs
@@ -0,0 +1,5 @@
+using SapientFi.Infrastructure.Cosmos;
+
+namespace SapientFi.Infrastructure.Terra2;
+
+public record Terra2Marker : CosmosMarker;
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/Terra2ServiceCollectionExtensions.cs b/src/Infrastructure/Terra2/Terra2ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..64ff643
--- /dev/null
+++ b/src/Infrastructure/Terra2/Terra2ServiceCollectionExtensions.cs
@@ -0,0 +1,32 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using SapientFi.Infrastructure.Cosmos;
+using SapientFi.Infrastructure.Terra2.Indexers.Delegations;
+using SapientFi.Infrastructure.Terra2.Storage;
+using SapientFi.Infrastructure.Terra2.TransactionListener;
+using TerraDotnet;
+
+namespace SapientFi.Infrastructure.Terra2;
+
+public static class Terra2ServiceCollectionExtensions
+{
+ public static IServiceCollection AddTerra2Stack(this IServiceCollection services, IConfiguration rawConfig)
+ {
+ var config = new Terra2TransactionListenerConfig(rawConfig);
+
+ services.AddSingleton(config);
+ services.AddTransient();
+ services.AddTerra2DelegationsIndexer();
+ services.AddTransient();
+ services.AddTransient>();
+ services.AddTransient();
+ services.AddCosmosDotnet(config.LcdUri());
+
+ if (config.DoEnable())
+ {
+ services.AddHostedService();
+ }
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionEnumerator.cs b/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionEnumerator.cs
new file mode 100644
index 0000000..ca1d3b5
--- /dev/null
+++ b/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionEnumerator.cs
@@ -0,0 +1,23 @@
+using Microsoft.Extensions.Logging;
+using TerraDotnet;
+using TerraDotnet.TerraLcd;
+
+namespace SapientFi.Infrastructure.Terra2.TransactionListener;
+
+public class Terra2TransactionEnumerator : CosmosTransactionEnumerator
+{
+ public Terra2TransactionEnumerator(
+ ILogger> logger,
+ ICosmosLcdApiClient cosmosClient
+ ) : base(logger,
+ cosmosClient,
+ new()
+ {
+ SecondsPerBlock = 6,
+ WindowBlockWidth = 1,
+ PaginationLimit = 200
+ }
+ )
+ {
+ }
+}
diff --git a/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionListenerConfig.cs b/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionListenerConfig.cs
new file mode 100644
index 0000000..bc595dc
--- /dev/null
+++ b/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionListenerConfig.cs
@@ -0,0 +1,20 @@
+using Microsoft.Extensions.Configuration;
+using RapidCore.Configuration;
+using SapientFi.Infrastructure.Cosmos.TransactionListener;
+
+namespace SapientFi.Infrastructure.Terra2.TransactionListener;
+
+public class Terra2TransactionListenerConfig : CosmosTransactionListenerConfig
+{
+ public Terra2TransactionListenerConfig(IConfiguration raw) : base(raw)
+ {
+ }
+
+ public override bool DoEnable() => Raw.Get("TERRA2__TRANSACTION_LISTENER__DO_ENABLE", true);
+
+ // Other options are:
+ // "https://phoenix-lcd.sapient.fi"
+ // "https://phoenix-lcd.terra.dev/"
+ public override string LcdUri() =>
+ Raw.Get("TERRA2__TRANSACTION_LISTENER__LCD_URI", "http://54.36.112.157:1317");
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionListenerHostedService.cs b/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionListenerHostedService.cs
new file mode 100644
index 0000000..bdd6b84
--- /dev/null
+++ b/src/Infrastructure/Terra2/TransactionListener/Terra2TransactionListenerHostedService.cs
@@ -0,0 +1,28 @@
+using MassTransit;
+using Microsoft.Extensions.Logging;
+using SapientFi.Infrastructure.Cosmos;
+using SapientFi.Infrastructure.Cosmos.TransactionListener;
+using SapientFi.Infrastructure.Terra2.BusMessages;
+using SapientFi.Infrastructure.Terra2.Storage;
+using TerraDotnet;
+
+namespace SapientFi.Infrastructure.Terra2.TransactionListener;
+
+public class Terra2TransactionListenerHostedService
+ : CosmosTransactionListenerHostedService<
+ Terra2Marker,
+ Terra2RawTransactionEntity,
+ RawTerra2TransactionAvailableAnnouncement>
+{
+ public Terra2TransactionListenerHostedService(
+ ILogger logger,
+ Terra2TransactionEnumerator transactionEnumerator,
+ Terra2RawRepository rawRepository,
+ CosmosFactory factory,
+ IBus massTransitBus
+ ) : base(logger, transactionEnumerator, rawRepository, factory, massTransitBus)
+ {
+ }
+
+ protected override string NameOfBlockChain => "Terra2";
+}
\ No newline at end of file
diff --git a/src/Kernel/Config/ICorsConfig.cs b/src/Kernel/Config/ICorsConfig.cs
index ba374b0..227486e 100644
--- a/src/Kernel/Config/ICorsConfig.cs
+++ b/src/Kernel/Config/ICorsConfig.cs
@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+
namespace SapientFi.Kernel.Config;
public interface ICorsConfig
diff --git a/src/Kernel/Config/IDbConfig.cs b/src/Kernel/Config/IDbConfig.cs
index 86795f4..025a02f 100644
--- a/src/Kernel/Config/IDbConfig.cs
+++ b/src/Kernel/Config/IDbConfig.cs
@@ -4,5 +4,5 @@ public interface IDbConfig
{
public string ConnectionString { get; }
- public bool DisableMigrationsDuringBoot { get; }
+ public bool RunMigrationsDuringBoot { get; }
}
\ No newline at end of file
diff --git a/src/Kernel/Config/IEnabledServiceRolesConfig.cs b/src/Kernel/Config/IEnabledServiceRolesConfig.cs
index 7608637..ef8f397 100644
--- a/src/Kernel/Config/IEnabledServiceRolesConfig.cs
+++ b/src/Kernel/Config/IEnabledServiceRolesConfig.cs
@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+
namespace SapientFi.Kernel.Config;
public interface IEnabledServiceRolesConfig
diff --git a/src/Kernel/Contracts/Exchanges/GetExchangeRate.cs b/src/Kernel/Contracts/Exchanges/GetExchangeRate.cs
index 15a4d60..b9af9ef 100644
--- a/src/Kernel/Contracts/Exchanges/GetExchangeRate.cs
+++ b/src/Kernel/Contracts/Exchanges/GetExchangeRate.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace SapientFi.Kernel.Contracts.Exchanges;
public class GetExchangeRate
diff --git a/src/Kernel/Contracts/Exchanges/GetExchangeRateResult.cs b/src/Kernel/Contracts/Exchanges/GetExchangeRateResult.cs
index bd3d186..e8a40ca 100644
--- a/src/Kernel/Contracts/Exchanges/GetExchangeRateResult.cs
+++ b/src/Kernel/Contracts/Exchanges/GetExchangeRateResult.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace SapientFi.Kernel.Contracts.Exchanges;
public class GetExchangeRateResult
diff --git a/src/Kernel/Contracts/Exchanges/GetLatestExchangeRateResult.cs b/src/Kernel/Contracts/Exchanges/GetLatestExchangeRateResult.cs
index da91647..447e972 100644
--- a/src/Kernel/Contracts/Exchanges/GetLatestExchangeRateResult.cs
+++ b/src/Kernel/Contracts/Exchanges/GetLatestExchangeRateResult.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace SapientFi.Kernel.Contracts.Exchanges;
public record GetLatestExchangeRateResult
diff --git a/src/Kernel/DAL/Entities/Exchanges/ExchangeMarketCandle.cs b/src/Kernel/DAL/Entities/Exchanges/ExchangeMarketCandle.cs
index f0ccd07..d6c09b9 100644
--- a/src/Kernel/DAL/Entities/Exchanges/ExchangeMarketCandle.cs
+++ b/src/Kernel/DAL/Entities/Exchanges/ExchangeMarketCandle.cs
@@ -1,3 +1,6 @@
+//TODO do we still need this?
+
+using System;
using ServiceStack.DataAnnotations;
namespace SapientFi.Kernel.DAL.Entities.Exchanges;
diff --git a/src/Kernel/DAL/Entities/Terra/TerraRawTransactionEntity.cs b/src/Kernel/DAL/Entities/Terra/TerraRawTransactionEntity.cs
deleted file mode 100644
index 31cb08d..0000000
--- a/src/Kernel/DAL/Entities/Terra/TerraRawTransactionEntity.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using ServiceStack.DataAnnotations;
-
-namespace SapientFi.Kernel.DAL.Entities.Terra;
-
-public class TerraRawTransactionEntity
-{
- [PrimaryKey]
- public long Id { get; set; }
-
- public DateTimeOffset CreatedAt { get; set; }
-
- [Unique]
- public string TxHash { get; set; } = string.Empty;
-
- [PgSqlJsonB]
- public string RawTx { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/Kernel/DAL/Entities/Terra/TerraRewardEntity.cs b/src/Kernel/DAL/Entities/Terra/TerraRewardEntity.cs
deleted file mode 100644
index 992d335..0000000
--- a/src/Kernel/DAL/Entities/Terra/TerraRewardEntity.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using ServiceStack.DataAnnotations;
-
-namespace SapientFi.Kernel.DAL.Entities.Terra
-{
- public class TerraRewardEntity
- {
- [PrimaryKey]
- public long Id { get; set; }
-
- [ForeignKey(typeof(TerraRawTransactionEntity))]
- [Required]
- public long TransactionId { get; set; }
-
- [Index]
- public string Wallet { get; set; } = string.Empty;
-
- [Index]
- public string FromContract { get; set; } = string.Empty;
-
- public decimal Amount { get; set; }
-
- public decimal? AmountUstAtClaim { get; set; }
-
- public decimal? AmountUstNow { get; set; }
- public string Denominator { get; set; } = string.Empty;
-
- public TerraRewardType RewardType { get; set; }
- public DateTimeOffset CreatedAt { get; set; }
-
- public DateTimeOffset UpdatedAt { get; set; }
- }
-
- public enum TerraRewardType
- {
- ///
- /// A token airdrop
- ///
- Airdrop,
-
- ///
- /// Native token rewards for staking
- ///
- StakingReward,
-
- ///
- /// LP farming reward
- ///
- LpFarm
- }
-}
\ No newline at end of file
diff --git a/src/Kernel/Exchange.cs b/src/Kernel/Exchange.cs
index 499103e..a753b56 100644
--- a/src/Kernel/Exchange.cs
+++ b/src/Kernel/Exchange.cs
@@ -1,3 +1,4 @@
+//TODO do we still need this?
namespace SapientFi.Kernel;
public enum Exchange
diff --git a/src/Kernel/Kernel.csproj b/src/Kernel/Kernel.csproj
index c6e7d06..d52fcdf 100644
--- a/src/Kernel/Kernel.csproj
+++ b/src/Kernel/Kernel.csproj
@@ -2,8 +2,6 @@
net6.0
- enable
- enable
SapientFi.Kernel
SapientFi.Kernel
@@ -21,6 +19,14 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/src/Kernel/Serialization/Jason.cs b/src/Kernel/Serialization/Jason.cs
new file mode 100644
index 0000000..3ede6bc
--- /dev/null
+++ b/src/Kernel/Serialization/Jason.cs
@@ -0,0 +1,31 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace SapientFi.Kernel.Serialization;
+
+///
+/// A JSON (de)serializer, which is named like this to avoid
+/// clashing with all of the other JsonSerializer classes in the world.
+///
+public class Jason
+{
+ private readonly JsonSerializerOptions _options = new JsonSerializerOptions
+ {
+ NumberHandling = JsonNumberHandling.AllowReadingFromString,
+ IncludeFields = false,
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ DefaultIgnoreCondition = JsonIgnoreCondition.Never
+ };
+
+ public virtual string ToJson(object thing)
+ {
+ return JsonSerializer.Serialize(thing, _options);
+ }
+
+ public virtual T? FromJson(string encoded)
+ {
+ return JsonSerializer.Deserialize(encoded, _options);
+ }
+}
\ No newline at end of file
diff --git a/src/Kernel/Serialization/JsonSerializerOptionsExtensions.cs b/src/Kernel/Serialization/JsonSerializerOptionsExtensions.cs
new file mode 100644
index 0000000..e8bc654
--- /dev/null
+++ b/src/Kernel/Serialization/JsonSerializerOptionsExtensions.cs
@@ -0,0 +1,13 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace SapientFi.Kernel.Serialization;
+
+public static class JsonSerializerOptionsExtensions
+{
+ public static JsonSerializerOptions AddConverter(this JsonSerializerOptions options, JsonConverter converter)
+ {
+ options.Converters.Add(converter);
+ return options;
+ }
+}
\ No newline at end of file
diff --git a/src/Kernel/Serialization/SerializationServiceCollectionExtensions.cs b/src/Kernel/Serialization/SerializationServiceCollectionExtensions.cs
new file mode 100644
index 0000000..9be39b1
--- /dev/null
+++ b/src/Kernel/Serialization/SerializationServiceCollectionExtensions.cs
@@ -0,0 +1,13 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace SapientFi.Kernel.Serialization;
+
+public static class SerializationServiceCollectionExtensions
+{
+ public static IServiceCollection AddSerialization(this IServiceCollection services)
+ {
+ services.AddTransient();
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/ServiceHost/CacheRefresherServiceWorker.cs b/src/ServiceHost/CacheRefresherServiceWorker.cs
index 2d0b3a2..6179833 100644
--- a/src/ServiceHost/CacheRefresherServiceWorker.cs
+++ b/src/ServiceHost/CacheRefresherServiceWorker.cs
@@ -1,4 +1,9 @@
+using System;
using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using NewRelic.Api.Agent;
using SapientFi.Infrastructure.Hosting.BackgroundWorkers;
using SapientFi.Kernel.Config;
diff --git a/src/ServiceHost/Config/CosmosIndexerConfig.cs b/src/ServiceHost/Config/CosmosIndexerConfig.cs
index d9b05b2..155bc47 100644
--- a/src/ServiceHost/Config/CosmosIndexerConfig.cs
+++ b/src/ServiceHost/Config/CosmosIndexerConfig.cs
@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+using Microsoft.Extensions.Configuration;
using RapidCore.Configuration;
using SapientFi.Kernel.Config;
@@ -28,11 +30,12 @@ bool IEnabledServiceRolesConfig.IsRoleEnabled(string role)
"User ID=sapientfi_indexer_user;Password=sapientfi_indexer_user_pass;Host=localhost;Port=35432;Database=sapientfi_indexer;Pooling=true;Minimum Pool Size=10;Maximum Pool Size=100;"
);
- bool IDbConfig.DisableMigrationsDuringBoot => _config.Get(
- "SAPIENTFI_DB_DISABLE_MIGRATIONS_ON_BOOT",
- false
+ bool IDbConfig.RunMigrationsDuringBoot => _config.Get(
+ "SAPIENTFI_DB_RUN_MIGRATIONS_ON_BOOT",
+ true
);
+ //TODO: Do we still need this?
int IGatewayPoolsConfig.NumberOfElementsInDepositsPrWallet => _config.Get(
"SAPIENTFI_NR_ELEMENTS_DEPOSITS_PR_WALLET",
11
diff --git a/src/ServiceHost/ConfigureServices.cs b/src/ServiceHost/ConfigureServices.cs
new file mode 100644
index 0000000..d693f49
--- /dev/null
+++ b/src/ServiceHost/ConfigureServices.cs
@@ -0,0 +1,58 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using SapientFi.Infrastructure.Kujira;
+using SapientFi.Infrastructure.Terra2;
+using SapientFi.Kernel.Config;
+using SapientFi.ServiceHost.Config;
+using SapientFi.ServiceHost.Endpoints;
+using SapientFi.ServiceHost.ServiceCollectionExtensions;
+
+namespace SapientFi.ServiceHost;
+
+public static class ConfigureServices
+{
+ public static void DoIt(WebApplicationBuilder builder)
+ {
+ DoIt(builder.Services, builder.Configuration);
+ }
+
+ public static void DoIt(IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddControllers();
+ services.AddHealthChecks();
+ services.AddSignalR();
+
+ services.AddCors(options =>
+ {
+ var config = new CosmosIndexerConfig(configuration) as ICorsConfig;
+
+ options.AddPolicy(name: "cosmos-indexer-cors",
+ corsPolicyBuilder =>
+ {
+ corsPolicyBuilder
+ .WithOrigins(config.AllowedOrigins.ToArray())
+ .AllowCredentials()
+ .WithMethods(new []{"GET", "POST", "PUT", "HEAD", "CONNECT", "OPTIONS", "TRACE"})
+ .WithHeaders(new []
+ {
+ "accept", "Access-Control-Request-Method", "Access-Control-Request-Headers", "origin", "User-agent",
+ "Referer", "Accept-Encoding", "Accept-Language", "connection", "host", "content-type",
+ "GraphQL-Tracing", "X-Apollo-Tracing", "newrelic", "traceparent", "tracestate"
+ });
+ });
+ });
+ services.AddTerraMoney(configuration);
+ services.AddMessageBus(new CosmosIndexerConfig(configuration), configuration);
+ services.AddDbStack(configuration);
+ services.AddBackgroundJobStack(configuration);
+ services.AddEndpointServices();
+ services.AddKujiraStack(configuration);
+ services.AddTerra2Stack(configuration);
+
+ services.AddGraphQLServer()
+ .AddQueryType()
+ .AddDiagnosticEventListener()
+ .AddApolloTracing(); // https://chillicream.com/docs/hotchocolate/server/instrumentation#on-demand
+ }
+}
\ No newline at end of file
diff --git a/src/ServiceHost/CronjobManager.cs b/src/ServiceHost/CronjobManager.cs
index 5217e73..66c311a 100644
--- a/src/ServiceHost/CronjobManager.cs
+++ b/src/ServiceHost/CronjobManager.cs
@@ -1,5 +1,7 @@
+using System.Threading;
using Hangfire;
using Hangfire.Common;
+using Microsoft.Extensions.Logging;
using SapientFi.Kernel.Config;
using SapientFi.ServiceHost.RecurringJobs;
@@ -48,12 +50,7 @@ public virtual void RegisterJobsIfRequired()
Job.FromExpression(job => job.DoWorkAsync(CancellationToken.None)),
"13 * * * *"
);
-
- _jobManager.AddOrUpdate("fx-rate-download",
- Job.FromExpression(job => job.DoWorkAsync(CancellationToken.None)),
- "43 * * * *"
- );
-
+
if (_featureConfig.TriggerGatewayPoolFullResync
|| _featureConfig.TriggerMineStakingFullResync
|| _featureConfig.TriggerMineBuyBackFullResync
diff --git a/src/ServiceHost/Endpoints/FxRates/FxRateGraph.cs b/src/ServiceHost/Endpoints/FxRates/FxRateGraph.cs
index e8f5a9f..6742121 100644
--- a/src/ServiceHost/Endpoints/FxRates/FxRateGraph.cs
+++ b/src/ServiceHost/Endpoints/FxRates/FxRateGraph.cs
@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+
namespace SapientFi.ServiceHost.Endpoints.FxRates;
public record FxRateGraph
diff --git a/src/ServiceHost/Endpoints/FxRates/FxRatesService.cs b/src/ServiceHost/Endpoints/FxRates/FxRatesService.cs
index 629ac8e..14e086e 100644
--- a/src/ServiceHost/Endpoints/FxRates/FxRatesService.cs
+++ b/src/ServiceHost/Endpoints/FxRates/FxRatesService.cs
@@ -1,4 +1,8 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
using MassTransit;
+using Microsoft.Extensions.Logging;
using SapientFi.Kernel.Contracts.Exchanges;
namespace SapientFi.ServiceHost.Endpoints.FxRates;
diff --git a/src/ServiceHost/Endpoints/NewRelicReportingDiagnosticEventListener.cs b/src/ServiceHost/Endpoints/NewRelicReportingDiagnosticEventListener.cs
index 5322247..19b4413 100644
--- a/src/ServiceHost/Endpoints/NewRelicReportingDiagnosticEventListener.cs
+++ b/src/ServiceHost/Endpoints/NewRelicReportingDiagnosticEventListener.cs
@@ -1,7 +1,10 @@
+using System;
using System.Text;
+using HotChocolate;
using HotChocolate.Execution;
using HotChocolate.Execution.Instrumentation;
using HotChocolate.Resolvers;
+using Microsoft.Extensions.Logging;
namespace SapientFi.ServiceHost.Endpoints;
diff --git a/src/ServiceHost/Endpoints/Query.cs b/src/ServiceHost/Endpoints/Query.cs
index 37c1868..0e95034 100644
--- a/src/ServiceHost/Endpoints/Query.cs
+++ b/src/ServiceHost/Endpoints/Query.cs
@@ -1,3 +1,6 @@
+using System.Threading;
+using System.Threading.Tasks;
+using HotChocolate;
using NewRelic.Api.Agent;
using SapientFi.ServiceHost.Endpoints.FxRates;
diff --git a/src/ServiceHost/Endpoints/Staking/ValidatorStakingGraph.cs b/src/ServiceHost/Endpoints/Staking/ValidatorStakingGraph.cs
new file mode 100644
index 0000000..5b6c0bf
--- /dev/null
+++ b/src/ServiceHost/Endpoints/Staking/ValidatorStakingGraph.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+
+namespace SapientFi.ServiceHost.Endpoints.Staking;
+
+public class ValidatorStakingGraph
+{
+ public List ValidatorStakings { get; set; } = new();
+}
+
+public class ValidatorStakingItemGraph
+{
+ public List<(string, long)> ValidatorStakingList { get; set; } = new();
+ public DateTime? From { get; set; } = null;
+ public DateTime? To { get; set; } = null;
+}
diff --git a/src/ServiceHost/Endpoints/Types/TimeSeriesStatEntry.cs b/src/ServiceHost/Endpoints/Types/TimeSeriesStatEntry.cs
index f2deb96..1b23646 100644
--- a/src/ServiceHost/Endpoints/Types/TimeSeriesStatEntry.cs
+++ b/src/ServiceHost/Endpoints/Types/TimeSeriesStatEntry.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace SapientFi.ServiceHost.Endpoints.Types;
public class TimeSeriesStatEntry
diff --git a/src/ServiceHost/Program.cs b/src/ServiceHost/Program.cs
index e4dd3d1..8e60caf 100644
--- a/src/ServiceHost/Program.cs
+++ b/src/ServiceHost/Program.cs
@@ -1,9 +1,10 @@
+using System;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using RapidCore.Migration;
using SapientFi.Kernel.Config;
using SapientFi.ServiceHost;
-using SapientFi.ServiceHost.Config;
-using SapientFi.ServiceHost.Endpoints;
-using SapientFi.ServiceHost.ServiceCollectionExtensions;
using Serilog;
ServiceStack.Licensing.RegisterLicense("OSS MIT 2022 https://github.com/pylonboard/dotnet-pylonboard-monolith UgSDQqrNk1uD12MhR04DQogTC/hqGtwW44By+pXTmOl/YuMFf6fINN9DVFJwQU/zoZ8AkM1phq/soc95pHgfqWBMIzAQ1XTC7mUPfHcHxzGSb9am9ps46Y//YpVGQ07mXQxKhmdzYy2nP7Z8CNffS3YaFxOmOQJQO5FZ0dH4988=");
@@ -22,41 +23,9 @@
Log.Logger = loggerConfiguration.CreateLogger();
-builder.Services.AddControllers();
-builder.Services.AddHealthChecks();
-builder.Services.AddSignalR();
+ConfigureServices.DoIt(builder);
-builder.Services.AddCors(options =>
-{
- var config = new CosmosIndexerConfig(builder.Configuration) as ICorsConfig;
-
- options.AddPolicy(name: "cosmos-indexer-cors",
- corsPolicyBuilder =>
- {
- corsPolicyBuilder
- .WithOrigins(config.AllowedOrigins.ToArray())
- .AllowCredentials()
- .WithMethods(new []{"GET", "POST", "PUT", "HEAD", "CONNECT", "OPTIONS", "TRACE"})
- .WithHeaders(new []
- {
- "accept", "Access-Control-Request-Method", "Access-Control-Request-Headers", "origin", "User-agent",
- "Referer", "Accept-Encoding", "Accept-Language", "connection", "host", "content-type",
- "GraphQL-Tracing", "X-Apollo-Tracing", "newrelic", "traceparent", "tracestate"
- });
- });
-});
-builder.Services.AddTerraMoney(builder.Configuration);
-builder.Services.AddMessageBus(new CosmosIndexerConfig(builder.Configuration), builder.Configuration);
-builder.Services.AddDbStack(builder.Configuration);
-builder.Services.AddBackgroundJobStack(builder.Configuration);
-builder.Services.AddEndpointServices();
-
-builder.Services.AddGraphQLServer()
- .AddQueryType()
- .AddDiagnosticEventListener()
- .AddApolloTracing(); // https://chillicream.com/docs/hotchocolate/server/instrumentation#on-demand
-
var app = builder.Build();
app.UseCors("cosmos-indexer-cors");
app.UseSerilogRequestLogging();
@@ -76,14 +45,14 @@
{
var config = scope.ServiceProvider.GetRequiredService();
- if (config.DisableMigrationsDuringBoot)
+ if (config.RunMigrationsDuringBoot)
{
- logger.Warning("{Setting} is false so will not apply migrations", nameof(config.DisableMigrationsDuringBoot));
+ var migrator = scope.ServiceProvider.GetRequiredService();
+ await migrator.UpgradeAsync();
}
else
{
- var migrator = scope.ServiceProvider.GetRequiredService();
- await migrator.UpgradeAsync();
+ logger.Warning("{Setting} is false so will not apply migrations", nameof(config.RunMigrationsDuringBoot));
}
var cronJobs = app.Services.GetRequiredService();
cronJobs.RegisterJobsIfRequired();
diff --git a/src/ServiceHost/Properties/launchSettings.json b/src/ServiceHost/Properties/launchSettings.json
index 51245b6..0d7e5b0 100644
--- a/src/ServiceHost/Properties/launchSettings.json
+++ b/src/ServiceHost/Properties/launchSettings.json
@@ -17,7 +17,8 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"SAPIENT_SERVICE_ROLES_ENABLED": "API,BACKGROUND_WORKER",
- "SAPIENT_TRIGGER_MINE_STAKING_FULL_RESYNC": "false"
+ "KUJIRA__TRANSACTION_LISTENER__DO_ENABLE": false,
+ "TERRA2__TRANSACTION_LISTENER__DO_ENABLE": true
}
},
"IIS Express": {
diff --git a/src/ServiceHost/RecurringJobs/FxRateDownloadJob.cs b/src/ServiceHost/RecurringJobs/FxRateDownloadJob.cs
deleted file mode 100644
index 970ce6b..0000000
--- a/src/ServiceHost/RecurringJobs/FxRateDownloadJob.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using System.Data;
-using SapientFi.Infrastructure.Oracles.ExchangeRates.Terra;
-using SapientFi.Kernel;
-using SapientFi.Kernel.Config;
-using SapientFi.Kernel.DAL.Entities.Exchanges;
-using SapientFi.Kernel.IdGeneration;
-using ServiceStack.Data;
-using ServiceStack.OrmLite;
-using TerraDotnet;
-
-namespace SapientFi.ServiceHost.RecurringJobs;
-
-public class FxRateDownloadJob
-{
- private readonly ILogger _logger;
- private readonly IEnabledServiceRolesConfig _serviceRolesConfig;
- private readonly IDbConnectionFactory _dbConnectionFactory;
- private readonly TerraExchangeRateOracle _terraExchangeRateOracle;
- private readonly IdProvider _idProvider;
-
- public FxRateDownloadJob(
- ILogger logger,
- IEnabledServiceRolesConfig serviceRolesConfig,
- IDbConnectionFactory dbConnectionFactory,
- TerraExchangeRateOracle terraExchangeRateOracle,
- IdProvider idProvider
- )
- {
- _logger = logger;
- _serviceRolesConfig = serviceRolesConfig;
- _dbConnectionFactory = dbConnectionFactory;
- _terraExchangeRateOracle = terraExchangeRateOracle;
- _idProvider = idProvider;
- }
-
- public async Task DoWorkAsync(CancellationToken stoppingToken)
- {
- if (!_serviceRolesConfig.IsRoleEnabled(ServiceRoles.BACKGROUND_WORKER))
- {
- _logger.LogInformation("Background worker role not active, not starting materialized view refresher");
- return;
- }
-
-
- _logger.LogInformation("FX rate downloader is active - downloading rates");
-
- using var db = await _dbConnectionFactory.OpenDbConnectionAsync(token: stoppingToken);
- {
- await FetchAndStoreRateAsync(TerraDenominators.Anc, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Apollo, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Glow, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Loop, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Loopr, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Luna, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.nLuna, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Mine, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Mir, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.nEth, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.bEth, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Orion, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Psi, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Sayve, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Stt, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Twd, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Vkr, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Whale, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Xdefi, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Arts, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.WCoin, TerraDenominators.Ust, db, stoppingToken);
- await FetchAndStoreRateAsync(TerraDenominators.Sayve, TerraDenominators.Ust, db, stoppingToken);
- }
- _logger.LogInformation("FX rate downloader completed");
- }
-
- private async Task FetchAndStoreRateAsync(string from, string to, IDbConnection db, CancellationToken stoppingToken)
- {
- var (close, closeAt) =
- await _terraExchangeRateOracle.GetExchangeRateAsync(from, to, DateTimeOffset.UtcNow, interval: "15m");
- await db.InsertAsync(new ExchangeMarketCandle
- {
- CloseTime = closeAt,
- OpenTime = closeAt.AddMinutes(-15),
- Close = close,
- Exchange = Exchange.Terra,
- Market = $"{from.ToUpperInvariant()}-{to.ToUpperInvariant()}",
- Id = _idProvider.Snowflake(),
- }, token: stoppingToken);
- }
-}
\ No newline at end of file
diff --git a/src/ServiceHost/RecurringJobs/MaterializedViewRefresherJob.cs b/src/ServiceHost/RecurringJobs/MaterializedViewRefresherJob.cs
index 3a9fe60..b815fa3 100644
--- a/src/ServiceHost/RecurringJobs/MaterializedViewRefresherJob.cs
+++ b/src/ServiceHost/RecurringJobs/MaterializedViewRefresherJob.cs
@@ -1,4 +1,8 @@
+using System;
using System.Data;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
using SapientFi.Kernel.Config;
using ServiceStack.Data;
using ServiceStack.OrmLite;
diff --git a/src/ServiceHost/RecurringJobs/TerraMoneyRefreshJob.cs b/src/ServiceHost/RecurringJobs/TerraMoneyRefreshJob.cs
index ac28b94..139557d 100644
--- a/src/ServiceHost/RecurringJobs/TerraMoneyRefreshJob.cs
+++ b/src/ServiceHost/RecurringJobs/TerraMoneyRefreshJob.cs
@@ -1,4 +1,8 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
using Medallion.Threading;
+using Microsoft.Extensions.Logging;
using Polly;
using SapientFi.Kernel.Config;
diff --git a/src/ServiceHost/ServiceCollectionExtensions/BackgroundJobServiceCollectionExtension.cs b/src/ServiceHost/ServiceCollectionExtensions/BackgroundJobServiceCollectionExtension.cs
index faf94bc..1bf36bf 100644
--- a/src/ServiceHost/ServiceCollectionExtensions/BackgroundJobServiceCollectionExtension.cs
+++ b/src/ServiceHost/ServiceCollectionExtensions/BackgroundJobServiceCollectionExtension.cs
@@ -2,6 +2,8 @@
using Hangfire.InMemory;
using Medallion.Threading;
using Medallion.Threading.Postgres;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using SapientFi.Infrastructure.Oracles.ExchangeRates.Terra;
using SapientFi.Kernel.Config;
using SapientFi.ServiceHost.Config;
@@ -32,7 +34,6 @@ public static IServiceCollection AddBackgroundJobStack(
services.AddTransient();
services.AddTransient();
services.AddTransient();
- services.AddTransient();
services.AddTransient();
services.AddTransient();
diff --git a/src/ServiceHost/ServiceCollectionExtensions/DbServiceCollectionExtensions.cs b/src/ServiceHost/ServiceCollectionExtensions/DbServiceCollectionExtensions.cs
index 11af565..9e53dc4 100644
--- a/src/ServiceHost/ServiceCollectionExtensions/DbServiceCollectionExtensions.cs
+++ b/src/ServiceHost/ServiceCollectionExtensions/DbServiceCollectionExtensions.cs
@@ -1,4 +1,8 @@
+using System;
using System.Reflection;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using RapidCore.DependencyInjection;
using RapidCore.Migration;
using RapidCore.PostgreSql.Migration;
diff --git a/src/ServiceHost/ServiceCollectionExtensions/EndpointsServiceCollectionExtensions.cs b/src/ServiceHost/ServiceCollectionExtensions/EndpointsServiceCollectionExtensions.cs
index e95899f..6e69794 100644
--- a/src/ServiceHost/ServiceCollectionExtensions/EndpointsServiceCollectionExtensions.cs
+++ b/src/ServiceHost/ServiceCollectionExtensions/EndpointsServiceCollectionExtensions.cs
@@ -1,3 +1,4 @@
+using Microsoft.Extensions.DependencyInjection;
using SapientFi.ServiceHost.Endpoints.FxRates;
using ServiceStack.Caching;
diff --git a/src/ServiceHost/ServiceCollectionExtensions/MessageBusServiceExtensions.cs b/src/ServiceHost/ServiceCollectionExtensions/MessageBusServiceExtensions.cs
index fdf71d1..d95d59a 100644
--- a/src/ServiceHost/ServiceCollectionExtensions/MessageBusServiceExtensions.cs
+++ b/src/ServiceHost/ServiceCollectionExtensions/MessageBusServiceExtensions.cs
@@ -1,4 +1,8 @@
+using System;
using MassTransit;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using SapientFi.Infrastructure;
using SapientFi.Kernel.Config;
using SapientFi.ServiceHost.Config;
@@ -17,20 +21,21 @@ public static IServiceCollection AddMessageBus(this IServiceCollection services,
// Check this out: https://masstransit-project.com/usage/containers/#consumer-definition
// Add all consumers in the specified assembly
+ /*
+ // TODO this seems weird... why would the API listen to the bus?
if (enabledServiceRolesConfig.IsRoleEnabled(ServiceRoles.API))
{
x.AddConsumers(
typeof(MessageBusServiceExtensions).Assembly
);
}
+ //*/
if (enabledServiceRolesConfig.IsRoleEnabled(ServiceRoles.BACKGROUND_WORKER))
{
- /*
x.AddConsumers(
- typeof(MineStakingTransactionConsumer).Assembly
+ typeof(InfrastructureAssemblyMarker).Assembly
);
- //*/
}
x.UsingRabbitMq((context, cfg) =>
diff --git a/src/ServiceHost/ServiceCollectionExtensions/TerraMoneyServiceCollectionExtensions.cs b/src/ServiceHost/ServiceCollectionExtensions/TerraMoneyServiceCollectionExtensions.cs
index 7b181f3..69e70ee 100644
--- a/src/ServiceHost/ServiceCollectionExtensions/TerraMoneyServiceCollectionExtensions.cs
+++ b/src/ServiceHost/ServiceCollectionExtensions/TerraMoneyServiceCollectionExtensions.cs
@@ -1,12 +1,16 @@
+using System;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using RapidCore.Locking;
using Refit;
using SapientFi.Infrastructure.Hosting.BackgroundWorkers;
using SapientFi.Infrastructure.Oracles.ExchangeRates.Terra.LowLevel;
+using SapientFi.Infrastructure.Terra2;
+using SapientFi.Infrastructure.Terra2.TransactionListener;
using SapientFi.Kernel.Config;
using SapientFi.Kernel.IdGeneration;
using SapientFi.ServiceHost.Config;
using TerraDotnet;
-using TerraDotnet.TerraFcd;
namespace SapientFi.ServiceHost.ServiceCollectionExtensions;
@@ -23,9 +27,6 @@ public static IServiceCollection AddTerraMoney(this IServiceCollection services,
services.AddSingleton(_ => new NoopDistributedAppLockProvider());
services.AddSingleton(_ => new IdProvider(new IdGen.IdGenerator(0)));
- services.AddRefitClient()
- .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://fcd.terra.dev"));
-
services.AddRefitClient()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.coinhall.org"));
@@ -33,7 +34,7 @@ public static IServiceCollection AddTerraMoney(this IServiceCollection services,
services.AddHostedService>();
- services.AddTransient();
+ services.AddTransient();
return services;
diff --git a/src/ServiceHost/ServiceHost.csproj b/src/ServiceHost/ServiceHost.csproj
index 780063c..854a7a1 100644
--- a/src/ServiceHost/ServiceHost.csproj
+++ b/src/ServiceHost/ServiceHost.csproj
@@ -2,8 +2,6 @@
net6.0
- enable
- enable
SapientFi.ServiceHost
SapientFi.ServiceHost
0.18.0
diff --git a/src/ServiceHost/appsettings.Development.json b/src/ServiceHost/appsettings.Development.json
index b3a3139..23af934 100644
--- a/src/ServiceHost/appsettings.Development.json
+++ b/src/ServiceHost/appsettings.Development.json
@@ -37,5 +37,11 @@
"Properties": {
"Application": "Pylonboard.ServiceHost"
}
- }
+ },
+ "Terra2": {
+ "TransactionListener": {
+ "DoEnable": true
+ }
+ },
+ "SAPIENTFI_DB_RUN_MIGRATIONS_ON_BOOT": true
}
diff --git a/src/TerraDotnet/TerraFcd/Messages/Coin.cs b/src/TerraDotnet/CosmosAmount.cs
similarity index 78%
rename from src/TerraDotnet/TerraFcd/Messages/Coin.cs
rename to src/TerraDotnet/CosmosAmount.cs
index 83ce202..a63b0d0 100644
--- a/src/TerraDotnet/TerraFcd/Messages/Coin.cs
+++ b/src/TerraDotnet/CosmosAmount.cs
@@ -1,12 +1,11 @@
using System.Text.Json.Serialization;
-namespace TerraDotnet.TerraFcd.Messages;
+namespace TerraDotnet;
-public record Coin
+public record CosmosAmount
{
[JsonPropertyName("denom")]
public string Denominator { get; set; } = string.Empty;
-
[JsonPropertyName("amount")]
public string Amount { get; set; } = string.Empty;
-}
\ No newline at end of file
+}
diff --git a/src/TerraDotnet/CosmosDotnetServiceCollectionExtensions.cs b/src/TerraDotnet/CosmosDotnetServiceCollectionExtensions.cs
new file mode 100644
index 0000000..1b3ce73
--- /dev/null
+++ b/src/TerraDotnet/CosmosDotnetServiceCollectionExtensions.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+using Refit;
+using TerraDotnet.TerraLcd;
+
+namespace TerraDotnet;
+
+public static class TerraDotnetServiceCollectionExtensions
+{
+ public static IServiceCollection AddCosmosDotnet(this IServiceCollection services, string lcdClientBaseUrl)
+ {
+ services.InternalAddLcdClient(lcdClientBaseUrl);
+ services.AddTransient();
+
+ return services;
+ }
+
+
+ public static IServiceCollection InternalAddLcdClient(this IServiceCollection services, string lcdClientBaseUrl)
+ {
+ services.AddRefitClient>(serviceProvider =>
+ {
+ var settings = new RefitSettings();
+
+ settings.ContentSerializer = new SystemTextJsonContentSerializer(TerraJsonSerializerOptions.GetThem());
+
+ return settings;
+ })
+ .ConfigureHttpClient(client =>
+ {
+ client.BaseAddress = new Uri(lcdClientBaseUrl);
+ });
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/CosmosMessageParser.cs b/src/TerraDotnet/CosmosMessageParser.cs
new file mode 100644
index 0000000..0258639
--- /dev/null
+++ b/src/TerraDotnet/CosmosMessageParser.cs
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+using System.Text.Json;
+using TerraDotnet.Extensions;
+using TerraDotnet.TerraFcd.Messages;
+using TerraDotnet.TerraLcd.Messages;
+
+namespace TerraDotnet;
+
+public class TerraMessageParser
+{
+ public static bool TryParse(Dictionary txMessage, out IMsg? message)
+ {
+ message = null;
+ var parseStatus = false;
+
+ var containsTypeProperty = txMessage.TryGetValue("@type", out var typeProperty);
+ if (!containsTypeProperty)
+ {
+ return parseStatus;
+ }
+
+ switch (typeProperty.GetString())
+ {
+ case "/cosmos.staking.v1beta1.MsgDelegate":
+ message = new CosmosDelegateMessage
+ {
+ Type = txMessage["@type"].GetString()!,
+ DelegatorAddress = txMessage["delegator_address"].GetString()!,
+ ValidatorAddress = txMessage["validator_address"].GetString()!,
+ Amount = txMessage["amount"].ToObject()
+ };
+ break;
+
+ case "/cosmos.staking.v1beta1.MsgUndelegate":
+ message = new CosmosUndelegateMessage
+ {
+ Type = txMessage["@type"].GetString()!,
+ DelegatorAddress = txMessage["delegator_address"].GetString()!,
+ ValidatorAddress = txMessage["validator_address"].GetString()!,
+ Amount = txMessage["amount"].ToObject()
+ };
+ break;
+
+ case "/cosmos.staking.v1beta1.MsgBeginRedelegate":
+ message = new CosmosRedelegateMessage
+ {
+ Type = txMessage["@type"].GetString()!,
+ DelegatorAddress = txMessage["delegator_address"].GetString()!,
+ ValidatorSourceAddress = txMessage["validator_src_address"].GetString()!,
+ ValidatorDestinationAddress = txMessage["validator_dst_address"].GetString()!,
+ Amount = txMessage["amount"].ToObject()
+ };
+ break;
+
+ default:
+ // Does not contain a message type we are interested in.
+ break;
+ }
+
+ if (message is not null)
+ {
+ parseStatus = true;
+ }
+
+ return parseStatus;
+ }
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/CosmosTransactionEnumerator.cs b/src/TerraDotnet/CosmosTransactionEnumerator.cs
new file mode 100644
index 0000000..c405b90
--- /dev/null
+++ b/src/TerraDotnet/CosmosTransactionEnumerator.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using NewRelic.Api.Agent;
+using Polly;
+using Refit;
+using TerraDotnet.TerraLcd;
+using TerraDotnet.TerraLcd.Messages;
+
+// ReSharper disable MemberCanBePrivate.Global
+
+namespace TerraDotnet;
+
+public struct EnumeratorOptions
+{
+ public EnumeratorOptions(int secondsPerBlock)
+ {
+ SecondsPerBlock = secondsPerBlock;
+ }
+
+ public int SecondsPerBlock { get; init; }
+ public int WindowBlockWidth { get; init; } = 2000;
+ public int PaginationLimit { get; init; } = 200;
+}
+
+public abstract class CosmosTransactionEnumerator
+{
+ protected readonly ILogger> Logger;
+ protected readonly ICosmosLcdApiClient CosmosClient;
+ protected int SecondsPerBlock { get; }
+ protected int WindowBlockWidth { get; }
+ protected int PaginationLimit { get; }
+
+ protected CosmosTransactionEnumerator(
+ ILogger> logger,
+ ICosmosLcdApiClient cosmosClient,
+ EnumeratorOptions options
+ )
+ {
+ Logger = logger;
+ CosmosClient = cosmosClient;
+ SecondsPerBlock = options.SecondsPerBlock;
+ WindowBlockWidth = options.WindowBlockWidth;
+ PaginationLimit = options.PaginationLimit;
+ }
+
+ [Trace]
+ public async IAsyncEnumerable EnumerateTransactionsAsync(
+ int fromAndIncludingBlockHeight,
+ [EnumeratorCancellation] CancellationToken stoppingToken
+ )
+ {
+ var queryWindow = new QueryWindow
+ {
+ WindowBlockWidth = WindowBlockWidth,
+ StartBlockHeight = fromAndIncludingBlockHeight,
+ PaginationLimit = PaginationLimit,
+ PaginationOffset = 0
+ };
+
+ // Fetch the latest block in the chain before we start enumerating. Will be used later.
+ var latestBlockResponse = await CosmosClient.GetLatestBlockAsync();
+ if (!latestBlockResponse.IsSuccessStatusCode)
+ {
+ Logger.LogCritical(latestBlockResponse.Error, "Unable to request latest block, abort");
+ throw new Exception("latest block request went wrong... figure it out", latestBlockResponse.Error);
+ }
+
+ int latestBlockHeight = latestBlockResponse.Content.Block.Header.HeightAsInt;
+
+ // Fire off initial request....
+ var response = await SendLcdQueryAsync(queryWindow);
+ while (true)
+ {
+ if (response.Pagination?.TotalAsInt == 0)
+ {
+ // now we have to figure out if we went past the
+ // latest block, or whether we just hit a "dead period"
+ if (queryWindow.EndBlockHeight >= latestBlockHeight)
+ {
+ // we should wait a bit for some blocks to be formed
+ await Task.Delay(TimeSpan.FromSeconds(SecondsPerBlock), stoppingToken);
+ // do NOT advance the query window in this scenario, as we want to
+ // retry the same window later
+ continue;
+ }
+ }
+ queryWindow.Advance(response?.Pagination);
+ // Queue up the next request for async completion while we yield the current result set
+ var nextResponseTask = SendLcdQueryAsync(queryWindow);
+ foreach (var txResponse in response.TransactionResponses)
+ {
+ yield return txResponse;
+ }
+
+ // await settlement of the api query and reassign response so the restart of the loop works as expected
+ response = await nextResponseTask;
+ }
+ }
+
+ private Task SendLcdQueryAsync(QueryWindow queryWindow)
+ {
+ return Policy
+ .Handle(exception =>
+ exception.StatusCode is HttpStatusCode.TooManyRequests or HttpStatusCode.BadGateway
+ or HttpStatusCode.Gone or HttpStatusCode.GatewayTimeout or HttpStatusCode.RequestTimeout
+ or HttpStatusCode.ServiceUnavailable)
+ .WaitAndRetryAsync(10, retryCounter => TimeSpan.FromMilliseconds(Math.Pow(10, retryCounter)),
+ (_, span) =>
+ {
+ Logger.LogWarning("Handling retry while enumerating Cosmos Transactions, waiting {Time:c}",
+ span);
+ }
+ )
+ .ExecuteAsync(
+ async () => await CosmosClient.GetTransactionsMatchingQueryAsync(
+ queryWindow.GetConditions(),
+ queryWindow.PaginationLimit,
+ queryWindow.PaginationOffset
+ )
+ );
+ }
+
+
+ private class QueryWindow
+ {
+ public int WindowBlockWidth { get; set; }
+ public int StartBlockHeight { get; set; }
+ public int EndBlockHeight => StartBlockHeight + WindowBlockWidth;
+ public int PaginationLimit { get; set; }
+ public int PaginationOffset { get; set; }
+
+ public string[] GetConditions()
+ {
+ return new[]
+ {
+ $"tx.height={StartBlockHeight}"
+ };
+ }
+
+ public void Advance(LcdPagination? pagination)
+ {
+ // did we reach the final page of this "block window"?
+ if (IsFinalPage(pagination))
+ {
+ // advance to the next block window
+ StartBlockHeight += WindowBlockWidth;
+ PaginationOffset = 0;
+ }
+ else
+ {
+ // advance to the next page of transactions
+ // within the current block window
+ PaginationOffset += PaginationLimit;
+ }
+ }
+
+ private bool IsFinalPage(LcdPagination? pagination)
+ {
+ if (pagination == null) return true;
+
+ return pagination.TotalAsInt <= (PaginationOffset + PaginationLimit);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/Extensions/ListExtensions.cs b/src/TerraDotnet/Extensions/ListExtensions.cs
index af67066..3496ff7 100644
--- a/src/TerraDotnet/Extensions/ListExtensions.cs
+++ b/src/TerraDotnet/Extensions/ListExtensions.cs
@@ -1,4 +1,6 @@
-using ServiceStack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
namespace TerraDotnet.Extensions;
@@ -12,7 +14,7 @@ public static void EnsureUstIsLast(this List list)
return;
}
- if (list.All(i => !i.Denominator.EqualsIgnoreCase(TerraDenominators.Ust))) return;
+ if (list.All(i => !i.Denominator.Equals(TerraDenominators.Ust, StringComparison.CurrentCultureIgnoreCase))) return;
if (list[1].Denominator != TerraDenominators.Ust)
{
diff --git a/src/TerraDotnet/Extensions/SerializerExtensions.cs b/src/TerraDotnet/Extensions/SerializerExtensions.cs
index ebe4fa9..d0c88c1 100644
--- a/src/TerraDotnet/Extensions/SerializerExtensions.cs
+++ b/src/TerraDotnet/Extensions/SerializerExtensions.cs
@@ -1,15 +1,8 @@
+using System;
+using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
-using Serilog;
-using ServiceStack;
-using TerraDotnet.TerraFcd.Messages;
-using TerraDotnet.TerraFcd.Messages.Bank;
-using TerraDotnet.TerraFcd.Messages.Distributions;
-using TerraDotnet.TerraFcd.Messages.Gov;
-using TerraDotnet.TerraFcd.Messages.Market;
-using TerraDotnet.TerraFcd.Messages.Staking;
-using TerraDotnet.TerraFcd.Messages.Wasm;
namespace TerraDotnet.Extensions;
@@ -71,97 +64,6 @@ public static class SerializerExtensions
{
return JsonSerializer.Deserialize(Convert.FromBase64String(base64StringMessage), Options);
}
-
- public static List? FromCoreStdTxMessage(this Dictionary? dict,
- string key,
- string chainId,
- string txTransactionHash
- )
- {
- var logger = Log.ForContext(typeof(SerializerExtensions));
- if (dict == null)
- {
- return default;
- }
-
- if (!dict.TryGetValue(key, out var val))
- {
- return default;
- }
-
- var intermediate = val.ToObject>();
-
- var toRet = new List();
- foreach (var inter in intermediate)
- {
- var fromCoreStdTxMessage = inter.FromCoreStdTxMessage(chainId);
- if (fromCoreStdTxMessage == default)
- {
- logger.Error("Unable to parse type {Type} for tx-hash {TxHash}", inter.Type, txTransactionHash);
- continue;
- }
- toRet.Add(fromCoreStdTxMessage);
- }
-
- return toRet;
- }
-
- public static IMsg? FromCoreStdTxMessage(this TxMsg msg, string chainId)
- {
- return msg.Type switch
- {
- "wasm/MsgExecuteContract" => new WasmMsgExecuteContract
- {
- Type = msg.Type,
- Value = chainId.EqualsIgnoreCase("columbus-4")
- ? msg.Value.ToObject() : msg.Value.ToObject()
- },
- "wasm/MsgInstantiateContract" => new WasmMsgInstantiateContract
- {
- Type = msg.Type,
- },
- // TODO complete this type handler
- "bank/MsgSend" => new BankMsgSend
- {
- Type = msg.Type
- },
- // TODO complete this type handler
- "staking/MsgUndelegate" => new StakingMsgUndelegate
- {
- Type = msg.Type
- },
- // TODO complete this type handler
- "staking/MsgBeginRedelegate" => new StakingMsgBeginRedelegate
- {
- Type = msg.Type,
- },
- // TODO complete this type handler
- "staking/MsgDelegate" => new StakingMsgDelegate
- {
- Type = msg.Type,
- },
- // TODO complete this type handler
- "gov/MsgVote" => new GovMsgVote
- {
- Type = msg.Type,
- },
- "distribution/MsgWithdrawDelegationReward" => new DistributionMsgWithdrawDelegationReward
- {
- Type = msg.Type,
- Value = msg.Value.ToObject()
- },
- // TODO complete this type handler
- "market/MsgSwap" => new MarketMsgSwap
- {
- Type = msg.Type
- },
- "wasm/MsgMigrateContract" => new WasmMsgMigrateContract
- {
- Type = msg.Type
- },
- _ => null,
- };
- }
public static bool IsBase64String(this string s)
{
diff --git a/src/TerraDotnet/Extensions/StringExtensions.cs b/src/TerraDotnet/Extensions/StringExtensions.cs
index a7f9789..f4bfd5a 100644
--- a/src/TerraDotnet/Extensions/StringExtensions.cs
+++ b/src/TerraDotnet/Extensions/StringExtensions.cs
@@ -1,49 +1,11 @@
+using System;
using System.Text.RegularExpressions;
-using ServiceStack;
using TerraDotnet.TerraFcd.Messages;
namespace TerraDotnet.Extensions;
public static class StringExtensions
{
- public static TerraStringAmount ToTerraStringAmount(this string str)
- {
- var amountDenomRegex = new Regex("^([0-9]+)([a-z0-9]+)$",
- RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);
-
- var match = amountDenomRegex.Match(str.Trim());
-
- if(match.Groups.Count != 3)
- {
- throw new ArgumentException($"Input amount seems invalid: {str}", nameof(str));
- }
-
- return new TerraStringAmount
- {
- Amount = match.Groups[1].Value,
- Denominator = match.Groups[2].Value,
- };
- }
-
- public static TerraAmount ToTerraAmount(this string str)
- {
- var terraStrAmount = str.ToTerraStringAmount();
-
- var amount = new TerraAmount(terraStrAmount.Amount, terraStrAmount.Denominator);
-
- // is this a terra address we need to convert?
- if (amount.Denominator.StartsWithIgnoreCase("terra"))
- {
- amount.Denominator = TerraDenominators.AssetTokenAddressToDenominator[amount.Denominator];
- }
- else if (amount.Denominator.IsMuDemominator())
- {
- amount.Denominator = amount.Denominator.FromNativeDenomToDenom();
- }
-
- return amount;
- }
-
///
/// Determines whether a given currency denominator (like uluna) is a U amount
///
@@ -55,7 +17,7 @@ public static TerraAmount ToTerraAmount(this string str)
///
public static bool IsMuDemominator(this string denom)
{
- return denom.Length >= 4 && denom.StartsWithIgnoreCase("u");
+ return denom.Length >= 4 && denom.StartsWith("u", StringComparison.OrdinalIgnoreCase);
}
///
@@ -65,7 +27,7 @@ public static bool IsMuDemominator(this string denom)
///
public static bool IsTerraDenominatorAddress(this string denom)
{
- return denom.StartsWithIgnoreCase("terra");
+ return denom.StartsWith("terra", StringComparison.OrdinalIgnoreCase);
}
public static string FromNativeDenomToDenom(this string denom)
diff --git a/src/TerraDotnet/Extensions/TxLogExtensions.cs b/src/TerraDotnet/Extensions/TxLogExtensions.cs
deleted file mode 100644
index b74c41e..0000000
--- a/src/TerraDotnet/Extensions/TxLogExtensions.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using ServiceStack;
-using TerraDotnet.TerraFcd.Messages;
-
-namespace TerraDotnet.Extensions;
-
-public static class TxLogExtensions
-{
- public static TxLogEventAttribute? QueryTxLogsForAttributeFirstOrDefault(
- this List logs,
- string txLogEventPropToFind,
- string attributeKeyToFind
- )
- {
- return logs.QueryTxLogsForAttributeFirstOrDefault(
- txLogEventPropToFind,
- attribute => attribute.Key == attributeKeyToFind);
- }
-
- public static TxLogEventAttribute? QueryTxLogsForAttributeFirstOrDefault(
- this List logs,
- string txLogEventPropToFind,
- Func predicate
- )
- {
- return logs
- .SelectMany(l =>
- l.Events)
- .Where(e => e.Type.EqualsIgnoreCase(txLogEventPropToFind))
- .SelectMany(evt => evt.Attributes)
- .FirstOrDefault(
- predicate
- );
- }
-
- public static TxLogEventAttribute? QueryTxLogsForAttributeLastOrDefault(
- this List logs,
- string txLogEventPropToFind,
- string attributeKeyToFind
- )
- {
- return logs.QueryTxLogsForAttributeLastOrDefault(
- txLogEventPropToFind,
- attribute => attribute.Key == attributeKeyToFind);
- }
-
- public static TxLogEventAttribute? QueryTxLogsForAttributeLastOrDefault(
- this List logs,
- string txLogEventPropToFind,
- Func predicate
- )
- {
- return logs
- .SelectMany(l =>
- l.Events)
- .Where(e => e.Type.EqualsIgnoreCase(txLogEventPropToFind))
- .SelectMany(evt => evt.Attributes)
- .LastOrDefault(
- predicate
- );
- }
- public static IEnumerable QueryTxLogsForAttributes(
- this List logs,
- string txLogEventPropToFind,
- Func predicate
- )
- {
- return logs
- .SelectMany(l =>
- l.Events)
- .Where(e => e.Type.EqualsIgnoreCase(txLogEventPropToFind))
- .SelectMany(evt => evt.Attributes)
- .Where(
- predicate
- );
- }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/IMsg.cs b/src/TerraDotnet/IMsg.cs
similarity index 100%
rename from src/TerraDotnet/TerraFcd/Messages/IMsg.cs
rename to src/TerraDotnet/IMsg.cs
diff --git a/src/TerraDotnet/JsonNumberToStringConverter.cs b/src/TerraDotnet/JsonNumberToStringConverter.cs
index ab29593..0ec620b 100644
--- a/src/TerraDotnet/JsonNumberToStringConverter.cs
+++ b/src/TerraDotnet/JsonNumberToStringConverter.cs
@@ -1,3 +1,4 @@
+using System;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/TerraDotnet/TerraAmount.cs b/src/TerraDotnet/TerraAmount.cs
index 9440247..b4aa7e5 100644
--- a/src/TerraDotnet/TerraAmount.cs
+++ b/src/TerraDotnet/TerraAmount.cs
@@ -1,3 +1,5 @@
+using System.Text.Json.Serialization;
+
namespace TerraDotnet;
public record TerraAmount
@@ -12,7 +14,9 @@ public TerraAmount(string amount, string denomOrToken)
public decimal Divisor { get; }
+ [JsonPropertyName("amount")]
public decimal Value { get; }
-
+
+ [JsonPropertyName("denom")]
public string Denominator { get; set; }
}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraDenominators.cs b/src/TerraDotnet/TerraDenominators.cs
index 399dbc3..a39c072 100644
--- a/src/TerraDotnet/TerraDenominators.cs
+++ b/src/TerraDotnet/TerraDenominators.cs
@@ -1,6 +1,8 @@
// ReSharper disable InconsistentNaming
// ReSharper disable IdentifierTypo
+using System.Collections.Generic;
+
namespace TerraDotnet;
public static class TerraDenominators
diff --git a/src/TerraDotnet/TerraDotnet.csproj b/src/TerraDotnet/TerraDotnet.csproj
index 36454a0..774d157 100644
--- a/src/TerraDotnet/TerraDotnet.csproj
+++ b/src/TerraDotnet/TerraDotnet.csproj
@@ -2,18 +2,17 @@
net6.0
- enable
- enable
TerraDotnet
+
+
-
diff --git a/src/TerraDotnet/TerraFcd/Messages/Bank/BankMsgSend.cs b/src/TerraDotnet/TerraFcd/Messages/Bank/BankMsgSend.cs
deleted file mode 100644
index 0e30aee..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Bank/BankMsgSend.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Bank;
-
-public record BankMsgSend : IMsg
-{
- [JsonPropertyName("type")]
- public string Type { get; set; } = string.Empty;
-
- [JsonPropertyName("value")]
- public BankMsgSendValue Value { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Bank/BankMsgSendValue.cs b/src/TerraDotnet/TerraFcd/Messages/Bank/BankMsgSendValue.cs
deleted file mode 100644
index 7cc5195..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Bank/BankMsgSendValue.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Bank;
-
-public record BankMsgSendValue
-{
- [JsonPropertyName("amount")]
- public List Amounts { get; set; } = new();
-
- [JsonPropertyName("to_address")]
- public string ToAddress { get; set; } = string.Empty;
-
- [JsonPropertyName("from_address")]
- public string FromAddress { get; set; } = string.Empty;
-
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/CoreStdTx.cs b/src/TerraDotnet/TerraFcd/Messages/CoreStdTx.cs
deleted file mode 100644
index 06189d3..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/CoreStdTx.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages;
-
-public record CoreStdTx
-{
- public long Id { get; set; }
- public string TransactionHash { get; set; } = string.Empty;
-
- [JsonPropertyName("fee")]
- public TxFee Fee { get; set; } = new();
-
- [JsonPropertyName("msg")]
- public List Messages { get; set; } = new();
-
- [JsonPropertyName("memo")]
- public string Memo { get; set; } = string.Empty;
-
- public List Logs { get; set; } = new();
-
- // TODO Signatures
- // TODO timeout_height
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Distributions/DistributionMsgWithdrawDelegationReward.cs b/src/TerraDotnet/TerraFcd/Messages/Distributions/DistributionMsgWithdrawDelegationReward.cs
deleted file mode 100644
index b537d59..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Distributions/DistributionMsgWithdrawDelegationReward.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Distributions;
-
-public record DistributionMsgWithdrawDelegationReward : IMsg
-{
- public string Type { get; set; } = string.Empty;
- public DistributionWithdrawDelegationRewardValue Value { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Distributions/DistributionWithdrawDelegationRewardValue.cs b/src/TerraDotnet/TerraFcd/Messages/Distributions/DistributionWithdrawDelegationRewardValue.cs
deleted file mode 100644
index 3b81686..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Distributions/DistributionWithdrawDelegationRewardValue.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Distributions;
-
-public record DistributionWithdrawDelegationRewardValue
-{
- [JsonPropertyName("delegator_address")]
- public string DelegatorAddress { get; set; } = string.Empty;
-
- [JsonPropertyName("validator_address")]
- public string ValidatorAddress { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Gov/GovMsgVote.cs b/src/TerraDotnet/TerraFcd/Messages/Gov/GovMsgVote.cs
deleted file mode 100644
index 6047ac7..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Gov/GovMsgVote.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Gov;
-
-public record GovMsgVote : IMsg
-{
- public string Type { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Market/MarketMsgSwap.cs b/src/TerraDotnet/TerraFcd/Messages/Market/MarketMsgSwap.cs
deleted file mode 100644
index 4dff10b..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Market/MarketMsgSwap.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Market;
-
-public record MarketMsgSwap : IMsg
-{
- public string Type { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgBeginRedelegate.cs b/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgBeginRedelegate.cs
deleted file mode 100644
index 72fac26..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgBeginRedelegate.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Staking;
-
-public record StakingMsgBeginRedelegate : IMsg
-{
- [JsonPropertyName("type")]
- public string Type { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgDelegate.cs b/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgDelegate.cs
deleted file mode 100644
index c566cd6..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgDelegate.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Staking;
-
-public record StakingMsgDelegate : IMsg
-{
- public string Type { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgUndelegate.cs b/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgUndelegate.cs
deleted file mode 100644
index c20d0fb..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Staking/StakingMsgUndelegate.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Staking;
-
-public record StakingMsgUndelegate : IMsg
-{
- [JsonPropertyName("type")]
- public string Type { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/TerraStringAmount.cs b/src/TerraDotnet/TerraFcd/Messages/TerraStringAmount.cs
deleted file mode 100644
index 223fef4..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/TerraStringAmount.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System.Text.Json.Serialization;
-using TerraDotnet.Extensions;
-using TerraDotnet.TerraFcd.Messages.Wasm;
-
-namespace TerraDotnet.TerraFcd.Messages;
-
-public record TerraStringAmount
-{
- [JsonPropertyName("denom")]
- public string Denominator { get; set; } = string.Empty;
-
- [JsonPropertyName("amount")]
- public string Amount { get; set; } = string.Empty;
-
- public WasmExecuteMessageAsset ToWasmExecuteMessageAsset()
- {
- if (Denominator.IsMuDemominator())
- {
- return new WasmExecuteMessageAsset
- {
- Amount = Amount,
- Info = new WasmExecuteMessageAssetInfo
- {
- NativeToken = new WasmExecuteMessageAssetInfoNativeToken
- {
- Denominator = Denominator
- }
- }
- };
- }
-
- return new WasmExecuteMessageAsset
- {
- Amount = Amount,
- Info = new WasmExecuteMessageAssetInfo
- {
- Token = new WasmExecuteMessageAssetInfoToken
- {
- ContractAddress = Denominator,
- }
- }
- };
- }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/TerraTxWrapper.cs b/src/TerraDotnet/TerraFcd/Messages/TerraTxWrapper.cs
deleted file mode 100644
index 31868b3..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/TerraTxWrapper.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages;
-
-public record TerraTxWrapper
-{
- ///
- /// Unique id of the transaction
- ///
- public long Id { get; set; }
-
- ///
- /// Id of the chain it was performed on
- ///
- public string ChainId { get; set; } = string.Empty;
-
- ///
- /// A generic transaction carrier
- ///
- public TerraTxGeneric Tx { get; set; } = new();
-
- ///
- /// Block height at which this transaction happened
- ///
- [JsonPropertyName("height")]
- public string Height { get; set; } = string.Empty;
-
- ///
- /// Transaction hash
- ///
- [JsonPropertyName("txhash")]
- public string TransactionHash { get; set; } = string.Empty;
-
- [JsonPropertyName("gas_used")]
- public string GasUsed { get; set; } = string.Empty;
-
- [JsonPropertyName("gas_wanted")]
- public string GasWanted { get; set; } = string.Empty;
-
- [JsonPropertyName("timestamp")]
- public DateTimeOffset CreatedAt { get; set; }
-
- ///
- /// Code 0 means all good, other codes means some kind of failure
- ///
- [JsonPropertyName("code")]
- public int Code { get; set; }
-
- [JsonPropertyName("logs")]
- public List Logs { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/TxFee.cs b/src/TerraDotnet/TerraFcd/Messages/TxFee.cs
deleted file mode 100644
index c44ac1f..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/TxFee.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages;
-
-public record TxFee
-{
- [JsonPropertyName("gas")]
- public string Gas { get; set; } = string.Empty;
-
- [JsonPropertyName("amount")]
- public List Amount { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/TxLog.cs b/src/TerraDotnet/TerraFcd/Messages/TxLog.cs
deleted file mode 100644
index 51d2536..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/TxLog.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages;
-
-public record TxLog
-{
- [JsonPropertyName("events")]
- public List Events { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/TxLogEvent.cs b/src/TerraDotnet/TerraFcd/Messages/TxLogEvent.cs
deleted file mode 100644
index 61f8e3d..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/TxLogEvent.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages;
-
-public record TxLogEvent
-{
- [JsonPropertyName("type")]
- public string Type { get; set; } = string.Empty;
-
- [JsonPropertyName("attributes")]
- public List Attributes { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/TxLogEventAttribute.cs b/src/TerraDotnet/TerraFcd/Messages/TxLogEventAttribute.cs
deleted file mode 100644
index 6f34aa0..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/TxLogEventAttribute.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages;
-
-public record TxLogEventAttribute
-{
- [JsonPropertyName("key")]
- public string Key { get; set; } = string.Empty;
-
- [JsonPropertyName("value")]
- public string Value { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/TxMsg.cs b/src/TerraDotnet/TerraFcd/Messages/TxMsg.cs
deleted file mode 100644
index a8ea270..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/TxMsg.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages;
-
-public record TxMsg
-{
- [JsonPropertyName("type")]
- public string Type { get; set; } = string.Empty;
-
- [JsonPropertyName("value")]
- public JsonElement Value { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/IWasmMsgExecuteContractValue.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/IWasmMsgExecuteContractValue.cs
deleted file mode 100644
index f7babed..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/IWasmMsgExecuteContractValue.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public interface IWasmMsgExecuteContractValue
-{
- List Coins { get; set; }
- string Sender { get; set; }
- string Contract { get; set; }
- WasmExecuteMessage? ExecuteMessage { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMessageAssetInfoToken.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMessageAssetInfoToken.cs
deleted file mode 100644
index 4606b06..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMessageAssetInfoToken.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMessageAssetInfoToken
-{
- [JsonPropertyName("contract_addr")]
- public string ContractAddress { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsg.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsg.cs
deleted file mode 100644
index 852da98..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsg.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMessage
-{
- [JsonPropertyName("send")]
- public WasmExecuteMsgSend? Send { get; set; }
-
- [JsonPropertyName("withdraw_voting_tokens")]
- public WasmExecuteMsgWithdrawVotingTokens? WithdrawVotingTokens { get; set; }
-
- [JsonPropertyName("deposit")]
- public WasmExecuteMsgDeposit? Deposit { get; set; }
-
- [JsonPropertyName("deposit_stable")]
- public WasmExecuteMsgDepositStable? DepositStable { get; set; }
-
- [JsonPropertyName("swap")] public WasmExecuteMsgSwap? Swap { get; set; } = null;
-
- [JsonPropertyName("unbond")]
- public WasmExecuteMsgUnbond? Unbond { get; set; }
-
- [JsonPropertyName("claim")]
- public WasmExecuteMsgClaim? Claim { get; set; }
-
- [JsonPropertyName("withdraw")]
- public WasmExecuteMsgWithdraw? Withdraw { get; set; }
-
- [JsonPropertyName("auto_stake")]
- public WasmExecuteMsgProvideLiquidity? AutoStake { get; set; }
-
- [JsonPropertyName("provide_liquidity")]
- public WasmExecuteMsgProvideLiquidity? ProvideLiquidity { get; set; }
-
- [JsonPropertyName("cast_vote")]
- public WasmExecuteMsgCastVote? CastVote { get; set; }
-
- [JsonPropertyName("mint")]
- public WasmExecuteMsgMint? Mint { get; set; }
-
- [JsonPropertyName("compound")]
- public WasmExecuteMsgCompound? Compound { get; set; }
-
- [JsonPropertyName("sweep")]
- public WasmExecuteMsgSweep? Sweep { get; set; }
-
- [JsonPropertyName("earn")]
- public WasmExecuteMsgEarn? Earn { get; set; }
-
- [JsonPropertyName("update_config")]
- public WasmExecuteMsgUpdateConfig? UpdateConfig { get; set; }
-
- [JsonPropertyName("transfer")]
- public WasmExecuteMsgTransfer? Transfer { get; set; }
-
- [JsonPropertyName("end_poll")]
- public WasmExecuteMsgEndPoll? EndPoll { get; set; }
-
- [JsonPropertyName("staking")]
- public WasmExecuteMsgStaking? Staking { get; set; }
-
- [JsonPropertyName("airdrop")]
- public WasmExecuteMsgAirdrop? Airdrop { get; set; }
-
- [JsonPropertyName("execute_swap_operations")]
- public WasmExecuteMsgExecuteSwapOperations? ExecuteSwapOperations { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAirdrop.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAirdrop.cs
deleted file mode 100644
index c0bdf92..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAirdrop.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgAirdrop
-{
- [JsonPropertyName("claim")]
- public WasmExecuteMsgAirdropClaim Claim { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAirdropClaim.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAirdropClaim.cs
deleted file mode 100644
index d676cf6..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAirdropClaim.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgAirdropClaim
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAsset.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAsset.cs
deleted file mode 100644
index 98bfee2..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAsset.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMessageAsset
-{
- [JsonPropertyName("info")]
- public WasmExecuteMessageAssetInfo Info { get; set; } = new();
-
- [JsonPropertyName("amount")]
- public string Amount { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAssetInfo.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAssetInfo.cs
deleted file mode 100644
index 413732b..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAssetInfo.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMessageAssetInfo
-{
- [JsonPropertyName("token")]
- public WasmExecuteMessageAssetInfoToken Token { get; set; } = new();
-
- [JsonPropertyName("native_token")]
- public WasmExecuteMessageAssetInfoNativeToken NativeToken { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAssetInfoNativeToken.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAssetInfoNativeToken.cs
deleted file mode 100644
index 50953c4..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgAssetInfoNativeToken.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMessageAssetInfoNativeToken
-{
- [JsonPropertyName("denom")]
- public string Denominator { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgCastVote.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgCastVote.cs
deleted file mode 100644
index eaa06fb..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgCastVote.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgCastVote
-{
- [JsonPropertyName("vote")]
- public string Vote { get; set; } = string.Empty;
-
- [JsonPropertyName("amount")]
- public string Amount { get; set; } = string.Empty;
-
- [JsonPropertyName("poll_id")]
- public int PollId { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgClaim.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgClaim.cs
deleted file mode 100644
index 584db88..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgClaim.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgClaim
-{
- [JsonPropertyName("stage")]
- public int Stage { get; set; }
-
- [JsonPropertyName("amount")]
- public string Amount { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgCompound.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgCompound.cs
deleted file mode 100644
index 2e774d4..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgCompound.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgCompound
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgDeposit.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgDeposit.cs
deleted file mode 100644
index b9137a2..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgDeposit.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgDeposit
-{
- public WasmExecuteMessageAsset Asset { get; set; } = new();
-
- [JsonPropertyName("amount")]
- public string Amount { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgDepositStable.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgDepositStable.cs
deleted file mode 100644
index 0d9177e..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgDepositStable.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgDepositStable
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgEarn.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgEarn.cs
deleted file mode 100644
index e103a9d..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgEarn.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgEarn
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgEndPoll.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgEndPoll.cs
deleted file mode 100644
index 84e2236..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgEndPoll.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgEndPoll
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperations.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperations.cs
deleted file mode 100644
index 4c27001..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperations.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgExecuteSwapOperations
-{
- [JsonPropertyName("operations")]
- public List Operations { get; set; } = new();
-
- [JsonPropertyName("offer_amount")]
- public string? OfferAmount { get; set; }
-
- [JsonPropertyName("MinimumReceive")]
- public string? MinimumReceive { get; set; }
-
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperation.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperation.cs
deleted file mode 100644
index 4f84000..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperation.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgExecuteSwapOperationsOperation
-{
- [JsonPropertyName("terra_swap")]
- public WasmExecuteMsgExecuteSwapOperationsOperationTerraSwap? TerraSwap { get; set; }
-
- [JsonPropertyName("native_swap")]
- public WasmExecuteMsgExecuteSwapOperationsOperationNativeSwap? NativeSwap { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperationNativeSwap.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperationNativeSwap.cs
deleted file mode 100644
index ecbd2ea..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperationNativeSwap.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgExecuteSwapOperationsOperationNativeSwap
-{
- [JsonPropertyName("offer_denom")]
- public string OfferDenom { get; set; } = string.Empty;
- [JsonPropertyName("ask_denom")]
- public string AskDenom { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperationTerraSwap.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperationTerraSwap.cs
deleted file mode 100644
index a2fa3e6..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgExecuteSwapOperationsOperationTerraSwap.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgExecuteSwapOperationsOperationTerraSwap
-{
- [JsonPropertyName("ask_asset_info")]
- public WasmExecuteMessageAssetInfo AskAssetInfo { get; set; } = new();
-
- [JsonPropertyName("offer_asset_info")]
- public WasmExecuteMessageAssetInfo OfferAssetInfo { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgMint.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgMint.cs
deleted file mode 100644
index d19e063..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgMint.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgMint
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgProvideLiquidity.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgProvideLiquidity.cs
deleted file mode 100644
index d63015b..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgProvideLiquidity.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgProvideLiquidity
-{
- [JsonPropertyName("assets")]
- public List? Assets { get; set; }
-
- [JsonPropertyName("slippage_tolerance")]
- public string? SlippageTolerance { get; set; }
-
- [JsonPropertyName("token_amount")]
- public string? TokenAmount { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSend.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSend.cs
deleted file mode 100644
index bf63027..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSend.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgSend
-{
- [JsonPropertyName("msg")] public string? Message { get; set; }
-
- [JsonPropertyName("amount")] public string? Amount { get; set; }
-
- [JsonPropertyName("contract")] public string? Contract { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgStaking.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgStaking.cs
deleted file mode 100644
index 84a62ec..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgStaking.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgStaking
-{
- [JsonPropertyName("unstake")]
- public WasmExecuteMsgStakingUnstake Unstake { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgStakingUnstake.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgStakingUnstake.cs
deleted file mode 100644
index 3a86048..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgStakingUnstake.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgStakingUnstake
-{
- [JsonPropertyName("amount")]
- public string Amount { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSwap.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSwap.cs
deleted file mode 100644
index 0b5235d..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSwap.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgSwap
-{
- [JsonPropertyName("max_spread")]
- public string MaxSpread { get; set; } = string.Empty;
-
- [JsonPropertyName("offer_spread")]
- public WasmExecuteMessageAsset OfferAsset { get; set; } = new();
-
- [JsonPropertyName("belief_price")]
- public string BeliefPrice { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSweep.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSweep.cs
deleted file mode 100644
index 6e8782e..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgSweep.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgSweep
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgTransfer.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgTransfer.cs
deleted file mode 100644
index b310355..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgTransfer.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgTransfer
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgUnbond.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgUnbond.cs
deleted file mode 100644
index 94ff7ab..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgUnbond.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgUnbond
-{
- [JsonPropertyName("amount")]
- public string Amount { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgUpdateConfig.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgUpdateConfig.cs
deleted file mode 100644
index 5fec856..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgUpdateConfig.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgUpdateConfig
-{
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgWithdraw.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgWithdraw.cs
deleted file mode 100644
index ad49241..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgWithdraw.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgWithdraw
-{
- [JsonPropertyName("asset")]
- public WasmExecuteMessageAsset? Asset { get; init; }
-
- [JsonPropertyName("amount")]
- public string? Amount { get; init; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgWithdrawVotingTokens.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgWithdrawVotingTokens.cs
deleted file mode 100644
index 7fe7584..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmExecuteMsgWithdrawVotingTokens.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmExecuteMsgWithdrawVotingTokens
-{
- [JsonPropertyName("amount")]
- public string Amount { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContract.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContract.cs
deleted file mode 100644
index 415d7d7..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContract.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmMsgExecuteContract : IMsg
-{
- public string Type { get; set; } = string.Empty;
- public IWasmMsgExecuteContractValue? Value { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContractValueCol4.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContractValueCol4.cs
deleted file mode 100644
index 30e1520..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContractValueCol4.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using TerraDotnet.Extensions;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmMsgExecuteContractValueCol4 : IWasmMsgExecuteContractValue
-{
- [JsonPropertyName("coins")]
- public List Coins { get; set; } = new();
-
- [JsonPropertyName("sender")] public string Sender { get; set; } = string.Empty;
-
- [JsonPropertyName("contract")] public string Contract { get; set; } = string.Empty;
-
- WasmExecuteMessage? IWasmMsgExecuteContractValue.ExecuteMessage
- {
- get => ExecuteMessageRaw.ToObject();
- set => ExecuteMessageRaw = Convert.ToBase64String(JsonSerializer.SerializeToUtf8Bytes(value));
- }
-
- [JsonPropertyName("execute_msg")] public string ExecuteMessageRaw { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContractValueCol5.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContractValueCol5.cs
deleted file mode 100644
index 6df6d25..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgExecuteContractValueCol5.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmMsgExecuteContractValueCol5 : IWasmMsgExecuteContractValue
-{
- [JsonPropertyName("coins")]
- public List Coins { get; set; } = new();
-
- [JsonPropertyName("sender")] public string Sender { get; set; } = string.Empty;
-
- [JsonPropertyName("contract")] public string Contract { get; set; } = string.Empty;
-
- [JsonPropertyName("execute_msg")] public WasmExecuteMessage? ExecuteMessage { get; set; }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgInstantiateContract.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgInstantiateContract.cs
deleted file mode 100644
index 9651d00..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgInstantiateContract.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmMsgInstantiateContract : IMsg
-{
- public string Type { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgMigrateContract.cs b/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgMigrateContract.cs
deleted file mode 100644
index c91189f..0000000
--- a/src/TerraDotnet/TerraFcd/Messages/Wasm/WasmMsgMigrateContract.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace TerraDotnet.TerraFcd.Messages.Wasm;
-
-public record WasmMsgMigrateContract : IMsg
-{
- public string Type { get; set; } = string.Empty;
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/TerraMoneyFcdApiClient.cs b/src/TerraDotnet/TerraFcd/TerraMoneyFcdApiClient.cs
deleted file mode 100644
index 885d66c..0000000
--- a/src/TerraDotnet/TerraFcd/TerraMoneyFcdApiClient.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Refit;
-
-namespace TerraDotnet.TerraFcd;
-
-public interface ITerraMoneyFcdApiClient
-{
- // ?offset=0&limit=100&account=terra14qul6swv2p3vcfqk38fm8dvkezf0gj52m6a78k
- [Get("/v1/txs")]
- public Task ListTxesAsync(long offset, int limit, string account);
-
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/TerraTransactionValueFactory.cs b/src/TerraDotnet/TerraFcd/TerraTransactionValueFactory.cs
deleted file mode 100644
index 1dace9d..0000000
--- a/src/TerraDotnet/TerraFcd/TerraTransactionValueFactory.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using TerraDotnet.Extensions;
-using TerraDotnet.TerraFcd.Messages;
-
-namespace TerraDotnet.TerraFcd;
-
-public static class TerraTransactionValueFactory
-{
- public static CoreStdTx GetIt(TerraTxWrapper tx)
- {
- return tx.Tx.Type switch
- {
- "core/StdTx" => new CoreStdTx{
- Id = tx.Id,
- TransactionHash = tx.TransactionHash,
- Fee = tx.Tx.Value.ToObject("fee"),
- Memo = tx.Tx.Value.ToObject("memo"),
- Messages = tx.Tx.Value.FromCoreStdTxMessage("msg", tx.ChainId, tx.TransactionHash),
- Logs = tx.Logs,
- },
- _ => throw new ArgumentOutOfRangeException(nameof(tx.Tx.Type), $"Unable to handle tx type: {tx.Tx.Type}"),
- };
- }
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/TerraTxesResponse.cs b/src/TerraDotnet/TerraFcd/TerraTxesResponse.cs
deleted file mode 100644
index 28cc2bd..0000000
--- a/src/TerraDotnet/TerraFcd/TerraTxesResponse.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using TerraDotnet.TerraFcd.Messages;
-
-namespace TerraDotnet.TerraFcd;
-
-public record TerraTxesResponse
-{
- ///
- /// Used for paging, the value of Next should go in as Offset for the next call
- ///
- public long Next { get; set; }
-
- ///
- /// The limit applied to the returned data
- ///
- public int Limit { get; set; }
-
- ///
- /// The list of txes returned
- ///
- public List Txs { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraJsonSerializerOptions.cs b/src/TerraDotnet/TerraJsonSerializerOptions.cs
new file mode 100644
index 0000000..611c073
--- /dev/null
+++ b/src/TerraDotnet/TerraJsonSerializerOptions.cs
@@ -0,0 +1,19 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace TerraDotnet;
+
+public class TerraJsonSerializerOptions
+{
+ public static JsonSerializerOptions GetThem()
+ {
+ return new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString,
+ Converters = {new JsonNumberToStringConverter()},
+ AllowTrailingCommas = true,
+ PropertyNameCaseInsensitive = true
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/ICosmosLcdApiClient.cs b/src/TerraDotnet/TerraLcd/ICosmosLcdApiClient.cs
new file mode 100644
index 0000000..cd06dda
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/ICosmosLcdApiClient.cs
@@ -0,0 +1,18 @@
+using System.Threading.Tasks;
+using Refit;
+using TerraDotnet.TerraLcd.Messages;
+
+namespace TerraDotnet.TerraLcd;
+
+public interface ICosmosLcdApiClient
+{
+ [Get("/cosmos/base/tendermint/v1beta1/blocks/latest")]
+ public Task> GetLatestBlockAsync();
+
+ [Get("/cosmos/tx/v1beta1/txs")]
+ public Task GetTransactionsMatchingQueryAsync(
+ [AliasAs("events"), Query(CollectionFormat.Multi)] string[] conditions,
+ [AliasAs("pagination.limit"), Query("pagination.limit")] int paginationLimit = 100,
+ [AliasAs("pagination.offset"), Query("pagination.offset")] int paginationOffset = 0
+ );
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/ITerraMoneyLcdApiClient.cs b/src/TerraDotnet/TerraLcd/ITerraMoneyLcdApiClient.cs
deleted file mode 100644
index 920be93..0000000
--- a/src/TerraDotnet/TerraLcd/ITerraMoneyLcdApiClient.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Refit;
-using TerraDotnet.TerraLcd.Messages;
-
-namespace TerraDotnet.TerraLcd;
-
-public interface ITerraMoneyLcdApiClient
-{
- [Get("/blocks/latest")]
- public Task> GetLatestBlockAsync();
-}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/TerraBlock.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosBlock.cs
similarity index 58%
rename from src/TerraDotnet/TerraLcd/Messages/TerraBlock.cs
rename to src/TerraDotnet/TerraLcd/Messages/CosmosBlock.cs
index a918e3b..8ef8fb6 100644
--- a/src/TerraDotnet/TerraLcd/Messages/TerraBlock.cs
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosBlock.cs
@@ -2,9 +2,9 @@
namespace TerraDotnet.TerraLcd.Messages;
-public record TerraBlock
+public record CosmosBlock
{
[JsonPropertyName("header")]
- public TerraBlockHeader Header { get; set; } = new();
+ public CosmosBlockHeader Header { get; set; } = new();
}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/TerraBlockHeader.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosBlockHeader.cs
similarity index 70%
rename from src/TerraDotnet/TerraLcd/Messages/TerraBlockHeader.cs
rename to src/TerraDotnet/TerraLcd/Messages/CosmosBlockHeader.cs
index 4cd6a24..64ae1ed 100644
--- a/src/TerraDotnet/TerraLcd/Messages/TerraBlockHeader.cs
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosBlockHeader.cs
@@ -1,8 +1,9 @@
+using System;
using System.Text.Json.Serialization;
namespace TerraDotnet.TerraLcd.Messages;
-public record TerraBlockHeader
+public record CosmosBlockHeader
{
[JsonPropertyName("chain_id")]
public string ChainId { get; set; } = string.Empty;
@@ -10,9 +11,12 @@ public record TerraBlockHeader
[JsonPropertyName("height")]
public string Height { get; set; } = string.Empty;
+ public int HeightAsInt => int.Parse(Height);
+
[JsonPropertyName("time")]
public DateTimeOffset Time { get; set; }
[JsonPropertyName("last_block_id")]
- public TerraBlockId LastBlockId { get; set; } = new();
-}
\ No newline at end of file
+ public CosmosBlockId LastBlockId { get; set; } = new();
+}
+
diff --git a/src/TerraDotnet/TerraLcd/Messages/TerraBlockId.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosBlockId.cs
similarity index 69%
rename from src/TerraDotnet/TerraLcd/Messages/TerraBlockId.cs
rename to src/TerraDotnet/TerraLcd/Messages/CosmosBlockId.cs
index 88be84b..d0e926a 100644
--- a/src/TerraDotnet/TerraLcd/Messages/TerraBlockId.cs
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosBlockId.cs
@@ -2,11 +2,11 @@
namespace TerraDotnet.TerraLcd.Messages;
-public record TerraBlockId
+public record CosmosBlockId
{
[JsonPropertyName("hash")]
public string Hash { get; set; } = string.Empty;
[JsonPropertyName("parts")]
- public TerraBlockIdParts Parts { get; set; } = new();
+ public CosmosBlockIdParts Parts { get; set; } = new();
}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/TerraBlockIdParts.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosBlockIdParts.cs
similarity index 87%
rename from src/TerraDotnet/TerraLcd/Messages/TerraBlockIdParts.cs
rename to src/TerraDotnet/TerraLcd/Messages/CosmosBlockIdParts.cs
index f7e9dcc..9506a29 100644
--- a/src/TerraDotnet/TerraLcd/Messages/TerraBlockIdParts.cs
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosBlockIdParts.cs
@@ -2,7 +2,7 @@
namespace TerraDotnet.TerraLcd.Messages;
-public record TerraBlockIdParts
+public record CosmosBlockIdParts
{
[JsonPropertyName("total")]
public int Total { get; set; }
diff --git a/src/TerraDotnet/TerraLcd/Messages/TerraBlockResponse.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosBlockResponse.cs
similarity index 51%
rename from src/TerraDotnet/TerraLcd/Messages/TerraBlockResponse.cs
rename to src/TerraDotnet/TerraLcd/Messages/CosmosBlockResponse.cs
index f868df0..a473fb1 100644
--- a/src/TerraDotnet/TerraLcd/Messages/TerraBlockResponse.cs
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosBlockResponse.cs
@@ -2,11 +2,11 @@
namespace TerraDotnet.TerraLcd.Messages;
-public record TerraBlockResponse
+public record CosmosBlockResponse
{
[JsonPropertyName("block_id")]
- public TerraBlockId BlockId { get; set; } = new();
+ public CosmosBlockId BlockId { get; set; } = new();
[JsonPropertyName("block")]
- public TerraBlock Block { get; set; } = new();
+ public CosmosBlock Block { get; set; } = new();
}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/TerraClaimableRewardBody.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosClaimableRewardBody.cs
similarity index 86%
rename from src/TerraDotnet/TerraLcd/Messages/TerraClaimableRewardBody.cs
rename to src/TerraDotnet/TerraLcd/Messages/CosmosClaimableRewardBody.cs
index 959705c..cb23052 100644
--- a/src/TerraDotnet/TerraLcd/Messages/TerraClaimableRewardBody.cs
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosClaimableRewardBody.cs
@@ -2,7 +2,7 @@
namespace TerraDotnet.TerraLcd.Messages;
-public record TerraClaimableRewardBody
+public record CosmosClaimableRewardBody
{
[JsonPropertyName("owner")]
public string Owner { get; set; } = string.Empty;
diff --git a/src/TerraDotnet/TerraLcd/Messages/TerraClaimableRewardsRequest.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosClaimableRewardsRequest.cs
similarity index 51%
rename from src/TerraDotnet/TerraLcd/Messages/TerraClaimableRewardsRequest.cs
rename to src/TerraDotnet/TerraLcd/Messages/CosmosClaimableRewardsRequest.cs
index 1e0a7e4..a7ff7f9 100644
--- a/src/TerraDotnet/TerraLcd/Messages/TerraClaimableRewardsRequest.cs
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosClaimableRewardsRequest.cs
@@ -2,8 +2,8 @@
namespace TerraDotnet.TerraLcd.Messages;
-public record TerraClaimableRewardsRequest
+public record CosmosClaimableRewardsRequest
{
[JsonPropertyName("claimable_reward")]
- public TerraClaimableRewardBody ClaimableReward { get; set; } = new();
+ public CosmosClaimableRewardBody ClaimableReward { get; set; } = new();
}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/CosmosDelegateMessage.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosDelegateMessage.cs
new file mode 100644
index 0000000..53628e4
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosDelegateMessage.cs
@@ -0,0 +1,14 @@
+using TerraDotnet.TerraFcd.Messages;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record CosmosDelegateMessage : IMsg
+{
+ public string Type { get; set; } = string.Empty;
+
+ public string DelegatorAddress { get; set; } = string.Empty;
+
+ public string ValidatorAddress { get; set; } = string.Empty;
+
+ public CosmosAmount? Amount { get; set; }
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/CosmosRedelegateMessage.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosRedelegateMessage.cs
new file mode 100644
index 0000000..4493feb
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosRedelegateMessage.cs
@@ -0,0 +1,22 @@
+using System.Text.Json.Serialization;
+using TerraDotnet.TerraFcd.Messages;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record CosmosRedelegateMessage : IMsg
+{
+ [JsonPropertyName("@type")]
+ public string Type { get; set; } = string.Empty;
+
+ [JsonPropertyName("delegator_address")]
+ public string DelegatorAddress { get; set; } = string.Empty;
+
+ [JsonPropertyName("validator_src_address")]
+ public string ValidatorSourceAddress { get; set; } = string.Empty;
+
+ [JsonPropertyName("validator_dst_address")]
+ public string ValidatorDestinationAddress { get; set; } = string.Empty;
+
+ [JsonPropertyName("amount")]
+ public CosmosAmount? Amount { get; set; }
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/CosmosUndelegateMessage.cs b/src/TerraDotnet/TerraLcd/Messages/CosmosUndelegateMessage.cs
new file mode 100644
index 0000000..e4620c1
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/CosmosUndelegateMessage.cs
@@ -0,0 +1,5 @@
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record CosmosUndelegateMessage : CosmosDelegateMessage
+{
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponse.cs b/src/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponse.cs
new file mode 100644
index 0000000..98ed986
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponse.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record GetTransactionsMatchingQueryResponse
+{
+ ///
+ /// These are fairly "light-weight", so what you probably want
+ /// is the
+ ///
+ [JsonPropertyName("txs")]
+ public List Transactions { get; set; } = new();
+
+ [JsonPropertyName("tx_responses")]
+ public List TransactionResponses { get; set; } = new();
+
+ public LcdPagination? Pagination { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/ILcdMessage.cs b/src/TerraDotnet/TerraLcd/Messages/ILcdMessage.cs
new file mode 100644
index 0000000..98ef483
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/ILcdMessage.cs
@@ -0,0 +1,9 @@
+using System.Text.Json.Serialization;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public interface ILcdMessage
+{
+ [JsonPropertyName("@type")]
+ public string Type { get; set; }
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/LcdError.cs b/src/TerraDotnet/TerraLcd/Messages/LcdError.cs
new file mode 100644
index 0000000..5dba144
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdError.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public class LcdError
+{
+ public int Code { get; set; }
+
+ public string Message { get; set; } = string.Empty;
+
+ public List Details { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/LcdEvent.cs b/src/TerraDotnet/TerraLcd/Messages/LcdEvent.cs
new file mode 100644
index 0000000..f245bca
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdEvent.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record LcdEvent
+{
+ public string Type { get; set; } = string.Empty;
+
+ public List Attributes { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraFcd/Messages/TerraTxGeneric.cs b/src/TerraDotnet/TerraLcd/Messages/LcdMessageGeneric.cs
similarity index 59%
rename from src/TerraDotnet/TerraFcd/Messages/TerraTxGeneric.cs
rename to src/TerraDotnet/TerraLcd/Messages/LcdMessageGeneric.cs
index ea4ca1d..92a0725 100644
--- a/src/TerraDotnet/TerraFcd/Messages/TerraTxGeneric.cs
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdMessageGeneric.cs
@@ -1,16 +1,19 @@
+using System.Collections.Generic;
using System.Text.Json;
+using System.Text.Json.Serialization;
-namespace TerraDotnet.TerraFcd.Messages;
+namespace TerraDotnet.TerraLcd.Messages;
-public class TerraTxGeneric
+public record LcdMessageGeneric
{
///
/// The type of the transaction - contains valuable information on how the parse the
///
+ [JsonPropertyName("@type")]
public string Type { get; set; } = string.Empty;
///
- /// Generic blobbed storage to carry arbitrary
+ /// Generic blobbed storage to carry arbitrary data
///
public Dictionary Value { get; set; } = new();
}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/LcdPagination.cs b/src/TerraDotnet/TerraLcd/Messages/LcdPagination.cs
new file mode 100644
index 0000000..cecf548
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdPagination.cs
@@ -0,0 +1,13 @@
+using System.Text.Json.Serialization;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record LcdPagination
+{
+ [JsonPropertyName("next_key")]
+ public string? NextKey { get; set; }
+
+ public string Total { get; set; } = string.Empty;
+
+ public int TotalAsInt => int.Parse(Total);
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/LcdTx.cs b/src/TerraDotnet/TerraLcd/Messages/LcdTx.cs
new file mode 100644
index 0000000..6691fdb
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdTx.cs
@@ -0,0 +1,13 @@
+using System.Text.Json.Serialization;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record LcdTx
+{
+ [JsonPropertyName("body")]
+ public LcdTxBody Body { get; set; } = new();
+
+ // TODO auth_info
+
+ // TODO signatures
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/LcdTxBody.cs b/src/TerraDotnet/TerraLcd/Messages/LcdTxBody.cs
new file mode 100644
index 0000000..a7695e0
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdTxBody.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record LcdTxBody
+{
+ [JsonPropertyName("messages")]
+ public List> Messages { get; set; } = new();
+
+ public string Memo { get; set; } = string.Empty;
+
+ [JsonPropertyName("timeout_height")]
+ public string TimeoutHeight { get; set; } = string.Empty;
+
+ // TODO extension_options
+
+ // TODO non_critical_extension_options
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/LcdTxResponse.cs b/src/TerraDotnet/TerraLcd/Messages/LcdTxResponse.cs
new file mode 100644
index 0000000..fd8e414
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdTxResponse.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record LcdTxResponse
+{
+ public string Height { get; set; } = string.Empty;
+
+ public int HeightAsInt => int.Parse(Height);
+
+ [JsonPropertyName("txhash")]
+ public string TransactionHash { get; set; } = string.Empty;
+
+ [JsonPropertyName("codespace")]
+ public string CodeSpace { get; set; } = string.Empty;
+
+ public int Code { get; set; }
+
+ public string Data { get; set; } = string.Empty;
+
+ [JsonPropertyName("raw_log")]
+ public string RawLog { get; set; } = string.Empty;
+
+ public List Logs { get; set; }
+
+ public string Info { get; set; } = string.Empty;
+
+ [JsonPropertyName("gas_wanted")]
+ public string RawGasWanted { get; set; } = string.Empty;
+
+ public int GasWanted => int.Parse(RawGasWanted);
+
+ [JsonPropertyName("gas_used")]
+ public string RawGasUsed { get; set; } = string.Empty;
+
+ public int GasUsed => int.Parse(RawGasUsed);
+
+ [JsonPropertyName("tx")]
+ public LcdTx Transaction { get; set; } = new();
+
+ [JsonPropertyName("timestamp")]
+ public DateTimeOffset CreatedAt { get; set; }
+
+ public List Events { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/LcdTxResponseEventAttribute.cs b/src/TerraDotnet/TerraLcd/Messages/LcdTxResponseEventAttribute.cs
new file mode 100644
index 0000000..2d4c04e
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdTxResponseEventAttribute.cs
@@ -0,0 +1,8 @@
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record LcdTxResponseEventAttribute
+{
+ public string Key { get; set; } = string.Empty;
+ public string Value { get; set; } = string.Empty;
+ public bool? Index { get; set; }
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraLcd/Messages/LcdTxResponseLog.cs b/src/TerraDotnet/TerraLcd/Messages/LcdTxResponseLog.cs
new file mode 100644
index 0000000..679e323
--- /dev/null
+++ b/src/TerraDotnet/TerraLcd/Messages/LcdTxResponseLog.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace TerraDotnet.TerraLcd.Messages;
+
+public record LcdTxResponseLog
+{
+ [JsonPropertyName("msg_index")]
+ public int MessageIndex { get; set; }
+
+ public string Log { get; set; } = string.Empty;
+
+ public List Events { get; set; } = new();
+
+
+}
\ No newline at end of file
diff --git a/src/TerraDotnet/TerraStakingContracts.cs b/src/TerraDotnet/TerraStakingContracts.cs
index 8628705..04288d2 100644
--- a/src/TerraDotnet/TerraStakingContracts.cs
+++ b/src/TerraDotnet/TerraStakingContracts.cs
@@ -1,6 +1,8 @@
// ReSharper disable InconsistentNaming
// ReSharper disable IdentifierTypo
+using System.Collections.Generic;
+
namespace TerraDotnet;
// TODO update address
diff --git a/src/TerraDotnet/TerraTransactionEnumerator.cs b/src/TerraDotnet/TerraTransactionEnumerator.cs
deleted file mode 100644
index 61a7c8e..0000000
--- a/src/TerraDotnet/TerraTransactionEnumerator.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using System.Diagnostics;
-using System.Net;
-using System.Runtime.CompilerServices;
-using Microsoft.Extensions.Logging;
-using NewRelic.Api.Agent;
-using Polly;
-using Refit;
-using TerraDotnet.TerraFcd;
-using TerraDotnet.TerraFcd.Messages;
-
-namespace TerraDotnet;
-
-public class TerraTransactionEnumerator
-{
- private readonly ILogger _logger;
- private readonly ITerraMoneyFcdApiClient _terraClient;
-
- public TerraTransactionEnumerator(
- ILogger logger,
- ITerraMoneyFcdApiClient terraClient
- )
- {
- _logger = logger;
- _terraClient = terraClient;
- }
-
- [Trace]
- public async IAsyncEnumerable<(TerraTxWrapper tx, CoreStdTx msg)> EnumerateTransactionsAsync(
- long offset,
- int limit,
- string contract,
- [EnumeratorCancellation] CancellationToken stoppingToken
- )
- {
- var response = await Policy
- .Handle(exception => exception.StatusCode == HttpStatusCode.TooManyRequests)
- .WaitAndRetryAsync(10, retryCounter => TimeSpan.FromMilliseconds(Math.Pow(10, retryCounter)),
- (_, span) =>
- {
- _logger.LogWarning("Handling retry while enumerating Terra Transactions, waiting {Time:c}", span);
- })
- .ExecuteAsync(async () => await _terraClient.ListTxesAsync(offset, limit, contract));
- var txes = response;
- var next = txes.Next;
- var stopwatch = new Stopwatch();
- var doContinue = true;
- do
- {
- if (next == 0)
- {
- doContinue = false;
- }
-
- _logger.LogInformation("`next` continuation token: {Next}", next);
-
- // NO await to perform background work
- // Capture the async method as a func so we can invoke it again during retry
- Func> listTxesAsync = () => _terraClient.ListTxesAsync(
- txes.Next,
- txes.Limit,
- contract
- );
- var nextTxes = listTxesAsync();
- stopwatch.Reset();
- stopwatch.Start();
- foreach (var tx in txes.Txs)
- {
- if (tx.Code != 0)
- {
- _logger.LogInformation("Skipping tx with Id {Id} since code is {Code}", tx.Id, tx.Code);
- continue;
- }
-
- _logger.LogDebug("Processing transaction with id {Id}", tx.Id);
-
- var msg = TerraTransactionValueFactory.GetIt(tx);
- yield return (tx, msg);
- }
- stopwatch.Stop();
-
- var expectedWallTime = 2d;
- if (stopwatch.Elapsed.TotalSeconds < expectedWallTime)
- {
- var nappieTime = TimeSpan.FromSeconds(expectedWallTime-stopwatch.Elapsed.TotalSeconds);
- _logger.LogDebug("Rate limiting, less than 2 seconds was spent on processing by the consumer. Napping for {Sleep}", nappieTime.ToString("g"));
- await Task.Delay(nappieTime, stoppingToken);
- }
- _logger.LogDebug("waiting for next http result-set");
-
- Policy.Handle(exception => exception.StatusCode == HttpStatusCode.TooManyRequests)
- .WaitAndRetry(10, retryCounter => TimeSpan.FromMilliseconds(Math.Pow(10, retryCounter)),
- (_, span) =>
- {
- _logger.LogWarning("Handling retry while enumerating Terra Transactions, waiting {Time:c}", span);
- nextTxes = listTxesAsync();
- })
- .Execute(() => { Task.WaitAll(new Task[] { nextTxes }, cancellationToken: stoppingToken); });
-
- _logger.LogDebug("done, iterating");
- txes = nextTxes.Result;
- next = nextTxes.Result.Next;
- } while (doContinue);
- }
-}
\ No newline at end of file
diff --git a/test/NarrowIntegrationTests/Infrastructure/Terra2/Storage/Terra2RawRepositoryTests.cs b/test/NarrowIntegrationTests/Infrastructure/Terra2/Storage/Terra2RawRepositoryTests.cs
new file mode 100644
index 0000000..ea02fd2
--- /dev/null
+++ b/test/NarrowIntegrationTests/Infrastructure/Terra2/Storage/Terra2RawRepositoryTests.cs
@@ -0,0 +1,62 @@
+using System.Threading;
+using System.Threading.Tasks;
+using SapientFi.Infrastructure.Terra2.Storage;
+using Xunit;
+
+namespace NarrowIntegrationTests.Infrastructure.Terra2.Storage;
+
+public class Terra2RawRepositoryTests : IntegrationBaseTest
+{
+ private readonly Terra2RawRepository _repository;
+
+ public Terra2RawRepositoryTests()
+ {
+ _repository = new Terra2RawRepository(GetDbConnectionFactory());
+ }
+
+ /* ************************************************************************************************************
+ */
+ [Fact]
+ public async Task GetLatestSeenBlockHeightAsync_returns_0_whenTableIsEmpty()
+ {
+ await EnsureEmptyAsync();
+
+ var actual = await _repository.GetLatestSeenBlockHeightAsync(CancellationToken.None);
+
+ Assert.Equal(0, actual);
+ }
+
+
+ /* ************************************************************************************************************
+ */
+ [Fact]
+ public async Task GetLatestSeenBlockHeightAsync_returns_maxHeight_whenTableHasRows()
+ {
+ await EnsureEmptyAsync();
+
+ await InsertAsync(new Terra2RawTransactionEntity
+ {
+ Id = 1,
+ Height = 1,
+ TxHash = "1"
+ });
+
+ await InsertAsync(new Terra2RawTransactionEntity
+ {
+ Id = 2,
+ Height = 666,
+ TxHash = "2"
+ });
+
+ await InsertAsync(new Terra2RawTransactionEntity
+ {
+ Id = 3,
+ Height = 2,
+ TxHash = "3"
+ });
+
+ var actual = await _repository.GetLatestSeenBlockHeightAsync(CancellationToken.None);
+
+ Assert.Equal(666, actual);
+ }
+}
\ No newline at end of file
diff --git a/test/NarrowIntegrationTests/IntegrationBaseTest.cs b/test/NarrowIntegrationTests/IntegrationBaseTest.cs
index d2c23e9..1d312ff 100644
--- a/test/NarrowIntegrationTests/IntegrationBaseTest.cs
+++ b/test/NarrowIntegrationTests/IntegrationBaseTest.cs
@@ -1,7 +1,10 @@
using System;
+using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SapientFi.ServiceHost.ServiceCollectionExtensions;
+using ServiceStack.Data;
+using ServiceStack.OrmLite;
namespace NarrowIntegrationTests;
@@ -34,9 +37,35 @@ public virtual void AddServices(IServiceCollection services)
}
+ public virtual IDbConnectionFactory GetDbConnectionFactory()
+ {
+ return ServiceProvider.GetRequiredService();
+ }
+
public void Dispose()
{
ServiceProvider.Dispose();
Scope.Dispose();
}
+
+
+ ///
+ /// Ensure that the table for the entity exists and is empty.
+ ///
+ /// A database Entity
+ protected async Task EnsureEmptyAsync()
+ {
+ using var db = await GetDbConnectionFactory().OpenDbConnectionAsync();
+ db.CreateTableIfNotExists();
+ await db.DeleteAllAsync();
+ }
+
+
+ protected async Task InsertAsync(TEntity entity)
+ {
+ using var db = await GetDbConnectionFactory().OpenDbConnectionAsync();
+ await db.InsertAsync(entity);
+
+ return entity;
+ }
}
\ No newline at end of file
diff --git a/test/NarrowIntegrationTests/NarrowIntegrationTests.csproj b/test/NarrowIntegrationTests/NarrowIntegrationTests.csproj
index 989cd68..9e85750 100644
--- a/test/NarrowIntegrationTests/NarrowIntegrationTests.csproj
+++ b/test/NarrowIntegrationTests/NarrowIntegrationTests.csproj
@@ -26,7 +26,12 @@
-
+
+ Always
+
+
+ Always
+
diff --git a/test/NarrowIntegrationTests/Oracles/TerraLcdClient/TerraMoneyFcdApiClientTests.cs b/test/NarrowIntegrationTests/Oracles/TerraLcdClient/TerraMoneyFcdApiClientTests.cs
deleted file mode 100644
index 39594eb..0000000
--- a/test/NarrowIntegrationTests/Oracles/TerraLcdClient/TerraMoneyFcdApiClientTests.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using Refit;
-using TerraDotnet;
-using TerraDotnet.TerraFcd;
-using Xunit;
-
-namespace NarrowIntegrationTests.Oracles.TerraLcdClient
-{
- public class TerraMoneyFcdApiClientTests
- {
- private readonly ITerraMoneyFcdApiClient _client;
-
- public TerraMoneyFcdApiClientTests()
- {
- _client = RestService.For(
- new HttpClient(new HttpClientHandler
- {
- // To route via Charles proxy for debugging use:
- // Proxy = new WebProxy("http://localhost:8888"),
- ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true,
- })
- {
- BaseAddress = new Uri("https://fcd.terra.dev"),
- });
- }
-
- [Fact]
- public async Task List_txs_work()
- {
- var txs = await _client.ListTxesAsync(121079826, 100, TerraStakingContracts.MINE_STAKING_CONTRACT);
-
- Assert.NotEmpty(txs.Txs);
- }
- }
-
-
-}
\ No newline at end of file
diff --git a/test/NarrowIntegrationTests/Oracles/TerraLcdClient/TerraMoneyLcdApiClientTests.cs b/test/NarrowIntegrationTests/Oracles/TerraLcdClient/TerraMoneyLcdApiClientTests.cs
index ebf6921..9740a4d 100644
--- a/test/NarrowIntegrationTests/Oracles/TerraLcdClient/TerraMoneyLcdApiClientTests.cs
+++ b/test/NarrowIntegrationTests/Oracles/TerraLcdClient/TerraMoneyLcdApiClientTests.cs
@@ -2,6 +2,7 @@
using System.Net.Http;
using System.Threading.Tasks;
using Refit;
+using SapientFi.Infrastructure.Terra2;
using TerraDotnet.TerraLcd;
using Xunit;
@@ -9,11 +10,11 @@ namespace NarrowIntegrationTests.Oracles.TerraLcdClient;
public class TerraMoneyLcdApiClientTests
{
- private readonly ITerraMoneyLcdApiClient _client;
+ private readonly ICosmosLcdApiClient _client;
public TerraMoneyLcdApiClientTests()
{
- _client = RestService.For(
+ _client = RestService.For>(
new HttpClient(new HttpClientHandler
{
// To route via Charles proxy for debugging use:
@@ -21,7 +22,7 @@ public TerraMoneyLcdApiClientTests()
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true,
})
{
- BaseAddress = new Uri("https://phoenix-lcd.terra.dev"),
+ BaseAddress = new Uri("https://phoenix-lcd.sapient.fi"),
});
}
diff --git a/test/NarrowIntegrationTests/TerraDotnet/GetTransactionsMatchingQueryResponse_01.json b/test/NarrowIntegrationTests/TerraDotnet/GetTransactionsMatchingQueryResponse_01.json
new file mode 100644
index 0000000..5e69e8b
--- /dev/null
+++ b/test/NarrowIntegrationTests/TerraDotnet/GetTransactionsMatchingQueryResponse_01.json
@@ -0,0 +1,1262 @@
+{
+ "txs": [
+ {
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgBeginRedelegate",
+ "delegator_address": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0",
+ "validator_src_address": "terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx",
+ "validator_dst_address": "terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt",
+ "amount": {
+ "denom": "uluna",
+ "amount": "1932000000"
+ }
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AiVuv8AMDt0fGGxF8DEcDhR28sflA+j8GfH5/D59FG1L"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_LEGACY_AMINO_JSON"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "67288"
+ }
+ ],
+ "gas_limit": "448586",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "0sIQZiBfcQrBzGA6eJe0OrdTx3GBhClUBsKYWSOmiUEwelf5uqJC0wNiuMXxhLdxdBSxpcE8Xg2wFR6UYrf4Ww=="
+ ]
+ },
+ {
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.bank.v1beta1.MsgSend",
+ "from_address": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20",
+ "to_address": "terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm",
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "1000000"
+ }
+ ]
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AgLuDN/jos126YbtrTAxvL76/JZ8SxR+F/NM9yHh8U8G"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_DIRECT"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "21237"
+ }
+ ],
+ "gas_limit": "141580",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "iekH9rrafxakxEmbTQbpkg9h0KsHGkgUG0eZKVFgwKUi32FoZaa/UAS+n8wnCEQ1Wa5z66r93lOicd1reZjCBg=="
+ ]
+ },
+ {
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgUndelegate",
+ "delegator_address": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu",
+ "validator_address": "terravaloper1wwq9mfkkqfta334d4hcjrjau9y50a4hw37zhdl",
+ "amount": {
+ "denom": "uluna",
+ "amount": "32454390"
+ }
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "A4bnZP/9hrCbP8gW3W1vNf3pp0ClUhHoY0TAhu8tMo4R"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_LEGACY_AMINO_JSON"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "53087"
+ }
+ ],
+ "gas_limit": "353908",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "7YN1xYnhJhFwqC/8DZQPhUNT4fTwZzhj4EGE7wTx1dtfOZgQCeWv6emVnLAf6EV5TSqnbIUN6jsvb/6Mtv69xw=="
+ ]
+ }
+ ],
+ "tx_responses": [
+ {
+ "height": "201",
+ "txhash": "74B8212F209C7F225F79B7C0CA064160CC3C9D589A4F4AB6849071A0DAFED5A3",
+ "codespace": "",
+ "code": 0,
+ "data": "0A3C0A2A2F636F736D6F732E7374616B696E672E763162657461312E4D7367426567696E526564656C6567617465120E0A0C08ADE0B595061095B3998502",
+ "raw_log": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.staking.v1beta1.MsgBeginRedelegate\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"}]},{\"type\":\"redelegate\",\"attributes\":[{\"key\":\"source_validator\",\"value\":\"terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx\"},{\"key\":\"destination_validator\",\"value\":\"terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt\"},{\"key\":\"amount\",\"value\":\"1932000000uluna\"},{\"key\":\"completion_time\",\"value\":\"2022-06-18T06:26:53Z\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]}]}]",
+ "logs": [
+ {
+ "msg_index": 0,
+ "log": "",
+ "events": [
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "receiver",
+ "value": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0"
+ },
+ {
+ "key": "amount",
+ "value": "22551uluna"
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "spender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "22551uluna"
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "action",
+ "value": "/cosmos.staking.v1beta1.MsgBeginRedelegate"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "module",
+ "value": "staking"
+ },
+ {
+ "key": "sender",
+ "value": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0"
+ }
+ ]
+ },
+ {
+ "type": "redelegate",
+ "attributes": [
+ {
+ "key": "source_validator",
+ "value": "terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx"
+ },
+ {
+ "key": "destination_validator",
+ "value": "terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt"
+ },
+ {
+ "key": "amount",
+ "value": "1932000000uluna"
+ },
+ {
+ "key": "completion_time",
+ "value": "2022-06-18T06:26:53Z"
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "recipient",
+ "value": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "22551uluna"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "info": "",
+ "gas_wanted": "448586",
+ "gas_used": "261855",
+ "tx": {
+ "@type": "/cosmos.tx.v1beta1.Tx",
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgBeginRedelegate",
+ "delegator_address": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0",
+ "validator_src_address": "terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx",
+ "validator_dst_address": "terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt",
+ "amount": {
+ "denom": "uluna",
+ "amount": "1932000000"
+ }
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AiVuv8AMDt0fGGxF8DEcDhR28sflA+j8GfH5/D59FG1L"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_LEGACY_AMINO_JSON"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "67288"
+ }
+ ],
+ "gas_limit": "448586",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "0sIQZiBfcQrBzGA6eJe0OrdTx3GBhClUBsKYWSOmiUEwelf5uqJC0wNiuMXxhLdxdBSxpcE8Xg2wFR6UYrf4Ww=="
+ ]
+ },
+ "timestamp": "2022-05-28T06:26:53Z",
+ "events": [
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NjcyODh1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NjcyODh1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NjcyODh1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "ZmVl",
+ "value": "NjcyODh1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "YWNjX3NlcQ==",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzAvMA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "c2lnbmF0dXJl",
+ "value": "MHNJUVppQmZjUXJCekdBNmVKZTBPcmRUeDNHQmhDbFVCc0tZV1NPbWlVRXdlbGY1dXFKQzB3Tml1TVh4aExkeGRCU3hwY0U4WGcyd0ZSNlVZcmY0V3c9PQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "YWN0aW9u",
+ "value": "L2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnQmVnaW5SZWRlbGVnYXRl",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjI1NTF1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjI1NTF1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjI1NTF1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "redelegate",
+ "attributes": [
+ {
+ "key": "c291cmNlX3ZhbGlkYXRvcg==",
+ "value": "dGVycmF2YWxvcGVyMWt0dTdhNndxbGs2dmx5d2Y0cnQ2d2ZjeHVwaGMwZXMyN3AwcXZ4",
+ "index": true
+ },
+ {
+ "key": "ZGVzdGluYXRpb25fdmFsaWRhdG9y",
+ "value": "dGVycmF2YWxvcGVyMTBjMDR5c3o5dXpueDJta2VudWszajNlc2pjenlxaDBqNzgzbnp0",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTkzMjAwMDAwMHVsdW5h",
+ "index": true
+ },
+ {
+ "key": "Y29tcGxldGlvbl90aW1l",
+ "value": "MjAyMi0wNi0xOFQwNjoyNjo1M1o=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "bW9kdWxl",
+ "value": "c3Rha2luZw==",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "height": "201",
+ "txhash": "D084544055655650C275E74F7B4C8CEAFF2EC12A3D613467E67D5033FC3E459E",
+ "codespace": "",
+ "code": 0,
+ "data": "0A1E0A1C2F636F736D6F732E62616E6B2E763162657461312E4D736753656E64",
+ "raw_log": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm\"},{\"key\":\"amount\",\"value\":\"1000000uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20\"},{\"key\":\"amount\",\"value\":\"1000000uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.bank.v1beta1.MsgSend\"},{\"key\":\"sender\",\"value\":\"terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm\"},{\"key\":\"sender\",\"value\":\"terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20\"},{\"key\":\"amount\",\"value\":\"1000000uluna\"}]}]}]",
+ "logs": [
+ {
+ "msg_index": 0,
+ "log": "",
+ "events": [
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "receiver",
+ "value": "terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm"
+ },
+ {
+ "key": "amount",
+ "value": "1000000uluna"
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "spender",
+ "value": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20"
+ },
+ {
+ "key": "amount",
+ "value": "1000000uluna"
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "action",
+ "value": "/cosmos.bank.v1beta1.MsgSend"
+ },
+ {
+ "key": "sender",
+ "value": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20"
+ },
+ {
+ "key": "module",
+ "value": "bank"
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "recipient",
+ "value": "terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm"
+ },
+ {
+ "key": "sender",
+ "value": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20"
+ },
+ {
+ "key": "amount",
+ "value": "1000000uluna"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "info": "",
+ "gas_wanted": "141580",
+ "gas_used": "97104",
+ "tx": {
+ "@type": "/cosmos.tx.v1beta1.Tx",
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.bank.v1beta1.MsgSend",
+ "from_address": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20",
+ "to_address": "terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm",
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "1000000"
+ }
+ ]
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AgLuDN/jos126YbtrTAxvL76/JZ8SxR+F/NM9yHh8U8G"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_DIRECT"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "21237"
+ }
+ ],
+ "gas_limit": "141580",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "iekH9rrafxakxEmbTQbpkg9h0KsHGkgUG0eZKVFgwKUi32FoZaa/UAS+n8wnCEQ1Wa5z66r93lOicd1reZjCBg=="
+ ]
+ },
+ "timestamp": "2022-05-28T06:26:53Z",
+ "events": [
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjEyMzd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjEyMzd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjEyMzd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "ZmVl",
+ "value": "MjEyMzd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "YWNjX3NlcQ==",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjAvMA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "c2lnbmF0dXJl",
+ "value": "aWVrSDlycmFmeGFreEVtYlRRYnBrZzloMEtzSEdrZ1VHMGVaS1ZGZ3dLVWkzMkZvWmFhL1VBUytuOHduQ0VRMVdhNXo2NnI5M2xPaWNkMXJlWmpDQmc9PQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "YWN0aW9u",
+ "value": "L2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTAwMDAwMHVsdW5h",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExemp3Y2RnOW1oODB4dHUzN3ZxcXVlYWU2anV4ZnNkMGY4MmF5bG0=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTAwMDAwMHVsdW5h",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExemp3Y2RnOW1oODB4dHUzN3ZxcXVlYWU2anV4ZnNkMGY4MmF5bG0=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTAwMDAwMHVsdW5h",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "bW9kdWxl",
+ "value": "YmFuaw==",
+ "index": true
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "height": "201",
+ "txhash": "C5967C255A473CE4D9D47CBE9DC1E32613ACBC5D98363EFF3754370E4F714627",
+ "codespace": "",
+ "code": 0,
+ "data": "0A370A252F636F736D6F732E7374616B696E672E763162657461312E4D7367556E64656C6567617465120E0A0C08ADE0B595061095B3998502",
+ "raw_log": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu\"},{\"key\":\"amount\",\"value\":\"191uluna\"},{\"key\":\"receiver\",\"value\":\"terra1tygms3xhhs3yv487phx3dw4a95jn7t7l8l07dr\"},{\"key\":\"amount\",\"value\":\"32454390uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"191uluna\"},{\"key\":\"spender\",\"value\":\"terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh\"},{\"key\":\"amount\",\"value\":\"32454390uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.staking.v1beta1.MsgUndelegate\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"sender\",\"value\":\"terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"191uluna\"},{\"key\":\"recipient\",\"value\":\"terra1tygms3xhhs3yv487phx3dw4a95jn7t7l8l07dr\"},{\"key\":\"sender\",\"value\":\"terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh\"},{\"key\":\"amount\",\"value\":\"32454390uluna\"}]},{\"type\":\"unbond\",\"attributes\":[{\"key\":\"validator\",\"value\":\"terravaloper1wwq9mfkkqfta334d4hcjrjau9y50a4hw37zhdl\"},{\"key\":\"amount\",\"value\":\"32454390uluna\"},{\"key\":\"completion_time\",\"value\":\"2022-06-18T06:26:53Z\"}]}]}]",
+ "logs": [
+ {
+ "msg_index": 0,
+ "log": "",
+ "events": [
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "receiver",
+ "value": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu"
+ },
+ {
+ "key": "amount",
+ "value": "191uluna"
+ },
+ {
+ "key": "receiver",
+ "value": "terra1tygms3xhhs3yv487phx3dw4a95jn7t7l8l07dr"
+ },
+ {
+ "key": "amount",
+ "value": "32454390uluna"
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "spender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "191uluna"
+ },
+ {
+ "key": "spender",
+ "value": "terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh"
+ },
+ {
+ "key": "amount",
+ "value": "32454390uluna"
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "action",
+ "value": "/cosmos.staking.v1beta1.MsgUndelegate"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "sender",
+ "value": "terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh"
+ },
+ {
+ "key": "module",
+ "value": "staking"
+ },
+ {
+ "key": "sender",
+ "value": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu"
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "recipient",
+ "value": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "191uluna"
+ },
+ {
+ "key": "recipient",
+ "value": "terra1tygms3xhhs3yv487phx3dw4a95jn7t7l8l07dr"
+ },
+ {
+ "key": "sender",
+ "value": "terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh"
+ },
+ {
+ "key": "amount",
+ "value": "32454390uluna"
+ }
+ ]
+ },
+ {
+ "type": "unbond",
+ "attributes": [
+ {
+ "key": "validator",
+ "value": "terravaloper1wwq9mfkkqfta334d4hcjrjau9y50a4hw37zhdl"
+ },
+ {
+ "key": "amount",
+ "value": "32454390uluna"
+ },
+ {
+ "key": "completion_time",
+ "value": "2022-06-18T06:26:53Z"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "info": "",
+ "gas_wanted": "353908",
+ "gas_used": "194239",
+ "tx": {
+ "@type": "/cosmos.tx.v1beta1.Tx",
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgUndelegate",
+ "delegator_address": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu",
+ "validator_address": "terravaloper1wwq9mfkkqfta334d4hcjrjau9y50a4hw37zhdl",
+ "amount": {
+ "denom": "uluna",
+ "amount": "32454390"
+ }
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "A4bnZP/9hrCbP8gW3W1vNf3pp0ClUhHoY0TAhu8tMo4R"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_LEGACY_AMINO_JSON"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "53087"
+ }
+ ],
+ "gas_limit": "353908",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "7YN1xYnhJhFwqC/8DZQPhUNT4fTwZzhj4EGE7wTx1dtfOZgQCeWv6emVnLAf6EV5TSqnbIUN6jsvb/6Mtv69xw=="
+ ]
+ },
+ "timestamp": "2022-05-28T06:26:53Z",
+ "events": [
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NTMwODd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NTMwODd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NTMwODd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "ZmVl",
+ "value": "NTMwODd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "YWNjX3NlcQ==",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHUvMA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "c2lnbmF0dXJl",
+ "value": "N1lOMXhZbmhKaEZ3cUMvOERaUVBoVU5UNGZUd1p6aGo0RUdFN3dUeDFkdGZPWmdRQ2VXdjZlbVZuTEFmNkVWNVRTcW5iSVVONmpzdmIvNk10djY5eHc9PQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "YWN0aW9u",
+ "value": "L2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnVW5kZWxlZ2F0ZQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTkxdWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTkxdWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTkxdWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExZmw0OHZzbm1zZHpjdjg1cTVkMnE0ejVhamRoYTh5dTNubG4wbWg=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzI0NTQzOTB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExdHlnbXMzeGhoczN5djQ4N3BoeDNkdzRhOTVqbjd0N2w4bDA3ZHI=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzI0NTQzOTB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExdHlnbXMzeGhoczN5djQ4N3BoeDNkdzRhOTVqbjd0N2w4bDA3ZHI=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExZmw0OHZzbm1zZHpjdjg1cTVkMnE0ejVhamRoYTh5dTNubG4wbWg=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzI0NTQzOTB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExZmw0OHZzbm1zZHpjdjg1cTVkMnE0ejVhamRoYTh5dTNubG4wbWg=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "unbond",
+ "attributes": [
+ {
+ "key": "dmFsaWRhdG9y",
+ "value": "dGVycmF2YWxvcGVyMXd3cTltZmtrcWZ0YTMzNGQ0aGNqcmphdTl5NTBhNGh3Mzd6aGRs",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzI0NTQzOTB1bHVuYQ==",
+ "index": true
+ },
+ {
+ "key": "Y29tcGxldGlvbl90aW1l",
+ "value": "MjAyMi0wNi0xOFQwNjoyNjo1M1o=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "bW9kdWxl",
+ "value": "c3Rha2luZw==",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "pagination": {
+ "next_key": null,
+ "total": "540"
+ }
+}
\ No newline at end of file
diff --git a/test/NarrowIntegrationTests/TerraDotnet/GetTransactionsMatchingQueryResponse_02.json b/test/NarrowIntegrationTests/TerraDotnet/GetTransactionsMatchingQueryResponse_02.json
new file mode 100644
index 0000000..d854e6e
--- /dev/null
+++ b/test/NarrowIntegrationTests/TerraDotnet/GetTransactionsMatchingQueryResponse_02.json
@@ -0,0 +1,465 @@
+{
+ "txs": [
+ {
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward",
+ "delegator_address": "terra1puzp2yjqps43x7nse33svljc550xjz35jfygpe",
+ "validator_address": "terravaloper1puzp2yjqps43x7nse33svljc550xjz35jxg432"
+ },
+ {
+ "@type": "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission",
+ "validator_address": "terravaloper1puzp2yjqps43x7nse33svljc550xjz35jxg432"
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AjfxAgPiOEsXq4MR3NwZOTruL168Rx4o8DlU5kLmDJ4m"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_DIRECT"
+ }
+ },
+ "sequence": "3"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "30000"
+ }
+ ],
+ "gas_limit": "200000",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "iJm1PRHHyLem29CQS8xdzgw6Qp7111UKohsik4TJdCNwko0Y3reTq6QUPBTbqi4aEX3qiiZHrzmWX6CwkZ45Pg=="
+ ]
+ }
+ ],
+ "tx_responses": [
+ {
+ "height": "24",
+ "txhash": "0B701285895C87AFB2F7FB01B4C68EFE804A16C8A6C294F737E760FEC654B394",
+ "codespace": "",
+ "code": 0,
+ "data": "0A390A372F636F736D6F732E646973747269627574696F6E2E763162657461312E4D7367576974686472617744656C656761746F725265776172640A3D0A3B2F636F736D6F732E646973747269627574696F6E2E763162657461312E4D7367576974686472617756616C696461746F72436F6D6D697373696F6E",
+ "raw_log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\"},{\"key\":\"module\",\"value\":\"distribution\"},{\"key\":\"sender\",\"value\":\"terra1puzp2yjqps43x7nse33svljc550xjz35jfygpe\"}]},{\"type\":\"withdraw_rewards\",\"attributes\":[{\"key\":\"amount\"},{\"key\":\"validator\",\"value\":\"terravaloper1puzp2yjqps43x7nse33svljc550xjz35jxg432\"}]}]},{\"msg_index\":1,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1puzp2yjqps43x7nse33svljc550xjz35jfygpe\"},{\"key\":\"amount\",\"value\":\"138872uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"138872uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"module\",\"value\":\"distribution\"},{\"key\":\"sender\",\"value\":\"terravaloper1puzp2yjqps43x7nse33svljc550xjz35jxg432\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1puzp2yjqps43x7nse33svljc550xjz35jfygpe\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"138872uluna\"}]},{\"type\":\"withdraw_commission\",\"attributes\":[{\"key\":\"amount\",\"value\":\"138872uluna\"}]}]}]",
+ "logs": [
+ {
+ "msg_index": 0,
+ "log": "",
+ "events": [
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "action",
+ "value": "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward"
+ },
+ {
+ "key": "module",
+ "value": "distribution"
+ },
+ {
+ "key": "sender",
+ "value": "terra1puzp2yjqps43x7nse33svljc550xjz35jfygpe"
+ }
+ ]
+ },
+ {
+ "type": "withdraw_rewards",
+ "attributes": [
+ {
+ "key": "amount",
+ "value": ""
+ },
+ {
+ "key": "validator",
+ "value": "terravaloper1puzp2yjqps43x7nse33svljc550xjz35jxg432"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "msg_index": 1,
+ "log": "",
+ "events": [
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "receiver",
+ "value": "terra1puzp2yjqps43x7nse33svljc550xjz35jfygpe"
+ },
+ {
+ "key": "amount",
+ "value": "138872uluna"
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "spender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "138872uluna"
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "action",
+ "value": "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "module",
+ "value": "distribution"
+ },
+ {
+ "key": "sender",
+ "value": "terravaloper1puzp2yjqps43x7nse33svljc550xjz35jxg432"
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "recipient",
+ "value": "terra1puzp2yjqps43x7nse33svljc550xjz35jfygpe"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "138872uluna"
+ }
+ ]
+ },
+ {
+ "type": "withdraw_commission",
+ "attributes": [
+ {
+ "key": "amount",
+ "value": "138872uluna"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "info": "",
+ "gas_wanted": "200000",
+ "gas_used": "130157",
+ "tx": {
+ "@type": "/cosmos.tx.v1beta1.Tx",
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward",
+ "delegator_address": "terra1puzp2yjqps43x7nse33svljc550xjz35jfygpe",
+ "validator_address": "terravaloper1puzp2yjqps43x7nse33svljc550xjz35jxg432"
+ },
+ {
+ "@type": "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission",
+ "validator_address": "terravaloper1puzp2yjqps43x7nse33svljc550xjz35jxg432"
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AjfxAgPiOEsXq4MR3NwZOTruL168Rx4o8DlU5kLmDJ4m"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_DIRECT"
+ }
+ },
+ "sequence": "3"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "30000"
+ }
+ ],
+ "gas_limit": "200000",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "iJm1PRHHyLem29CQS8xdzgw6Qp7111UKohsik4TJdCNwko0Y3reTq6QUPBTbqi4aEX3qiiZHrzmWX6CwkZ45Pg=="
+ ]
+ },
+ "timestamp": "2022-05-28T06:07:38Z",
+ "events": [
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExcHV6cDJ5anFwczQzeDduc2UzM3N2bGpjNTUweGp6MzVqZnlncGU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzAwMDB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzAwMDB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExcHV6cDJ5anFwczQzeDduc2UzM3N2bGpjNTUweGp6MzVqZnlncGU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzAwMDB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExcHV6cDJ5anFwczQzeDduc2UzM3N2bGpjNTUweGp6MzVqZnlncGU=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "ZmVl",
+ "value": "MzAwMDB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "YWNjX3NlcQ==",
+ "value": "dGVycmExcHV6cDJ5anFwczQzeDduc2UzM3N2bGpjNTUweGp6MzVqZnlncGUvMw==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "c2lnbmF0dXJl",
+ "value": "aUptMVBSSEh5TGVtMjlDUVM4eGR6Z3c2UXA3MTExVUtvaHNpazRUSmRDTndrbzBZM3JlVHE2UVVQQlRicWk0YUVYM3FpaVpIcnptV1g2Q3drWjQ1UGc9PQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "YWN0aW9u",
+ "value": "L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd0RlbGVnYXRvclJld2FyZA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "withdraw_rewards",
+ "attributes": [
+ {
+ "key": "YW1vdW50",
+ "value": null,
+ "index": true
+ },
+ {
+ "key": "dmFsaWRhdG9y",
+ "value": "dGVycmF2YWxvcGVyMXB1enAyeWpxcHM0M3g3bnNlMzNzdmxqYzU1MHhqejM1anhnNDMy",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "bW9kdWxl",
+ "value": "ZGlzdHJpYnV0aW9u",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExcHV6cDJ5anFwczQzeDduc2UzM3N2bGpjNTUweGp6MzVqZnlncGU=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "YWN0aW9u",
+ "value": "L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd1ZhbGlkYXRvckNvbW1pc3Npb24=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTM4ODcydWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExcHV6cDJ5anFwczQzeDduc2UzM3N2bGpjNTUweGp6MzVqZnlncGU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTM4ODcydWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExcHV6cDJ5anFwczQzeDduc2UzM3N2bGpjNTUweGp6MzVqZnlncGU=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTM4ODcydWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "withdraw_commission",
+ "attributes": [
+ {
+ "key": "YW1vdW50",
+ "value": "MTM4ODcydWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "bW9kdWxl",
+ "value": "ZGlzdHJpYnV0aW9u",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmF2YWxvcGVyMXB1enAyeWpxcHM0M3g3bnNlMzNzdmxqYzU1MHhqejM1anhnNDMy",
+ "index": true
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "pagination": {
+ "next_key": null,
+ "total": "1"
+ }
+}
\ No newline at end of file
diff --git a/test/NarrowIntegrationTests/TerraDotnet/Terra2StakingQuerierTests.cs b/test/NarrowIntegrationTests/TerraDotnet/Terra2StakingQuerierTests.cs
new file mode 100644
index 0000000..7c61236
--- /dev/null
+++ b/test/NarrowIntegrationTests/TerraDotnet/Terra2StakingQuerierTests.cs
@@ -0,0 +1,48 @@
+using System.IO;
+using System.Text.Json;
+using System.Threading;
+using SapientFi.Infrastructure.Terra2.Indexers.Delegations.Storage;
+using SapientFi.Infrastructure.Terra2.Storage;
+using ServiceStack.OrmLite;
+using TerraDotnet;
+using TerraDotnet.TerraLcd.Messages;
+using Xunit;
+
+namespace NarrowIntegrationTests.TerraDotnet;
+
+public class Terra2StakingQuerierTests : IntegrationBaseTest
+{
+ private readonly Terra2RawRepository _rawRepository;
+ private readonly Terra2DelegationsRepository _delegationsRepository;
+
+ public Terra2StakingQuerierTests()
+ {
+ _rawRepository = new Terra2RawRepository(GetDbConnectionFactory());
+ _delegationsRepository = new Terra2DelegationsRepository(GetDbConnectionFactory());
+ }
+
+ private GetTransactionsMatchingQueryResponse? GetParsedJson()
+ {
+ var json = File.ReadAllText("TerraDotnet/GetTransactionsMatchingQueryResponse_02.json");
+
+ return JsonSerializer.Deserialize(
+ json,
+ TerraJsonSerializerOptions.GetThem()
+ );
+ }
+
+ [Fact]
+ public async void gfjdghjd()
+ {
+ var db = await _delegationsRepository.GetDbConnectionAsync(new CancellationToken());
+ var valAddr = "terravaloper1plxp55happ379eevsnk2xeuwzsrldsmqduyu36";
+
+ var theSum = await db.ScalarAsync(
+ db.From()
+ .Select(q => Sql.Sum(q.Amount))
+ .Where(x => x.ValidatorAddress == valAddr)
+ );
+
+ Assert.Equal(0L, theSum);
+ }
+}
diff --git a/test/NarrowIntegrationTests/TerraDotnet/TerraTransactionEnumeratorTests.cs b/test/NarrowIntegrationTests/TerraDotnet/TerraTransactionEnumeratorTests.cs
new file mode 100644
index 0000000..6431f94
--- /dev/null
+++ b/test/NarrowIntegrationTests/TerraDotnet/TerraTransactionEnumeratorTests.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging.Abstractions;
+using SapientFi.Infrastructure.Terra2;
+using SapientFi.Infrastructure.Terra2.TransactionListener;
+using TerraDotnet;
+using TerraDotnet.TerraLcd;
+using Xunit;
+
+namespace NarrowIntegrationTests.TerraDotnet;
+
+public class TerraTransactionEnumeratorTests
+{
+ private readonly CosmosTransactionEnumerator _transactionEnumerator;
+
+ public TerraTransactionEnumeratorTests()
+ {
+ var services = new ServiceCollection();
+ services.InternalAddLcdClient("https://phoenix-lcd.sapient.fi");
+
+ var client = services.BuildServiceProvider().GetRequiredService>();
+
+ _transactionEnumerator = new Terra2TransactionEnumerator(
+ NullLogger>.Instance,
+ client
+ );
+ }
+
+ [Fact(Skip = "this is only meant to be an easy way for devs to debug the enumerator")]
+ public async Task TriggerTheEnumerator()
+ {
+ var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+
+ var enumeration = _transactionEnumerator.EnumerateTransactionsAsync(0, tokenSource.Token);
+
+ await foreach (var response in enumeration.WithCancellation(tokenSource.Token))
+ {
+ var stop = "hammer time";
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/NarrowIntegrationTests/TerraDotnet/TerraTransactionParsingTests.cs b/test/NarrowIntegrationTests/TerraDotnet/TerraTransactionParsingTests.cs
new file mode 100644
index 0000000..3b2241f
--- /dev/null
+++ b/test/NarrowIntegrationTests/TerraDotnet/TerraTransactionParsingTests.cs
@@ -0,0 +1,206 @@
+using System;
+using System.IO;
+using System.Text.Json;
+using System.Threading;
+using SapientFi.Infrastructure.Terra2.Storage;
+using TerraDotnet;
+using TerraDotnet.TerraLcd.Messages;
+using Xunit;
+
+namespace NarrowIntegrationTests.TerraDotnet;
+
+public class TerraTransactionParsingTests : IntegrationBaseTest
+{
+ private readonly Terra2RawRepository _rawRepository;
+
+ public TerraTransactionParsingTests()
+ {
+ _rawRepository = new Terra2RawRepository(GetDbConnectionFactory());
+ }
+
+ private GetTransactionsMatchingQueryResponse? GetParsedJson_01()
+ {
+ var json = File.ReadAllText("TerraDotnet/GetTransactionsMatchingQueryResponse_01.json");
+
+ return JsonSerializer.Deserialize(
+ json,
+ TerraJsonSerializerOptions.GetThem()
+ );
+ }
+
+ private GetTransactionsMatchingQueryResponse? GetParsedJson_02()
+ {
+ var json = File.ReadAllText("TerraDotnet/GetTransactionsMatchingQueryResponse_02.json");
+
+ return JsonSerializer.Deserialize(
+ json,
+ TerraJsonSerializerOptions.GetThem()
+ );
+ }
+
+ [Fact]
+ public async void SerializingAndDeserializingTransactionFromDatabaseProducesOriginalTransaction()
+ {
+ #region ___ARRANGE___
+ const int ID = 2;
+
+ await EnsureEmptyAsync();
+
+ var tx = GetParsedJson_02()!.TransactionResponses[0];
+ var rawTxEntity = new Terra2RawTransactionEntity
+ {
+ Id = ID,
+ Height = tx.HeightAsInt,
+ CreatedAt = tx.CreatedAt,
+ TxHash = tx.TransactionHash,
+ RawTx = JsonSerializer.Serialize(tx, TerraJsonSerializerOptions.GetThem())
+ };
+
+ #endregion
+
+ // ************************************************************************************************
+
+ #region ___ACT___
+
+ await _rawRepository.SaveRawTransactionAsync(rawTxEntity);
+ var rawTxEntityFromDb = await _rawRepository.GetByIdOrDefaultAsync(ID);
+
+ var rawTxDes = JsonSerializer.Deserialize(rawTxEntity.RawTx);
+ var txFromDbDes = JsonSerializer.Deserialize(rawTxEntityFromDb!.RawTx);
+
+ #endregion
+
+ // ************************************************************************************************
+
+ #region ___ASSERT___
+
+ Assert.Equal(2, rawTxDes!.Transaction.Body.Messages.Count);
+ Assert.Equal(2, txFromDbDes!.Transaction.Body.Messages.Count);
+
+
+ Assert.Equal(rawTxEntity.Id, rawTxEntityFromDb.Id);
+ Assert.Equal(rawTxEntity.Height, rawTxEntityFromDb.Height);
+ Assert.Equal(rawTxEntity.CreatedAt, rawTxEntityFromDb.CreatedAt);
+ Assert.Equal(rawTxEntity.TxHash, rawTxEntityFromDb.TxHash);
+
+ // RawTx equality
+ var txBeforeDb = rawTxDes;
+ var txFromDb = txFromDbDes;
+
+ Assert.Equal(txBeforeDb.Code, txFromDb.Code);
+ Assert.Equal(txBeforeDb.CodeSpace, txFromDb.CodeSpace);
+ Assert.Equal(txBeforeDb.Data, txFromDb.Data);
+ Assert.Equal(txBeforeDb.CreatedAt, txFromDb.CreatedAt);
+ Assert.Equal(txBeforeDb.Events.Count, txFromDb.Events.Count);
+ Assert.Equal(txBeforeDb.GasUsed, txFromDb.GasUsed);
+ Assert.Equal(txBeforeDb.GasWanted, txFromDb.GasWanted);
+ Assert.Equal(txBeforeDb.Height, txFromDb.Height);
+ Assert.Equal(txBeforeDb.HeightAsInt, txFromDb.HeightAsInt);
+ Assert.Equal(txBeforeDb.Info, txFromDb.Info);
+ Assert.Equal(txBeforeDb.Logs.Count, txFromDb.Logs.Count);
+ Assert.Equal(txBeforeDb.RawGasUsed, txFromDb.RawGasUsed);
+ Assert.Equal(txBeforeDb.RawGasWanted, txFromDb.RawGasWanted);
+ Assert.Equal(txBeforeDb.RawLog, txFromDb.RawLog);
+ Assert.Equal(txBeforeDb.TransactionHash, txFromDb.TransactionHash);
+
+ #endregion
+ }
+
+ [Fact]
+ public async void CanDeserialize_TxFromDb()
+ {
+ await EnsureEmptyAsync();
+ var actual = GetParsedJson_01();
+
+ Assert.NotNull(actual);
+
+ // transactions
+ Assert.Equal(3, actual!.TransactionResponses.Count);
+
+ var tx = actual.TransactionResponses[0];
+
+ var rawTxEntity = new Terra2RawTransactionEntity
+ {
+ Id = 1,
+ Height = tx.HeightAsInt,
+ CreatedAt = tx.CreatedAt,
+ TxHash = tx.TransactionHash,
+ RawTx = JsonSerializer.Serialize(tx, TerraJsonSerializerOptions.GetThem())
+ };
+
+ var ct = new CancellationToken();
+ await _rawRepository.SaveRawTransactionAsync(rawTxEntity, ct);
+
+ var txFromDb = await _rawRepository.GetByIdOrDefaultAsync(1, ct);
+
+ Assert.NotNull(txFromDb);
+ Assert.Equal(201, txFromDb!.Height);
+ Assert.Equal("74B8212F209C7F225F79B7C0CA064160CC3C9D589A4F4AB6849071A0DAFED5A3", txFromDb.TxHash);
+ Assert.Equal(new DateTimeOffset(2022, 5, 28, 6, 26, 53, TimeSpan.Zero), txFromDb.CreatedAt);
+
+
+ var rawTxFromDb = JsonSerializer.Deserialize(txFromDb.RawTx)!;
+
+ Assert.Equal("201", rawTxFromDb.Height);
+ Assert.Equal(201, rawTxFromDb.HeightAsInt);
+ Assert.Equal("74B8212F209C7F225F79B7C0CA064160CC3C9D589A4F4AB6849071A0DAFED5A3", rawTxFromDb.TransactionHash);
+ Assert.Empty(rawTxFromDb.CodeSpace);
+ Assert.Equal(0, rawTxFromDb.Code);
+ Assert.Equal("0A3C0A2A2F636F736D6F732E7374616B696E672E763162657461312E4D7367426567696E526564656C6567617465120E0A0C08ADE0B595061095B3998502", rawTxFromDb.Data);
+ Assert.Equal("[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.staking.v1beta1.MsgBeginRedelegate\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"}]},{\"type\":\"redelegate\",\"attributes\":[{\"key\":\"source_validator\",\"value\":\"terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx\"},{\"key\":\"destination_validator\",\"value\":\"terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt\"},{\"key\":\"amount\",\"value\":\"1932000000uluna\"},{\"key\":\"completion_time\",\"value\":\"2022-06-18T06:26:53Z\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]}]}]", rawTxFromDb.RawLog);
+ Assert.Single(rawTxFromDb.Logs);
+
+ var log = rawTxFromDb.Logs[0];
+ Assert.Equal(0, log.MessageIndex);
+ Assert.Empty(log.Log);
+ Assert.Equal(5, log.Events.Count);
+
+ var logEvent2 = log.Events[1];
+ Assert.Equal("coin_spent", logEvent2.Type);
+ Assert.Equal(2, logEvent2.Attributes.Count);
+ Assert.Equal("spender", logEvent2.Attributes[0].Key);
+ Assert.Equal("terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl", logEvent2.Attributes[0].Value);
+ Assert.Equal("amount", logEvent2.Attributes[1].Key);
+ Assert.Equal("22551uluna", logEvent2.Attributes[1].Value);
+
+ Assert.Empty(rawTxFromDb.Info);
+ Assert.Equal("448586", rawTxFromDb.RawGasWanted);
+ Assert.Equal(448586, rawTxFromDb.GasWanted);
+ Assert.Equal("261855", rawTxFromDb.RawGasUsed);
+ Assert.Equal(261855, rawTxFromDb.GasUsed);
+ Assert.NotNull(rawTxFromDb.Transaction);
+ Assert.Equal(new DateTimeOffset(2022, 5, 28, 6, 26, 53, TimeSpan.Zero), rawTxFromDb.CreatedAt);
+
+ Assert.Equal(14, rawTxFromDb.Events.Count);
+
+ var event1 = rawTxFromDb.Events[0];
+ Assert.Equal("coin_spent", event1.Type);
+ Assert.Equal(2, event1.Attributes.Count);
+ Assert.Equal("c3BlbmRlcg==", event1.Attributes[0].Key);
+ Assert.Equal("dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=", event1.Attributes[0].Value);
+ Assert.True(event1.Attributes[0].Index);
+ Assert.Equal("YW1vdW50", event1.Attributes[1].Key);
+ Assert.Equal("NjcyODh1bHVuYQ==", event1.Attributes[1].Value);
+ Assert.True(event1.Attributes[1].Index);
+
+
+ var messages = rawTxFromDb.Transaction.Body.Messages;
+ Assert.Single(messages);
+
+ var parseStatusResult = TerraMessageParser.TryParse(messages[0], out var parsedMessage);
+ Assert.True(parseStatusResult);
+ Assert.NotNull(parsedMessage);
+
+ Assert.Equal("/cosmos.staking.v1beta1.MsgBeginRedelegate", parsedMessage!.Type);
+ var parsedRedelegateMessage = (CosmosRedelegateMessage)parsedMessage;
+
+ Assert.Equal("terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0", parsedRedelegateMessage.DelegatorAddress);
+ Assert.Equal("terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx", parsedRedelegateMessage.ValidatorSourceAddress);
+ Assert.Equal("terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt", parsedRedelegateMessage.ValidatorDestinationAddress);
+
+ Assert.NotNull(parsedRedelegateMessage.Amount);
+ Assert.Equal("uluna", parsedRedelegateMessage.Amount!.Denominator);
+ Assert.Equal("1932000000", parsedRedelegateMessage.Amount!.Amount);
+ //*/
+ }
+}
diff --git a/test/UnitTests/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponseTests.cs b/test/UnitTests/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponseTests.cs
new file mode 100644
index 0000000..a13f7e1
--- /dev/null
+++ b/test/UnitTests/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponseTests.cs
@@ -0,0 +1,246 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text.Json;
+using SapientFi.Infrastructure.Terra2.Storage;
+using TerraDotnet;
+using TerraDotnet.TerraLcd.Messages;
+using Xunit;
+
+namespace UnitTests.TerraDotnet.TerraLcd.Messages;
+
+public class GetTransactionsMatchingQueryResponseTests
+{
+ private GetTransactionsMatchingQueryResponse? GetParsedJson()
+ {
+ var json = File.ReadAllText("TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponse_01.json");
+
+ return JsonSerializer.Deserialize(json,
+ TerraJsonSerializerOptions.GetThem()
+ );
+ }
+
+ /* ************************************************************************************************************
+ */
+ [Fact]
+ public void CanDeserialize_txs()
+ {
+ var actual = GetParsedJson();
+
+ Assert.NotNull(actual);
+
+ // transactions
+ Assert.Equal(3, actual!.Transactions.Count);
+
+ var first = actual.Transactions[0];
+
+ Assert.NotNull(first.Body);
+ Assert.Single(first.Body.Messages);
+ Assert.Empty(first.Body.Memo);
+ Assert.Equal("0", first.Body.TimeoutHeight);
+ }
+
+
+ /* ************************************************************************************************************
+ */
+ [Fact]
+ public void CanDeserialize_tx_responses()
+ {
+ var actual = GetParsedJson();
+
+ Assert.NotNull(actual);
+
+ // transactions
+ Assert.Equal(3, actual!.TransactionResponses.Count);
+
+ var first = actual.TransactionResponses[0];
+
+ Assert.Equal("201", first.Height);
+ Assert.Equal(201, first.HeightAsInt);
+ Assert.Equal("74B8212F209C7F225F79B7C0CA064160CC3C9D589A4F4AB6849071A0DAFED5A3", first.TransactionHash);
+ Assert.Empty(first.CodeSpace);
+ Assert.Equal(0, first.Code);
+ Assert.Equal(
+ "0A3C0A2A2F636F736D6F732E7374616B696E672E763162657461312E4D7367426567696E526564656C6567617465120E0A0C08ADE0B595061095B3998502",
+ first.Data
+ );
+ Assert.Equal(
+ "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.staking.v1beta1.MsgBeginRedelegate\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"}]},{\"type\":\"redelegate\",\"attributes\":[{\"key\":\"source_validator\",\"value\":\"terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx\"},{\"key\":\"destination_validator\",\"value\":\"terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt\"},{\"key\":\"amount\",\"value\":\"1932000000uluna\"},{\"key\":\"completion_time\",\"value\":\"2022-06-18T06:26:53Z\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]}]}]",
+ first.RawLog
+ );
+ Assert.Single(first.Logs);
+
+ var log = first.Logs[0];
+ Assert.Equal(0, log.MessageIndex);
+ Assert.Empty(log.Log);
+ Assert.Equal(5, log.Events.Count);
+
+ var logEvent2 = log.Events[1];
+ Assert.Equal("coin_spent", logEvent2.Type);
+ Assert.Equal(2, logEvent2.Attributes.Count);
+ Assert.Equal("spender", logEvent2.Attributes[0].Key);
+ Assert.Equal("terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl", logEvent2.Attributes[0].Value);
+ Assert.Equal("amount", logEvent2.Attributes[1].Key);
+ Assert.Equal("22551uluna", logEvent2.Attributes[1].Value);
+
+ Assert.Empty(first.Info);
+ Assert.Equal("448586", first.RawGasWanted);
+ Assert.Equal(448586, first.GasWanted);
+ Assert.Equal("261855", first.RawGasUsed);
+ Assert.Equal(261855, first.GasUsed);
+ Assert.NotNull(first.Transaction);
+ Assert.Equal(new DateTimeOffset(2022, 5, 28, 6, 26, 53, TimeSpan.Zero), first.CreatedAt);
+
+ Assert.Equal(14, first.Events.Count);
+
+ var event1 = first.Events[0];
+ Assert.Equal("coin_spent", event1.Type);
+ Assert.Equal(2, event1.Attributes.Count);
+ Assert.Equal("c3BlbmRlcg==", event1.Attributes[0].Key);
+ Assert.Equal("dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=", event1.Attributes[0].Value);
+ Assert.True(event1.Attributes[0].Index);
+ Assert.Equal("YW1vdW50", event1.Attributes[1].Key);
+ Assert.Equal("NjcyODh1bHVuYQ==", event1.Attributes[1].Value);
+ Assert.True(event1.Attributes[1].Index);
+
+
+
+ var messages = first.Transaction.Body.Messages;
+ Assert.Single(messages);
+
+ var parseStatusResult = TerraMessageParser.TryParse(messages[0], out var parsedMessage);
+ Assert.True(parseStatusResult);
+ Assert.NotNull(parsedMessage);
+
+ Assert.Equal("/cosmos.staking.v1beta1.MsgBeginRedelegate", parsedMessage!.Type);
+ var parsedRedelegateMessage = (CosmosRedelegateMessage)parsedMessage;
+
+ Assert.Equal("terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0", parsedRedelegateMessage.DelegatorAddress);
+ Assert.Equal("terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx",
+ parsedRedelegateMessage.ValidatorSourceAddress
+ );
+ Assert.Equal("terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt",
+ parsedRedelegateMessage.ValidatorDestinationAddress
+ );
+
+ Assert.NotNull(parsedRedelegateMessage.Amount);
+ Assert.Equal("uluna", parsedRedelegateMessage.Amount!.Denominator);
+ Assert.Equal("1932000000", parsedRedelegateMessage.Amount!.Amount);
+ //*/
+ }
+
+ [Fact]
+ public void CanDeserialize_LcdTxResponse()
+ {
+ const int ID = 1;
+ var actual = GetParsedJson();
+
+ // transactions
+ Assert.Equal(3, actual!.TransactionResponses.Count);
+
+ var tx = actual.TransactionResponses[0];
+
+ var rawTxEntity = new Terra2RawTransactionEntity
+ {
+ Id = ID,
+ Height = tx.HeightAsInt,
+ CreatedAt = tx.CreatedAt,
+ TxHash = tx.TransactionHash,
+ RawTx = JsonSerializer.Serialize(tx, TerraJsonSerializerOptions.GetThem())
+ };
+
+ #region rawTransaction
+ var rawTransaction = rawTxEntity.RawTx;
+
+ var deserializedTx = JsonSerializer.Deserialize(rawTransaction)!;
+
+ Assert.Equal("201", deserializedTx.Height);
+ Assert.Equal(201, deserializedTx.HeightAsInt);
+ Assert.Equal("74B8212F209C7F225F79B7C0CA064160CC3C9D589A4F4AB6849071A0DAFED5A3", deserializedTx.TransactionHash);
+ Assert.Empty(deserializedTx.CodeSpace);
+ Assert.Equal(0, deserializedTx.Code);
+ Assert.Equal("0A3C0A2A2F636F736D6F732E7374616B696E672E763162657461312E4D7367426567696E526564656C6567617465120E0A0C08ADE0B595061095B3998502", deserializedTx.Data);
+ Assert.Equal("[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.staking.v1beta1.MsgBeginRedelegate\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"}]},{\"type\":\"redelegate\",\"attributes\":[{\"key\":\"source_validator\",\"value\":\"terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx\"},{\"key\":\"destination_validator\",\"value\":\"terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt\"},{\"key\":\"amount\",\"value\":\"1932000000uluna\"},{\"key\":\"completion_time\",\"value\":\"2022-06-18T06:26:53Z\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]}]}]", deserializedTx.RawLog);
+ Assert.Single(deserializedTx.Logs);
+
+ var log = deserializedTx.Logs[0];
+ Assert.Equal(0, log.MessageIndex);
+ Assert.Empty(log.Log);
+ Assert.Equal(5, log.Events.Count);
+
+ var logEvent2 = log.Events[1];
+ Assert.Equal("coin_spent", logEvent2.Type);
+ Assert.Equal(2, logEvent2.Attributes.Count);
+ Assert.Equal("spender", logEvent2.Attributes[0].Key);
+ Assert.Equal("terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl", logEvent2.Attributes[0].Value);
+ Assert.Equal("amount", logEvent2.Attributes[1].Key);
+ Assert.Equal("22551uluna", logEvent2.Attributes[1].Value);
+
+ Assert.Empty(deserializedTx.Info);
+ Assert.Equal("448586", deserializedTx.RawGasWanted);
+ Assert.Equal(448586, deserializedTx.GasWanted);
+ Assert.Equal("261855", deserializedTx.RawGasUsed);
+ Assert.Equal(261855, deserializedTx.GasUsed);
+ Assert.NotNull(deserializedTx.Transaction);
+ Assert.Equal(new DateTimeOffset(2022, 5, 28, 6, 26, 53, TimeSpan.Zero), deserializedTx.CreatedAt);
+
+ Assert.Equal(14, deserializedTx.Events.Count);
+
+ var event1 = deserializedTx.Events[0];
+ Assert.Equal("coin_spent", event1.Type);
+ Assert.Equal(2, event1.Attributes.Count);
+ Assert.Equal("c3BlbmRlcg==", event1.Attributes[0].Key);
+ Assert.Equal("dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=", event1.Attributes[0].Value);
+ Assert.True(event1.Attributes[0].Index);
+ Assert.Equal("YW1vdW50", event1.Attributes[1].Key);
+ Assert.Equal("NjcyODh1bHVuYQ==", event1.Attributes[1].Value);
+ Assert.True(event1.Attributes[1].Index);
+
+ List> messages = deserializedTx.Transaction.Body.Messages;
+ Assert.Single(messages);
+
+ var parseStatusResult = TerraMessageParser.TryParse(messages[0], out var parsedMessage);
+ Assert.True(parseStatusResult);
+ Assert.NotNull(parsedMessage);
+
+ Assert.Equal("/cosmos.staking.v1beta1.MsgBeginRedelegate", parsedMessage!.Type);
+ var parsedRedelegateMessage = (CosmosRedelegateMessage)parsedMessage;
+
+ Assert.Equal("terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0", parsedRedelegateMessage.DelegatorAddress);
+ Assert.Equal("terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx", parsedRedelegateMessage.ValidatorSourceAddress);
+ Assert.Equal("terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt", parsedRedelegateMessage.ValidatorDestinationAddress);
+
+ Assert.NotNull(parsedRedelegateMessage.Amount);
+ Assert.Equal("uluna", parsedRedelegateMessage.Amount!.Denominator);
+ Assert.Equal("1932000000", parsedRedelegateMessage.Amount!.Amount);
+ //*/
+ #endregion
+
+ /* **************************************************************** */
+
+ #region rawTxEntity
+
+ Assert.Equal(201, rawTxEntity.Height);
+ Assert.Equal("74B8212F209C7F225F79B7C0CA064160CC3C9D589A4F4AB6849071A0DAFED5A3", rawTxEntity.TxHash);
+
+ Assert.Equal(new DateTimeOffset(2022, 5, 28, 6, 26, 53, TimeSpan.Zero), rawTxEntity.CreatedAt);
+
+ #endregion
+ }
+
+ /* ************************************************************************************************************
+ */
+ [Fact]
+ public void CanDeserialize_pagination()
+ {
+ var actual = GetParsedJson();
+
+ Assert.NotNull(actual);
+
+ Assert.NotNull(actual!.Pagination);
+
+ Assert.Null(actual.Pagination.NextKey);
+ Assert.Equal("540", actual.Pagination.Total);
+ Assert.Equal(540, actual.Pagination.TotalAsInt);
+ }
+}
\ No newline at end of file
diff --git a/test/UnitTests/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponse_01.json b/test/UnitTests/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponse_01.json
new file mode 100644
index 0000000..5e69e8b
--- /dev/null
+++ b/test/UnitTests/TerraDotnet/TerraLcd/Messages/GetTransactionsMatchingQueryResponse_01.json
@@ -0,0 +1,1262 @@
+{
+ "txs": [
+ {
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgBeginRedelegate",
+ "delegator_address": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0",
+ "validator_src_address": "terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx",
+ "validator_dst_address": "terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt",
+ "amount": {
+ "denom": "uluna",
+ "amount": "1932000000"
+ }
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AiVuv8AMDt0fGGxF8DEcDhR28sflA+j8GfH5/D59FG1L"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_LEGACY_AMINO_JSON"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "67288"
+ }
+ ],
+ "gas_limit": "448586",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "0sIQZiBfcQrBzGA6eJe0OrdTx3GBhClUBsKYWSOmiUEwelf5uqJC0wNiuMXxhLdxdBSxpcE8Xg2wFR6UYrf4Ww=="
+ ]
+ },
+ {
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.bank.v1beta1.MsgSend",
+ "from_address": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20",
+ "to_address": "terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm",
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "1000000"
+ }
+ ]
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AgLuDN/jos126YbtrTAxvL76/JZ8SxR+F/NM9yHh8U8G"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_DIRECT"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "21237"
+ }
+ ],
+ "gas_limit": "141580",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "iekH9rrafxakxEmbTQbpkg9h0KsHGkgUG0eZKVFgwKUi32FoZaa/UAS+n8wnCEQ1Wa5z66r93lOicd1reZjCBg=="
+ ]
+ },
+ {
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgUndelegate",
+ "delegator_address": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu",
+ "validator_address": "terravaloper1wwq9mfkkqfta334d4hcjrjau9y50a4hw37zhdl",
+ "amount": {
+ "denom": "uluna",
+ "amount": "32454390"
+ }
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "A4bnZP/9hrCbP8gW3W1vNf3pp0ClUhHoY0TAhu8tMo4R"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_LEGACY_AMINO_JSON"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "53087"
+ }
+ ],
+ "gas_limit": "353908",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "7YN1xYnhJhFwqC/8DZQPhUNT4fTwZzhj4EGE7wTx1dtfOZgQCeWv6emVnLAf6EV5TSqnbIUN6jsvb/6Mtv69xw=="
+ ]
+ }
+ ],
+ "tx_responses": [
+ {
+ "height": "201",
+ "txhash": "74B8212F209C7F225F79B7C0CA064160CC3C9D589A4F4AB6849071A0DAFED5A3",
+ "codespace": "",
+ "code": 0,
+ "data": "0A3C0A2A2F636F736D6F732E7374616B696E672E763162657461312E4D7367426567696E526564656C6567617465120E0A0C08ADE0B595061095B3998502",
+ "raw_log": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.staking.v1beta1.MsgBeginRedelegate\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"}]},{\"type\":\"redelegate\",\"attributes\":[{\"key\":\"source_validator\",\"value\":\"terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx\"},{\"key\":\"destination_validator\",\"value\":\"terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt\"},{\"key\":\"amount\",\"value\":\"1932000000uluna\"},{\"key\":\"completion_time\",\"value\":\"2022-06-18T06:26:53Z\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"22551uluna\"}]}]}]",
+ "logs": [
+ {
+ "msg_index": 0,
+ "log": "",
+ "events": [
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "receiver",
+ "value": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0"
+ },
+ {
+ "key": "amount",
+ "value": "22551uluna"
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "spender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "22551uluna"
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "action",
+ "value": "/cosmos.staking.v1beta1.MsgBeginRedelegate"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "module",
+ "value": "staking"
+ },
+ {
+ "key": "sender",
+ "value": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0"
+ }
+ ]
+ },
+ {
+ "type": "redelegate",
+ "attributes": [
+ {
+ "key": "source_validator",
+ "value": "terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx"
+ },
+ {
+ "key": "destination_validator",
+ "value": "terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt"
+ },
+ {
+ "key": "amount",
+ "value": "1932000000uluna"
+ },
+ {
+ "key": "completion_time",
+ "value": "2022-06-18T06:26:53Z"
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "recipient",
+ "value": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "22551uluna"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "info": "",
+ "gas_wanted": "448586",
+ "gas_used": "261855",
+ "tx": {
+ "@type": "/cosmos.tx.v1beta1.Tx",
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgBeginRedelegate",
+ "delegator_address": "terra1wcff43z8jweneamztk8uy74w3sy7rlmudxjdc0",
+ "validator_src_address": "terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx",
+ "validator_dst_address": "terravaloper10c04ysz9uznx2mkenuk3j3esjczyqh0j783nzt",
+ "amount": {
+ "denom": "uluna",
+ "amount": "1932000000"
+ }
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AiVuv8AMDt0fGGxF8DEcDhR28sflA+j8GfH5/D59FG1L"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_LEGACY_AMINO_JSON"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "67288"
+ }
+ ],
+ "gas_limit": "448586",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "0sIQZiBfcQrBzGA6eJe0OrdTx3GBhClUBsKYWSOmiUEwelf5uqJC0wNiuMXxhLdxdBSxpcE8Xg2wFR6UYrf4Ww=="
+ ]
+ },
+ "timestamp": "2022-05-28T06:26:53Z",
+ "events": [
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NjcyODh1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NjcyODh1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NjcyODh1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "ZmVl",
+ "value": "NjcyODh1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "YWNjX3NlcQ==",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzAvMA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "c2lnbmF0dXJl",
+ "value": "MHNJUVppQmZjUXJCekdBNmVKZTBPcmRUeDNHQmhDbFVCc0tZV1NPbWlVRXdlbGY1dXFKQzB3Tml1TVh4aExkeGRCU3hwY0U4WGcyd0ZSNlVZcmY0V3c9PQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "YWN0aW9u",
+ "value": "L2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnQmVnaW5SZWRlbGVnYXRl",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjI1NTF1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjI1NTF1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjI1NTF1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "redelegate",
+ "attributes": [
+ {
+ "key": "c291cmNlX3ZhbGlkYXRvcg==",
+ "value": "dGVycmF2YWxvcGVyMWt0dTdhNndxbGs2dmx5d2Y0cnQ2d2ZjeHVwaGMwZXMyN3AwcXZ4",
+ "index": true
+ },
+ {
+ "key": "ZGVzdGluYXRpb25fdmFsaWRhdG9y",
+ "value": "dGVycmF2YWxvcGVyMTBjMDR5c3o5dXpueDJta2VudWszajNlc2pjenlxaDBqNzgzbnp0",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTkzMjAwMDAwMHVsdW5h",
+ "index": true
+ },
+ {
+ "key": "Y29tcGxldGlvbl90aW1l",
+ "value": "MjAyMi0wNi0xOFQwNjoyNjo1M1o=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "bW9kdWxl",
+ "value": "c3Rha2luZw==",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExd2NmZjQzejhqd2VuZWFtenRrOHV5NzR3M3N5N3JsbXVkeGpkYzA=",
+ "index": true
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "height": "201",
+ "txhash": "D084544055655650C275E74F7B4C8CEAFF2EC12A3D613467E67D5033FC3E459E",
+ "codespace": "",
+ "code": 0,
+ "data": "0A1E0A1C2F636F736D6F732E62616E6B2E763162657461312E4D736753656E64",
+ "raw_log": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm\"},{\"key\":\"amount\",\"value\":\"1000000uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20\"},{\"key\":\"amount\",\"value\":\"1000000uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.bank.v1beta1.MsgSend\"},{\"key\":\"sender\",\"value\":\"terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm\"},{\"key\":\"sender\",\"value\":\"terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20\"},{\"key\":\"amount\",\"value\":\"1000000uluna\"}]}]}]",
+ "logs": [
+ {
+ "msg_index": 0,
+ "log": "",
+ "events": [
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "receiver",
+ "value": "terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm"
+ },
+ {
+ "key": "amount",
+ "value": "1000000uluna"
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "spender",
+ "value": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20"
+ },
+ {
+ "key": "amount",
+ "value": "1000000uluna"
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "action",
+ "value": "/cosmos.bank.v1beta1.MsgSend"
+ },
+ {
+ "key": "sender",
+ "value": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20"
+ },
+ {
+ "key": "module",
+ "value": "bank"
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "recipient",
+ "value": "terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm"
+ },
+ {
+ "key": "sender",
+ "value": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20"
+ },
+ {
+ "key": "amount",
+ "value": "1000000uluna"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "info": "",
+ "gas_wanted": "141580",
+ "gas_used": "97104",
+ "tx": {
+ "@type": "/cosmos.tx.v1beta1.Tx",
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.bank.v1beta1.MsgSend",
+ "from_address": "terra17dvkwc6srd82jk0eh96ayutf4fvwawjvrzkw20",
+ "to_address": "terra1zjwcdg9mh80xtu37vqqueae6juxfsd0f82aylm",
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "1000000"
+ }
+ ]
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "AgLuDN/jos126YbtrTAxvL76/JZ8SxR+F/NM9yHh8U8G"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_DIRECT"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "21237"
+ }
+ ],
+ "gas_limit": "141580",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "iekH9rrafxakxEmbTQbpkg9h0KsHGkgUG0eZKVFgwKUi32FoZaa/UAS+n8wnCEQ1Wa5z66r93lOicd1reZjCBg=="
+ ]
+ },
+ "timestamp": "2022-05-28T06:26:53Z",
+ "events": [
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjEyMzd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjEyMzd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MjEyMzd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "ZmVl",
+ "value": "MjEyMzd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "YWNjX3NlcQ==",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjAvMA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "c2lnbmF0dXJl",
+ "value": "aWVrSDlycmFmeGFreEVtYlRRYnBrZzloMEtzSEdrZ1VHMGVaS1ZGZ3dLVWkzMkZvWmFhL1VBUytuOHduQ0VRMVdhNXo2NnI5M2xPaWNkMXJlWmpDQmc9PQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "YWN0aW9u",
+ "value": "L2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTAwMDAwMHVsdW5h",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExemp3Y2RnOW1oODB4dHUzN3ZxcXVlYWU2anV4ZnNkMGY4MmF5bG0=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTAwMDAwMHVsdW5h",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExemp3Y2RnOW1oODB4dHUzN3ZxcXVlYWU2anV4ZnNkMGY4MmF5bG0=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTAwMDAwMHVsdW5h",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExN2R2a3djNnNyZDgyamswZWg5NmF5dXRmNGZ2d2F3anZyemt3MjA=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "bW9kdWxl",
+ "value": "YmFuaw==",
+ "index": true
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "height": "201",
+ "txhash": "C5967C255A473CE4D9D47CBE9DC1E32613ACBC5D98363EFF3754370E4F714627",
+ "codespace": "",
+ "code": 0,
+ "data": "0A370A252F636F736D6F732E7374616B696E672E763162657461312E4D7367556E64656C6567617465120E0A0C08ADE0B595061095B3998502",
+ "raw_log": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu\"},{\"key\":\"amount\",\"value\":\"191uluna\"},{\"key\":\"receiver\",\"value\":\"terra1tygms3xhhs3yv487phx3dw4a95jn7t7l8l07dr\"},{\"key\":\"amount\",\"value\":\"32454390uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"191uluna\"},{\"key\":\"spender\",\"value\":\"terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh\"},{\"key\":\"amount\",\"value\":\"32454390uluna\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.staking.v1beta1.MsgUndelegate\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"sender\",\"value\":\"terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu\"},{\"key\":\"sender\",\"value\":\"terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl\"},{\"key\":\"amount\",\"value\":\"191uluna\"},{\"key\":\"recipient\",\"value\":\"terra1tygms3xhhs3yv487phx3dw4a95jn7t7l8l07dr\"},{\"key\":\"sender\",\"value\":\"terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh\"},{\"key\":\"amount\",\"value\":\"32454390uluna\"}]},{\"type\":\"unbond\",\"attributes\":[{\"key\":\"validator\",\"value\":\"terravaloper1wwq9mfkkqfta334d4hcjrjau9y50a4hw37zhdl\"},{\"key\":\"amount\",\"value\":\"32454390uluna\"},{\"key\":\"completion_time\",\"value\":\"2022-06-18T06:26:53Z\"}]}]}]",
+ "logs": [
+ {
+ "msg_index": 0,
+ "log": "",
+ "events": [
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "receiver",
+ "value": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu"
+ },
+ {
+ "key": "amount",
+ "value": "191uluna"
+ },
+ {
+ "key": "receiver",
+ "value": "terra1tygms3xhhs3yv487phx3dw4a95jn7t7l8l07dr"
+ },
+ {
+ "key": "amount",
+ "value": "32454390uluna"
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "spender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "191uluna"
+ },
+ {
+ "key": "spender",
+ "value": "terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh"
+ },
+ {
+ "key": "amount",
+ "value": "32454390uluna"
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "action",
+ "value": "/cosmos.staking.v1beta1.MsgUndelegate"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "sender",
+ "value": "terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh"
+ },
+ {
+ "key": "module",
+ "value": "staking"
+ },
+ {
+ "key": "sender",
+ "value": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu"
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "recipient",
+ "value": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu"
+ },
+ {
+ "key": "sender",
+ "value": "terra1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8pm7utl"
+ },
+ {
+ "key": "amount",
+ "value": "191uluna"
+ },
+ {
+ "key": "recipient",
+ "value": "terra1tygms3xhhs3yv487phx3dw4a95jn7t7l8l07dr"
+ },
+ {
+ "key": "sender",
+ "value": "terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh"
+ },
+ {
+ "key": "amount",
+ "value": "32454390uluna"
+ }
+ ]
+ },
+ {
+ "type": "unbond",
+ "attributes": [
+ {
+ "key": "validator",
+ "value": "terravaloper1wwq9mfkkqfta334d4hcjrjau9y50a4hw37zhdl"
+ },
+ {
+ "key": "amount",
+ "value": "32454390uluna"
+ },
+ {
+ "key": "completion_time",
+ "value": "2022-06-18T06:26:53Z"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "info": "",
+ "gas_wanted": "353908",
+ "gas_used": "194239",
+ "tx": {
+ "@type": "/cosmos.tx.v1beta1.Tx",
+ "body": {
+ "messages": [
+ {
+ "@type": "/cosmos.staking.v1beta1.MsgUndelegate",
+ "delegator_address": "terra1v8n93xnsth6xlknnyprgs3wecv0j2ve7xgx8hu",
+ "validator_address": "terravaloper1wwq9mfkkqfta334d4hcjrjau9y50a4hw37zhdl",
+ "amount": {
+ "denom": "uluna",
+ "amount": "32454390"
+ }
+ }
+ ],
+ "memo": "",
+ "timeout_height": "0",
+ "extension_options": [],
+ "non_critical_extension_options": []
+ },
+ "auth_info": {
+ "signer_infos": [
+ {
+ "public_key": {
+ "@type": "/cosmos.crypto.secp256k1.PubKey",
+ "key": "A4bnZP/9hrCbP8gW3W1vNf3pp0ClUhHoY0TAhu8tMo4R"
+ },
+ "mode_info": {
+ "single": {
+ "mode": "SIGN_MODE_LEGACY_AMINO_JSON"
+ }
+ },
+ "sequence": "0"
+ }
+ ],
+ "fee": {
+ "amount": [
+ {
+ "denom": "uluna",
+ "amount": "53087"
+ }
+ ],
+ "gas_limit": "353908",
+ "payer": "",
+ "granter": ""
+ }
+ },
+ "signatures": [
+ "7YN1xYnhJhFwqC/8DZQPhUNT4fTwZzhj4EGE7wTx1dtfOZgQCeWv6emVnLAf6EV5TSqnbIUN6jsvb/6Mtv69xw=="
+ ]
+ },
+ "timestamp": "2022-05-28T06:26:53Z",
+ "events": [
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NTMwODd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NTMwODd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "NTMwODd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "ZmVl",
+ "value": "NTMwODd1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "YWNjX3NlcQ==",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHUvMA==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "tx",
+ "attributes": [
+ {
+ "key": "c2lnbmF0dXJl",
+ "value": "N1lOMXhZbmhKaEZ3cUMvOERaUVBoVU5UNGZUd1p6aGo0RUdFN3dUeDFkdGZPWmdRQ2VXdjZlbVZuTEFmNkVWNVRTcW5iSVVONmpzdmIvNk10djY5eHc9PQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "YWN0aW9u",
+ "value": "L2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnVW5kZWxlZ2F0ZQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTkxdWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTkxdWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MTkxdWx1bmE=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExanY2NXMzZ3JxZjZ2NmpsM2RwNHQ2Yzl0OXJrOTljZDhwbTd1dGw=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_spent",
+ "attributes": [
+ {
+ "key": "c3BlbmRlcg==",
+ "value": "dGVycmExZmw0OHZzbm1zZHpjdjg1cTVkMnE0ejVhamRoYTh5dTNubG4wbWg=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzI0NTQzOTB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "coin_received",
+ "attributes": [
+ {
+ "key": "cmVjZWl2ZXI=",
+ "value": "dGVycmExdHlnbXMzeGhoczN5djQ4N3BoeDNkdzRhOTVqbjd0N2w4bDA3ZHI=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzI0NTQzOTB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "transfer",
+ "attributes": [
+ {
+ "key": "cmVjaXBpZW50",
+ "value": "dGVycmExdHlnbXMzeGhoczN5djQ4N3BoeDNkdzRhOTVqbjd0N2w4bDA3ZHI=",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExZmw0OHZzbm1zZHpjdjg1cTVkMnE0ejVhamRoYTh5dTNubG4wbWg=",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzI0NTQzOTB1bHVuYQ==",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExZmw0OHZzbm1zZHpjdjg1cTVkMnE0ejVhamRoYTh5dTNubG4wbWg=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "unbond",
+ "attributes": [
+ {
+ "key": "dmFsaWRhdG9y",
+ "value": "dGVycmF2YWxvcGVyMXd3cTltZmtrcWZ0YTMzNGQ0aGNqcmphdTl5NTBhNGh3Mzd6aGRs",
+ "index": true
+ },
+ {
+ "key": "YW1vdW50",
+ "value": "MzI0NTQzOTB1bHVuYQ==",
+ "index": true
+ },
+ {
+ "key": "Y29tcGxldGlvbl90aW1l",
+ "value": "MjAyMi0wNi0xOFQwNjoyNjo1M1o=",
+ "index": true
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "attributes": [
+ {
+ "key": "bW9kdWxl",
+ "value": "c3Rha2luZw==",
+ "index": true
+ },
+ {
+ "key": "c2VuZGVy",
+ "value": "dGVycmExdjhuOTN4bnN0aDZ4bGtubnlwcmdzM3dlY3YwajJ2ZTd4Z3g4aHU=",
+ "index": true
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "pagination": {
+ "next_key": null,
+ "total": "540"
+ }
+}
\ No newline at end of file
diff --git a/test/UnitTests/TerraDotnet/TerraLcd/Messages/RawTransaction_D670_01.json b/test/UnitTests/TerraDotnet/TerraLcd/Messages/RawTransaction_D670_01.json
new file mode 100644
index 0000000..dfbf6e3
--- /dev/null
+++ b/test/UnitTests/TerraDotnet/TerraLcd/Messages/RawTransaction_D670_01.json
@@ -0,0 +1,259 @@
+{
+ "Code": 0,
+ "Data": "0A250A232F636F736D6F732E7374616B696E672E763162657461312E4D736744656C6567617465",
+ "Info": "",
+ "Logs": [
+ {
+ "Log": "",
+ "Events": [
+ {
+ "Type": "coin_received",
+ "Attributes": [
+ {
+ "Key": "receiver",
+ "Value": "terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh"
+ },
+ {
+ "Key": "amount",
+ "Value": "150000000uluna"
+ }
+ ]
+ },
+ {
+ "Type": "coin_spent",
+ "Attributes": [
+ {
+ "Key": "spender",
+ "Value": "terra187reejg4et24jpggqt55vnc47l926w0p530fkt"
+ },
+ {
+ "Key": "amount",
+ "Value": "150000000uluna"
+ }
+ ]
+ },
+ {
+ "Type": "delegate",
+ "Attributes": [
+ {
+ "Key": "validator",
+ "Value": "terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx"
+ },
+ {
+ "Key": "amount",
+ "Value": "150000000uluna"
+ },
+ {
+ "Key": "new_shares",
+ "Value": "150000000.000000000000000000"
+ }
+ ]
+ },
+ {
+ "Type": "message",
+ "Attributes": [
+ {
+ "Key": "action",
+ "Value": "/cosmos.staking.v1beta1.MsgDelegate"
+ },
+ {
+ "Key": "module",
+ "Value": "staking"
+ },
+ {
+ "Key": "sender",
+ "Value": "terra187reejg4et24jpggqt55vnc47l926w0p530fkt"
+ }
+ ]
+ }
+ ],
+ "MessageIndex": 0
+ }
+ ],
+ "Events": [
+ {
+ "Type": "coin_spent",
+ "Attributes": [
+ {
+ "Key": "c3BlbmRlcg==",
+ "Index": true,
+ "Value": "dGVycmExODdyZWVqZzRldDI0anBnZ3F0NTV2bmM0N2w5MjZ3MHA1MzBma3Q="
+ },
+ {
+ "Key": "YW1vdW50",
+ "Index": true,
+ "Value": "MzAwMDB1bHVuYQ=="
+ }
+ ]
+ },
+ {
+ "Type": "coin_received",
+ "Attributes": [
+ {
+ "Key": "cmVjZWl2ZXI=",
+ "Index": true,
+ "Value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE="
+ },
+ {
+ "Key": "YW1vdW50",
+ "Index": true,
+ "Value": "MzAwMDB1bHVuYQ=="
+ }
+ ]
+ },
+ {
+ "Type": "transfer",
+ "Attributes": [
+ {
+ "Key": "cmVjaXBpZW50",
+ "Index": true,
+ "Value": "dGVycmExN3hwZnZha20yYW1nOTYyeWxzNmY4NHoza2VsbDhjNWxrYWVxZmE="
+ },
+ {
+ "Key": "c2VuZGVy",
+ "Index": true,
+ "Value": "dGVycmExODdyZWVqZzRldDI0anBnZ3F0NTV2bmM0N2w5MjZ3MHA1MzBma3Q="
+ },
+ {
+ "Key": "YW1vdW50",
+ "Index": true,
+ "Value": "MzAwMDB1bHVuYQ=="
+ }
+ ]
+ },
+ {
+ "Type": "message",
+ "Attributes": [
+ {
+ "Key": "c2VuZGVy",
+ "Index": true,
+ "Value": "dGVycmExODdyZWVqZzRldDI0anBnZ3F0NTV2bmM0N2w5MjZ3MHA1MzBma3Q="
+ }
+ ]
+ },
+ {
+ "Type": "tx",
+ "Attributes": [
+ {
+ "Key": "ZmVl",
+ "Index": true,
+ "Value": "MzAwMDB1bHVuYQ=="
+ }
+ ]
+ },
+ {
+ "Type": "tx",
+ "Attributes": [
+ {
+ "Key": "YWNjX3NlcQ==",
+ "Index": true,
+ "Value": "dGVycmExODdyZWVqZzRldDI0anBnZ3F0NTV2bmM0N2w5MjZ3MHA1MzBma3QvMA=="
+ }
+ ]
+ },
+ {
+ "Type": "tx",
+ "Attributes": [
+ {
+ "Key": "c2lnbmF0dXJl",
+ "Index": true,
+ "Value": "d2EvQldHRUhOWUsxQTFuNGRIMlZGYmg4aEMwMzBKYkUxR0FsSFh5bGNBUXNvVmFIM0ZxTlBweStScHV6REY1bFJEQXQ3VG5qOXcyOUNDOUc3Wlk3bVE9PQ=="
+ }
+ ]
+ },
+ {
+ "Type": "message",
+ "Attributes": [
+ {
+ "Key": "YWN0aW9u",
+ "Index": true,
+ "Value": "L2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGU="
+ }
+ ]
+ },
+ {
+ "Type": "coin_spent",
+ "Attributes": [
+ {
+ "Key": "c3BlbmRlcg==",
+ "Index": true,
+ "Value": "dGVycmExODdyZWVqZzRldDI0anBnZ3F0NTV2bmM0N2w5MjZ3MHA1MzBma3Q="
+ },
+ {
+ "Key": "YW1vdW50",
+ "Index": true,
+ "Value": "MTUwMDAwMDAwdWx1bmE="
+ }
+ ]
+ },
+ {
+ "Type": "coin_received",
+ "Attributes": [
+ {
+ "Key": "cmVjZWl2ZXI=",
+ "Index": true,
+ "Value": "dGVycmExZmw0OHZzbm1zZHpjdjg1cTVkMnE0ejVhamRoYTh5dTNubG4wbWg="
+ },
+ {
+ "Key": "YW1vdW50",
+ "Index": true,
+ "Value": "MTUwMDAwMDAwdWx1bmE="
+ }
+ ]
+ },
+ {
+ "Type": "delegate",
+ "Attributes": [
+ {
+ "Key": "dmFsaWRhdG9y",
+ "Index": true,
+ "Value": "dGVycmF2YWxvcGVyMWt0dTdhNndxbGs2dmx5d2Y0cnQ2d2ZjeHVwaGMwZXMyN3AwcXZ4"
+ },
+ {
+ "Key": "YW1vdW50",
+ "Index": true,
+ "Value": "MTUwMDAwMDAwdWx1bmE="
+ },
+ {
+ "Key": "bmV3X3NoYXJlcw==",
+ "Index": true,
+ "Value": "MTUwMDAwMDAwLjAwMDAwMDAwMDAwMDAwMDAwMA=="
+ }
+ ]
+ },
+ {
+ "Type": "message",
+ "Attributes": [
+ {
+ "Key": "bW9kdWxl",
+ "Index": true,
+ "Value": "c3Rha2luZw=="
+ },
+ {
+ "Key": "c2VuZGVy",
+ "Index": true,
+ "Value": "dGVycmExODdyZWVqZzRldDI0anBnZ3F0NTV2bmM0N2w5MjZ3MHA1MzBma3Q="
+ }
+ ]
+ }
+ ],
+ "Height": "2",
+ "RawLog": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"terra1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3nln0mh\"},{\"key\":\"amount\",\"value\":\"150000000uluna\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"terra187reejg4et24jpggqt55vnc47l926w0p530fkt\"},{\"key\":\"amount\",\"value\":\"150000000uluna\"}]},{\"type\":\"delegate\",\"attributes\":[{\"key\":\"validator\",\"value\":\"terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx\"},{\"key\":\"amount\",\"value\":\"150000000uluna\"},{\"key\":\"new_shares\",\"value\":\"150000000.000000000000000000\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.staking.v1beta1.MsgDelegate\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"terra187reejg4et24jpggqt55vnc47l926w0p530fkt\"}]}]}]",
+ "GasUsed": 162215,
+ "CodeSpace": "",
+ "CreatedAt": "/Date(1653717617000)/",
+ "GasWanted": 200000,
+ "RawGasUsed": "162215",
+ "HeightAsInt": 2,
+ "Transaction": {
+ "Body": {
+ "Memo": "",
+ "Messages": [
+ "{\n \"@type\": \"/cosmos.staking.v1beta1.MsgDelegate\",\n \"delegator_address\": \"terra187reejg4et24jpggqt55vnc47l926w0p530fkt\",\n \"validator_address\": \"terravaloper1ktu7a6wqlk6vlywf4rt6wfcxuphc0es27p0qvx\",\n \"amount\": {\n \"denom\": \"uluna\",\n \"amount\": \"150000000\"\n }\n }"
+ ],
+ "TimeoutHeight": "0"
+ }
+ },
+ "RawGasWanted": "200000",
+ "TransactionHash": "D6704E3E53C0514A3AAF2C4359CFD3EA33F62E3223C66D7098672F524F1DB644"
+}
\ No newline at end of file
diff --git a/test/UnitTests/TerraDotnet/TerraLcd/Transactions/RawTransactionIndexerTests.cs b/test/UnitTests/TerraDotnet/TerraLcd/Transactions/RawTransactionIndexerTests.cs
new file mode 100644
index 0000000..7c421fd
--- /dev/null
+++ b/test/UnitTests/TerraDotnet/TerraLcd/Transactions/RawTransactionIndexerTests.cs
@@ -0,0 +1,32 @@
+using System.IO;
+using System.Text.Json;
+using MassTransit;
+using TerraDotnet;
+using TerraDotnet.TerraLcd.Messages;
+
+namespace UnitTests.TerraDotnet.TerraLcd.Transactions;
+
+public class RawTransactionIndexerTests
+{
+ private readonly IBus _massTransitBus;
+
+ public RawTransactionIndexerTests(
+ IBus massTransitBus
+ )
+ {
+ _massTransitBus = massTransitBus;
+ }
+
+ private GetTransactionsMatchingQueryResponse? GetParsedJson()
+ {
+ var json = File.ReadAllText("TerraDotnet/TerraLcd/Messages/RawTransaction_D670_01.json");
+
+ return JsonSerializer.Deserialize(
+ json,
+ TerraJsonSerializerOptions.GetThem()
+ );
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj
index 761dd82..444e7a2 100644
--- a/test/UnitTests/UnitTests.csproj
+++ b/test/UnitTests/UnitTests.csproj
@@ -70,6 +70,9 @@
Always
+
+ Always
+
diff --git a/thunder-tests/thunderclient.json b/thunder-tests/thunderclient.json
index 5018fd5..919f074 100644
--- a/thunder-tests/thunderclient.json
+++ b/thunder-tests/thunderclient.json
@@ -4,13 +4,127 @@
"colId": "6404331f-4dc7-4360-8741-960c7fb00bc1",
"containerId": "",
"name": "Transactions from specific block",
- "url": "{{terra2BaseUrl}}/cosmos/tx/v1beta1/txs/block/352708",
+ "url": "{{terra2BaseUrl}}/cosmos/tx/v1beta1/txs/block/141",
"method": "GET",
"sortNum": 10000,
"created": "2022-06-21T12:35:09.514Z",
- "modified": "2022-06-21T12:35:44.158Z",
+ "modified": "2022-06-24T13:26:58.804Z",
"headers": [],
"params": [],
"tests": []
+ },
+ {
+ "_id": "c7bcf9a9-78f0-4b92-95c2-e8c6ee38cb44",
+ "colId": "6404331f-4dc7-4360-8741-960c7fb00bc1",
+ "containerId": "",
+ "name": "Transactions before block",
+ "url": "{{terra2BaseUrl}}/cosmos/tx/v1beta1/txs?events=tx.height<=100",
+ "method": "GET",
+ "sortNum": 20000,
+ "created": "2022-06-22T08:23:40.974Z",
+ "modified": "2022-06-22T08:24:17.802Z",
+ "headers": [],
+ "params": [
+ {
+ "name": "events",
+ "value": "tx.height<=100",
+ "isPath": false
+ }
+ ],
+ "tests": []
+ },
+ {
+ "_id": "467681eb-c185-486c-9b27-0569f8c89102",
+ "colId": "6404331f-4dc7-4360-8741-960c7fb00bc1",
+ "containerId": "",
+ "name": "Transactions in block window",
+ "url": "{{terra2BaseUrl}}/cosmos/tx/v1beta1/txs?events=tx.height>=100&events=tx.height<=200&pagination.limit=100&pagination.offset=0",
+ "method": "GET",
+ "sortNum": 30000,
+ "created": "2022-06-22T08:24:27.911Z",
+ "modified": "2022-06-24T14:35:56.000Z",
+ "headers": [],
+ "params": [
+ {
+ "name": "events",
+ "value": "tx.height>=100",
+ "isPath": false
+ },
+ {
+ "name": "events",
+ "value": "tx.height<=200",
+ "isPath": false
+ },
+ {
+ "name": "pagination.limit",
+ "value": "100",
+ "isPath": false
+ },
+ {
+ "name": "pagination.offset",
+ "value": "0",
+ "isPath": false
+ },
+ {
+ "name": "pagination.key",
+ "value": "",
+ "isDisabled": true,
+ "isPath": false
+ },
+ {
+ "name": "order_by",
+ "value": "ORDER_BY_ASC",
+ "isDisabled": true,
+ "isPath": false
+ }
+ ],
+ "tests": []
+ },
+ {
+ "_id": "9023d2b9-d47a-4e44-957e-18951a4d73ee",
+ "colId": "6404331f-4dc7-4360-8741-960c7fb00bc1",
+ "containerId": "",
+ "name": "Transactions in using single block window",
+ "url": "{{terra2BaseUrl}}/cosmos/tx/v1beta1/txs?events=tx.height>=24&events=tx.height<=24&pagination.limit=100&pagination.offset=0",
+ "method": "GET",
+ "sortNum": 40000,
+ "created": "2022-06-24T14:37:25.407Z",
+ "modified": "2022-07-01T08:31:47.336Z",
+ "headers": [],
+ "params": [
+ {
+ "name": "events",
+ "value": "tx.height>=24",
+ "isPath": false
+ },
+ {
+ "name": "events",
+ "value": "tx.height<=24",
+ "isPath": false
+ },
+ {
+ "name": "pagination.limit",
+ "value": "100",
+ "isPath": false
+ },
+ {
+ "name": "pagination.offset",
+ "value": "0",
+ "isPath": false
+ },
+ {
+ "name": "pagination.key",
+ "value": "",
+ "isDisabled": true,
+ "isPath": false
+ },
+ {
+ "name": "order_by",
+ "value": "ORDER_BY_ASC",
+ "isDisabled": true,
+ "isPath": false
+ }
+ ],
+ "tests": []
}
]
\ No newline at end of file