diff --git a/cmd/connector-common-lootboxes-timescale/main.go b/cmd/connector-common-lootboxes-timescale/main.go index b7f0d2e10..2ef4db027 100644 --- a/cmd/connector-common-lootboxes-timescale/main.go +++ b/cmd/connector-common-lootboxes-timescale/main.go @@ -2,9 +2,18 @@ package main import ( "github.com/fluidity-money/fluidity-app/lib/databases/timescale/lootboxes" + "github.com/fluidity-money/fluidity-app/lib/databases/timescale/user-actions" queue "github.com/fluidity-money/fluidity-app/lib/queues/lootboxes" ) func main() { - queue.LootboxesAll(lootboxes.InsertLootbox) + queue.LootboxesAll(func(lootbox lootboxes.Lootbox) { + user_actions.UpdateAggregatedUserTransactionByHashWithLootbottles( + lootbox.LootboxCount, + lootbox.RewardTier, + lootbox.TransactionHash, + ) + + lootboxes.InsertLootbox(lootbox) + }) } diff --git a/cmd/microservice-ethereum-create-transaction-lootboxes/main.go b/cmd/microservice-ethereum-create-transaction-lootboxes/main.go index a523578a7..4d03a67a9 100644 --- a/cmd/microservice-ethereum-create-transaction-lootboxes/main.go +++ b/cmd/microservice-ethereum-create-transaction-lootboxes/main.go @@ -309,6 +309,7 @@ func main() { RewardTier: rewardTier, LootboxCount: lootboxCountFloat, Application: application, + Epoch: currentEpoch, } queue.SendMessage(lootboxes_queue.TopicLootboxes, lootbox) diff --git a/database/102-up-timescale/20240110163905-user_actions_user_actions_lootbottles_make_table.sql b/database/102-up-timescale/20240110163905-user_actions_user_actions_lootbottles_make_table.sql new file mode 100644 index 000000000..241c9029c --- /dev/null +++ b/database/102-up-timescale/20240110163905-user_actions_user_actions_lootbottles_make_table.sql @@ -0,0 +1,67 @@ +-- migrate:up + +DROP FUNCTION aggregated_user_transactions_lootbottles; + +DROP TABLE aggregated_user_transactions_lootbottles_return; + +ALTER TABLE aggregated_user_transactions + ADD COLUMN lootbox_count DECIMAL, + ADD COLUMN reward_tier INTEGER; + +-- migrate:down + +ALTER TABLE aggregated_user_transactions + DROP COLUMN lootbox_count, + DROP COLUMN reward_tier; + +CREATE TABLE aggregated_user_transactions_lootbottles_return ( + token_short_name VARCHAR NOT NULL, + network network_blockchain NOT NULL, + time TIMESTAMP WITHOUT TIME ZONE NOT NULL, + transaction_hash VARCHAR NOT NULL, + sender_address VARCHAR NOT NULL, + recipient_address VARCHAR NOT NULL, + amount DOUBLE PRECISION NOT NULL, + application VARCHAR NOT NULL, + winning_amount DOUBLE PRECISION NOT NULL, + winning_address VARCHAR NOT NULL, + reward_hash VARCHAR NOT NULL, + type user_action NOT NULL, + swap_in BOOLEAN NOT NULL, + utility_amount DOUBLE PRECISION NOT NULL, + utility_name VARCHAR NOT NULL, + + lootbox_count DECIMAL, + reward_tier INTEGER +); + +CREATE FUNCTION aggregated_user_transactions_lootbottles() +RETURNS SETOF aggregated_user_transactions_lootbottles_return +LANGUAGE SQL +STABLE +AS +$$ +SELECT + token_short_name, + network, + time, + transaction_hash, + sender_address, + recipient_address, + amount, + application, + winning_amount, + winning_address, + reward_hash, + type user_action, + swap_in, + utility_amount, + utility_name, + lootbox_count, + reward_tier +FROM + aggregated_user_transactions +LEFT JOIN ( + SELECT reward_tier, lootbox_count FROM lootbox +) AS lootbox_user_actions ON transaction_hash = aggregated_user_transactions.transaction_hash +$$; diff --git a/database/102-up-timescale/20240111140041-lootbottles_reward_ambassadors_and_launch_epoch_2.sql b/database/102-up-timescale/20240111140041-lootbottles_reward_ambassadors_and_launch_epoch_2.sql new file mode 100644 index 000000000..8c13f97e4 --- /dev/null +++ b/database/102-up-timescale/20240111140041-lootbottles_reward_ambassadors_and_launch_epoch_2.sql @@ -0,0 +1,83 @@ + +-- migrate:up + +INSERT INTO lootbox ( + address, + source, + awarded_time, + volume, + reward_tier, + lootbox_count, + application, + epoch +) + +VALUES + ('0x75964ac8cc1676eb51451f25faaaefd40ccde602', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0x75964ac8cc1676eb51451f25faaaefd40ccde602', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0x75964ac8cc1676eb51451f25faaaefd40ccde602', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0x75964ac8cc1676eb51451f25faaaefd40ccde602', 'airdrop', timezone('utc', now()), 0, 4, 1, 'none', 'epoch_2'), + ('0xd1d13003128cb454ff562850f86413498c1ebedf', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0xd1d13003128cb454ff562850f86413498c1ebedf', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0xd1d13003128cb454ff562850f86413498c1ebedf', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0x8cb42fe548bb51ca4125eac3c14cf177b2b183d8', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0x8cb42fe548bb51ca4125eac3c14cf177b2b183d8', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0x8cb42fe548bb51ca4125eac3c14cf177b2b183d8', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0xb363335ff6e0a91f870c200bf6fcb38d1fd0f346', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0xb363335ff6e0a91f870c200bf6fcb38d1fd0f346', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0xb363335ff6e0a91f870c200bf6fcb38d1fd0f346', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0x2d0eb64ffc36339a15c531b64fd55404ca7f8f62', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0x2d0eb64ffc36339a15c531b64fd55404ca7f8f62', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0x2d0eb64ffc36339a15c531b64fd55404ca7f8f62', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0x32f5de0d8f24094c660979ae34772985a1a8c831', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0x32f5de0d8f24094c660979ae34772985a1a8c831', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0x32f5de0d8f24094c660979ae34772985a1a8c831', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0x2da7958dc2a33e7720935102107f0886e8ab6574', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0x2da7958dc2a33e7720935102107f0886e8ab6574', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0x2da7958dc2a33e7720935102107f0886e8ab6574', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0xf6352e3a8e502b93af36821cc6f45d6f37ac4aed', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0xf6352e3a8e502b93af36821cc6f45d6f37ac4aed', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0xf6352e3a8e502b93af36821cc6f45d6f37ac4aed', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0xef05ee60799ea122db2ace21425f6d4991dd3805', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0xef05ee60799ea122db2ace21425f6d4991dd3805', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0xef05ee60799ea122db2ace21425f6d4991dd3805', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0x0b0154cbfaba8a18caf5fcba2b83ce383c1a0095', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0x0b0154cbfaba8a18caf5fcba2b83ce383c1a0095', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0x0b0154cbfaba8a18caf5fcba2b83ce383c1a0095', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'), + ('0x225e02bd307a5af7608ceff02a4e48ba06be4ddd', 'airdrop', timezone('utc', now()), 0, 1, 100, 'none', 'epoch_2'), + ('0x225e02bd307a5af7608ceff02a4e48ba06be4ddd', 'airdrop', timezone('utc', now()), 0, 2, 50, 'none', 'epoch_2'), + ('0x225e02bd307a5af7608ceff02a4e48ba06be4ddd', 'airdrop', timezone('utc', now()), 0, 3, 10, 'none', 'epoch_2'); + +INSERT INTO lootbox_config ( + is_current_program, + program_begin, + program_end, + epoch_identifier, + ethereum_application +) + +VALUES ( + FALSE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP + INTERVAL '30 days', + 'epoch_2', + 'none' +); + +-- migrate:down + +DELETE FROM lootbox +WHERE + (address = '0x75964ac8cc1676eb51451f25faaaefd40ccde602' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0x75964ac8cc1676eb51451f25faaaefd40ccde602' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0x75964ac8cc1676eb51451f25faaaefd40ccde602' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') OR (address = '0x75964ac8cc1676eb51451f25faaaefd40ccde602' AND source = 'airdrop' AND reward_tier = 4 AND lootbox_count = 1 AND epoch = 'epoch_2') + OR (address = '0xd1d13003128cb454ff562850f86413498c1ebedf' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0xd1d13003128cb454ff562850f86413498c1ebedf' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0xd1d13003128cb454ff562850f86413498c1ebedf' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0x8cb42fe548bb51ca4125eac3c14cf177b2b183d8' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0x8cb42fe548bb51ca4125eac3c14cf177b2b183d8' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0x8cb42fe548bb51ca4125eac3c14cf177b2b183d8' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0xb363335ff6e0a91f870c200bf6fcb38d1fd0f346' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0xb363335ff6e0a91f870c200bf6fcb38d1fd0f346' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0xb363335ff6e0a91f870c200bf6fcb38d1fd0f346' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0x2d0eb64ffc36339a15c531b64fd55404ca7f8f62' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0x2d0eb64ffc36339a15c531b64fd55404ca7f8f62' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0x2d0eb64ffc36339a15c531b64fd55404ca7f8f62' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0x32f5de0d8f24094c660979ae34772985a1a8c831' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0x32f5de0d8f24094c660979ae34772985a1a8c831' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0x32f5de0d8f24094c660979ae34772985a1a8c831' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0x2da7958dc2a33e7720935102107f0886e8ab6574' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0x2da7958dc2a33e7720935102107f0886e8ab6574' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0x2da7958dc2a33e7720935102107f0886e8ab6574' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0xf6352e3a8e502b93af36821cc6f45d6f37ac4aed' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0xf6352e3a8e502b93af36821cc6f45d6f37ac4aed' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0xf6352e3a8e502b93af36821cc6f45d6f37ac4aed' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0xef05ee60799ea122db2ace21425f6d4991dd3805' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0xef05ee60799ea122db2ace21425f6d4991dd3805' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0xef05ee60799ea122db2ace21425f6d4991dd3805' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0x0b0154cbfaba8a18caf5fcba2b83ce383c1a0095' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0x0b0154cbfaba8a18caf5fcba2b83ce383c1a0095' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0x0b0154cbfaba8a18caf5fcba2b83ce383c1a0095' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + OR (address = '0x225e02bd307a5af7608ceff02a4e48ba06be4ddd' AND source = 'airdrop' AND reward_tier = 1 AND lootbox_count = 100 AND epoch = 'epoch_2') OR (address = '0x225e02bd307a5af7608ceff02a4e48ba06be4ddd' AND source = 'airdrop' AND reward_tier = 2 AND lootbox_count = 50 AND epoch = 'epoch_2') OR (address = '0x225e02bd307a5af7608ceff02a4e48ba06be4ddd' AND source = 'airdrop' AND reward_tier = 3 AND lootbox_count = 10 AND epoch = 'epoch_2') + +DELETE FROM lootbox_config WHERE epoch_identifier = 'epoch_2'; diff --git a/lib/databases/timescale/lootboxes/config.go b/lib/databases/timescale/lootboxes/config.go index 5f8e1f12e..ce39b1383 100644 --- a/lib/databases/timescale/lootboxes/config.go +++ b/lib/databases/timescale/lootboxes/config.go @@ -33,7 +33,7 @@ func GetLootboxConfig() (programFound bool, hasBegun bool, curEpoch string, curA program_begin < (select timestamp from t) AND program_end > (select timestamp from t), epoch_identifier, - current_application + ethereum_application FROM %s WHERE is_current_program;`, diff --git a/lib/databases/timescale/lootboxes/rewards.go b/lib/databases/timescale/lootboxes/rewards.go index a0df54a3b..d5887f843 100644 --- a/lib/databases/timescale/lootboxes/rewards.go +++ b/lib/databases/timescale/lootboxes/rewards.go @@ -301,11 +301,11 @@ func UpdateOrInsertAmountsRewarded(network_ network.BlockchainNetwork, lootboxCu $4, $5, $6, - $7 + CURRENT_TIMESTAMP ) - ON CONFLICT (id) + ON CONFLICT (network, epoch, token_short_name, winner, application) DO UPDATE SET - amount_earned = amount_earned + $4, + amount_earned = lootbox_amounts_rewarded.amount_earned + $4, last_updated = CURRENT_TIMESTAMP`, TableLootboxAmountsRewarded, @@ -317,8 +317,8 @@ func UpdateOrInsertAmountsRewarded(network_ network.BlockchainNetwork, lootboxCu lootboxCurrentEpoch, tokenShortName, amountNormalLossy, - application, winnerAddress, + application, ) if err != nil { diff --git a/lib/databases/timescale/user-actions/user-actions-aggregate.go b/lib/databases/timescale/user-actions/user-actions-aggregate.go index 1fa7f7538..ef5e752b8 100644 --- a/lib/databases/timescale/user-actions/user-actions-aggregate.go +++ b/lib/databases/timescale/user-actions/user-actions-aggregate.go @@ -14,9 +14,7 @@ import ( user_actions "github.com/fluidity-money/fluidity-app/lib/types/user-actions" ) -const ( - TableAggregatedUserTransactions = `aggregated_user_transactions` -) +const TableAggregatedUserTransactions = `aggregated_user_transactions` func InsertAggregatedUserTransaction(userTransaction user_actions.AggregatedUserTransaction) { timescaleClient := timescale.Client() @@ -218,3 +216,46 @@ func UpdateAggregatedUserTransactionByHash(userTransaction user_actions.Aggregat }) } } + +// UpdateAggregatedUserTransactionByHashWithLootbottles after finding it first +func UpdateAggregatedUserTransactionByHashWithLootbottles(lootbottlesCount float64, rewardTier int, transactionHash string) { + timescaleClient := timescale.Client() + + statementText := fmt.Sprintf( + `UPDATE %s + SET lootbox_count = $1, reward_tier = $2 + WHERE transaction_hash = $3`, + + TableAggregatedUserTransactions, + ) + + r, err := timescaleClient.Exec( + statementText, + lootbottlesCount, + rewardTier, + transactionHash, + ) + + if err != nil { + log.Fatal(func(k *log.Log) { + k.Context = Context + k.Message = "Failed to update an aggregated user transaction with a lootbottle" + k.Payload = err + }) + } + + rows, _ := r.RowsAffected() + + if rows != 1 { + log.Fatal(func(k *log.Log) { + k.Context = Context + + k.Format( + "%d rows affected by an aggregate user transaction with lootbottle update, expected 1!", + rows, + ) + + k.Payload = err + }) + } +} diff --git a/lib/types/user-actions/user-actions.go b/lib/types/user-actions/user-actions.go index f799592ae..fecc3eb6a 100644 --- a/lib/types/user-actions/user-actions.go +++ b/lib/types/user-actions/user-actions.go @@ -132,7 +132,7 @@ type ( // AggregatedTransactionFromUserAction to create a partially aggregated transaction from a user action func AggregatedTransactionFromUserAction(userAction UserAction) AggregatedUserTransaction { - var ( + var ( senderAddress string recipientAddress string ) @@ -180,7 +180,7 @@ func AggregatedTransactionFromPendingWinner(pendingWinner winners.PendingWinner) SenderAddress: senderAddress, // the sender is the winner of a pending win WinningAddress: senderAddress, - Type: "send", + Type: "send", } if pendingWinner.Utility == "FLUID" { diff --git a/web/app.fluidity.money/app/components/JoeFarmlandsOrCamelotKingdom/index.tsx b/web/app.fluidity.money/app/components/JoeFarmlandsOrCamelotKingdom/index.tsx index 1339df2c4..90d2cc980 100644 --- a/web/app.fluidity.money/app/components/JoeFarmlandsOrCamelotKingdom/index.tsx +++ b/web/app.fluidity.money/app/components/JoeFarmlandsOrCamelotKingdom/index.tsx @@ -7,13 +7,21 @@ export const JoeFarmlandsOrCamelotKingdomLinks = () => [ const JoeFarmlandsOrCamelotKingdom = () => { return (