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
31 changes: 24 additions & 7 deletions src/Net.Cache.DynamoDb.ERC20/Erc20CacheService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Nethereum.Web3;
using System.Threading.Tasks;
using Net.Web3.EthereumWallet;
using Net.Cache.DynamoDb.ERC20.Rpc;
Expand Down Expand Up @@ -39,11 +40,27 @@ public Erc20CacheService()
)
{ }

/// <inheritdoc cref="IErc20CacheService.GetOrAddAsync"/>
public async Task<Erc20TokenDynamoDbEntry> GetOrAddAsync(HashKey hashKey, Func<Task<string>> rpcUrlFactory, Func<Task<EthereumAddress>> multiCallFactory)
/// <inheritdoc cref="IErc20CacheService.GetOrAddAsync(HashKey, Func{Task{string}}, Func{Task{EthereumAddress}})"/>
public Task<Erc20TokenDynamoDbEntry> GetOrAddAsync(HashKey hashKey, Func<Task<string>> rpcUrlFactory, Func<Task<EthereumAddress>> multiCallFactory)
{
if (hashKey == null) throw new ArgumentNullException(nameof(hashKey));
if (rpcUrlFactory == null) throw new ArgumentNullException(nameof(rpcUrlFactory));

return GetOrAddAsync(
hashKey,
async () =>
{
var rpcUrl = await rpcUrlFactory().ConfigureAwait(false);
return new Nethereum.Web3.Web3(rpcUrl);
},
multiCallFactory
);
}

/// <inheritdoc cref="IErc20CacheService.GetOrAddAsync(HashKey, Func{Task{IWeb3}}, Func{Task{EthereumAddress}})"/>
public async Task<Erc20TokenDynamoDbEntry> GetOrAddAsync(HashKey hashKey, Func<Task<IWeb3>> web3Factory, Func<Task<EthereumAddress>> multiCallFactory)
{
if (hashKey == null) throw new ArgumentNullException(nameof(hashKey));
if (web3Factory == null) throw new ArgumentNullException(nameof(web3Factory));
if (multiCallFactory == null) throw new ArgumentNullException(nameof(multiCallFactory));

if (_inMemoryCache.TryGetValue(hashKey.Value, out var cachedEntry)) return cachedEntry;
Expand All @@ -57,15 +74,15 @@ public async Task<Erc20TokenDynamoDbEntry> GetOrAddAsync(HashKey hashKey, Func<T
return entry;
}

var rpcUrlTask = rpcUrlFactory();
var web3Task = web3Factory();
var multiCallTask = multiCallFactory();

await Task.WhenAll(rpcUrlTask, multiCallTask);
await Task.WhenAll(web3Task, multiCallTask).ConfigureAwait(false);

var rpcUrl = rpcUrlTask.Result;
var web3 = web3Task.Result;
var multiCall = multiCallTask.Result;

var erc20Service = _erc20ServiceFactory.Create(new Nethereum.Web3.Web3(rpcUrl), multiCall);
var erc20Service = _erc20ServiceFactory.Create(web3, multiCall);
var erc20Token = await erc20Service.GetErc20TokenAsync(hashKey.Address).ConfigureAwait(false);

entry = new Erc20TokenDynamoDbEntry(hashKey, erc20Token);
Expand Down
10 changes: 10 additions & 0 deletions src/Net.Cache.DynamoDb.ERC20/IErc20CacheService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Nethereum.Web3;
using System.Threading.Tasks;
using Net.Web3.EthereumWallet;
using Net.Cache.DynamoDb.ERC20.DynamoDb.Models;
Expand All @@ -18,5 +19,14 @@ public interface IErc20CacheService
/// <param name="multiCallFactory">Factory used to resolve the address of the multicall contract.</param>
/// <returns>The cached or newly created <see cref="Erc20TokenDynamoDbEntry"/> instance.</returns>
public Task<Erc20TokenDynamoDbEntry> GetOrAddAsync(HashKey hashKey, Func<Task<string>> rpcUrlFactory, Func<Task<EthereumAddress>> multiCallFactory);

/// <summary>
/// Retrieves a cached ERC20 token entry or adds a new one when it is missing using a Web3 factory.
/// </summary>
/// <param name="hashKey">The composite key of chain identifier and token address.</param>
/// <param name="web3Factory">Factory used to resolve the Web3 client instance.</param>
/// <param name="multiCallFactory">Factory used to resolve the address of the multicall contract.</param>
/// <returns>The cached or newly created <see cref="Erc20TokenDynamoDbEntry"/> instance.</returns>
public Task<Erc20TokenDynamoDbEntry> GetOrAddAsync(HashKey hashKey, Func<Task<IWeb3>> web3Factory, Func<Task<EthereumAddress>> multiCallFactory);
}
}
18 changes: 16 additions & 2 deletions src/Net.Cache.DynamoDb.ERC20/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ var cacheService = new Erc20CacheService(dynamoDbClient, erc20ServiceFactory);

#### Caching ERC20 Token Information

To cache ERC20 token information, create a `HashKey` and use the `GetOrAddAsync` method of `Erc20CacheService`:
To cache ERC20 token information, create a `HashKey` and use one of the `GetOrAddAsync` overloads of `Erc20CacheService`.

The overload that accepts RPC and multicall address factories is useful when you only need to supply connection metadata:

```csharp
var chainId = 1L; // Ethereum mainnet
Expand All @@ -58,4 +60,16 @@ var tokenInfo = await cacheService.GetOrAddAsync(
Console.WriteLine($"Token Name: {tokenInfo.Name}, Symbol: {tokenInfo.Symbol}");
```

This method retrieves the token information from the cache if it exists, or fetches it from the blockchain and stores it in both the DynamoDB table and the in-memory cache otherwise.
If you need to control the entire `IWeb3` client creation, you can use the overload that accepts a `Func<Task<IWeb3>>` alongside the multicall factory:

```csharp
var tokenInfo = await cacheService.GetOrAddAsync(
hashKey,
web3Factory: () => Task.FromResult((IWeb3)new Web3("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID")),
multiCallFactory: () => Task.FromResult(new EthereumAddress("0x...multicall"))
);

Console.WriteLine($"Token Name: {tokenInfo.Name}, Symbol: {tokenInfo.Symbol}");
```

Both overloads retrieve the token information from the cache if it exists, or fetch it from the blockchain and store it in both the DynamoDB table and the in-memory cache otherwise.
Loading