diff --git a/Circles.Index.CirclesV2.StandardTreasury/DatabaseSchema.cs b/Circles.Index.CirclesV2.StandardTreasury/DatabaseSchema.cs index d44e4b8..0338f70 100644 --- a/Circles.Index.CirclesV2.StandardTreasury/DatabaseSchema.cs +++ b/Circles.Index.CirclesV2.StandardTreasury/DatabaseSchema.cs @@ -14,13 +14,13 @@ public class DatabaseSchema : IDatabaseSchema EventSchema.FromSolidity("CrcV2", "event CreateVault(address indexed group, address indexed vault)"); - public static readonly EventSchema GroupMintSingle = + public static readonly EventSchema CollateralLockedSingle = EventSchema.FromSolidity("CrcV2", - "event GroupMintSingle(address indexed group, uint256 indexed id, uint256 value, bytes userData)"); + "event CollateralLockedSingle(address indexed group, uint256 indexed id, uint256 value, bytes userData)"); - public static readonly EventSchema GroupMintBatch = - new EventSchema("CrcV2", "GroupMintBatch", - Keccak.Compute("GroupMintBatch(address,uint256[],uint256[],bytes)").BytesToArray(), + public static readonly EventSchema CollateralLockedBatch = + new EventSchema("CrcV2", "CollateralLockedBatch", + Keccak.Compute("CollateralLockedBatch(address,uint256[],uint256[],bytes)").BytesToArray(), new List() { new("blockNumber", ValueTypes.Int, true), @@ -80,12 +80,12 @@ public class DatabaseSchema : IDatabaseSchema CreateVault }, { - ("CrcV2", "GroupMintSingle"), - GroupMintSingle + ("CrcV2", "CollateralLockedSingle"), + CollateralLockedSingle }, { - ("CrcV2", "GroupMintBatch"), - GroupMintBatch + ("CrcV2", "CollateralLockedBatch"), + CollateralLockedBatch }, { ("CrcV2", "GroupRedeem"), @@ -116,9 +116,9 @@ public DatabaseSchema() { "vault", e => e.Vault } }); - EventDtoTableMap.Add(("CrcV2", "GroupMintSingle")); - SchemaPropertyMap.Add(("CrcV2", "GroupMintSingle"), - new Dictionary> + EventDtoTableMap.Add(("CrcV2", "CollateralLockedSingle")); + SchemaPropertyMap.Add(("CrcV2", "CollateralLockedSingle"), + new Dictionary> { { "blockNumber", e => e.BlockNumber }, { "timestamp", e => e.Timestamp }, @@ -131,9 +131,9 @@ public DatabaseSchema() { "userData", e => e.UserData } }); - EventDtoTableMap.Add(("CrcV2", "GroupMintBatch")); - SchemaPropertyMap.Add(("CrcV2", "GroupMintBatch"), - new Dictionary> + EventDtoTableMap.Add(("CrcV2", "CollateralLockedBatch")); + SchemaPropertyMap.Add(("CrcV2", "CollateralLockedBatch"), + new Dictionary> { { "blockNumber", e => e.BlockNumber }, { "timestamp", e => e.Timestamp }, diff --git a/Circles.Index.CirclesV2.StandardTreasury/Events.cs b/Circles.Index.CirclesV2.StandardTreasury/Events.cs index 4308576..aa35f75 100644 --- a/Circles.Index.CirclesV2.StandardTreasury/Events.cs +++ b/Circles.Index.CirclesV2.StandardTreasury/Events.cs @@ -12,7 +12,7 @@ public record CreateVault( string Group, string Vault) : IIndexEvent; -public record GroupMintSingle( +public record CollateralLockedSingle( long BlockNumber, long Timestamp, int TransactionIndex, @@ -23,7 +23,7 @@ public record GroupMintSingle( UInt256 Value, byte[] UserData) : IIndexEvent; -public record GroupMintBatch( +public record CollateralLockedBatch( long BlockNumber, long Timestamp, int TransactionIndex, diff --git a/Circles.Index.CirclesV2.StandardTreasury/LogParser.cs b/Circles.Index.CirclesV2.StandardTreasury/LogParser.cs index a0a30bb..1406415 100644 --- a/Circles.Index.CirclesV2.StandardTreasury/LogParser.cs +++ b/Circles.Index.CirclesV2.StandardTreasury/LogParser.cs @@ -12,8 +12,8 @@ namespace Circles.Index.CirclesV2.StandardTreasury; public class LogParser(Address standardTreasuryAddress) : ILogParser { private readonly Hash256 _createVaultTopic = new(DatabaseSchema.CreateVault.Topic); - private readonly Hash256 _groupMintSingleTopic = new(DatabaseSchema.GroupMintSingle.Topic); - private readonly Hash256 _groupMintBatchTopic = new(DatabaseSchema.GroupMintBatch.Topic); + private readonly Hash256 _groupMintSingleTopic = new(DatabaseSchema.CollateralLockedSingle.Topic); + private readonly Hash256 _groupMintBatchTopic = new(DatabaseSchema.CollateralLockedBatch.Topic); private readonly Hash256 _groupRedeemTopic = new(DatabaseSchema.GroupRedeem.Topic); private readonly Hash256 _groupRedeemCollateralReturnTopic = new(DatabaseSchema.GroupRedeemCollateralReturn.Topic); private readonly Hash256 _groupRedeemCollateralBurnTopic = new(DatabaseSchema.GroupRedeemCollateralBurn.Topic); @@ -41,12 +41,12 @@ public IEnumerable ParseLog(Block block, Transaction transaction, T if (topic == _groupMintSingleTopic) { - yield return GroupMintSingle(block, receipt, log, logIndex); + yield return CollateralLockedSingle(block, receipt, log, logIndex); } if (topic == _groupMintBatchTopic) { - foreach (var batchEvent in GroupMintBatch(block, receipt, log, logIndex)) + foreach (var batchEvent in CollateralLockedBatch(block, receipt, log, logIndex)) { yield return batchEvent; } @@ -90,14 +90,14 @@ private CreateVault CreateVault(Block block, TxReceipt receipt, LogEntry log, in vaultAddress); } - private GroupMintSingle GroupMintSingle(Block block, TxReceipt receipt, LogEntry log, int logIndex) + private CollateralLockedSingle CollateralLockedSingle(Block block, TxReceipt receipt, LogEntry log, int logIndex) { string groupAddress = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength); UInt256 id = new UInt256(log.Topics[2].Bytes, true); UInt256 value = new UInt256(log.Data.Slice(0, 32), true); byte[] userData = log.Data.Slice(32); - return new GroupMintSingle( + return new CollateralLockedSingle( block.Number, (long)block.Timestamp, receipt.Index, @@ -109,7 +109,7 @@ private GroupMintSingle GroupMintSingle(Block block, TxReceipt receipt, LogEntry userData); } - private IEnumerable GroupMintBatch(Block block, TxReceipt receipt, LogEntry log, int logIndex) + private IEnumerable CollateralLockedBatch(Block block, TxReceipt receipt, LogEntry log, int logIndex) { string groupAddress = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength); @@ -138,7 +138,7 @@ private IEnumerable GroupMintBatch(Block block, TxReceipt receip for (int i = 0; i < idsLength; i++) { - yield return new GroupMintBatch( + yield return new CollateralLockedBatch( block.Number, (long)block.Timestamp, receipt.Index, diff --git a/Circles.Index.CirclesV2/DatabaseSchema.cs b/Circles.Index.CirclesV2/DatabaseSchema.cs index 73bee77..d744bf5 100644 --- a/Circles.Index.CirclesV2/DatabaseSchema.cs +++ b/Circles.Index.CirclesV2/DatabaseSchema.cs @@ -21,7 +21,7 @@ public class DatabaseSchema : IDatabaseSchema "event RegisterGroup(address indexed group, address indexed mint, address indexed treasury, string indexed name, string indexed symbol)"); public static readonly EventSchema RegisterHuman = - EventSchema.FromSolidity("CrcV2", "event RegisterHuman(address indexed avatar)"); + EventSchema.FromSolidity("CrcV2", "event RegisterHuman(address indexed avatar, address indexed inviter)"); public static readonly EventSchema RegisterOrganization = EventSchema.FromSolidity("CrcV2", @@ -33,24 +33,29 @@ public class DatabaseSchema : IDatabaseSchema public static readonly EventSchema Trust = EventSchema.FromSolidity("CrcV2", "event Trust(address indexed truster, address indexed trustee, uint256 expiryTime)"); + + public static readonly EventSchema DiscountCost = + EventSchema.FromSolidity("CrcV2", + "event DiscountCost(address indexed account, uint256 indexed id, uint256 discountCost)"); + // // public static readonly EventSchema TransferSingle = EventSchema.FromSolidity( // "CrcV2", // "event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 indexed id, uint256 value)"); - public static readonly EventSchema TransferSingle = new("CrcV2", "TransferSingle", + public static readonly EventSchema TransferSingle = new("CrcV2", "TransferSingle", Keccak.Compute("TransferSingle(address,address,address,uint256,uint256)").BytesToArray(), [ - new("blockNumber", ValueTypes.Int, true), - new("timestamp", ValueTypes.Int, true), - new("transactionIndex", ValueTypes.Int, true), - new("logIndex", ValueTypes.Int, true), - new("transactionHash", ValueTypes.String, true), - new("operator", ValueTypes.Address, true), - new("from", ValueTypes.Address, true), - new("to", ValueTypes.Address, true), - new("id", ValueTypes.BigInt, true), - new("value", ValueTypes.BigInt, false), - new("tokenAddress", ValueTypes.Address, true) - ]); + new("blockNumber", ValueTypes.Int, true), + new("timestamp", ValueTypes.Int, true), + new("transactionIndex", ValueTypes.Int, true), + new("logIndex", ValueTypes.Int, true), + new("transactionHash", ValueTypes.String, true), + new("operator", ValueTypes.Address, true), + new("from", ValueTypes.Address, true), + new("to", ValueTypes.Address, true), + new("id", ValueTypes.BigInt, true), + new("value", ValueTypes.BigInt, false), + new("tokenAddress", ValueTypes.Address, true) + ]); // public static readonly EventSchema URI = // EventSchema.FromSolidity("CrcV2", "event URI(string value, uint256 indexed id)"); @@ -207,6 +212,10 @@ public class DatabaseSchema : IDatabaseSchema { ("CrcV2", "StreamCompleted"), StreamCompleted + }, + { + ("CrcV2", "DiscountCost"), + DiscountCost } }; @@ -265,7 +274,8 @@ public DatabaseSchema() { "transactionIndex", e => e.TransactionIndex }, { "logIndex", e => e.LogIndex }, { "transactionHash", e => e.TransactionHash }, - { "avatar", e => e.Avatar } + { "avatar", e => e.Avatar }, + { "inviter", e => e.Inviter } }); EventDtoTableMap.Add(("CrcV2", "RegisterOrganization")); @@ -472,5 +482,19 @@ public DatabaseSchema() { "amount", e => (BigInteger)e.Amount }, { "tokenAddress", e => ConversionUtils.UInt256ToAddress(e.Id).ToString(true, false) } }); + + EventDtoTableMap.Add(("CrcV2", "DiscountCost")); + SchemaPropertyMap.Add(("CrcV2", "DiscountCost"), + new Dictionary> + { + { "blockNumber", e => e.BlockNumber }, + { "timestamp", e => e.Timestamp }, + { "transactionIndex", e => e.TransactionIndex }, + { "logIndex", e => e.LogIndex }, + { "transactionHash", e => e.TransactionHash }, + { "account", e => e.Account }, + { "id", e => (BigInteger)e.Id }, + { "discountCost", e => (BigInteger)e.Cost } + }); } } \ No newline at end of file diff --git a/Circles.Index.CirclesV2/Events.cs b/Circles.Index.CirclesV2/Events.cs index a247754..bf3ecef 100644 --- a/Circles.Index.CirclesV2/Events.cs +++ b/Circles.Index.CirclesV2/Events.cs @@ -30,7 +30,8 @@ public record RegisterHuman( int TransactionIndex, int LogIndex, string TransactionHash, - string Avatar) : IIndexEvent; + string Avatar, + string Inviter) : IIndexEvent; public record PersonalMint( long BlockNumber, @@ -186,4 +187,14 @@ public record StreamCompleted( string From, string To, UInt256 Id, - UInt256 Amount) : IIndexEvent; \ No newline at end of file + UInt256 Amount) : IIndexEvent; + +public record DiscountCost( + long BlockNumber, + long Timestamp, + int TransactionIndex, + int LogIndex, + string TransactionHash, + string Account, + UInt256 Id, + UInt256 Cost) : IIndexEvent; \ No newline at end of file diff --git a/Circles.Index.CirclesV2/LogParser.cs b/Circles.Index.CirclesV2/LogParser.cs index c5c5417..638f6ee 100644 --- a/Circles.Index.CirclesV2/LogParser.cs +++ b/Circles.Index.CirclesV2/LogParser.cs @@ -31,6 +31,7 @@ public class LogParser(Address v2HubAddress, Address erc20LiftAddress) : ILogPar private readonly Hash256 _depositDemurraged = new(DatabaseSchema.DepositDemurraged.Topic); private readonly Hash256 _withdrawDemurraged = new(DatabaseSchema.WithdrawDemurraged.Topic); private readonly Hash256 _streamCompletedTopic = new(DatabaseSchema.StreamCompleted.Topic); + private readonly Hash256 _discountCostTopic = new(DatabaseSchema.DiscountCost.Topic); public static readonly ConcurrentDictionary Erc20WrapperAddresses = new(); @@ -166,6 +167,11 @@ public IEnumerable ParseLog(Block block, Transaction transaction, T yield return streamCompleted; } } + + if (topic == _discountCostTopic) + { + yield return DiscountCost(block, receipt, log, logIndex); + } } if (log.LoggersAddress == erc20LiftAddress) @@ -350,6 +356,7 @@ private RegisterGroup CrcV2RegisterGroup(Block block, TxReceipt receipt, LogEntr private RegisterHuman CrcV2RegisterHuman(Block block, TxReceipt receipt, LogEntry log, int logIndex) { string humanAddress = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength); + string inviterAddress = "0x" + log.Topics[2].ToString().Substring(Consts.AddressEmptyBytesPrefixLength); return new RegisterHuman( block.Number, @@ -357,7 +364,8 @@ private RegisterHuman CrcV2RegisterHuman(Block block, TxReceipt receipt, LogEntr receipt.Index, logIndex, receipt.TxHash!.ToString(), - humanAddress); + humanAddress, + inviterAddress); } private PersonalMint CrcV2PersonalMint(Block block, TxReceipt receipt, LogEntry log, int logIndex) @@ -496,6 +504,23 @@ private WithdrawDemurraged WithdrawDemurraged(Block block, TxReceipt receipt, Lo inflationaryAmount); } + private DiscountCost DiscountCost(Block block, TxReceipt receipt, LogEntry log, int logIndex) + { + string account = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength); + UInt256 id = new UInt256(log.Topics[2].Bytes, true); + UInt256 discountCost = new UInt256(log.Data, true); + + return new DiscountCost( + block.Number, + (long)block.Timestamp, + receipt.Index, + logIndex, + receipt.TxHash!.ToString(), + account, + id, + discountCost); + } + private IEnumerable StreamCompleted(Block block, TxReceipt receipt, LogEntry log, int logIndex) { string operatorAddress = "0x" + log.Topics[1].ToString().Substring(Consts.AddressEmptyBytesPrefixLength); diff --git a/Circles.Index.CirclesViews/DatabaseSchema.cs b/Circles.Index.CirclesViews/DatabaseSchema.cs index 04b8402..31211d9 100644 --- a/Circles.Index.CirclesViews/DatabaseSchema.cs +++ b/Circles.Index.CirclesViews/DatabaseSchema.cs @@ -610,6 +610,53 @@ WITH latestmetadata AS ( ") }; + public static readonly EventSchema V_CrcV1_BalancesByAccountAndToken = new("V_CrcV1", "BalancesByAccountAndToken", + new byte[32], + [ + new("account", ValueTypes.Address, true), + new("tokenId", ValueTypes.String, true), + new("tokenAddress", ValueTypes.String, true), + new("lastActivity", ValueTypes.Int, true), + new("totalBalance", ValueTypes.BigInt, true), + ]) + { + SqlMigrationItem = new SqlMigrationItem(@" + create or replace view public.""V_CrcV1_BalancesByAccountAndToken""(account, ""tokenAddress"", ""lastActivity"", ""totalBalance"", ""tokenOwner"") as + WITH transfers AS (SELECT ""CrcV1_Transfer"".""timestamp"", + ""CrcV1_Transfer"".""from"", + ""CrcV1_Transfer"".""to"", + ""CrcV1_Transfer"".amount AS value, + ""CrcV1_Transfer"".""tokenAddress"" + FROM ""CrcV1_Transfer""), + ""accountBalances"" AS (SELECT all_transfers.account, + sum(all_transfers.amount) AS balance, + max(all_transfers.""timestamp"") AS ""timestamp"", + all_transfers.""tokenAddress"" + FROM (SELECT transfers.""from"" AS account, + - transfers.value AS amount, + transfers.""timestamp"", + transfers.""tokenAddress"" + FROM transfers + UNION ALL + SELECT transfers.""to"" AS account, + transfers.value AS amount, + transfers.""timestamp"", + transfers.""tokenAddress"" + FROM transfers) all_transfers + GROUP BY all_transfers.account, all_transfers.""tokenAddress"") + SELECT ""accountBalances"".account, + ""accountBalances"".""tokenAddress"", + ""accountBalances"".""timestamp"" AS ""lastActivity"", + ""accountBalances"".balance AS ""totalBalance"", + ""CrcV1_Signup"".""user"" AS ""tokenOwner"" + FROM ""accountBalances"" + JOIN ""CrcV1_Signup"" ON ""accountBalances"".""tokenAddress"" = ""CrcV1_Signup"".token + WHERE ""accountBalances"".account <> '0x0000000000000000000000000000000000000000'::text + AND ""accountBalances"".balance > 0::numeric; + ") + }; + + public static readonly EventSchema V_CrcV2_BalancesByAccountAndToken = new("V_CrcV2", "BalancesByAccountAndToken", new byte[32], [ @@ -643,47 +690,50 @@ RETURNS numeric AS $$ END; $$ LANGUAGE plpgsql; - create or replace view ""V_CrcV2_BalancesByAccountAndToken"" as - WITH ""transfers"" AS ( - SELECT ""timestamp"", - ""from"", - ""to"", - ""id"", - ""value"" - FROM ""CrcV2_TransferSingle"" - UNION ALL - SELECT ""timestamp"", - ""from"", - ""to"", - ""id"", - ""value"" - FROM ""CrcV2_TransferBatch"" - ), ""accountBalances"" AS ( - SELECT - account, - id, - SUM(amount) AS balance, - MAX(""timestamp"") AS ""timestamp"" - FROM ( - SELECT ""from"" AS account, id, -value AS amount, ""timestamp"" - FROM ""transfers"" - UNION ALL - SELECT ""to"" AS account, id, value AS amount, ""timestamp"" - FROM ""transfers"" - ) AS all_transfers - GROUP BY account, id - ) - select account - , id::text as ""tokenId"" - , ""accountBalances"".""timestamp"" as ""lastActivity"" - , floor(crc_demurrage(1675209600, ""accountBalances"".""timestamp"", balance)) as ""demurragedTotalBalance"" - from ""accountBalances"" - LEFT JOIN ""CrcV2_RegisterHuman"" hum ON hum.avatar = ""account"" - LEFT JOIN ""CrcV2_InviteHuman"" hum2 ON hum2.invited = ""account"" - LEFT JOIN ""CrcV2_RegisterOrganization"" org ON org.""organization"" = ""account"" - LEFT JOIN ""CrcV2_RegisterGroup"" grp ON grp.""group"" = ""account"" - WHERE ""account"" != '0x0000000000000000000000000000000000000000' - AND balance > 0; + create or replace view public.""V_CrcV2_BalancesByAccountAndToken"" + (account, ""tokenId"", ""tokenAddress"", ""lastActivity"", ""demurragedTotalBalance"") as + WITH transfers AS (SELECT ""CrcV2_TransferSingle"".""timestamp"", + ""CrcV2_TransferSingle"".""from"", + ""CrcV2_TransferSingle"".""to"", + ""CrcV2_TransferSingle"".id, + ""CrcV2_TransferSingle"".value, + ""CrcV2_TransferSingle"".""tokenAddress"" + FROM ""CrcV2_TransferSingle"" + UNION ALL + SELECT ""CrcV2_TransferBatch"".""timestamp"", + ""CrcV2_TransferBatch"".""from"", + ""CrcV2_TransferBatch"".""to"", + ""CrcV2_TransferBatch"".id, + ""CrcV2_TransferBatch"".value, + ""CrcV2_TransferBatch"".""tokenAddress"" + FROM ""CrcV2_TransferBatch""), + ""accountBalances"" AS (SELECT all_transfers.account, + all_transfers.id, + sum(all_transfers.amount) AS balance, + max(all_transfers.""timestamp"") AS ""timestamp"", + all_transfers.""tokenAddress"" + FROM (SELECT transfers.""from"" AS account, + transfers.id, + - transfers.value AS amount, + transfers.""timestamp"", + transfers.""tokenAddress"" + FROM transfers + UNION ALL + SELECT transfers.""to"" AS account, + transfers.id, + transfers.value AS amount, + transfers.""timestamp"", + transfers.""tokenAddress"" + FROM transfers) all_transfers + GROUP BY all_transfers.account, all_transfers.id, all_transfers.""tokenAddress"") + SELECT account, + id::text AS ""tokenId"", + ""tokenAddress"", + ""timestamp"" AS ""lastActivity"", + floor(crc_demurrage(1675209600::bigint, ""timestamp"", balance)) AS ""demurragedTotalBalance"" + FROM ""accountBalances"" + WHERE account <> '0x0000000000000000000000000000000000000000'::text + AND balance > 0::numeric; ") }; @@ -864,6 +914,10 @@ union all ("V_CrcV2", "Groups"), V_CrcV2_Groups }, + { + ("V_CrcV1", "BalancesByAccountAndToken"), + V_CrcV1_BalancesByAccountAndToken + }, { ("V_CrcV2", "BalancesByAccountAndToken"), V_CrcV2_BalancesByAccountAndToken diff --git a/Circles.Index.Query.Tests/TestConversionUtils.cs b/Circles.Index.Query.Tests/TestConversionUtils.cs index 052e9ad..b517207 100644 --- a/Circles.Index.Query.Tests/TestConversionUtils.cs +++ b/Circles.Index.Query.Tests/TestConversionUtils.cs @@ -32,4 +32,23 @@ public class TestConversionUtils // { // ConversionUtils.StaticCirclesToCircles(); // } + + + [Test] + public void TestConvertCirclesToStaticCircles() + { + var inflation = 0.07m; + var year = 4; + var baseAmount = 8m; + var inflatedAmount = baseAmount; + for (int i = 0; i < year; i++) + { + inflatedAmount *= 1 + inflation; + } + + + + decimal circlesBalance = 20; + decimal staticCirclesBalance = ConversionUtils.CirclesToStaticCircles(circlesBalance, DateTime.Now); + } } \ No newline at end of file diff --git a/Circles.Index.Rpc/CirclesRpcModule.cs b/Circles.Index.Rpc/CirclesRpcModule.cs index eb2c298..8a07dee 100644 --- a/Circles.Index.Rpc/CirclesRpcModule.cs +++ b/Circles.Index.Rpc/CirclesRpcModule.cs @@ -95,7 +95,7 @@ private async Task> GetTokenBalancesForAccount(Address cirlces = ConversionUtils.CrcToCircles(crc); attoCircles = ConversionUtils.CirclesToAttoCircles(cirlces); - staticCircles = ConversionUtils.CirclesToStaticCircles(cirlces); + staticCircles = ConversionUtils.CirclesToStaticCircles(cirlces, DateTime.Now); staticAttoCircles = ConversionUtils.CirclesToAttoCircles(staticCircles); } else @@ -119,7 +119,7 @@ private async Task> GetTokenBalancesForAccount(Address crc = ConversionUtils.CirclesToCrc(cirlces); attoCrc = ConversionUtils.CirclesToAttoCircles(crc); - staticCircles = ConversionUtils.CirclesToStaticCircles(cirlces); + staticCircles = ConversionUtils.CirclesToStaticCircles(cirlces, DateTime.Now); staticAttoCircles = ConversionUtils.CirclesToAttoCircles(staticCircles); } } diff --git a/Circles.Index.Utils/ConversionUtils.cs b/Circles.Index.Utils/ConversionUtils.cs index 9aaafc8..d3c60c4 100644 --- a/Circles.Index.Utils/ConversionUtils.cs +++ b/Circles.Index.Utils/ConversionUtils.cs @@ -7,86 +7,17 @@ namespace Circles.Index.Utils; using System; -public static class DemurrageConverter -{ - const decimal Gamma = 0.99980133200859895743m; // Daily demurrage factor - const decimal Beta = 1.0001987074682146291562714890133039617432343970799554367508m; - - const long DemurrageWindow = 86400; // 1 day in seconds - const long InflationDayZero = 1602720000L; // 15th October 2020 - - // Method to compute the power of Γ^i (Gamma^i) - public static decimal GammaPower(long i) - { - return (decimal)Math.Pow((double)Gamma, i); - } - - public static decimal BetaPower(long i) - { - return (decimal)Math.Pow((double)Beta, i); - } - - public static long Day(long timestamp) - { - // calculate which day the timestamp is in, rounding down - // note: max uint64 is 2^64 - 1, so we can safely cast the result - return (timestamp - InflationDayZero) / DemurrageWindow; - } - - /// - /// Converts an inflationary balance to a demurraged balance. - /// - /// The demurraged balance to convert. - /// - public static decimal ConvertDemurragedToInflationary(decimal demurragedBalance) - { - decimal CalculateInflationaryBalance() - { - /* Solidity code: - * // calculate the inflationary balance by dividing the balance by GAMMA^days - // note: GAMMA < 1, so dividing by a power of it, returns a bigger number, - // so the numerical imprecision is in the least significant bits. - int128 i = Math64x64.pow(BETA_64x64, _dayUpdated); - return Math64x64.mulu(i, _balance); - */ - var timestamp = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds(); - return demurragedBalance * BetaPower(Day(timestamp)); - } - - decimal inflationaryAmount = CalculateInflationaryBalance(); - return inflationaryAmount; - } - - public static decimal ConvertInflationaryToDemurraged(decimal inflationaryBalance) - { - /** From solidity: - * // calculate the demurrage value by multiplying the value by GAMMA^days - // note: GAMMA < 1, so multiplying by a power of it, returns a smaller number, - // so we lose the least significant bits, but our ground truth is the demurrage value, - // and the inflationary value is a numerical approximation (where the least significant digits - // are not reliable). - int128 r = Math64x64.pow(GAMMA_64x64, uint256(_day)); - return Math64x64.mulu(r, _inflationaryValue); - */ - decimal CalculateDemurragedBalance() - { - var timestamp = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds(); - return inflationaryBalance * GammaPower(Day(timestamp)); - } - - decimal demurragedAmount = CalculateDemurragedBalance(); - return demurragedAmount; - } -} - public abstract class ConversionUtils { + private static readonly DateTime CirclesInceptionDate = new(2020, 10, 15, 0, 0, 0, DateTimeKind.Utc); + private static readonly long CirclesInceptionTimestamp = - ConvertToUnixTimestamp(new DateTime(2020, 10, 15, 0, 0, 0, DateTimeKind.Utc)); + ConvertToUnixTimestamp(CirclesInceptionDate); private const decimal OneDayInMilliSeconds = 86400m * 1000m; private const decimal OneCirclesYearInDays = 365.25m; private const decimal OneCirclesYearInMilliSeconds = OneCirclesYearInDays * 24m * 60m * 60m * 1000m; + private const decimal Beta = 1.0001987074682146291562714890133039617432343970799554367508m; public static long ConvertToUnixTimestamp(DateTime dateTime) { @@ -153,10 +84,13 @@ public static decimal CirclesToCrc(decimal circlesBalance) /// Converts a regular v2 personal Circles balance (demurraged) to a static Circles balance (inflationary). /// /// + /// The day the demurrage value was last updated since inflation_day_zero /// - public static decimal CirclesToStaticCircles(decimal circlesBalance) + public static decimal CirclesToStaticCircles(decimal circlesBalance, DateTime lastUpdate) { - return CirclesToCrc(circlesBalance) * 3; + var lastUpdateDay = (lastUpdate - CirclesInceptionDate).TotalDays; + var f = (decimal)Math.Pow((double)Beta, lastUpdateDay); + return f * circlesBalance; } /// @@ -166,7 +100,10 @@ public static decimal CirclesToStaticCircles(decimal circlesBalance) /// public static decimal StaticCirclesToCircles(decimal staticCirclesBalance) { - return CrcToCircles(staticCirclesBalance / 3); + var lastUpdate = DateTime.Now; + var lastUpdateDay = (lastUpdate - CirclesInceptionDate).TotalDays; + var f = (decimal)Math.Pow((double)Beta, lastUpdateDay); + return staticCirclesBalance / f; } public static decimal AttoCirclesToCircles(UInt256 weiBalance) diff --git a/Circles.Index/Circles.Index.csproj b/Circles.Index/Circles.Index.csproj index f0f525e..d80d958 100644 --- a/Circles.Index/Circles.Index.csproj +++ b/Circles.Index/Circles.Index.csproj @@ -8,8 +8,8 @@ Daniel Janz (Gnosis Service GmbH) Gnosis Service GmbH Circles - 1.8.3 - 1.8.3 + 1.9.1 + 1.9.1 diff --git a/Readme.md b/Readme.md index 0b95fa8..beb782b 100644 --- a/Readme.md +++ b/Readme.md @@ -510,12 +510,12 @@ Tables for batch events have an additional `batchIndex` column. * `CreateVault` * `group` (Address) * `vault` (Address) -* `GroupMintSingle` +* `CollateralLockedSingle` * `group` (Address) * `id` (BigInteger) * `value` (BigInteger) * `userData` (Bytes) -* `GroupMintBatch` +* `CollateralLockedBatch` * `group` (Address) * `id` (BigInteger) * `value` (BigInteger) diff --git a/docker-compose.gnosis.yml b/docker-compose.gnosis.yml index c0fe910..58459e5 100644 --- a/docker-compose.gnosis.yml +++ b/docker-compose.gnosis.yml @@ -37,10 +37,10 @@ services: - .env environment: - V1_HUB_ADDRESS=0x29b9a7fBb8995b2423a71cC17cf9810798F6C543 - - V2_HUB_ADDRESS=0xa5c7ADAE2fd3844f12D52266Cb7926f8649869Da - - V2_NAME_REGISTRY_ADDRESS=0x738fFee24770d0DE1f912adf2B48b0194780E9AD - - V2_STANDARD_TREASURY_ADDRESS=0xbb76CF35ec106c5c7a447246257dcfCB7244cA04 - - V2_ERC20_LIFT_ADDRESS=0xB6B79BeEfd58cf33b298A456934554cf440354aD + - V2_HUB_ADDRESS=0x3a0F7848071f067c25b0747eC5bEdc77cb3778eb + - V2_NAME_REGISTRY_ADDRESS=0x6192069E85afBD09D03f7e85eB6c35982A847e16 + - V2_STANDARD_TREASURY_ADDRESS=0x421ae522F756412808Ff62F74C20e5ebDA8C4208 + - V2_ERC20_LIFT_ADDRESS=0x1CAc5fE351EFFa130223aC0f84EB6B7Efc7a66AD - POSTGRES_CONNECTION_STRING=Server=postgres-gnosis;Port=5432;Database=postgres;User Id=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}; - START_BLOCK=12000000