Skip to content

Commit

Permalink
nft module
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkCherepovskyi committed Nov 1, 2024
1 parent 868daac commit e049870
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 41 deletions.
19 changes: 14 additions & 5 deletions database/nft.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,28 @@ func (db *Db) SaveNFTEvent(eventType string, nftAddress, validator, newValidator
// -------------------------------------------------------------------------------------------------------------------

// SaveNFT allows to save new NFT
func (db *Db) SaveNFT(address, owner string, availableAmount sdk.Coin) error {
func (db *Db) SaveNFT(address, owner string, availableAmount sdk.Coin, lastVestingTime int64, vestingPeriod int64, rewardPerPeriod sdk.Coin, vestingCounter int64, denom string) error {
query := `
INSERT INTO nfts(address, owner, available_amount)
VALUES ($1, $2, $3)
INSERT INTO nfts(address, owner, available_amount, vesting_period, reward_per_period, last_vesting_time, vesting_counter, denom)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT (address) DO UPDATE
SET owner = excluded.owner,
available_amount = excluded.available_amount
available_amount = excluded.available_amount,
last_vesting_time = excluded.last_vesting_time,
vesting_counter = excluded.vesting_counter
WHERE nfts.address <= excluded.address
`
_, err := db.SQL.Exec(query, address, owner, pq.Array(dbtypes.NewDbCoins(sdk.NewCoins(availableAmount))))
_, err := db.SQL.Exec(query, address, owner, pq.Array(dbtypes.NewDbCoins(sdk.NewCoins(availableAmount))), vestingPeriod, pq.Array(dbtypes.NewDbCoins(sdk.NewCoins(rewardPerPeriod))), lastVestingTime, vestingCounter, denom)
if err != nil {
return fmt.Errorf("error while storing nft: %s", err)
}

return nil
}

// GetNFTsToUpdate returns all the nfts that are currently stored inside the database and should be updated.
func (db *Db) GetNFTsToUpdate() ([]dbtypes.NFTsRow, error) {
var rows []dbtypes.NFTsRow
err := db.Sqlx.Select(&rows, `SELECT * FROM nfts WHERE to_timestamp(last_vesting_time) + INTERVAL '1 second' * last_vesting_time < NOW();`)
return rows, err
}
9 changes: 8 additions & 1 deletion database/schema/13-nft.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ CREATE TABLE nfts
(
address TEXT NOT NULL UNIQUE,
owner TEXT NOT NULL,
available_amount COIN[] NOT NULL DEFAULT '{}'
available_amount COIN[] NOT NULL DEFAULT '{}',
vesting_period INTEGER,
reward_per_period COIN[] NOT NULL DEFAULT '{}',
last_vesting_time INTEGER,
vesting_counter SMALLINT,
denom TEXT
);
CREATE INDEX nft_address_index ON nfts(address);



-- +migrate Down
Expand Down
43 changes: 21 additions & 22 deletions database/types/nft.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import "math/big"
import (
"math/big"
)

// NFTEventsRow represents a single row inside the nft_events table
type NFTEventsRow struct {
Expand All @@ -24,29 +26,26 @@ func NewNFTEventsRow(id uint64, eventType string, nftAddress string, owner strin

// NFTsRow represents a single row inside the nfts table
type NFTsRow struct {
Address string `db:"address"`
Owner string `db:"owner"`
Delegations []NFTDelegation `db:"delegations"`
Address string `db:"address"`
Owner string `db:"owner"`
AvailableAmount DbCoins `db:"available_amount"`
VestingPeriod int `db:"vesting_period"`
RewardPerPeriod DbCoins `db:"reward_per_period"`
LastVestingTime int `db:"last_vesting_time"`
VestingCounter int16 `db:"vesting_counter"`
Denom string `db:"denom"`
}

func NewNFTsRow(address, owner string) *NFTsRow {
// NewNFTsRow creates a new instance of NFTsRow
func NewNFTsRow(address, owner string, availableAmount, rewardPerPeriod DbCoins, vestingPeriod, lastVestingTime int, vestingCounter int16, denom string) *NFTsRow {
return &NFTsRow{
Address: address,
Owner: owner,
}
}

// NFTDelegation represent delegation type
type NFTDelegation struct {
Validator string `db:"validator"`
Amount *big.Int `db:"amount"`
Timestamp uint64 `db:"timestamp"`
}

func NewDelegations(validator string, timestamp uint64, amount *big.Int) *NFTDelegation {
return &NFTDelegation{
Validator: validator,
Timestamp: timestamp,
Amount: amount,
Address: address,
Owner: owner,
AvailableAmount: availableAmount,
VestingPeriod: vestingPeriod,
RewardPerPeriod: rewardPerPeriod,
LastVestingTime: lastVestingTime,
VestingCounter: vestingCounter,
Denom: denom,
}
}
4 changes: 2 additions & 2 deletions modules/accumulator/handle_genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ func (m *Module) HandleGenesis(doc *tmtypes.GenesisDoc, appState map[string]json
var genState accumulatortypes.GenesisState
err := m.cdc.UnmarshalJSON(appState[accumulatortypes.ModuleName], &genState)
if err != nil {
return fmt.Errorf("error while reading mint genesis data: %s", err)
return fmt.Errorf("error while reading accumulator genesis data: %s", err)
}

// Save the params
err = m.db.SaveAccumulatorParams(types.NewAccumulatorParams(genState.Params, doc.InitialHeight))
if err != nil {
return fmt.Errorf("error while storing genesis mint params: %s", err)
return fmt.Errorf("error while storing genesis accumulator params: %s", err)
}

return nil
Expand Down
32 changes: 32 additions & 0 deletions modules/nft/handle_genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package nft

import (
"encoding/json"
"fmt"
nfttypes "github.com/cosmos/cosmos-sdk/x/nft/types"

tmtypes "github.com/tendermint/tendermint/types"

"github.com/rs/zerolog/log"
)

// HandleGenesis implements modules.Module
func (m *Module) HandleGenesis(doc *tmtypes.GenesisDoc, appState map[string]json.RawMessage) error {
log.Debug().Str("module", "nft").Msg("parsing genesis")

// Read the genesis state
var genState nfttypes.GenesisState
err := m.cdc.UnmarshalJSON(appState[nfttypes.ModuleName], &genState)
if err != nil {
return fmt.Errorf("error while reading nft genesis data: %s", err)
}

// Save NFTs
for _, nft := range genState.Nfts {
err = m.db.SaveNFT(nft.Address, nft.Owner, nft.AvailableToWithdraw, nft.LastVestingTime, nft.VestingPeriod, nft.RewardPerPeriod, nft.VestingPeriodsCount, nft.Denom)
if err != nil {
return fmt.Errorf("error while storing genesis nft: %s", err)
}
}
return nil
}
6 changes: 6 additions & 0 deletions modules/nft/handle_msg_send.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ func (m *Module) handleMsgSend(tx *juno.Tx, msg *nft.MsgSend) error {
return errors.New("nft does not exist")
}

// Update the nft by setting a new owner
err := m.db.SaveNFT(nft.Address, nft.Owner, nft.AvailableToWithdraw, nft.LastVestingTime, nft.VestingPeriod, nft.RewardPerPeriod, nft.VestingPeriodsCount, nft.Denom)
if err != nil {
return errors.Wrap(err, "error while saving nft")
}

return m.db.SaveNFTEvent(
msg.Type(),
nft.Address,
Expand Down
15 changes: 14 additions & 1 deletion modules/nft/handle_msg_withdraw.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@ package nft

import (
nft "github.com/cosmos/cosmos-sdk/x/nft/types"
juno "github.com/forbole/juno/v4/types"
"github.com/pkg/errors"
)

// handleMsgSend allows to properly handle a MsgSend
func (m *Module) handleMsgWithdrawal(msg *nft.MsgWithdrawal) error {
func (m *Module) handleMsgWithdrawal(tx *juno.Tx, msg *nft.MsgWithdrawal) error {
nft, ok := m.keeper.GetNFT(msg.Address, tx.Height)
if !ok {
return errors.New("nft does not exist")
}

// Update the nft by setting a new owner
err := m.db.SaveNFT(nft.Address, nft.Owner, nft.AvailableToWithdraw, nft.LastVestingTime, nft.VestingPeriod, nft.RewardPerPeriod, nft.VestingPeriodsCount, nft.Denom)
if err != nil {
return errors.Wrap(err, "error while saving nft")
}

return m.db.SaveNFTEvent(
msg.Type(),
msg.Address,
Expand Down
21 changes: 11 additions & 10 deletions modules/nft/handle_periodic_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,35 @@ package nft

import (
"fmt"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/go-co-op/gocron"
"github.com/rs/zerolog/log"
"math"
)

// RegisterPeriodicOperations implements modules.Module
func (m *Module) RegisterPeriodicOperations(scheduler *gocron.Scheduler) error {
log.Debug().Str("module", "nft").Msg("setting up periodic tasks")
pagination := &query.PageRequest{
Limit: math.MaxInt32,
}

if _, err := scheduler.Every(5).Minutes().Do(func() {
if _, err := scheduler.Every(1).Minutes().Do(func() {
height, err := m.db.GetLastBlockHeight()
if err != nil {
log.Error().Str("module", "nft").Err(err).Msg("unable to get last block height")
return
}

val, _, err := m.keeper.GetNFTsWithPagination(pagination, height)
nfts, err := m.db.GetNFTsToUpdate()
if err != nil {
log.Error().Str("module", "nft").Err(err).Msg("unable to get nfts")
log.Error().Str("module", "nft").Err(err).Msg("unable to get nfts to update")
return
}

for _, nft := range val {
if err = m.db.SaveNFT(nft.Address, nft.Owner, nft.AvailableToWithdraw); err != nil {
for _, nft := range nfts {
updatedNFT, ok := m.keeper.GetNFT(nft.Address, height)
if !ok {
log.Error().Str("module", "nft").Str("address", nft.Address).Msg("nft does not exist, skipping")
continue
}

if err = m.db.SaveNFT(nft.Address, nft.Owner, updatedNFT.AvailableToWithdraw, updatedNFT.LastVestingTime, updatedNFT.VestingPeriod, updatedNFT.RewardPerPeriod, updatedNFT.VestingCounter, nft.Denom); err != nil {
log.Error().Str("module", "nft").Err(err).Msg("unable to save nft")
}
}
Expand Down
3 changes: 3 additions & 0 deletions modules/nft/source/local/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/query"
nfttypes "github.com/cosmos/cosmos-sdk/x/nft/types"
"github.com/forbole/juno/v4/node/local"
"github.com/rs/zerolog/log"

nftkeeper "github.com/forbole/bdjuno/v4/modules/nft/source"
)
Expand Down Expand Up @@ -47,11 +48,13 @@ func NewSource(source *local.Source, nk nfttypes.QueryServer) *Source {
func (s Source) GetNFT(address string, height int64) (val nfttypes.NFT, found bool) {
ctx, err := s.LoadHeight(height)
if err != nil {
log.Err(err).Msg("error while loading height")
return nfttypes.NFT{}, false
}

nft, err := s.q.GetNFTByAddress(sdk.WrapSDKContext(ctx), &nfttypes.QueryNFTByAddress{Address: address})
if err != nil {
log.Err(err).Msg("error while loading nft by height")
return nfttypes.NFT{}, false
}

Expand Down
3 changes: 3 additions & 0 deletions modules/nft/source/remote/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"cosmossdk.io/errors"
"github.com/cosmos/cosmos-sdk/types/query"
nfttypes "github.com/cosmos/cosmos-sdk/x/nft/types"
"github.com/rs/zerolog/log"

"github.com/forbole/juno/v4/node/remote"

Expand All @@ -24,6 +25,7 @@ func (s Source) GetNFTsWithPagination(pagination *query.PageRequest, height int6

response, err := s.nftClient.GetAllNFTs(ctx, &nfttypes.QueryAllNFTs{Pagination: pagination})
if err != nil {
log.Err(err).Msg("failed to query all nfts")
return nil, nil, errors.Wrap(err, "failed to query all nfts")
}

Expand All @@ -44,6 +46,7 @@ func (s Source) GetNFT(address string, height int64) (val nfttypes.NFT, found bo
ctx := remote.GetHeightRequestContext(s.Ctx, height)
nft, err := s.nftClient.GetNFTByAddress(ctx, &nfttypes.QueryNFTByAddress{Address: address})
if err != nil {
log.Err(err).Msg("error while loading nft by height")
return nfttypes.NFT{}, false
}

Expand Down

0 comments on commit e049870

Please sign in to comment.