Skip to content

Commit

Permalink
coin distribution preparation
Browse files Browse the repository at this point in the history
  • Loading branch information
ice-ares committed Dec 15, 2023
1 parent fe80ef2 commit d89df2b
Show file tree
Hide file tree
Showing 23 changed files with 335 additions and 122 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ generate-swagger:
generate-swaggers:
go install github.com/swaggo/swag/cmd/swag@latest
set -xe; \
[ -d cmd ] && find ./cmd -mindepth 1 -maxdepth 1 -type d -print | grep -v 'fixture' | grep -v 'freezer-miner' | sed 's/\.\///g' | while read service; do \
[ -d cmd ] && find ./cmd -mindepth 1 -maxdepth 1 -type d -print | grep -v 'fixture' | grep -v 'freezer-miner' | grep -v 'freezer-coin-distributer' | sed 's/\.\///g' | while read service; do \
env SERVICE=$${service} $(MAKE) generate-swagger; \
done;

Expand Down
7 changes: 6 additions & 1 deletion application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,12 @@ wintr/connectors/storage/v2: &db
miner:
bookkeeper/storage: *bookkeeperStorage
development: true
workers: 1
workers: 2
batchSize: 100
wintr/connectors/storage/v2: *db
coin-distribution:
development: true
workers: 2
batchSize: 100
wintr/connectors/storage/v2: *db
extra-bonus-notifier:
Expand Down
11 changes: 8 additions & 3 deletions cmd/freezer-refrigerant/api/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,13 +383,17 @@ const docTemplate = `{
}
},
"definitions": {
"coindistribution.CoinDistibutionForReview": {
"coindistribution.PendingReview": {
"type": "object",
"properties": {
"ethAddress": {
"type": "string",
"example": "0x43...."
},
"ice": {
"type": "number",
"example": 1000
},
"iceflakes": {
"type": "string",
"example": "100000000000000"
Expand All @@ -416,12 +420,13 @@ const docTemplate = `{
"type": "object",
"properties": {
"cursor": {
"type": "integer"
"type": "integer",
"example": 5065
},
"distributions": {
"type": "array",
"items": {
"$ref": "#/definitions/coindistribution.CoinDistibutionForReview"
"$ref": "#/definitions/coindistribution.PendingReview"
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions cmd/freezer-refrigerant/api/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -377,13 +377,17 @@
}
},
"definitions": {
"coindistribution.CoinDistibutionForReview": {
"coindistribution.PendingReview": {
"type": "object",
"properties": {
"ethAddress": {
"type": "string",
"example": "0x43...."
},
"ice": {
"type": "number",
"example": 1000
},
"iceflakes": {
"type": "string",
"example": "100000000000000"
Expand All @@ -410,12 +414,13 @@
"type": "object",
"properties": {
"cursor": {
"type": "integer"
"type": "integer",
"example": 5065
},
"distributions": {
"type": "array",
"items": {
"$ref": "#/definitions/coindistribution.CoinDistibutionForReview"
"$ref": "#/definitions/coindistribution.PendingReview"
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions cmd/freezer-refrigerant/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

basePath: /v1w
definitions:
coindistribution.CoinDistibutionForReview:
coindistribution.PendingReview:
properties:
ethAddress:
example: 0x43....
type: string
ice:
example: 1000
type: number
iceflakes:
example: "100000000000000"
type: string
Expand All @@ -26,10 +29,11 @@ definitions:
main.CoinDistributionsForReview:
properties:
cursor:
example: 5065
type: integer
distributions:
items:
$ref: '#/definitions/coindistribution.CoinDistibutionForReview'
$ref: '#/definitions/coindistribution.PendingReview'
type: array
type: object
main.StartNewMiningSessionRequestBody:
Expand Down
4 changes: 2 additions & 2 deletions cmd/freezer-refrigerant/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ type (
}

CoinDistributionsForReview struct {
Distributions []*coindistribution.CoinDistibutionForReview `json:"distributions"`
Cursor uint64 `json:"cursor" example: 5065`
Distributions []*coindistribution.PendingReview `json:"distributions"`
Cursor uint64 `json:"cursor" example:"5065"`
}

GetCoinDistributionForReviewParams struct {
Expand Down
62 changes: 24 additions & 38 deletions coin-distribution/DDL.sql
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
-- SPDX-License-Identifier: ice License 1.0
DO $$ BEGIN
CREATE DOMAIN uint256 AS NUMERIC(78,0) NOT NULL DEFAULT 0
CHECK (VALUE >= 0 AND VALUE <= 115792089237316195423570985008687907853269984665640564039457584007913129639935)
CHECK (SCALE(VALUE) = 0);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

CREATE TABLE IF NOT EXISTS pending_coin_distributions (
created_at timestamp NOT NULL,
internal_id bigint NOT NULL,
iceflakes bigint NOT NULL,
iceflakes uint256,
user_id text NOT NULL PRIMARY KEY,
eth_address text NOT NULL);

CREATE INDEX IF NOT EXISTS pending_coin_distributions_worker_number_ix ON pending_coin_distributions ((internal_id % 10), created_at ASC);

CREATE TABLE IF NOT EXISTS pending_coin_distribution_configurations (
CREATE TABLE IF NOT EXISTS global (
key text NOT NULL primary key,
value text NOT NULL );
INSERT INTO pending_coin_distribution_configurations(key,value) VALUES ('enabled','true') ON CONFLICT(key) DO NOTHING;

--- Flow:
--infinite loop: -- with 30 sec sleep between iterations if 0 rows returned
--do in transaction:
--1. SELECT *
-- FROM pending_coin_distributions
-- WHERE internal_id % 10 = $1
-- ORDER BY created_at ASC
-- LIMIT $2
-- FOR UPDATE
--2. delete from pending_coin_distributions WHERE user_id = ANY($1)
--3. call ERC-20 smart contract method to airdrop coins

DO $$ BEGIN
CREATE DOMAIN uint256 AS NUMERIC(78,0) NOT NULL DEFAULT 0
CHECK (VALUE >= 0 AND VALUE <= 115792089237316195423570985008687907853269984665640564039457584007913129639935)
CHECK (SCALE(VALUE) = 0);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
INSERT INTO global (key,value)
VALUES ('coin_distributer_enabled','true')
ON CONFLICT(key) DO NOTHING;

CREATE TABLE IF NOT EXISTS coin_distributions_by_earner (
created_at timestamp NOT NULL,
Expand All @@ -47,33 +37,29 @@ CREATE TABLE IF NOT EXISTS coin_distributions_by_earner (
CREATE TABLE IF NOT EXISTS coin_distributions_pending_review (
created_at timestamp NOT NULL,
internal_id bigint NOT NULL,
iceflakes uint256,
ice bigint NOT NULL,
day date NOT NULL,
iceflakes uint256 ,
username text NOT NULL,
referred_by_username text NOT NULL,
user_id text NOT NULL PRIMARY KEY,
eth_address text NOT NULL);
user_id text NOT NULL,
eth_address text NOT NULL,
PRIMARY KEY(day, user_id));

CREATE INDEX IF NOT EXISTS coin_distributions_pending_review_internal_id_ix ON coin_distributions_pending_review (internal_id);

CREATE TABLE IF NOT EXISTS reviewed_coin_distributions (
reviewed_at timestamp NOT NULL,
created_at timestamp NOT NULL,
internal_id bigint NOT NULL,
iceflakes uint256,
ice bigint NOT NULL,
day date NOT NULL,
review_day date NOT NULL,
iceflakes uint256 ,
username text NOT NULL,
referred_by_username text NOT NULL,
user_id text NOT NULL ,
eth_address text NOT NULL,
reviewer_user_id text NOT NULL,
decision text NOT NULL,
PRIMARY KEY(user_id, reviewed_at));

CREATE TABLE IF NOT EXISTS pending_coin_distribution_statistics (
created_at timestamp NOT NULL PRIMARY KEY,
total_iceflakes uint256,
marketing_25percent_iceflakes uint256,
marketing_45percent_iceflakes uint256,
marketing_30percent_iceflakes uint256,
marketing_25percent_eth_address text NOT NULL,
marketing_45percent_eth_address text NOT NULL,
marketing_30percent_eth_address text NOT NULL);
PRIMARY KEY(user_id, day, review_day));
4 changes: 2 additions & 2 deletions coin-distribution/coin_distribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (cd *coinDistributer) isEnabled(rooCtx context.Context) bool {
defer cancel()
val, err := storage.Get[struct {
Enabled bool
}](ctx, cd.db, `SELECT value::bool as enabled FROM pending_coin_distribution_configurations WHERE key = 'enabled'`)
}](ctx, cd.db, `SELECT value::bool as enabled FROM global WHERE key = 'coin_distributer_enabled'`)
if err != nil {
log.Error(errors.Wrap(err, "failed to check if coinDistributer is enabled"))

Expand All @@ -113,7 +113,7 @@ func (cd *coinDistributer) isEnabled(rooCtx context.Context) bool {
func (cd *coinDistributer) Disable(rooCtx context.Context) error {
ctx, cancel := context.WithTimeout(rooCtx, requestDeadline)
defer cancel()
rows, err := storage.Exec(ctx, cd.db, `UPDATE pending_coin_distribution_configurations SET value = 'false' WHERE key = 'enabled'`)
rows, err := storage.Exec(ctx, cd.db, `UPDATE global SET value = 'false' WHERE key = 'coin_distributer_enabled'`)
if err != nil {
return errors.Wrap(err, "failed to disable coinDistributer")
}
Expand Down
9 changes: 6 additions & 3 deletions coin-distribution/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@ type (

Repository interface {
io.Closer
GetCoinDistributionsForReview(ctx context.Context, cursor, limit uint64) (updatedCursor uint64, distributions []*CoinDistibutionForReview, err error)
GetCoinDistributionsForReview(ctx context.Context, cursor, limit uint64) (updatedCursor uint64, distributions []*PendingReview, err error)
CheckHealth(ctx context.Context) error
}

CoinDistibutionForReview struct {
PendingReview struct {
CreatedAt *time.Time `json:"time" swaggertype:"string" example:"2022-01-03T16:20:52.156534Z"`
Iceflakes string `json:"iceflakes" swaggertype:"string" example:"100000000000000"`
Username string `json:"username" swaggertype:"string" example:"myusername"`
ReferredByUsername string `json:"referredByUsername" swaggertype:"string" example:"myrefusername"`
UserID string `json:"userId" swaggertype:"string" example:"12746386-03de-44d7-91c7-856fa66b6ed6"`
EthAddress string `json:"ethAddress" swaggertype:"string" example:"0x43...."`
Ice float64 `json:"ice" db:"-" example:"1000"`
IceInternal int64 `json:"-" db:"ice" swaggerignore:"true"`
}
)

Expand Down Expand Up @@ -69,7 +71,8 @@ type (
Development bool `yaml:"development"`
}
coinDistribution struct {
*CoinDistibutionForReview
*PendingReview
Day stdlibtime.Time
InternalID uint64
}
)
61 changes: 61 additions & 0 deletions coin-distribution/eligibility.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: ice License 1.0

package coindistribution

import (
stdlibtime "time"

"github.com/ice-blockchain/eskimo/users"
"github.com/ice-blockchain/freezer/model"
"github.com/ice-blockchain/wintr/time"
)

const (
miningSessionSoloEndedAtNetworkDelayAdjustment = 20 * stdlibtime.Second
)

func CalculateEthereumDistributionICEBalance(
standardBalance float64,
ethereumDistributionFrequencyMin, ethereumDistributionFrequencyMax stdlibtime.Duration,
now, ethereumDistributionEndDate *time.Time,
) float64 {
delta := ethereumDistributionEndDate.Truncate(ethereumDistributionFrequencyMin).Sub(now.Truncate(ethereumDistributionFrequencyMin))
if delta <= ethereumDistributionFrequencyMax {
return standardBalance
}

//TODO: should this be fractional or natural?
return standardBalance / (float64(delta.Nanoseconds()) / float64(ethereumDistributionFrequencyMax.Nanoseconds()))
}

func IsEligibleForEthereumDistribution(
minMiningStreaksRequired uint64,
standardBalance, minEthereumDistributionICEBalanceRequired float64,
ethAddress, country string,
distributionDeniedCountries map[string]struct{},
now, miningSessionSoloStartedAt, miningSessionSoloEndedAt, ethereumDistributionEndDate *time.Time,
kycState model.KYCState,
miningSessionDuration, ethereumDistributionFrequencyMin, ethereumDistributionFrequencyMax stdlibtime.Duration) bool {
var countryAllowed bool
if _, countryDenied := distributionDeniedCountries[country]; country != "" && !countryDenied {
countryAllowed = true
}

return countryAllowed &&
!miningSessionSoloEndedAt.IsNil() && miningSessionSoloEndedAt.After(now.Add(miningSessionSoloEndedAtNetworkDelayAdjustment)) &&
isEthereumAddressValid(ethAddress) &&
CalculateEthereumDistributionICEBalance(standardBalance, ethereumDistributionFrequencyMin, ethereumDistributionFrequencyMax, now, ethereumDistributionEndDate) >= minEthereumDistributionICEBalanceRequired && //nolint:lll // .
model.CalculateMiningStreak(now, miningSessionSoloStartedAt, miningSessionSoloEndedAt, miningSessionDuration) >= minMiningStreaksRequired &&
kycState.KYCStepPassedCorrectly(users.QuizKYCStep)
}

func isEthereumAddressValid(ethAddress string) bool {
if ethAddress == "" {
return false
}
if ethAddress == "skip" {
return true
}

return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import (
"github.com/ice-blockchain/wintr/connectors/storage/v2"
)

func (r *repository) GetCoinDistributionsForReview(ctx context.Context, cursor, limit uint64) (updCursor uint64, distributions []*CoinDistibutionForReview, err error) {
sql := `SELECT * FROM coin_distributions_pending_review WHERE internal_id > $1 ORDER BY internal_id LIMIT $2`
func (r *repository) GetCoinDistributionsForReview(ctx context.Context, cursor, limit uint64) (updCursor uint64, distributions []*PendingReview, err error) {
sql := `SELECT *
FROM coin_distributions_pending_review
WHERE internal_id > $1
ORDER BY internal_id
LIMIT $2`
args := []any{cursor, limit}
result, err := storage.Select[coinDistribution](ctx, r.db, sql, args...)
if err != nil {
Expand All @@ -21,9 +25,10 @@ func (r *repository) GetCoinDistributionsForReview(ctx context.Context, cursor,
if uint64(len(result)) == limit {
updCursor = result[len(result)-1].InternalID
}
distributions = make([]*CoinDistibutionForReview, len(result)) //nolint:makezero // .
distributions = make([]*PendingReview, len(result)) //nolint:makezero // .
for i, d := range result {
distributions[i] = d.CoinDistibutionForReview
d.PendingReview.Ice = float64(d.PendingReview.IceInternal) / 100
distributions[i] = d.PendingReview
}

return
Expand Down
Loading

0 comments on commit d89df2b

Please sign in to comment.