Skip to content

Commit

Permalink
Merge pull request #6 from CirclesUBI/table-schema-from-solidity
Browse files Browse the repository at this point in the history
Table schema from solidity
  • Loading branch information
jaensen authored May 10, 2024
2 parents 56496c2 + d0f3786 commit 955d93e
Show file tree
Hide file tree
Showing 98 changed files with 3,143 additions and 1,435,754 deletions.
3 changes: 2 additions & 1 deletion Circles.Index/.dockerignore → .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.container-state
.state
.container-state
.spaceneth-state
circles-contracts
circles-contracts-v2
24 changes: 24 additions & 0 deletions Circles.Index.CirclesV1/Circles.Index.CirclesV1.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Nethermind.Numerics.Int256" Version="1.2.0" />
<PackageReference Include="Nethermind.ReferenceAssemblies" Version="1.25.4" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Circles.Index.Common\Circles.Index.Common.csproj" />
</ItemGroup>

<ItemGroup>
<Reference Include="Npgsql">
<HintPath>..\..\..\..\.nuget\packages\npgsql\8.0.2\lib\net8.0\Npgsql.dll</HintPath>
</Reference>
</ItemGroup>

</Project>
133 changes: 133 additions & 0 deletions Circles.Index.CirclesV1/DatabaseSchema.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System.Numerics;
using Circles.Index.Common;
using Nethermind.Core.Crypto;

namespace Circles.Index.CirclesV1;

public class DatabaseSchema : IDatabaseSchema
{
public ISchemaPropertyMap SchemaPropertyMap { get; } = new SchemaPropertyMap();

public IEventDtoTableMap EventDtoTableMap { get; } = new EventDtoTableMap();

public static readonly EventSchema HubTransfer = EventSchema.FromSolidity("CrcV1",
"event HubTransfer(address indexed from, address indexed to, uint256 amount)");

public static readonly EventSchema Signup = EventSchema.FromSolidity("CrcV1",
"event Signup(address indexed user, address indexed token)");

public static readonly EventSchema OrganizationSignup = EventSchema.FromSolidity("CrcV1",
"event OrganizationSignup(address indexed organization)");

public static readonly EventSchema Trust = EventSchema.FromSolidity("CrcV1",
"event Trust(address indexed canSendTo, address indexed user, uint256 limit)");

public static readonly EventSchema Transfer = new("CrcV1", "Transfer",
Keccak.Compute("Transfer(address,address,uint256)"),
[
new("blockNumber", ValueTypes.Int, true),
new("timestamp", ValueTypes.Int, true),
new("transactionIndex", ValueTypes.Int, true),
new("logIndex", ValueTypes.Int, true),
new("tokenAddress", ValueTypes.Address, true),
new("from", ValueTypes.Address, true),
new("to", ValueTypes.Address, true),
new("amount", ValueTypes.BigInt, false)
]);

public IDictionary<(string Namespace, string Table), EventSchema> Tables { get; } =
new Dictionary<(string Namespace, string Table), EventSchema>
{
{
("CrcV1", "HubTransfer"),
HubTransfer
},
{
("CrcV1", "Signup"),
Signup
},
{
("CrcV1", "OrganizationSignup"),
OrganizationSignup
},
{
("CrcV1", "Trust"),
Trust
},
{
("CrcV1", "Transfer"),
Transfer
}
};

public DatabaseSchema()
{
EventDtoTableMap.Add<Signup>(("CrcV1", "Signup"));
SchemaPropertyMap.Add(("CrcV1", "Signup"),
new Dictionary<string, Func<Signup, object?>>
{
{ "blockNumber", e => e.BlockNumber },
{ "timestamp", e => e.Timestamp },
{ "transactionIndex", e => e.TransactionIndex },
{ "logIndex", e => e.LogIndex },
{ "transactionHash", e => e.TransactionHash },
{ "user", e => e.User },
{ "token", e => e.Token }
});

EventDtoTableMap.Add<OrganizationSignup>(("CrcV1", "OrganizationSignup"));
SchemaPropertyMap.Add(("CrcV1", "OrganizationSignup"),
new Dictionary<string, Func<OrganizationSignup, object?>>
{
{ "blockNumber", e => e.BlockNumber },
{ "timestamp", e => e.Timestamp },
{ "transactionIndex", e => e.TransactionIndex },
{ "logIndex", e => e.LogIndex },
{ "transactionHash", e => e.TransactionHash },
{ "organization", e => e.Organization }
});

EventDtoTableMap.Add<Trust>(("CrcV1", "Trust"));
SchemaPropertyMap.Add(("CrcV1", "Trust"),
new Dictionary<string, Func<Trust, object?>>
{
{ "blockNumber", e => e.BlockNumber },
{ "timestamp", e => e.Timestamp },
{ "transactionIndex", e => e.TransactionIndex },
{ "logIndex", e => e.LogIndex },
{ "transactionHash", e => e.TransactionHash },
{ "canSendTo", e => e.CanSendTo },
{ "user", e => e.User },
{ "limit", e => e.Limit }
});

EventDtoTableMap.Add<HubTransfer>(("CrcV1", "HubTransfer"));
SchemaPropertyMap.Add(("CrcV1", "HubTransfer"),
new Dictionary<string, Func<HubTransfer, object?>>
{
{ "blockNumber", e => e.BlockNumber },
{ "timestamp", e => e.Timestamp },
{ "transactionIndex", e => e.TransactionIndex },
{ "logIndex", e => e.LogIndex },
{ "transactionHash", e => e.TransactionHash },
{ "from", e => e.From },
{ "to", e => e.To },
{ "amount", e => (BigInteger)e.Amount }
});

EventDtoTableMap.Add<Transfer>(("CrcV1", "Transfer"));
SchemaPropertyMap.Add(("CrcV1", "Transfer"),
new Dictionary<string, Func<Transfer, object?>>
{
{ "blockNumber", e => e.BlockNumber },
{ "timestamp", e => e.Timestamp },
{ "transactionIndex", e => e.TransactionIndex },
{ "logIndex", e => e.LogIndex },
{ "transactionHash", e => e.TransactionHash },
{ "tokenAddress", e => e.TokenAddress },
{ "from", e => e.From },
{ "to", e => e.To },
{ "amount", e => (BigInteger)e.Value }
});
}
}
52 changes: 52 additions & 0 deletions Circles.Index.CirclesV1/Events.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Circles.Index.Common;
using Nethermind.Int256;

namespace Circles.Index.CirclesV1;

public record Signup(
long BlockNumber,
long Timestamp,
int TransactionIndex,
int LogIndex,
string TransactionHash,
string User,
string Token) : IIndexEvent;

public record OrganizationSignup(
long BlockNumber,
long Timestamp,
int TransactionIndex,
int LogIndex,
string TransactionHash,
string Organization) : IIndexEvent;

public record Trust(
long BlockNumber,
long Timestamp,
int TransactionIndex,
int LogIndex,
string TransactionHash,
string User,
string CanSendTo,
int Limit) : IIndexEvent;

public record HubTransfer(
long BlockNumber,
long Timestamp,
int TransactionIndex,
int LogIndex,
string TransactionHash,
string From,
string To,
UInt256 Amount) : IIndexEvent;

public record Transfer(
long BlockNumber,
long Timestamp,
int TransactionIndex,
int LogIndex,
string TransactionHash,
string TokenAddress,
string From,
string To,
UInt256 Value) : IIndexEvent;
164 changes: 164 additions & 0 deletions Circles.Index.CirclesV1/LogParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
using System.Collections.Concurrent;
using System.Globalization;
using Circles.Index.Common;
using Nethermind.Core;
using Nethermind.Core.Extensions;
using Nethermind.Int256;

namespace Circles.Index.CirclesV1;

public class LogParser(Address v1HubAddress) : ILogParser
{
public static readonly ConcurrentDictionary<Address, object?> CirclesTokenAddresses = new();

public IEnumerable<IIndexEvent> ParseLog(Block block, TxReceipt receipt, LogEntry log, int logIndex)
{
List<IIndexEvent> events = new();
if (log.Topics.Length == 0)
{
return events;
}

var topic = log.Topics[0];
if (topic == DatabaseSchema.Transfer.Topic &&
CirclesTokenAddresses.ContainsKey(log.LoggersAddress))
{
events.Add(Erc20Transfer(block, receipt, log, logIndex));
}

if (log.LoggersAddress == v1HubAddress)
{
if (topic == DatabaseSchema.Signup.Topic)
{
var signupEvents = CrcSignup(block, receipt, log, logIndex);
foreach (var signupEvent in signupEvents)
{
events.Add(signupEvent);
}
}

if (topic == DatabaseSchema.OrganizationSignup.Topic)
{
events.Add(CrcOrgSignup(block, receipt, log, logIndex));
}

if (topic == DatabaseSchema.HubTransfer.Topic)
{
events.Add(CrcHubTransfer(block, receipt, log, logIndex));
}

if (topic == DatabaseSchema.Trust.Topic)
{
events.Add(CrcTrust(block, receipt, log, logIndex));
}
}

return events;
}

private IIndexEvent Erc20Transfer(Block block, TxReceipt receipt, LogEntry log, int logIndex)
{
string from = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength);
string to = "0x" + log.Topics[2].ToString().Substring(Consts.AddressEmptyBytesPrefixLength);
UInt256 value = new(log.Data, true);

return new Transfer(
receipt.BlockNumber
, (long)block.Timestamp
, receipt.Index
, logIndex
, receipt.TxHash!.ToString()
, log.LoggersAddress.ToString(true, false)
, from
, to
, value);
}

private IIndexEvent CrcOrgSignup(Block block, TxReceipt receipt, LogEntry log, int logIndex)
{
string org = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength);

return new OrganizationSignup(
receipt.BlockNumber
, (long)block.Timestamp
, receipt.Index
, logIndex
, receipt.TxHash!.ToString()
, org);
}

private IIndexEvent CrcTrust(Block block, TxReceipt receipt, LogEntry log, int logIndex)
{
string user = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength);
string canSendTo = "0x" + log.Topics[2].ToString().Substring(Consts.AddressEmptyBytesPrefixLength);
int limit = new UInt256(log.Data, true).ToInt32(CultureInfo.InvariantCulture);

return new Trust(
receipt.BlockNumber
, (long)block.Timestamp
, receipt.Index
, logIndex
, receipt.TxHash!.ToString()
, user
, canSendTo
, limit);
}

private IIndexEvent CrcHubTransfer(Block block, TxReceipt receipt, LogEntry log, int logIndex)
{
string from = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength);
string to = "0x" + log.Topics[2].ToString().Substring(Consts.AddressEmptyBytesPrefixLength);
UInt256 amount = new(log.Data, true);

return new HubTransfer(
receipt.BlockNumber
, (long)block.Timestamp
, receipt.Index
, logIndex
, receipt.TxHash!.ToString()
, from
, to
, amount);
}

private IEnumerable<IIndexEvent> CrcSignup(Block block, TxReceipt receipt, LogEntry log, int logIndex)
{
string user = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength);
Address tokenAddress = new Address(log.Data.Slice(12));

IIndexEvent signupEvent = new Signup(
receipt.BlockNumber
, (long)block.Timestamp
, receipt.Index
, logIndex
, receipt.TxHash!.ToString()
, user
, tokenAddress.ToString(true, false));

CirclesTokenAddresses.TryAdd(tokenAddress, null);

IIndexEvent? signupBonusEvent = null;

// Every signup comes together with an Erc20 transfer (the signup bonus).
// Since the signup event is emitted after the transfer, the token wasn't known yet when we encountered the transfer.
// Look for the transfer again and process it.
for (int i = 0; i < receipt.Logs!.Length; i++)
{
var repeatedLogEntry = receipt.Logs[i];
if (repeatedLogEntry.LoggersAddress != tokenAddress)
{
continue;
}

if (repeatedLogEntry.Topics[0] == DatabaseSchema.Transfer.Topic)
{
signupBonusEvent = Erc20Transfer(block, receipt, repeatedLogEntry, i);
break;
}
}

return signupBonusEvent == null
? new[] { signupEvent }
: new[] { signupEvent, signupBonusEvent };
}
}
Loading

0 comments on commit 955d93e

Please sign in to comment.