Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 0 additions & 86 deletions src/Net.Cache.DynamoDb.ERC20/Api/ApiERC20Service.cs

This file was deleted.

70 changes: 70 additions & 0 deletions src/Net.Cache.DynamoDb.ERC20/DynamoDb/DynamoDbClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using Amazon.DynamoDBv2;
using System.Threading.Tasks;
using Amazon.DynamoDBv2.DataModel;
using Net.Cache.DynamoDb.ERC20.DynamoDb.Models;

namespace Net.Cache.DynamoDb.ERC20.DynamoDb
{
/// <summary>
/// Default implementation of <see cref="IDynamoDbClient"/> using the AWS SDK.
/// </summary>
public class DynamoDbClient : IDynamoDbClient
{
private readonly IDynamoDBContext _dynamoDbContext;

/// <summary>
/// Initializes a new instance of the <see cref="DynamoDbClient"/> class.
/// </summary>
/// <param name="dynamoDbContext">The DynamoDB context to operate on.</param>
public DynamoDbClient(IDynamoDBContext dynamoDbContext)
{
_dynamoDbContext = dynamoDbContext ?? throw new ArgumentNullException(nameof(dynamoDbContext));
}

/// <summary>
/// Initializes a new instance using a context builder.
/// </summary>
/// <param name="contextBuilder">Builder that produces a DynamoDB context.</param>
public DynamoDbClient(IDynamoDBContextBuilder contextBuilder)
: this((contextBuilder ?? throw new ArgumentNullException(nameof(contextBuilder))).Build())
{ }

/// <summary>
/// Initializes a new instance from a raw DynamoDB client.
/// </summary>
/// <param name="dynamoDb">The AWS DynamoDB client.</param>
public DynamoDbClient(IAmazonDynamoDB dynamoDb)
: this(
new DynamoDBContextBuilder()
.WithDynamoDBClient(() => dynamoDb ?? throw new ArgumentNullException(nameof(dynamoDb)))
)
{ }

/// <summary>
/// Initializes a new instance using default AWS configuration.
/// </summary>
public DynamoDbClient()
: this(
new DynamoDBContextBuilder()
.WithDynamoDBClient(() => new AmazonDynamoDBClient())
)
{ }

/// <inheritdoc cref="IDynamoDbClient.GetErc20TokenAsync"/>
public async Task<Erc20TokenDynamoDbEntry?> GetErc20TokenAsync(HashKey hashKey, LoadConfig? config = null)
{
return await _dynamoDbContext
.LoadAsync<Erc20TokenDynamoDbEntry>(hashKey.Value, config)
.ConfigureAwait(false);
}

/// <inheritdoc cref="IDynamoDbClient.SaveErc20TokenAsync"/>
public async Task SaveErc20TokenAsync(Erc20TokenDynamoDbEntry entry, SaveConfig? config = null)
{
await _dynamoDbContext
.SaveAsync(entry, config)
.ConfigureAwait(false);
}
}
}
27 changes: 27 additions & 0 deletions src/Net.Cache.DynamoDb.ERC20/DynamoDb/IDynamoDbClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Threading.Tasks;
using Amazon.DynamoDBv2.DataModel;
using Net.Cache.DynamoDb.ERC20.DynamoDb.Models;

namespace Net.Cache.DynamoDb.ERC20.DynamoDb
{
/// <summary>
/// Provides access to ERC20 token metadata stored in DynamoDB.
/// </summary>
public interface IDynamoDbClient
{
/// <summary>
/// Retrieves a token entry by its hash key.
/// </summary>
/// <param name="hashKey">The composite hash key of the token.</param>
/// <param name="config">Optional DynamoDB load configuration.</param>
/// <returns>The token entry if it exists; otherwise, <c>null</c>.</returns>
public Task<Erc20TokenDynamoDbEntry?> GetErc20TokenAsync(HashKey hashKey, LoadConfig? config = null);

/// <summary>
/// Persists a token entry into DynamoDB.
/// </summary>
/// <param name="entry">The token entry to store.</param>
/// <param name="config">Optional DynamoDB save configuration.</param>
public Task SaveErc20TokenAsync(Erc20TokenDynamoDbEntry entry, SaveConfig? config = null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using Amazon.DynamoDBv2.DataModel;
using Net.Cache.DynamoDb.ERC20.Rpc.Models;

namespace Net.Cache.DynamoDb.ERC20.DynamoDb.Models
{
/// <summary>
/// Represents a persisted ERC20 token entry in the DynamoDB cache table.
/// </summary>
[DynamoDBTable("TokensInfoCache")]
public class Erc20TokenDynamoDbEntry
{
/// <summary>
/// Gets or sets the composite hash key value.
/// </summary>
[DynamoDBHashKey]
public string HashKey { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the blockchain network identifier.
/// </summary>
[DynamoDBProperty]
public long ChainId { get; set; }

/// <summary>
/// Gets or sets the ERC20 token contract address.
/// </summary>
[DynamoDBProperty]
public string Address { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the token name.
/// </summary>
[DynamoDBProperty]
public string Name { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the token symbol.
/// </summary>
[DynamoDBProperty]
public string Symbol { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the number of decimal places used by the token.
/// </summary>
[DynamoDBProperty]
public byte Decimals { get; set; }

/// <summary>
/// Gets or sets the total supply of the token.
/// </summary>
[DynamoDBProperty]
public decimal TotalSupply { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="Erc20TokenDynamoDbEntry"/> class.<br/>
/// Constructor without parameters for working "AWSSDK.DynamoDBv2" library.
/// </summary>
public Erc20TokenDynamoDbEntry() { }

/// <summary>
/// Initializes a new instance of the <see cref="Erc20TokenDynamoDbEntry"/> class with specified values.
/// </summary>
/// <param name="hashKey">The hash key identifying the token.</param>
/// <param name="erc20Token">The token data retrieved from RPC.</param>
public Erc20TokenDynamoDbEntry(HashKey hashKey, Erc20TokenData erc20Token)
{
HashKey = hashKey.Value;
ChainId = hashKey.ChainId;
Address = hashKey.Address;
Name = erc20Token.Name;
Symbol = erc20Token.Symbol;
Decimals = erc20Token.Decimals;
TotalSupply = Nethereum.Web3.Web3.Convert.FromWei(erc20Token.TotalSupply, erc20Token.Decimals);
}
}
}
65 changes: 65 additions & 0 deletions src/Net.Cache.DynamoDb.ERC20/DynamoDb/Models/HashKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using Net.Cryptography.SHA256;
using Net.Web3.EthereumWallet;

namespace Net.Cache.DynamoDb.ERC20.DynamoDb.Models
{
/// <summary>
/// Represents a unique key that combines a blockchain chain identifier and an ERC20 token address.<br/>
/// The key value is a SHA256 hash of the combined chain identifier and address.
/// </summary>
public class HashKey
{
/// <summary>
/// Gets the blockchain network identifier.
/// </summary>
public long ChainId { get; }

/// <summary>
/// Gets the ERC20 token contract address.
/// </summary>
public EthereumAddress Address { get; }

/// <summary>
/// Gets the hashed representation of the <see cref="ChainId"/> and <see cref="Address"/> combination.
/// </summary>
public string Value { get; }

/// <summary>
/// Initializes a new instance of the <see cref="HashKey"/> class.
/// </summary>
/// <param name="chainId">The blockchain network identifier.</param>
/// <param name="address">The ERC20 token contract address.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="chainId"/> is less than or equal to zero.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="address"/> is <c>null</c>.</exception>
public HashKey(long chainId, EthereumAddress address)
{
if (chainId <= 0) throw new ArgumentOutOfRangeException(nameof(chainId));
if (address == null) throw new ArgumentNullException(nameof(address));

ChainId = chainId;
Address = address;
Value = Generate(chainId, address);
}

/// <summary>
/// Generates a hashed key for the specified chain identifier and address.
/// </summary>
/// <param name="chainId">The blockchain network identifier.</param>
/// <param name="address">The ERC20 token contract address.</param>
/// <returns>A SHA256 hash representing the combined chain identifier and address.</returns>
public static string Generate(long chainId, EthereumAddress address)
{
if (chainId <= 0) throw new ArgumentOutOfRangeException(nameof(chainId));
if (address == null) throw new ArgumentNullException(nameof(address));

return $"{chainId}-{address}".ToSha256();
}

/// <summary>
/// Returns the hash key value.
/// </summary>
/// <returns>The hashed key string.</returns>
public override string ToString() => Value;
}
}
Loading