481
vendor/github.com/status-im/status-go/services/stickers/api.go
generated
vendored
Normal file
481
vendor/github.com/status-im/status-go/services/stickers/api.go
generated
vendored
Normal file
@@ -0,0 +1,481 @@
|
||||
package stickers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/zenthangplus/goccm"
|
||||
"olympos.io/encoding/edn"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/contracts"
|
||||
"github.com/status-im/status-go/contracts/stickers"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/ipfs"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/server"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
const maxConcurrentRequests = 3
|
||||
const requestTimeout = time.Duration(5) * time.Second
|
||||
|
||||
// ConnectionType constants
|
||||
type stickerStatus int
|
||||
|
||||
const (
|
||||
statusAvailable stickerStatus = iota
|
||||
statusInstalled
|
||||
statusPending
|
||||
statusPurchased
|
||||
)
|
||||
|
||||
type API struct {
|
||||
contractMaker *contracts.ContractMaker
|
||||
accountsManager *account.GethManager
|
||||
accountsDB *accounts.Database
|
||||
pendingTracker *transactions.PendingTxTracker
|
||||
|
||||
keyStoreDir string
|
||||
downloader *ipfs.Downloader
|
||||
httpServer *server.MediaServer
|
||||
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
type Sticker struct {
|
||||
PackID *bigint.BigInt `json:"packID,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
}
|
||||
|
||||
type StickerPack struct {
|
||||
ID *bigint.BigInt `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Author string `json:"author"`
|
||||
Owner common.Address `json:"owner,omitempty"`
|
||||
Price *bigint.BigInt `json:"price"`
|
||||
Preview string `json:"preview"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Stickers []Sticker `json:"stickers"`
|
||||
|
||||
Status stickerStatus `json:"status"`
|
||||
}
|
||||
|
||||
type StickerPackCollection map[uint]StickerPack
|
||||
|
||||
type ednSticker struct {
|
||||
Hash string
|
||||
}
|
||||
|
||||
type ednStickerPack struct {
|
||||
Name string
|
||||
Author string
|
||||
Thumbnail string
|
||||
Preview string
|
||||
Stickers []ednSticker
|
||||
}
|
||||
type ednStickerPackInfo struct {
|
||||
Meta ednStickerPack
|
||||
}
|
||||
|
||||
func NewAPI(ctx context.Context, acc *accounts.Database, rpcClient *rpc.Client, accountsManager *account.GethManager, pendingTracker *transactions.PendingTxTracker, keyStoreDir string, downloader *ipfs.Downloader, httpServer *server.MediaServer) *API {
|
||||
result := &API{
|
||||
contractMaker: &contracts.ContractMaker{
|
||||
RPCClient: rpcClient,
|
||||
},
|
||||
accountsManager: accountsManager,
|
||||
accountsDB: acc,
|
||||
pendingTracker: pendingTracker,
|
||||
keyStoreDir: keyStoreDir,
|
||||
downloader: downloader,
|
||||
ctx: ctx,
|
||||
httpServer: httpServer,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (api *API) Market(chainID uint64) ([]StickerPack, error) {
|
||||
// TODO: eventually this should be changed to include pagination
|
||||
accs, err := api.accountsDB.GetActiveAccounts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allStickerPacks, err := api.getContractPacks(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
purchasedPacks := make(map[uint]struct{})
|
||||
|
||||
purchasedPackChan := make(chan *big.Int)
|
||||
errChan := make(chan error)
|
||||
doneChan := make(chan struct{}, 1)
|
||||
go api.getAccountsPurchasedPack(chainID, accs, purchasedPackChan, errChan, doneChan)
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case packID := <-purchasedPackChan:
|
||||
if packID != nil {
|
||||
purchasedPacks[uint(packID.Uint64())] = struct{}{}
|
||||
}
|
||||
|
||||
case <-doneChan:
|
||||
var result []StickerPack
|
||||
for _, pack := range allStickerPacks {
|
||||
packID := uint(pack.ID.Uint64())
|
||||
_, isPurchased := purchasedPacks[packID]
|
||||
if isPurchased {
|
||||
pack.Status = statusPurchased
|
||||
} else {
|
||||
pack.Status = statusAvailable
|
||||
}
|
||||
result = append(result, pack)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) execTokenPackID(chainID uint64, tokenIDs []*big.Int, resultChan chan<- *big.Int, errChan chan<- error, doneChan chan<- struct{}) {
|
||||
defer close(doneChan)
|
||||
defer close(errChan)
|
||||
defer close(resultChan)
|
||||
|
||||
stickerPack, err := api.contractMaker.NewStickerPack(chainID)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
if len(tokenIDs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
callOpts := &bind.CallOpts{Context: api.ctx, Pending: false}
|
||||
|
||||
c := goccm.New(maxConcurrentRequests)
|
||||
for _, tokenID := range tokenIDs {
|
||||
c.Wait()
|
||||
go func(tokenID *big.Int) {
|
||||
defer c.Done()
|
||||
packID, err := stickerPack.TokenPackId(callOpts, tokenID)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
resultChan <- packID
|
||||
}(tokenID)
|
||||
}
|
||||
c.WaitAllDone()
|
||||
}
|
||||
|
||||
func (api *API) getTokenPackIDs(chainID uint64, tokenIDs []*big.Int) ([]*big.Int, error) {
|
||||
tokenPackIDChan := make(chan *big.Int)
|
||||
errChan := make(chan error)
|
||||
doneChan := make(chan struct{}, 1)
|
||||
|
||||
go api.execTokenPackID(chainID, tokenIDs, tokenPackIDChan, errChan, doneChan)
|
||||
|
||||
var tokenPackIDs []*big.Int
|
||||
for {
|
||||
select {
|
||||
case <-doneChan:
|
||||
return tokenPackIDs, nil
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case t := <-tokenPackIDChan:
|
||||
if t != nil {
|
||||
tokenPackIDs = append(tokenPackIDs, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) getPurchasedPackIDs(chainID uint64, account types.Address) ([]*big.Int, error) {
|
||||
// TODO: this should be replaced in the future by something like TheGraph to reduce the number of requests to infura
|
||||
|
||||
stickerPack, err := api.contractMaker.NewStickerPack(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
callOpts := &bind.CallOpts{Context: api.ctx, Pending: false}
|
||||
|
||||
balance, err := stickerPack.BalanceOf(callOpts, common.Address(account))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenIDs, err := api.getTokenOwnerOfIndex(chainID, account, balance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return api.getTokenPackIDs(chainID, tokenIDs)
|
||||
}
|
||||
|
||||
func (api *API) fetchStickerPacks(chainID uint64, resultChan chan<- *StickerPack, errChan chan<- error, doneChan chan<- struct{}) {
|
||||
defer close(doneChan)
|
||||
defer close(errChan)
|
||||
defer close(resultChan)
|
||||
|
||||
installedPacks, err := api.Installed()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
pendingPacks, err := api.pendingStickerPacks()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
stickerType, err := api.contractMaker.NewStickerType(chainID)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
callOpts := &bind.CallOpts{Context: api.ctx, Pending: false}
|
||||
|
||||
numPacks, err := stickerType.PackCount(callOpts)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
if numPacks.Uint64() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
c := goccm.New(maxConcurrentRequests)
|
||||
for i := uint64(0); i < numPacks.Uint64(); i++ {
|
||||
c.Wait()
|
||||
go func(i uint64) {
|
||||
defer c.Done()
|
||||
|
||||
packID := new(big.Int).SetUint64(i)
|
||||
|
||||
_, exists := installedPacks[uint(i)]
|
||||
if exists {
|
||||
return // We already have the sticker pack data, no need to query it
|
||||
}
|
||||
|
||||
_, exists = pendingPacks[uint(i)]
|
||||
if exists {
|
||||
return // We already have the sticker pack data, no need to query it
|
||||
}
|
||||
|
||||
stickerPack, err := api.fetchPackData(stickerType, packID, true)
|
||||
if err != nil {
|
||||
log.Warn("Could not retrieve stickerpack data", "packID", packID, "error", err)
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
resultChan <- stickerPack
|
||||
}(i)
|
||||
}
|
||||
|
||||
c.WaitAllDone()
|
||||
}
|
||||
|
||||
func (api *API) fetchPackData(stickerType *stickers.StickerType, packID *big.Int, translateHashes bool) (*StickerPack, error) {
|
||||
|
||||
timeoutContext, timeoutCancel := context.WithTimeout(api.ctx, requestTimeout)
|
||||
defer timeoutCancel()
|
||||
|
||||
callOpts := &bind.CallOpts{Context: timeoutContext, Pending: false}
|
||||
packData, err := stickerType.GetPackData(callOpts, packID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stickerPack := &StickerPack{
|
||||
ID: &bigint.BigInt{Int: packID},
|
||||
Owner: packData.Owner,
|
||||
Price: &bigint.BigInt{Int: packData.Price},
|
||||
}
|
||||
|
||||
err = api.downloadPackData(stickerPack, packData.Contenthash, translateHashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stickerPack, nil
|
||||
}
|
||||
|
||||
func (api *API) downloadPackData(stickerPack *StickerPack, contentHash []byte, translateHashes bool) error {
|
||||
fileContent, err := api.downloader.Get(hexutil.Encode(contentHash)[2:], true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return api.populateStickerPackAttributes(stickerPack, fileContent, translateHashes)
|
||||
}
|
||||
|
||||
func (api *API) hashToURL(hash string) string {
|
||||
return api.httpServer.MakeStickerURL(hash)
|
||||
}
|
||||
|
||||
func (api *API) populateStickerPackAttributes(stickerPack *StickerPack, ednSource []byte, translateHashes bool) error {
|
||||
var stickerpackIPFSInfo ednStickerPackInfo
|
||||
err := edn.Unmarshal(ednSource, &stickerpackIPFSInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stickerPack.Author = stickerpackIPFSInfo.Meta.Author
|
||||
stickerPack.Name = stickerpackIPFSInfo.Meta.Name
|
||||
|
||||
if translateHashes {
|
||||
stickerPack.Preview = api.hashToURL(stickerpackIPFSInfo.Meta.Preview)
|
||||
stickerPack.Thumbnail = api.hashToURL(stickerpackIPFSInfo.Meta.Thumbnail)
|
||||
} else {
|
||||
stickerPack.Preview = stickerpackIPFSInfo.Meta.Preview
|
||||
stickerPack.Thumbnail = stickerpackIPFSInfo.Meta.Thumbnail
|
||||
}
|
||||
|
||||
for _, s := range stickerpackIPFSInfo.Meta.Stickers {
|
||||
url := ""
|
||||
if translateHashes {
|
||||
url = api.hashToURL(s.Hash)
|
||||
}
|
||||
|
||||
stickerPack.Stickers = append(stickerPack.Stickers, Sticker{
|
||||
PackID: stickerPack.ID,
|
||||
URL: url,
|
||||
Hash: s.Hash,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *API) getContractPacks(chainID uint64) ([]StickerPack, error) {
|
||||
stickerPackChan := make(chan *StickerPack)
|
||||
errChan := make(chan error)
|
||||
doneChan := make(chan struct{}, 1)
|
||||
|
||||
go api.fetchStickerPacks(chainID, stickerPackChan, errChan, doneChan)
|
||||
|
||||
var packs []StickerPack
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-doneChan:
|
||||
return packs, nil
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case pack := <-stickerPackChan:
|
||||
if pack != nil {
|
||||
packs = append(packs, *pack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) getAccountsPurchasedPack(chainID uint64, accs []*accounts.Account, resultChan chan<- *big.Int, errChan chan<- error, doneChan chan<- struct{}) {
|
||||
defer close(doneChan)
|
||||
defer close(errChan)
|
||||
defer close(resultChan)
|
||||
|
||||
if len(accs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
c := goccm.New(maxConcurrentRequests)
|
||||
for _, account := range accs {
|
||||
c.Wait()
|
||||
go func(acc *accounts.Account) {
|
||||
defer c.Done()
|
||||
packs, err := api.getPurchasedPackIDs(chainID, acc.Address)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
for _, p := range packs {
|
||||
resultChan <- p
|
||||
}
|
||||
}(account)
|
||||
}
|
||||
c.WaitAllDone()
|
||||
}
|
||||
|
||||
func (api *API) execTokenOwnerOfIndex(chainID uint64, account types.Address, balance *big.Int, resultChan chan<- *big.Int, errChan chan<- error, doneChan chan<- struct{}) {
|
||||
defer close(doneChan)
|
||||
defer close(errChan)
|
||||
defer close(resultChan)
|
||||
|
||||
stickerPack, err := api.contractMaker.NewStickerPack(chainID)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
if balance.Int64() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
callOpts := &bind.CallOpts{Context: api.ctx, Pending: false}
|
||||
|
||||
c := goccm.New(maxConcurrentRequests)
|
||||
for i := uint64(0); i < balance.Uint64(); i++ {
|
||||
c.Wait()
|
||||
go func(i uint64) {
|
||||
defer c.Done()
|
||||
tokenID, err := stickerPack.TokenOfOwnerByIndex(callOpts, common.Address(account), new(big.Int).SetUint64(i))
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
resultChan <- tokenID
|
||||
}(i)
|
||||
}
|
||||
c.WaitAllDone()
|
||||
}
|
||||
|
||||
func (api *API) getTokenOwnerOfIndex(chainID uint64, account types.Address, balance *big.Int) ([]*big.Int, error) {
|
||||
tokenIDChan := make(chan *big.Int)
|
||||
errChan := make(chan error)
|
||||
doneChan := make(chan struct{}, 1)
|
||||
|
||||
go api.execTokenOwnerOfIndex(chainID, account, balance, tokenIDChan, errChan, doneChan)
|
||||
|
||||
var tokenIDs []*big.Int
|
||||
for {
|
||||
select {
|
||||
case <-doneChan:
|
||||
return tokenIDs, nil
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case tokenID := <-tokenIDChan:
|
||||
if tokenID != nil {
|
||||
tokenIDs = append(tokenIDs, tokenID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
vendor/github.com/status-im/status-go/services/stickers/install.go
generated
vendored
Normal file
128
vendor/github.com/status-im/status-go/services/stickers/install.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
package stickers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/status-im/status-go/multiaccounts/settings"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
)
|
||||
|
||||
func (api *API) Install(chainID uint64, packID *bigint.BigInt) error {
|
||||
installedPacks, err := api.installedStickerPacks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, exists := installedPacks[uint(packID.Uint64())]; exists {
|
||||
return errors.New("sticker pack is already installed")
|
||||
}
|
||||
|
||||
// TODO: this does not validate if the pack is purchased. Should it?
|
||||
|
||||
stickerType, err := api.contractMaker.NewStickerType(chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stickerPack, err := api.fetchPackData(stickerType, packID.Int, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
installedPacks[uint(packID.Uint64())] = *stickerPack
|
||||
|
||||
err = api.accountsDB.SaveSettingField(settings.StickersPacksInstalled, installedPacks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *API) installedStickerPacks() (StickerPackCollection, error) {
|
||||
stickerPacks := make(StickerPackCollection)
|
||||
|
||||
installedStickersJSON, err := api.accountsDB.GetInstalledStickerPacks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if installedStickersJSON == nil {
|
||||
return stickerPacks, nil
|
||||
}
|
||||
|
||||
err = json.Unmarshal(*installedStickersJSON, &stickerPacks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stickerPacks, nil
|
||||
}
|
||||
|
||||
func (api *API) Installed() (StickerPackCollection, error) {
|
||||
stickerPacks, err := api.installedStickerPacks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for packID, stickerPack := range stickerPacks {
|
||||
stickerPack.Status = statusInstalled
|
||||
stickerPack.Preview = api.hashToURL(stickerPack.Preview)
|
||||
stickerPack.Thumbnail = api.hashToURL(stickerPack.Thumbnail)
|
||||
for i, sticker := range stickerPack.Stickers {
|
||||
sticker.URL = api.hashToURL(sticker.Hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stickerPack.Stickers[i] = sticker
|
||||
}
|
||||
stickerPacks[packID] = stickerPack
|
||||
}
|
||||
|
||||
return stickerPacks, nil
|
||||
}
|
||||
|
||||
func (api *API) Uninstall(packID *bigint.BigInt) error {
|
||||
installedPacks, err := api.installedStickerPacks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, exists := installedPacks[uint(packID.Uint64())]; !exists {
|
||||
return errors.New("sticker pack is not installed")
|
||||
}
|
||||
|
||||
delete(installedPacks, uint(packID.Uint64()))
|
||||
|
||||
err = api.accountsDB.SaveSettingField(settings.StickersPacksInstalled, installedPacks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Removing uninstalled pack from recent stickers
|
||||
|
||||
recentStickers, err := api.recentStickers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idx := -1
|
||||
for i, r := range recentStickers {
|
||||
if r.PackID.Cmp(packID.Int) == 0 {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if idx > -1 {
|
||||
var newRecentStickers []Sticker
|
||||
newRecentStickers = append(newRecentStickers, recentStickers[:idx]...)
|
||||
if idx != len(recentStickers)-1 {
|
||||
newRecentStickers = append(newRecentStickers, recentStickers[idx+1:]...)
|
||||
}
|
||||
return api.accountsDB.SaveSettingField(settings.StickersRecentStickers, newRecentStickers)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
133
vendor/github.com/status-im/status-go/services/stickers/pending.go
generated
vendored
Normal file
133
vendor/github.com/status-im/status-go/services/stickers/pending.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
package stickers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/status-im/status-go/multiaccounts/settings"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
)
|
||||
|
||||
func (api *API) AddPending(chainID uint64, packID *bigint.BigInt) error {
|
||||
pendingPacks, err := api.pendingStickerPacks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, exists := pendingPacks[uint(packID.Uint64())]; exists {
|
||||
return errors.New("sticker pack is already pending")
|
||||
}
|
||||
|
||||
stickerType, err := api.contractMaker.NewStickerType(chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stickerPack, err := api.fetchPackData(stickerType, packID.Int, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pendingPacks[uint(packID.Uint64())] = *stickerPack
|
||||
|
||||
return api.accountsDB.SaveSettingField(settings.StickersPacksPending, pendingPacks)
|
||||
}
|
||||
|
||||
func (api *API) pendingStickerPacks() (StickerPackCollection, error) {
|
||||
stickerPacks := make(StickerPackCollection)
|
||||
|
||||
pendingStickersJSON, err := api.accountsDB.GetPendingStickerPacks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pendingStickersJSON == nil {
|
||||
return stickerPacks, nil
|
||||
}
|
||||
|
||||
err = json.Unmarshal(*pendingStickersJSON, &stickerPacks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stickerPacks, nil
|
||||
}
|
||||
|
||||
func (api *API) Pending() (StickerPackCollection, error) {
|
||||
stickerPacks, err := api.pendingStickerPacks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for packID, stickerPack := range stickerPacks {
|
||||
stickerPack.Status = statusPending
|
||||
stickerPack.Preview = api.hashToURL(stickerPack.Preview)
|
||||
stickerPack.Thumbnail = api.hashToURL(stickerPack.Thumbnail)
|
||||
for i, sticker := range stickerPack.Stickers {
|
||||
sticker.URL = api.hashToURL(sticker.Hash)
|
||||
stickerPack.Stickers[i] = sticker
|
||||
}
|
||||
stickerPacks[packID] = stickerPack
|
||||
}
|
||||
|
||||
return stickerPacks, nil
|
||||
}
|
||||
|
||||
func (api *API) ProcessPending(chainID uint64) (pendingChanged StickerPackCollection, err error) {
|
||||
pendingStickerPacks, err := api.pendingStickerPacks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accs, err := api.accountsDB.GetActiveAccounts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
purchasedPacks := make(map[uint]struct{})
|
||||
purchasedPackChan := make(chan *big.Int)
|
||||
errChan := make(chan error)
|
||||
doneChan := make(chan struct{}, 1)
|
||||
go api.getAccountsPurchasedPack(chainID, accs, purchasedPackChan, errChan, doneChan)
|
||||
for {
|
||||
select {
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case packID := <-purchasedPackChan:
|
||||
if packID != nil {
|
||||
purchasedPacks[uint(packID.Uint64())] = struct{}{}
|
||||
}
|
||||
case <-doneChan:
|
||||
result := make(StickerPackCollection)
|
||||
for _, stickerPack := range pendingStickerPacks {
|
||||
packID := uint(stickerPack.ID.Uint64())
|
||||
if _, exists := purchasedPacks[packID]; !exists {
|
||||
continue
|
||||
}
|
||||
delete(pendingStickerPacks, packID)
|
||||
stickerPack.Status = statusPurchased
|
||||
result[packID] = stickerPack
|
||||
}
|
||||
err = api.accountsDB.SaveSettingField(settings.StickersPacksPending, pendingStickerPacks)
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) RemovePending(packID *bigint.BigInt) error {
|
||||
pendingPacks, err := api.pendingStickerPacks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, exists := pendingPacks[uint(packID.Uint64())]; !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
delete(pendingPacks, uint(packID.Uint64()))
|
||||
|
||||
return api.accountsDB.SaveSettingField(settings.StickersPacksPending, pendingPacks)
|
||||
}
|
||||
102
vendor/github.com/status-im/status-go/services/stickers/recent.go
generated
vendored
Normal file
102
vendor/github.com/status-im/status-go/services/stickers/recent.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package stickers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/status-im/status-go/multiaccounts/settings"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
)
|
||||
|
||||
const maxNumberRecentStickers = 24
|
||||
|
||||
func (api *API) recentStickers() ([]Sticker, error) {
|
||||
installedStickersPacksJSON, err := api.accountsDB.GetInstalledStickerPacks()
|
||||
|
||||
if err != nil || installedStickersPacksJSON == nil {
|
||||
return []Sticker{}, nil
|
||||
}
|
||||
|
||||
recentStickersJSON, err := api.accountsDB.GetRecentStickers()
|
||||
|
||||
if err != nil || recentStickersJSON == nil {
|
||||
return []Sticker{}, nil
|
||||
}
|
||||
|
||||
recentStickersList := make([]Sticker, 0)
|
||||
if err := json.Unmarshal(*recentStickersJSON, &recentStickersList); err != nil {
|
||||
return []Sticker{}, err
|
||||
}
|
||||
|
||||
var installedStickersPacks map[string]StickerPack
|
||||
if err := json.Unmarshal(*installedStickersPacksJSON, &installedStickersPacks); err != nil {
|
||||
return []Sticker{}, err
|
||||
}
|
||||
|
||||
recentStickersListInExistingPacks := make([]Sticker, 0)
|
||||
existingPackIDs := make(map[string]bool)
|
||||
|
||||
for k := range installedStickersPacks {
|
||||
existingPackIDs[k] = true
|
||||
}
|
||||
|
||||
for _, s := range recentStickersList {
|
||||
packIDStr := s.PackID.String()
|
||||
if _, exists := existingPackIDs[packIDStr]; exists {
|
||||
recentStickersListInExistingPacks = append(recentStickersListInExistingPacks, s)
|
||||
}
|
||||
}
|
||||
|
||||
return recentStickersListInExistingPacks, nil
|
||||
}
|
||||
|
||||
func (api *API) ClearRecent() error {
|
||||
var recentStickersList []Sticker
|
||||
return api.accountsDB.SaveSettingField(settings.StickersRecentStickers, recentStickersList)
|
||||
}
|
||||
|
||||
func (api *API) Recent() ([]Sticker, error) {
|
||||
recentStickersList, err := api.recentStickers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, sticker := range recentStickersList {
|
||||
sticker.URL = api.hashToURL(sticker.Hash)
|
||||
recentStickersList[i] = sticker
|
||||
}
|
||||
|
||||
return recentStickersList, nil
|
||||
}
|
||||
|
||||
func (api *API) AddRecent(packID *bigint.BigInt, hash string) error {
|
||||
sticker := Sticker{
|
||||
PackID: packID,
|
||||
Hash: hash,
|
||||
}
|
||||
|
||||
recentStickersList, err := api.recentStickers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove duplicated
|
||||
idx := -1
|
||||
for i, currSticker := range recentStickersList {
|
||||
if currSticker.PackID.Cmp(sticker.PackID.Int) == 0 && currSticker.Hash == sticker.Hash {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
if idx > -1 {
|
||||
recentStickersList = append(recentStickersList[:idx], recentStickersList[idx+1:]...)
|
||||
}
|
||||
|
||||
sticker.URL = ""
|
||||
|
||||
if len(recentStickersList) >= maxNumberRecentStickers {
|
||||
recentStickersList = append([]Sticker{sticker}, recentStickersList[:maxNumberRecentStickers-1]...)
|
||||
} else {
|
||||
recentStickersList = append([]Sticker{sticker}, recentStickersList...)
|
||||
}
|
||||
|
||||
return api.accountsDB.SaveSettingField(settings.StickersRecentStickers, recentStickersList)
|
||||
}
|
||||
76
vendor/github.com/status-im/status-go/services/stickers/service.go
generated
vendored
Normal file
76
vendor/github.com/status-im/status-go/services/stickers/service.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package stickers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
ethRpc "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/ipfs"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/server"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
// NewService initializes service instance.
|
||||
func NewService(acc *accounts.Database, rpcClient *rpc.Client, accountsManager *account.GethManager, config *params.NodeConfig, downloader *ipfs.Downloader, httpServer *server.MediaServer, pendingTracker *transactions.PendingTxTracker) *Service {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
return &Service{
|
||||
accountsDB: acc,
|
||||
rpcClient: rpcClient,
|
||||
accountsManager: accountsManager,
|
||||
keyStoreDir: config.KeyStoreDir,
|
||||
downloader: downloader,
|
||||
httpServer: httpServer,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
api: NewAPI(ctx, acc, rpcClient, accountsManager, pendingTracker, config.KeyStoreDir, downloader, httpServer),
|
||||
}
|
||||
}
|
||||
|
||||
// Service is a browsers service.
|
||||
type Service struct {
|
||||
accountsDB *accounts.Database
|
||||
rpcClient *rpc.Client
|
||||
accountsManager *account.GethManager
|
||||
downloader *ipfs.Downloader
|
||||
keyStoreDir string
|
||||
httpServer *server.MediaServer
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
api *API
|
||||
}
|
||||
|
||||
// Start a service.
|
||||
func (s *Service) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop a service.
|
||||
func (s *Service) Stop() error {
|
||||
s.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) API() *API {
|
||||
return s.api
|
||||
}
|
||||
|
||||
// APIs returns list of available RPC APIs.
|
||||
func (s *Service) APIs() []ethRpc.API {
|
||||
return []ethRpc.API{
|
||||
{
|
||||
Namespace: "stickers",
|
||||
Version: "0.1.0",
|
||||
Service: s.api,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Protocols returns list of p2p protocols.
|
||||
func (s *Service) Protocols() []p2p.Protocol {
|
||||
return nil
|
||||
}
|
||||
115
vendor/github.com/status-im/status-go/services/stickers/transactions.go
generated
vendored
Normal file
115
vendor/github.com/status-im/status-go/services/stickers/transactions.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package stickers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/contracts/snt"
|
||||
"github.com/status-im/status-go/contracts/stickers"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
)
|
||||
|
||||
func (api *API) BuyPrepareTxCallMsg(chainID uint64, from types.Address, packID *bigint.BigInt) (ethereum.CallMsg, error) {
|
||||
callOpts := &bind.CallOpts{Context: api.ctx, Pending: false}
|
||||
|
||||
stickerType, err := api.contractMaker.NewStickerType(chainID)
|
||||
if err != nil {
|
||||
return ethereum.CallMsg{}, err
|
||||
}
|
||||
|
||||
packInfo, err := stickerType.GetPackData(callOpts, packID.Int)
|
||||
if err != nil {
|
||||
return ethereum.CallMsg{}, err
|
||||
}
|
||||
|
||||
stickerMarketABI, err := abi.JSON(strings.NewReader(stickers.StickerMarketABI))
|
||||
if err != nil {
|
||||
return ethereum.CallMsg{}, err
|
||||
}
|
||||
|
||||
extraData, err := stickerMarketABI.Pack("buyToken", packID.Int, from, packInfo.Price)
|
||||
if err != nil {
|
||||
return ethereum.CallMsg{}, err
|
||||
}
|
||||
|
||||
sntABI, err := abi.JSON(strings.NewReader(snt.SNTABI))
|
||||
if err != nil {
|
||||
return ethereum.CallMsg{}, err
|
||||
}
|
||||
|
||||
stickerMarketAddress, err := stickers.StickerMarketContractAddress(chainID)
|
||||
if err != nil {
|
||||
return ethereum.CallMsg{}, err
|
||||
}
|
||||
|
||||
data, err := sntABI.Pack("approveAndCall", stickerMarketAddress, packInfo.Price, extraData)
|
||||
if err != nil {
|
||||
return ethereum.CallMsg{}, err
|
||||
}
|
||||
|
||||
sntAddress, err := snt.ContractAddress(chainID)
|
||||
if err != nil {
|
||||
return ethereum.CallMsg{}, err
|
||||
}
|
||||
|
||||
return ethereum.CallMsg{
|
||||
From: common.Address(from),
|
||||
To: &sntAddress,
|
||||
Value: big.NewInt(0),
|
||||
Data: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (api *API) BuyPrepareTx(ctx context.Context, chainID uint64, from types.Address, packID *bigint.BigInt) (interface{}, error) {
|
||||
callMsg, err := api.BuyPrepareTxCallMsg(chainID, from, packID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toCallArg(callMsg), nil
|
||||
}
|
||||
|
||||
func (api *API) BuyEstimate(ctx context.Context, chainID uint64, from types.Address, packID *bigint.BigInt) (uint64, error) {
|
||||
callMsg, err := api.BuyPrepareTxCallMsg(chainID, from, packID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ethClient, err := api.contractMaker.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ethClient.EstimateGas(ctx, callMsg)
|
||||
}
|
||||
|
||||
func (api *API) StickerMarketAddress(ctx context.Context, chainID uint64) (common.Address, error) {
|
||||
return stickers.StickerMarketContractAddress(chainID)
|
||||
}
|
||||
|
||||
func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||
arg := map[string]interface{}{
|
||||
"from": msg.From,
|
||||
"to": msg.To,
|
||||
}
|
||||
if len(msg.Data) > 0 {
|
||||
arg["data"] = hexutil.Bytes(msg.Data)
|
||||
}
|
||||
if msg.Value != nil {
|
||||
arg["value"] = (*hexutil.Big)(msg.Value)
|
||||
}
|
||||
if msg.Gas != 0 {
|
||||
arg["gas"] = hexutil.Uint64(msg.Gas)
|
||||
}
|
||||
if msg.GasPrice != nil {
|
||||
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
|
||||
}
|
||||
return arg
|
||||
}
|
||||
Reference in New Issue
Block a user