status-go/services/stickers/transactions.go

185 lines
4.8 KiB
Go

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"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"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"
"github.com/status-im/status-go/transactions"
)
func (api *API) getSigner(chainID uint64, from types.Address, password string) bind.SignerFn {
return func(addr common.Address, tx *ethTypes.Transaction) (*ethTypes.Transaction, error) {
selectedAccount, err := api.accountsManager.VerifyAccountPassword(api.keyStoreDir, from.Hex(), password)
if err != nil {
return nil, err
}
s := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
return ethTypes.SignTx(tx, s, selectedAccount.PrivateKey)
}
}
func (api *API) Buy(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, packID *bigint.BigInt, password string) (string, error) {
snt, err := api.contractMaker.NewSNT(chainID)
if err != nil {
return "", err
}
stickerType, err := api.contractMaker.NewStickerType(chainID)
if err != nil {
return "", err
}
callOpts := &bind.CallOpts{Context: api.ctx, Pending: false}
packInfo, err := stickerType.GetPackData(callOpts, packID.Int)
if err != nil {
return "", err
}
stickerMarketABI, err := abi.JSON(strings.NewReader(stickers.StickerMarketABI))
if err != nil {
return "", err
}
extraData, err := stickerMarketABI.Pack("buyToken", packID.Int, txArgs.From, packInfo.Price)
if err != nil {
return "", err
}
stickerMarketAddress, err := stickers.StickerMarketContractAddress(chainID)
if err != nil {
return "", err
}
txOpts := txArgs.ToTransactOpts(api.getSigner(chainID, txArgs.From, password))
tx, err := snt.ApproveAndCall(
txOpts,
stickerMarketAddress,
packInfo.Price,
extraData,
)
if err != nil {
return "", err
}
err = api.AddPending(chainID, packID)
if err != nil {
return "", err
}
// TODO: track pending transaction (do this in ENS service too)
go api.rpcFiltersSrvc.TriggerTransactionSentToUpstreamEvent(types.Hash(tx.Hash()))
return tx.Hash().String(), nil
}
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
}