Stefan 5b6f7226bb feat(wallet) extract json blobs and add custom migration support
Extended the migration process with a generic way of applying custom
migration code on top of the SQL files. The implementation provides
a safer way to run GO code along with the SQL migrations and possibility
of rolling back the changes in case of failure to keep the database
consistent.
This custom GO migration is needed to extract the status from
the JSON blob receipt and store it in transfers table.

Other changes:
- Add NULL DB value tracking to JSONBlob helper
- Index status column on transfers table
- Remove unnecessary panic calls
- Move log_parser to wallet's common package and use to extract token
  identity from the logs

Notes:
- there is already an index on transfers table, sqlite creates one for
  each unique constraint therefore add only status to a new index
- the planned refactoring and improvements to the database have been
  postponed due to time constraints. Got the time to migrate the data
  though, extracting it can be done later for a more efficient
  implementation

Update status-desktop #10746
2023-06-08 13:54:40 +02:00

119 lines
4.3 KiB
Go

package transfer
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
w_common "github.com/status-im/status-go/services/wallet/common"
)
// View stores only fields used by a client and ensures that all relevant fields are
// encoded in hex.
type View struct {
ID common.Hash `json:"id"`
Type w_common.Type `json:"type"`
Address common.Address `json:"address"`
BlockNumber *hexutil.Big `json:"blockNumber"`
BlockHash common.Hash `json:"blockhash"`
Timestamp hexutil.Uint64 `json:"timestamp"`
GasPrice *hexutil.Big `json:"gasPrice"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
EffectiveTip *hexutil.Big `json:"effectiveTip"`
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
GasUsed hexutil.Uint64 `json:"gasUsed"`
Nonce hexutil.Uint64 `json:"nonce"`
TxStatus hexutil.Uint64 `json:"txStatus"`
Input hexutil.Bytes `json:"input"`
TxHash common.Hash `json:"txHash"`
Value *hexutil.Big `json:"value"` // Only used for Type EthTransfer and Erc20Transfer
TokenID *hexutil.Big `json:"tokenId"` // Only used for Type Erc721Transfer
From common.Address `json:"from"`
To common.Address `json:"to"`
Contract common.Address `json:"contract"`
NetworkID uint64 `json:"networkId"`
MultiTransactionID int64 `json:"multiTransactionID"`
BaseGasFees string `json:"base_gas_fee"`
}
func castToTransferViews(transfers []Transfer) []View {
views := make([]View, 0, len(transfers))
for _, tx := range transfers {
switch tx.Type {
case w_common.EthTransfer, w_common.Erc20Transfer, w_common.Erc721Transfer:
view := CastToTransferView(tx)
views = append(views, view)
}
}
return views
}
func CastToTransferView(t Transfer) View {
view := View{}
view.ID = t.ID
view.Type = getFixedTransferType(t)
view.Address = t.Address
view.BlockNumber = (*hexutil.Big)(t.BlockNumber)
view.BlockHash = t.BlockHash
view.Timestamp = hexutil.Uint64(t.Timestamp)
view.GasPrice = (*hexutil.Big)(t.Transaction.GasPrice())
if t.BaseGasFees != "" {
baseFee := new(big.Int)
baseFee.SetString(t.BaseGasFees[2:], 16)
tip := t.Transaction.EffectiveGasTipValue(baseFee)
view.EffectiveTip = (*hexutil.Big)(tip)
price := new(big.Int).Add(baseFee, tip)
view.EffectiveGasPrice = (*hexutil.Big)(price)
}
view.MaxFeePerGas = (*hexutil.Big)(t.Transaction.GasFeeCap())
view.MaxPriorityFeePerGas = (*hexutil.Big)(t.Transaction.GasTipCap())
view.GasLimit = hexutil.Uint64(t.Transaction.Gas())
view.GasUsed = hexutil.Uint64(t.Receipt.GasUsed)
view.BaseGasFees = t.BaseGasFees
view.Nonce = hexutil.Uint64(t.Transaction.Nonce())
view.TxStatus = hexutil.Uint64(t.Receipt.Status)
view.Input = hexutil.Bytes(t.Transaction.Data())
view.TxHash = t.Transaction.Hash()
view.NetworkID = t.NetworkID
value := new(hexutil.Big)
tokenID := new(hexutil.Big)
switch view.Type {
case w_common.EthTransfer:
view.From = t.From
if t.Transaction.To() != nil {
view.To = *t.Transaction.To()
}
value = (*hexutil.Big)(t.Transaction.Value())
view.Contract = t.Receipt.ContractAddress
case w_common.Erc20Transfer:
view.Contract = t.Log.Address
from, to, valueInt := w_common.ParseErc20TransferLog(t.Log)
view.From, view.To, value = from, to, (*hexutil.Big)(valueInt)
case w_common.Erc721Transfer:
view.Contract = t.Log.Address
from, to, tokenIDInt := w_common.ParseErc721TransferLog(t.Log)
view.From, view.To, tokenID = from, to, (*hexutil.Big)(tokenIDInt)
}
view.MultiTransactionID = int64(t.MultiTransactionID)
view.Value = value
view.TokenID = tokenID
return view
}
func getFixedTransferType(tx Transfer) w_common.Type {
// erc721 transfers share signature with erc20 ones, so they both used to be categorized as erc20
// by the Downloader. We fix this here since they might be mis-categorized in the db.
if tx.Type == w_common.Erc20Transfer {
eventType := w_common.GetEventType(tx.Log)
return w_common.EventTypeToSubtransactionType(eventType)
}
return tx.Type
}