feat: Waku v2 bridge

Issue #12610
This commit is contained in:
Michal Iskierko
2023-11-12 13:29:38 +01:00
parent 56e7bd01ca
commit 6d31343205
6716 changed files with 1982502 additions and 5891 deletions

View 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)
}
}
}
}

View 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
}

View 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)
}

View 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)
}

View 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
}

View 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
}