WIP
This commit is contained in:
parent
fb150f3d16
commit
24a2c4cd89
|
@ -130,6 +130,7 @@ func (api *API) DeployCollectibles(ctx context.Context, chainID uint64, deployme
|
|||
transactions.DeployCommunityToken,
|
||||
transactions.Keep,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
@ -242,6 +243,7 @@ func (api *API) DeployOwnerToken(ctx context.Context, chainID uint64,
|
|||
transactions.DeployOwnerToken,
|
||||
transactions.Keep,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
@ -305,6 +307,7 @@ func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentPara
|
|||
transactions.DeployCommunityToken,
|
||||
transactions.Keep,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
@ -402,6 +405,7 @@ func (api *API) MintTokens(ctx context.Context, chainID uint64, contractAddress
|
|||
transactions.AirdropCommunityToken,
|
||||
transactions.Keep,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
@ -469,6 +473,7 @@ func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress
|
|||
transactions.RemoteDestructCollectible,
|
||||
transactions.Keep,
|
||||
additionalData,
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
@ -521,6 +526,7 @@ func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string
|
|||
transactions.BurnCommunityToken,
|
||||
transactions.Keep,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
|
|
@ -526,6 +526,7 @@ func (s *Service) SetSignerPubKey(ctx context.Context, chainID uint64, contractA
|
|||
transactions.SetSignerPublicKey,
|
||||
transactions.Keep,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
@ -711,17 +712,19 @@ func (s *Service) ReTrackOwnerTokenDeploymentTransaction(ctx context.Context, ch
|
|||
transactionType = transactions.DeployOwnerToken
|
||||
}
|
||||
|
||||
deployerAddress := common.HexToAddress(communityToken.Deployer)
|
||||
_, err = s.pendingTracker.GetPendingEntry(wcommon.ChainID(chainID), common.HexToHash(hashString))
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// start only if no pending transaction in database
|
||||
err = s.pendingTracker.TrackPendingTransaction(
|
||||
wcommon.ChainID(chainID),
|
||||
common.HexToHash(hashString),
|
||||
common.HexToAddress(communityToken.Deployer),
|
||||
deployerAddress,
|
||||
common.Address{},
|
||||
transactionType,
|
||||
transactions.Keep,
|
||||
"",
|
||||
&deployerAddress,
|
||||
)
|
||||
log.Debug("retracking pending transaction with hashId ", hashString)
|
||||
} else {
|
||||
|
|
|
@ -361,6 +361,7 @@ func (api *API) Release(ctx context.Context, chainID uint64, txArgs transactions
|
|||
transactions.ReleaseENS,
|
||||
transactions.AutoDelete,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
@ -490,6 +491,7 @@ func (api *API) Register(ctx context.Context, chainID uint64, txArgs transaction
|
|||
transactions.RegisterENS,
|
||||
transactions.AutoDelete,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
@ -608,6 +610,7 @@ func (api *API) SetPubKey(ctx context.Context, chainID uint64, txArgs transactio
|
|||
transactions.SetPubKey,
|
||||
transactions.AutoDelete,
|
||||
"",
|
||||
tx.To(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("TrackPendingTransaction error", "error", err)
|
||||
|
|
|
@ -67,8 +67,9 @@ type Entry struct {
|
|||
chainIDOut *common.ChainID
|
||||
chainIDIn *common.ChainID
|
||||
transferType *TransferType
|
||||
contractAddress *eth.Address
|
||||
deployedContractAddress *eth.Address
|
||||
communityID *string
|
||||
interactedContractAddress *eth.Address
|
||||
|
||||
isNew bool // isNew is used to indicate if the entry is newer than session start (changed state also)
|
||||
}
|
||||
|
@ -92,8 +93,9 @@ type EntryData struct {
|
|||
ChainIDOut *common.ChainID `json:"chainIdOut,omitempty"`
|
||||
ChainIDIn *common.ChainID `json:"chainIdIn,omitempty"`
|
||||
TransferType *TransferType `json:"transferType,omitempty"`
|
||||
ContractAddress *eth.Address `json:"contractAddress,omitempty"`
|
||||
DeployedContractAddress *eth.Address `json:"contractAddress,omitempty"`
|
||||
CommunityID *string `json:"communityId,omitempty"`
|
||||
InteractedContractAddress *eth.Address `json:"interactedContractAddress,omitempty"`
|
||||
|
||||
IsNew *bool `json:"isNew,omitempty"`
|
||||
|
||||
|
@ -117,8 +119,9 @@ func (e *Entry) MarshalJSON() ([]byte, error) {
|
|||
ChainIDOut: e.chainIDOut,
|
||||
ChainIDIn: e.chainIDIn,
|
||||
TransferType: e.transferType,
|
||||
ContractAddress: e.contractAddress,
|
||||
DeployedContractAddress: e.deployedContractAddress,
|
||||
CommunityID: e.communityID,
|
||||
InteractedContractAddress: e.interactedContractAddress,
|
||||
}
|
||||
|
||||
if e.payloadType == MultiTransactionPT {
|
||||
|
@ -486,6 +489,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
startFilterDisabled, filter.Period.StartTimestamp, endFilterDisabled, filter.Period.EndTimestamp,
|
||||
filterActivityTypeAll, sliceContains(filter.Types, SendAT), sliceContains(filter.Types, ReceiveAT),
|
||||
sliceContains(filter.Types, ContractDeploymentAT), sliceContains(filter.Types, MintAT),
|
||||
sliceContains(filter.Types, SwapAT), sliceContains(filter.Types, ApproveAT),
|
||||
transfer.MultiTransactionSend,
|
||||
fromTrType, toTrType,
|
||||
allAddresses, filterAllToAddresses,
|
||||
|
@ -511,21 +515,36 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
var timestamp int64
|
||||
var dbMtType, dbTrType sql.NullByte
|
||||
var toAddress, fromAddress eth.Address
|
||||
var toAddressDB, ownerAddressDB, contractAddressDB, dbTokenID sql.RawBytes
|
||||
var tokenAddress, contractAddress *eth.Address
|
||||
var toAddressDB, ownerAddressDB, deployedContractAddressDB, dbTokenID sql.RawBytes
|
||||
var tokenAddress, deployedContractAddress, interactedContractAddress *eth.Address
|
||||
var aggregatedStatus int
|
||||
var dbTrAmount sql.NullString
|
||||
dbPTrAmount := new(big.Int)
|
||||
var dbMtFromAmount, dbMtToAmount, contractType sql.NullString
|
||||
var dbMtFromAmount, dbMtToAmount, dbContractType sql.NullString
|
||||
var contractType string
|
||||
var tokenCode, fromTokenCode, toTokenCode sql.NullString
|
||||
var methodHash, communityID sql.NullString
|
||||
var transferType *TransferType
|
||||
var communityMintEventDB sql.NullBool
|
||||
var communityMintEvent bool
|
||||
var dbInteractedContractAddress sql.RawBytes
|
||||
// Transaction Input parameters
|
||||
var dbTrInputApprovalSpender sql.RawBytes
|
||||
var dbTrInputApprovalAmount = new(big.Int)
|
||||
var dbTrInputFromAsset sql.NullString
|
||||
var dbTrInputFromAmount = new(big.Int)
|
||||
var dbTrInputToAsset sql.NullString
|
||||
var dbTrInputToAmount = new(big.Int)
|
||||
var dbTrInputSide sql.NullInt64
|
||||
var dbTrInputSlippageBps sql.NullInt64
|
||||
|
||||
err := rows.Scan(&transferHash, &pendingHash, &chainID, &multiTxID, ×tamp, &dbMtType, &dbTrType, &fromAddress,
|
||||
&toAddressDB, &ownerAddressDB, &dbTrAmount, (*bigint.SQLBigIntBytes)(dbPTrAmount), &dbMtFromAmount, &dbMtToAmount, &aggregatedStatus, &aggregatedCount,
|
||||
&tokenAddress, &dbTokenID, &tokenCode, &fromTokenCode, &toTokenCode, &outChainIDDB, &inChainIDDB, &contractType,
|
||||
&contractAddressDB, &methodHash, &communityMintEventDB, &communityID)
|
||||
&tokenAddress, &dbTokenID, &tokenCode, &fromTokenCode, &toTokenCode, &outChainIDDB, &inChainIDDB, &dbContractType,
|
||||
&deployedContractAddressDB, &methodHash, &communityMintEventDB, &communityID,
|
||||
&dbTrInputApprovalSpender, (*bigint.SQLBigIntBytes)(dbTrInputApprovalAmount), &dbTrInputFromAsset, (*bigint.SQLBigIntBytes)(dbTrInputFromAmount),
|
||||
&dbTrInputToAsset, (*bigint.SQLBigIntBytes)(dbTrInputToAmount), &dbTrInputSide, &dbTrInputSlippageBps,
|
||||
&dbInteractedContractAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -534,24 +553,32 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
toAddress = eth.BytesToAddress(toAddressDB)
|
||||
}
|
||||
|
||||
if contractType.Valid {
|
||||
transferType = contractTypeFromDBType(contractType.String)
|
||||
if dbContractType.Valid {
|
||||
contractType = dbContractType.String
|
||||
transferType = contractTypeFromDBType(contractType)
|
||||
}
|
||||
|
||||
if communityMintEventDB.Valid {
|
||||
communityMintEvent = communityMintEventDB.Bool
|
||||
}
|
||||
|
||||
if len(contractAddressDB) > 0 {
|
||||
contractAddress = new(eth.Address)
|
||||
*contractAddress = eth.BytesToAddress(contractAddressDB)
|
||||
if len(deployedContractAddressDB) > 0 {
|
||||
deployedContractAddress = new(eth.Address)
|
||||
*deployedContractAddress = eth.BytesToAddress(deployedContractAddressDB)
|
||||
}
|
||||
|
||||
if len(dbInteractedContractAddress) > 0 {
|
||||
interactedContractAddress := new(eth.Address)
|
||||
*interactedContractAddress = eth.BytesToAddress(dbInteractedContractAddress)
|
||||
}
|
||||
|
||||
getActivityType := func(trType sql.NullByte) (activityType Type, filteredAddress eth.Address) {
|
||||
if trType.Valid {
|
||||
if trType.Byte == fromTrType {
|
||||
if toAddress == ZeroAddress && transferType != nil && *transferType == TransferTypeEth && contractAddress != nil && *contractAddress != ZeroAddress {
|
||||
if toAddress == ZeroAddress && transferType != nil && *transferType == TransferTypeEth && deployedContractAddress != nil && *deployedContractAddress != ZeroAddress {
|
||||
return ContractDeploymentAT, fromAddress
|
||||
} else if contractType == string(common.Erc20Approval) {
|
||||
return ApproveAT, fromAddress
|
||||
}
|
||||
return SendAT, fromAddress
|
||||
} else if trType.Byte == toTrType {
|
||||
|
@ -624,15 +651,12 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
entry.amountIn = inAmount
|
||||
} else if pendingHash != nil && chainID.Valid {
|
||||
// Process `pending_transactions` row
|
||||
|
||||
// Extract activity type: SendAT/ReceiveAT
|
||||
activityType, _ := getActivityType(dbTrType)
|
||||
|
||||
inAmount, outAmount := getTrInAndOutAmounts(activityType, dbTrAmount, dbPTrAmount)
|
||||
|
||||
outChainID = new(common.ChainID)
|
||||
*outChainID = common.ChainID(chainID.Int64)
|
||||
|
||||
// Extract activity type: SendAT/ReceiveAT/ApproveAT
|
||||
activityType, _ := getActivityType(dbTrType)
|
||||
|
||||
entry = newActivityEntryWithPendingTransaction(
|
||||
&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64),
|
||||
Hash: eth.BytesToHash(pendingHash),
|
||||
|
@ -640,6 +664,25 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
timestamp, activityType, activityStatus,
|
||||
)
|
||||
|
||||
if activityType == ApproveAT {
|
||||
// Extract Approve token
|
||||
if dbTrInputFromAsset.Valid {
|
||||
entry.tokenOut = deps.tokenFromSymbol(outChainID, dbTrInputFromAsset.String)
|
||||
entry.symbolOut = common.NewAndSet(dbTrInputFromAsset.String)
|
||||
}
|
||||
|
||||
// Extract Approve amount
|
||||
if dbTrInputApprovalAmount != nil {
|
||||
entry.amountOut = (*hexutil.Big)(dbTrInputApprovalAmount)
|
||||
}
|
||||
|
||||
// Extract Approve spender
|
||||
if len(dbTrInputApprovalSpender) > 0 {
|
||||
toAddress = eth.BytesToAddress(dbTrInputApprovalSpender)
|
||||
}
|
||||
} else {
|
||||
inAmount, outAmount := getTrInAndOutAmounts(activityType, dbTrAmount, dbPTrAmount)
|
||||
|
||||
// Extract tokens
|
||||
if tokenCode.Valid {
|
||||
cID := common.ChainID(chainID.Int64)
|
||||
|
@ -650,7 +693,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
// Complete the data
|
||||
entry.amountOut = outAmount
|
||||
entry.amountIn = inAmount
|
||||
|
||||
}
|
||||
} else if multiTxID.Valid {
|
||||
// Process `multi_transactions` row
|
||||
|
||||
|
@ -693,12 +736,12 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
}
|
||||
|
||||
// Complete common data
|
||||
entry.recipient = &toAddress
|
||||
entry.sender = &fromAddress
|
||||
entry.recipient = &toAddress
|
||||
entry.chainIDOut = outChainID
|
||||
entry.chainIDIn = inChainID
|
||||
entry.transferType = transferType
|
||||
entry.interactedContractAddress = interactedContractAddress
|
||||
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
|
|
@ -675,12 +675,14 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
|
|||
// Add 6 extractable transactions: one MultiTransactionSwap, two MultiTransactionBridge, two MultiTransactionSend and one MultiTransactionApprove
|
||||
multiTxs := make([]transfer.MultiTransaction, 6)
|
||||
trs, fromAddrs, toAddrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, len(multiTxs)*2)
|
||||
approveTxs, approveFromAddrs := transfer.GenerateTestApproves(t, deps.db, td.nextIndex+len(trs), 2)
|
||||
|
||||
multiTxs[0] = transfer.GenerateTestBridgeMultiTransaction(trs[0], trs[1])
|
||||
multiTxs[1] = transfer.GenerateTestSwapMultiTransaction(trs[2], testutils.SntSymbol, 100) // trs[3]
|
||||
multiTxs[2] = transfer.GenerateTestSendMultiTransaction(trs[4]) // trs[5]
|
||||
multiTxs[3] = transfer.GenerateTestBridgeMultiTransaction(trs[6], trs[7])
|
||||
multiTxs[4] = transfer.GenerateTestSendMultiTransaction(trs[8]) // trs[9]
|
||||
multiTxs[5] = transfer.GenerateTestApproveMultiTransaction(trs[10]) // trs[11]
|
||||
multiTxs[5] = transfer.GenerateTestApproveMultiTransaction(approveTxs[1])
|
||||
|
||||
var lastMT common.MultiTransactionIDType
|
||||
for i := range trs {
|
||||
|
@ -695,6 +697,7 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
|
|||
|
||||
// Here not to include the modified To and From addresses
|
||||
allAddresses := append(append(append(append(append(tdFromAdds, tdToAddrs...), fromAddrs...), toAddrs...), fromSpecial...), toSpecial...)
|
||||
allAddresses = append(allAddresses, approveFromAddrs...)
|
||||
|
||||
// Insert MintAT Collectible
|
||||
trsSpecial[0].From = eth.HexToAddress("0x0")
|
||||
|
|
|
@ -23,6 +23,8 @@ WITH filter_conditions AS (
|
|||
? AS filterActivityTypeReceive,
|
||||
? AS filterActivityTypeContractDeployment,
|
||||
? AS filterActivityTypeMint,
|
||||
? AS filterActivityTypeSwap,
|
||||
? AS filterActivityTypeApprove,
|
||||
? AS mTTypeSend,
|
||||
? AS fromTrType,
|
||||
? AS toTrType,
|
||||
|
@ -221,12 +223,22 @@ SELECT
|
|||
WHEN transfers.type = 'erc20' THEN (SELECT community_id FROM tokens WHERE transfers.token_address = tokens.address AND transfers.network_id = tokens.network_id)
|
||||
WHEN transfers.type = 'erc721' OR transfers.type = 'erc1155' THEN (SELECT community_id FROM collectible_data_cache WHERE transfers.token_address = collectible_data_cache.contract_address AND transfers.network_id = collectible_data_cache.chain_id)
|
||||
ELSE NULL
|
||||
END AS community_id
|
||||
END AS community_id,
|
||||
transaction_input_data.approval_spender as input_approval_spender,
|
||||
transaction_input_data.approval_amount as input_approval_amount,
|
||||
transaction_input_data.from_asset as input_from_asset,
|
||||
transaction_input_data.from_amount as input_from_amount,
|
||||
transaction_input_data.to_asset as input_to_asset,
|
||||
transaction_input_data.to_amount as input_to_amount,
|
||||
transaction_input_data.side as input_side,
|
||||
transaction_input_data.slippage_bps as input_slippage_bps,
|
||||
transfers.transaction_to as interacted_contract_address
|
||||
FROM
|
||||
transfers
|
||||
CROSS JOIN filter_conditions
|
||||
LEFT JOIN filter_addresses from_join ON transfers.tx_from_address = from_join.address
|
||||
LEFT JOIN filter_addresses to_join ON transfers.tx_to_address = to_join.address
|
||||
LEFT JOIN transaction_input_data ON transfers.network_id = transaction_input_data.chain_id AND transfers.tx_hash = transaction_input_data.tx_hash
|
||||
WHERE
|
||||
transfers.loaded == 1
|
||||
AND transfers.multi_transaction_id = 0
|
||||
|
@ -273,6 +285,11 @@ WHERE
|
|||
)
|
||||
)
|
||||
)
|
||||
OR (
|
||||
filterActivityTypeApprove
|
||||
AND tr_type = fromTrType
|
||||
AND transfers.type = 'Erc20Approval'
|
||||
)
|
||||
OR (
|
||||
filterActivityTypeContractDeployment
|
||||
AND tr_type = fromTrType
|
||||
|
@ -391,12 +408,22 @@ SELECT
|
|||
NULL as contract_address,
|
||||
NULL AS method_hash,
|
||||
NULL AS community_mint_event,
|
||||
NULL AS community_id
|
||||
NULL AS community_id,
|
||||
transaction_input_data.approval_spender as input_approval_spender,
|
||||
transaction_input_data.approval_amount as input_approval_amount,
|
||||
transaction_input_data.from_asset as input_from_asset,
|
||||
transaction_input_data.from_amount as input_from_amount,
|
||||
transaction_input_data.to_asset as input_to_asset,
|
||||
transaction_input_data.to_amount as input_to_amount,
|
||||
transaction_input_data.side as input_side,
|
||||
transaction_input_data.slippage_bps as input_slippage_bps,
|
||||
pending_transactions.transaction_to as interacted_contract_address
|
||||
FROM
|
||||
pending_transactions
|
||||
CROSS JOIN filter_conditions
|
||||
LEFT JOIN filter_addresses from_join ON pending_transactions.from_address = from_join.address
|
||||
LEFT JOIN filter_addresses to_join ON pending_transactions.to_address = to_join.address
|
||||
LEFT JOIN transaction_input_data ON pending_transactions.network_id = transaction_input_data.chain_id AND pending_transactions.hash = transaction_input_data.tx_hash
|
||||
WHERE
|
||||
pending_transactions.multi_transaction_id = 0
|
||||
AND pending_transactions.status = pendingStatus
|
||||
|
@ -484,7 +511,16 @@ SELECT
|
|||
NULL as contract_address,
|
||||
NULL AS method_hash,
|
||||
NULL AS community_mint_event,
|
||||
NULL AS community_id
|
||||
NULL AS community_id,
|
||||
NULL as input_approval_spender,
|
||||
NULL as input_approval_amount,
|
||||
NULL as input_from_asset,
|
||||
NULL as input_from_amount,
|
||||
NULL as input_to_asset,
|
||||
NULL as input_to_amount,
|
||||
NULL as input_side,
|
||||
NULL as input_slippage_bps,
|
||||
NULL as interacted_contract_address
|
||||
FROM
|
||||
multi_transactions
|
||||
CROSS JOIN filter_conditions
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
protocolCommon "github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/collectibles"
|
||||
w_common "github.com/status-im/status-go/services/wallet/common"
|
||||
|
@ -74,13 +75,22 @@ type Service struct {
|
|||
debounceDuration time.Duration
|
||||
|
||||
pendingTracker *transactions.PendingTxTracker
|
||||
|
||||
featureFlags *protocolCommon.FeatureFlags
|
||||
}
|
||||
|
||||
func (s *Service) nextSessionID() SessionID {
|
||||
return SessionID(s.lastSessionID.Add(1))
|
||||
}
|
||||
|
||||
func NewService(db *sql.DB, accountsDB *accounts.Database, tokenManager token.ManagerInterface, collectibles collectibles.ManagerInterface, eventFeed *event.Feed, pendingTracker *transactions.PendingTxTracker) *Service {
|
||||
func NewService(
|
||||
db *sql.DB,
|
||||
accountsDB *accounts.Database,
|
||||
tokenManager token.ManagerInterface,
|
||||
collectibles collectibles.ManagerInterface,
|
||||
eventFeed *event.Feed,
|
||||
pendingTracker *transactions.PendingTxTracker,
|
||||
featureFlags *protocolCommon.FeatureFlags) *Service {
|
||||
return &Service{
|
||||
db: db,
|
||||
accountsDB: accountsDB,
|
||||
|
@ -94,6 +104,8 @@ func NewService(db *sql.DB, accountsDB *accounts.Database, tokenManager token.Ma
|
|||
debounceDuration: 1 * time.Second,
|
||||
|
||||
pendingTracker: pendingTracker,
|
||||
|
||||
featureFlags: featureFlags,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -468,7 +468,7 @@ func TestService_IncrementalUpdateOnTop(t *testing.T) {
|
|||
ChainID: 5,
|
||||
}, newTx.tokenOut)
|
||||
require.Equal(t, (*Token)(nil), newTx.tokenIn)
|
||||
require.Equal(t, (*eth.Address)(nil), newTx.contractAddress)
|
||||
require.Equal(t, (*eth.Address)(nil), newTx.deployedContractAddress)
|
||||
|
||||
// Check the order of the following transaction data
|
||||
require.Equal(t, SimpleTransactionPT, payload.Activities[1].payloadType)
|
||||
|
|
|
@ -29,9 +29,11 @@ const (
|
|||
UniswapV3Swap Type = "uniswapV3Swap"
|
||||
HopBridgeFrom Type = "HopBridgeFrom"
|
||||
HopBridgeTo Type = "HopBridgeTo"
|
||||
Erc20Approval Type = "Erc20Approval"
|
||||
unknownTransaction Type = "unknown"
|
||||
|
||||
// Event types
|
||||
Erc20ApprovalEventType EventType = "erc20ApprovalEvent"
|
||||
WETHDepositEventType EventType = "wethDepositEvent"
|
||||
WETHWithdrawalEventType EventType = "wethWithdrawalEvent"
|
||||
Erc20TransferEventType EventType = "erc20Event"
|
||||
|
@ -46,6 +48,10 @@ const (
|
|||
HopBridgeTransferSentEventType EventType = "hopBridgeTransferSentEvent"
|
||||
UnknownEventType EventType = "unknownEvent"
|
||||
|
||||
// ERC20: Approval (index_topic_1 address owner, index_topic_2 address spender, uint256 value)
|
||||
Erc20_721ApprovalEventSignature = "Approval(address,address,uint256)"
|
||||
erc20ApprovalEventIndexedParameters = 3 // signature, owner, spender
|
||||
|
||||
// Deposit (index_topic_1 address dst, uint256 wad)
|
||||
wethDepositEventSignature = "Deposit(address,uint256)"
|
||||
// Withdrawal (index_topic_1 address src, uint256 wad)
|
||||
|
@ -83,6 +89,7 @@ var (
|
|||
|
||||
// Detect event type for a cetain item from the Events Log
|
||||
func GetEventType(log *types.Log) EventType {
|
||||
erc20_721ApprovalEventSignatureHash := GetEventSignatureHash(Erc20_721ApprovalEventSignature)
|
||||
wethDepositEventSignatureHash := GetEventSignatureHash(wethDepositEventSignature)
|
||||
wethWithdrawalEventSignatureHash := GetEventSignatureHash(wethWithdrawalEventSignature)
|
||||
erc20_721TransferEventSignatureHash := GetEventSignatureHash(Erc20_721TransferEventSignature)
|
||||
|
@ -97,6 +104,11 @@ func GetEventType(log *types.Log) EventType {
|
|||
|
||||
if len(log.Topics) > 0 {
|
||||
switch log.Topics[0] {
|
||||
case erc20_721ApprovalEventSignatureHash:
|
||||
switch len(log.Topics) {
|
||||
case erc20ApprovalEventIndexedParameters:
|
||||
return Erc20ApprovalEventType
|
||||
}
|
||||
case wethDepositEventSignatureHash:
|
||||
return WETHDepositEventType
|
||||
case wethWithdrawalEventSignatureHash:
|
||||
|
@ -132,6 +144,8 @@ func GetEventType(log *types.Log) EventType {
|
|||
|
||||
func EventTypeToSubtransactionType(eventType EventType) Type {
|
||||
switch eventType {
|
||||
case Erc20ApprovalEventType:
|
||||
return Erc20Approval
|
||||
case Erc20TransferEventType:
|
||||
return Erc20Transfer
|
||||
case Erc721TransferEventType:
|
||||
|
@ -309,6 +323,35 @@ func getFromToAddresses(ethlog types.Log) (from, to common.Address, err error) {
|
|||
|
||||
return from, to, fmt.Errorf("unsupported event type to get from/to adddresses %s", eventType)
|
||||
}
|
||||
|
||||
func ParseErc20ApprovalLog(ethlog *types.Log) (owner, spender common.Address, value *big.Int, err error) {
|
||||
value = new(big.Int)
|
||||
if len(ethlog.Topics) < erc20ApprovalEventIndexedParameters {
|
||||
err = fmt.Errorf("not enough topics for erc20 approval %s, %v", "topics", ethlog.Topics)
|
||||
log.Error("log_parser::ParseErc20ApprovalLog", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = checkTopicsLength(*ethlog, 1, erc20ApprovalEventIndexedParameters)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
addressIdx := common.HashLength - common.AddressLength
|
||||
copy(owner[:], ethlog.Topics[1][addressIdx:])
|
||||
copy(spender[:], ethlog.Topics[2][addressIdx:])
|
||||
|
||||
if len(ethlog.Data) != common.HashLength {
|
||||
err = fmt.Errorf("data is not padded to 32 byts big int %s, %v", "data", ethlog.Data)
|
||||
log.Error("log_parser::ParseErc20ApprovalLog", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
value.SetBytes(ethlog.Data[:common.HashLength])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ParseTransferLog(ethlog types.Log) (from, to common.Address, txIDs []common.Hash, tokenIDs, values []*big.Int, err error) {
|
||||
eventType := GetEventType(ðlog)
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ var (
|
|||
ZeroBigIntValue = big.NewInt(0)
|
||||
)
|
||||
|
||||
type SwapSide uint8
|
||||
|
||||
const (
|
||||
IncreaseEstimatedGasFactor = 1.1
|
||||
SevenDaysInSeconds = 60 * 60 * 24 * 7
|
||||
|
@ -31,6 +33,9 @@ const (
|
|||
ProcessorENSReleaseName = "ENSRelease"
|
||||
ProcessorENSPublicKeyName = "ENSPublicKey"
|
||||
ProcessorStickersBuyName = "StickersBuy"
|
||||
|
||||
SwapSideBuy SwapSide = 0
|
||||
SwapSideSell SwapSide = 1
|
||||
)
|
||||
|
||||
func IsProcessorBridge(name string) bool {
|
||||
|
|
|
@ -47,6 +47,7 @@ var (
|
|||
ErrPriceTimeout = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-037"), Details: "price timeout"}
|
||||
ErrNotEnoughLiquidity = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-038"), Details: "not enough liquidity"}
|
||||
ErrPriceImpactTooHigh = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-039"), Details: "price impact too high"}
|
||||
ErrFromTokenShouldNotBeNative = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-040"), Details: "from token should not be the native token"}
|
||||
)
|
||||
|
||||
func createErrorResponse(processorName string, err error) error {
|
||||
|
|
|
@ -211,3 +211,18 @@ func (mr *MockPathProcessorClearableMockRecorder) Clear() *gomock.Call {
|
|||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockPathProcessorClearable)(nil).Clear))
|
||||
}
|
||||
|
||||
// GetTransactionInputData mocks base method.
|
||||
func (m *MockPathProcessor) GetTransactionInputData(sendArgs *pathprocessor.MultipathProcessorTxArgs) (*pathprocessor.TransactionInputData, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetTransactionInputData", sendArgs)
|
||||
ret0, _ := ret[0].(*pathprocessor.TransactionInputData)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Send indicates an expected call of Send.
|
||||
func (mr *MockPathProcessorMockRecorder) GetTransactionInputData(sendArgs interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransactionInputData", reflect.TypeOf((*MockPathProcessor)(nil).GetTransactionInputData), sendArgs)
|
||||
}
|
|
@ -11,6 +11,7 @@ type MultipathProcessorTxArgs struct {
|
|||
Name string `json:"bridgeName"`
|
||||
ChainID uint64
|
||||
TransferTx *transactions.SendTxArgs
|
||||
ApproveTx *ApproveTxArgs
|
||||
HopTx *HopBridgeTxArgs
|
||||
CbridgeTx *CelerBridgeTxArgs
|
||||
ERC721TransferTx *ERC721TxArgs
|
||||
|
|
|
@ -31,6 +31,8 @@ type PathProcessor interface {
|
|||
GetContractAddress(params ProcessorInputParams) (common.Address, error)
|
||||
// BuildTransaction builds the transaction based on MultipathProcessorTxArgs, returns the transaction and the used nonce (lastUsedNonce is -1 if it's the first tx)
|
||||
BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error)
|
||||
// GetTransactionInputData returns the input data used to build the transaction
|
||||
GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error)
|
||||
}
|
||||
|
||||
type PathProcessorClearable interface {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package pathprocessor
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// Placeholder for Approve transaction until handling gets fully done status-go side
|
||||
type ApproveTxArgs struct {
|
||||
ApprovalSpender common.Address `json:"approvalSpender"`
|
||||
ApprovalAmount *hexutil.Big `json:"approvalAmount"`
|
||||
}
|
|
@ -388,3 +388,7 @@ func (s *CelerBridgeProcessor) CalculateAmountOut(params ProcessorInputParams) (
|
|||
amountOut, _ := new(big.Int).SetString(amt.EqValueTokenAmt, 10)
|
||||
return amountOut, nil
|
||||
}
|
||||
|
||||
func (s *CelerBridgeProcessor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -651,3 +651,7 @@ func (h *HopBridgeProcessor) sendL2BridgeTx(contractAddress common.Address, ethC
|
|||
|
||||
return tx, ErrTxForChainNotSupported
|
||||
}
|
||||
|
||||
func (s *HopBridgeProcessor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -125,3 +125,7 @@ func (s *ENSPublicKeyProcessor) GetContractAddress(params ProcessorInputParams)
|
|||
}
|
||||
return *addr, nil
|
||||
}
|
||||
|
||||
func (s *ENSPublicKeyProcessor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -154,3 +154,7 @@ func (s *ENSRegisterProcessor) CalculateAmountOut(params ProcessorInputParams) (
|
|||
func (s *ENSRegisterProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||
return snt.ContractAddress(params.FromChain.ChainID)
|
||||
}
|
||||
|
||||
func (s *ENSRegisterProcessor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -124,3 +124,7 @@ func (s *ENSReleaseProcessor) GetContractAddress(params ProcessorInputParams) (c
|
|||
}
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func (s *ENSReleaseProcessor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -170,3 +170,7 @@ func (s *ERC1155Processor) CalculateAmountOut(params ProcessorInputParams) (*big
|
|||
func (s *ERC1155Processor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||
return params.FromToken.Address, nil
|
||||
}
|
||||
|
||||
func (s *ERC1155Processor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -227,3 +227,7 @@ func (s *ERC721Processor) CalculateAmountOut(params ProcessorInputParams) (*big.
|
|||
func (s *ERC721Processor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||
return params.FromToken.Address, nil
|
||||
}
|
||||
|
||||
func (s *ERC721Processor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -146,3 +146,7 @@ func (s *StickersBuyProcessor) CalculateAmountOut(params ProcessorInputParams) (
|
|||
func (s *StickersBuyProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||
return snt.ContractAddress(params.FromChain.ChainID)
|
||||
}
|
||||
|
||||
func (s *StickersBuyProcessor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -208,15 +208,25 @@ func (s *SwapParaswapProcessor) GetContractAddress(params ProcessorInputParams)
|
|||
return priceRoute.TokenTransferProxy, nil
|
||||
}
|
||||
|
||||
func (s *SwapParaswapProcessor) prepareTransaction(sendArgs *MultipathProcessorTxArgs) error {
|
||||
slippageBP := uint(sendArgs.SwapTx.SlippagePercentage * 100) // convert to basis points
|
||||
|
||||
func (s *SwapParaswapProcessor) getPriceRoute(sendArgs *MultipathProcessorTxArgs) (*paraswap.Route, error) {
|
||||
key := makeKey(sendArgs.SwapTx.ChainID, sendArgs.SwapTx.ChainIDTo, sendArgs.SwapTx.TokenIDFrom, sendArgs.SwapTx.TokenIDTo)
|
||||
priceRouteIns, ok := s.priceRoute.Load(key)
|
||||
if !ok {
|
||||
return ErrPriceRouteNotFound
|
||||
return nil, ErrPriceRouteNotFound
|
||||
}
|
||||
return priceRouteIns.(*paraswap.Route), nil
|
||||
}
|
||||
|
||||
func percentageToBasisPoints(percentage float32) uint {
|
||||
return uint(percentage * 100)
|
||||
}
|
||||
|
||||
func (s *SwapParaswapProcessor) prepareTransaction(sendArgs *MultipathProcessorTxArgs) error {
|
||||
slippageBP := percentageToBasisPoints(sendArgs.SwapTx.SlippagePercentage)
|
||||
priceRoute, err := s.getPriceRoute(sendArgs)
|
||||
if err != nil {
|
||||
return statusErrors.CreateErrorResponseFromError(err)
|
||||
}
|
||||
priceRoute := priceRouteIns.(*paraswap.Route)
|
||||
|
||||
tx, err := s.paraswapClient.BuildTransaction(context.Background(), priceRoute.SrcTokenAddress, priceRoute.SrcTokenDecimals, priceRoute.SrcAmount.Int,
|
||||
priceRoute.DestTokenAddress, priceRoute.DestTokenDecimals, priceRoute.DestAmount.Int, slippageBP,
|
||||
|
@ -284,3 +294,31 @@ func (s *SwapParaswapProcessor) CalculateAmountOut(params ProcessorInputParams)
|
|||
|
||||
return destAmount, nil
|
||||
}
|
||||
|
||||
func paraswapSidetoCommon(side paraswap.SwapSide) SwapSide {
|
||||
if side == paraswap.BuySide {
|
||||
return SwapSideBuy
|
||||
}
|
||||
return SwapSideSell
|
||||
}
|
||||
|
||||
func (s *SwapParaswapProcessor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
slippageBP := (uint16)(percentageToBasisPoints(sendArgs.SwapTx.SlippagePercentage))
|
||||
priceRoute, err := s.getPriceRoute(sendArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
swapSide := paraswapSidetoCommon(priceRoute.Side)
|
||||
|
||||
ret := &TransactionInputData{
|
||||
ProcessorName: s.Name(),
|
||||
FromAsset: &sendArgs.SwapTx.TokenIDFrom,
|
||||
FromAmount: (*hexutil.Big)(priceRoute.SrcAmount.Int),
|
||||
ToAsset: &sendArgs.SwapTx.TokenIDTo,
|
||||
ToAmount: (*hexutil.Big)(priceRoute.DestAmount.Int),
|
||||
Side: &swapSide,
|
||||
SlippageBps: &slippageBP,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
@ -128,3 +128,18 @@ func (s *TransferProcessor) CalculateAmountOut(params ProcessorInputParams) (*bi
|
|||
func (s *TransferProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||
return common.Address{}, nil
|
||||
}
|
||||
|
||||
func (s *TransferProcessor) GetTransactionInputData(sendArgs *MultipathProcessorTxArgs) (*TransactionInputData, error) {
|
||||
if sendArgs.ApproveTx == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ret := &TransactionInputData{
|
||||
ProcessorName: s.Name(),
|
||||
FromAsset: &sendArgs.TransferTx.Symbol,
|
||||
ApprovalAmount: sendArgs.ApproveTx.ApprovalAmount,
|
||||
ApprovalSpender: &sendArgs.ApproveTx.ApprovalSpender,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package pathprocessor
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
type TransactionInputData struct {
|
||||
ProcessorName string
|
||||
FromAsset *string
|
||||
FromAmount *hexutil.Big
|
||||
ToAsset *string
|
||||
ToAmount *hexutil.Big
|
||||
Side *SwapSide
|
||||
SlippageBps *uint16
|
||||
ApprovalAmount *hexutil.Big
|
||||
ApprovalSpender *common.Address
|
||||
}
|
||||
|
||||
func NewInputData() *TransactionInputData {
|
||||
return &TransactionInputData{
|
||||
FromAmount: (*hexutil.Big)(new(big.Int)),
|
||||
ToAmount: (*hexutil.Big)(new(big.Int)),
|
||||
ApprovalAmount: (*hexutil.Big)(new(big.Int)),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package pathprocessor
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
w_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/sqlite"
|
||||
)
|
||||
|
||||
type TransactionInputDataDB struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewTransactionInputDataDB(db *sql.DB) *TransactionInputDataDB {
|
||||
return &TransactionInputDataDB{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (iDB *TransactionInputDataDB) UpsertInputData(chainID w_common.ChainID, txHash types.Hash, inputData TransactionInputData) error {
|
||||
return upsertInputData(iDB.db, chainID, txHash, inputData)
|
||||
}
|
||||
|
||||
func (iDB *TransactionInputDataDB) ReadInputData(chainID w_common.ChainID, txHash types.Hash) (*TransactionInputData, error) {
|
||||
return readInputData(iDB.db, chainID, txHash)
|
||||
}
|
||||
|
||||
func upsertInputData(creator sqlite.StatementCreator, chainID w_common.ChainID, txHash types.Hash, inputData TransactionInputData) error {
|
||||
q := sq.Replace("transaction_input_data").
|
||||
SetMap(sq.Eq{
|
||||
"chain_id": chainID,
|
||||
"tx_hash": txHash.Bytes(),
|
||||
"processor_name": inputData.ProcessorName,
|
||||
"from_asset": inputData.FromAsset,
|
||||
"from_amount": (*bigint.SQLBigIntBytes)(inputData.FromAmount),
|
||||
"to_asset": inputData.ToAsset,
|
||||
"to_amount": (*bigint.SQLBigIntBytes)(inputData.ToAmount),
|
||||
"side": inputData.Side,
|
||||
"slippage_bps": inputData.SlippageBps,
|
||||
"approval_amount": (*bigint.SQLBigIntBytes)(inputData.ApprovalAmount),
|
||||
"approval_spender": inputData.ApprovalSpender,
|
||||
})
|
||||
|
||||
query, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stmt, err := creator.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(args...)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func readInputData(creator sqlite.StatementCreator, chainID w_common.ChainID, txHash types.Hash) (*TransactionInputData, error) {
|
||||
q := sq.Select(
|
||||
"processor_name",
|
||||
"from_asset",
|
||||
"from_amount",
|
||||
"to_asset",
|
||||
"to_amount",
|
||||
"side",
|
||||
"slippage_bps",
|
||||
"approval_amount",
|
||||
"approval_spender",
|
||||
).
|
||||
From("transaction_input_data").
|
||||
Where(sq.Eq{"chain_id": chainID, "tx_hash": txHash.Bytes()})
|
||||
|
||||
query, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt, err := creator.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
inputData := NewInputData()
|
||||
err = stmt.QueryRow(args...).Scan(
|
||||
&inputData.ProcessorName,
|
||||
&inputData.FromAsset,
|
||||
(*bigint.SQLBigIntBytes)(inputData.FromAmount),
|
||||
&inputData.ToAsset,
|
||||
(*bigint.SQLBigIntBytes)(inputData.ToAmount),
|
||||
&inputData.Side,
|
||||
&inputData.SlippageBps,
|
||||
(*bigint.SQLBigIntBytes)(inputData.ApprovalAmount),
|
||||
&inputData.ApprovalSpender,
|
||||
)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return inputData, nil
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package pathprocessor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
w_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/walletdatabase"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTxInputDataDBTest(t *testing.T) (*TransactionInputDataDB, func()) {
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
return NewTransactionInputDataDB(db), func() {
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
}
|
||||
|
||||
type testTxInputData struct {
|
||||
ChainID w_common.ChainID
|
||||
TxHash types.Hash
|
||||
InputData TransactionInputData
|
||||
}
|
||||
|
||||
func generateTestTxInputData(offset int, count int) []testTxInputData {
|
||||
ret := make([]testTxInputData, 0, count)
|
||||
for i := offset; i < offset+count; i++ {
|
||||
inputData := NewInputData()
|
||||
|
||||
inputDataIdx := i % 3
|
||||
inputData.ProcessorName = fmt.Sprintf("processor_name_%d", inputDataIdx)
|
||||
|
||||
fromAsset := fmt.Sprintf("from_asset_%d", inputDataIdx)
|
||||
fromAmount := new(big.Int).SetInt64(int64(inputDataIdx))
|
||||
toAsset := fmt.Sprintf("to_asset_%d", inputDataIdx)
|
||||
toAmount := new(big.Int).SetInt64(int64(inputDataIdx * 2))
|
||||
side := SwapSide(i % 2)
|
||||
slippageBps := uint16(i % 100)
|
||||
approvalAmount := new(big.Int).SetInt64(int64(inputDataIdx * 3))
|
||||
approvalSpender := common.HexToAddress(fmt.Sprintf("0x%d", inputDataIdx*4))
|
||||
|
||||
switch inputDataIdx {
|
||||
case 0:
|
||||
inputData.FromAsset = &fromAsset
|
||||
inputData.FromAmount = (*hexutil.Big)(fromAmount)
|
||||
case 1:
|
||||
inputData.FromAsset = &fromAsset
|
||||
inputData.FromAmount = (*hexutil.Big)(fromAmount)
|
||||
inputData.ToAsset = &toAsset
|
||||
inputData.ToAmount = (*hexutil.Big)(toAmount)
|
||||
inputData.Side = &side
|
||||
inputData.SlippageBps = &slippageBps
|
||||
case 2:
|
||||
inputData.FromAsset = &fromAsset
|
||||
inputData.FromAmount = (*hexutil.Big)(fromAmount)
|
||||
inputData.ApprovalAmount = (*hexutil.Big)(approvalAmount)
|
||||
inputData.ApprovalSpender = &approvalSpender
|
||||
}
|
||||
testInputData := testTxInputData{
|
||||
ChainID: w_common.ChainID(i % 3),
|
||||
TxHash: types.HexToHash(uuid.New().String()),
|
||||
InputData: *inputData,
|
||||
}
|
||||
ret = append(ret, testInputData)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func TestUpsertTxInputData(t *testing.T) {
|
||||
iDB, cleanup := setupTxInputDataDBTest(t)
|
||||
defer cleanup()
|
||||
|
||||
testData := generateTestTxInputData(0, 15)
|
||||
for _, data := range testData {
|
||||
err := iDB.UpsertInputData(data.ChainID, data.TxHash, data.InputData)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
for _, data := range testData {
|
||||
readData, err := iDB.ReadInputData(data.ChainID, data.TxHash)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readData)
|
||||
require.Equal(t, data.InputData, *readData)
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/status-im/status-go/services/wallet/history"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/onramp"
|
||||
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/alchemy"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/coingecko"
|
||||
|
@ -112,7 +113,16 @@ func NewService(
|
|||
cryptoOnRampManager := onramp.NewManager(cryptoOnRampProviders)
|
||||
|
||||
savedAddressesManager := &SavedAddressesManager{db: db}
|
||||
transactionManager := transfer.NewTransactionManager(transfer.NewMultiTransactionDB(db), gethManager, transactor, config, accountsDB, pendingTxManager, feed)
|
||||
transactionManager := transfer.NewTransactionManager(
|
||||
transfer.NewMultiTransactionDB(db),
|
||||
pathprocessor.NewTransactionInputDataDB(db),
|
||||
gethManager,
|
||||
transactor,
|
||||
config,
|
||||
accountsDB,
|
||||
pendingTxManager,
|
||||
feed,
|
||||
)
|
||||
blockChainState := blockchainstate.NewBlockChainState()
|
||||
transferController := transfer.NewTransferController(db, accountsDB, rpcClient, accountFeed, feed, transactionManager, pendingTxManager,
|
||||
tokenManager, balanceCacher, blockChainState)
|
||||
|
@ -181,12 +191,7 @@ func NewService(
|
|||
)
|
||||
collectibles := collectibles.NewService(db, feed, accountsDB, accountFeed, settingsFeed, communityManager, rpcClient.NetworkManager, collectiblesManager)
|
||||
|
||||
activity := activity.NewService(db, accountsDB, tokenManager, collectiblesManager, feed, pendingTxManager)
|
||||
|
||||
featureFlags := &protocolCommon.FeatureFlags{}
|
||||
if config.WalletConfig.EnableCelerBridge {
|
||||
featureFlags.EnableCelerBridge = true
|
||||
}
|
||||
activity := activity.NewService(db, accountsDB, tokenManager, collectiblesManager, feed, pendingTxManager, featureFlags)
|
||||
|
||||
return &Service{
|
||||
db: db,
|
||||
|
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/status-im/status-go/services/wallet/balance"
|
||||
"github.com/status-im/status-go/services/wallet/blockchainstate"
|
||||
"github.com/status-im/status-go/services/wallet/community"
|
||||
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/t/utils"
|
||||
|
||||
|
@ -1322,7 +1323,7 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) {
|
|||
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
tm := &TransactionManager{NewMultiTransactionDB(db), nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||
tm := &TransactionManager{NewMultiTransactionDB(db), pathprocessor.NewTransactionInputDataDB(db), nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||
|
||||
mediaServer, err := server.NewMediaServer(appdb, nil, nil, db)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/status-im/status-go/services/accounts/accountsevent"
|
||||
"github.com/status-im/status-go/services/wallet/blockchainstate"
|
||||
wallet_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/walletdatabase"
|
||||
)
|
||||
|
@ -36,7 +37,7 @@ func TestController_watchAccountsChanges(t *testing.T) {
|
|||
|
||||
bcstate := blockchainstate.NewBlockChainState()
|
||||
SetMultiTransactionIDGenerator(StaticIDCounter()) // to have different multi-transaction IDs even with fast execution
|
||||
transactionManager := NewTransactionManager(NewInMemMultiTransactionStorage(), nil, nil, nil, accountsDB, nil, nil)
|
||||
transactionManager := NewTransactionManager(NewInMemMultiTransactionStorage(), NewInMemTransactionInputDataStorage(), nil, nil, nil, accountsDB, nil, nil)
|
||||
c := NewTransferController(
|
||||
walletDB,
|
||||
accountsDB,
|
||||
|
@ -239,7 +240,7 @@ func TestController_cleanupAccountLeftovers(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Len(t, storedAccs, 1)
|
||||
|
||||
transactionManager := NewTransactionManager(NewMultiTransactionDB(walletDB), nil, nil, nil, accountsDB, nil, nil)
|
||||
transactionManager := NewTransactionManager(NewMultiTransactionDB(walletDB), pathprocessor.NewTransactionInputDataDB(walletDB), nil, nil, nil, accountsDB, nil, nil)
|
||||
bcstate := blockchainstate.NewBlockChainState()
|
||||
c := NewTransferController(
|
||||
walletDB,
|
||||
|
|
|
@ -359,8 +359,23 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
|
|||
var tokenID *big.Int
|
||||
var txFrom *common.Address
|
||||
var txTo *common.Address
|
||||
var transactionTo *common.Address
|
||||
var eventLogAddress *common.Address
|
||||
if t.Transaction != nil {
|
||||
if t.Log != nil {
|
||||
eventType := w_common.GetEventType(t.Log)
|
||||
switch eventType {
|
||||
case w_common.Erc20ApprovalEventType:
|
||||
tokenAddress = &t.Log.Address
|
||||
owner, spender, value, err := w_common.ParseErc20ApprovalLog(t.Log)
|
||||
if err != nil {
|
||||
log.Error("can't parse erc20 approval log", "err", err)
|
||||
continue
|
||||
}
|
||||
txFrom = &owner
|
||||
txTo = &spender
|
||||
txValue = value
|
||||
default:
|
||||
_, tokenAddress, txFrom, txTo = w_common.ExtractTokenTransferData(t.Type, t.Log, t.Transaction)
|
||||
tokenID = t.TokenID
|
||||
// Zero tokenID can be used for ERC721 and ERC1155 transfers but when serialzed/deserialized it becomes nil
|
||||
|
@ -369,6 +384,8 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
|
|||
tokenID = big.NewInt(0)
|
||||
}
|
||||
txValue = t.TokenValue
|
||||
}
|
||||
eventLogAddress = &t.Log.Address
|
||||
} else {
|
||||
txValue = new(big.Int).Set(t.Transaction.Value())
|
||||
txFrom = &t.From
|
||||
|
@ -388,6 +405,7 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
|
|||
*txNonce = t.Transaction.Nonce()
|
||||
txSize = new(uint64)
|
||||
*txSize = t.Transaction.Size()
|
||||
transactionTo = t.Transaction.To()
|
||||
}
|
||||
|
||||
dbFields := transferDBFields{
|
||||
|
@ -426,6 +444,8 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
|
|||
tokenID: tokenID,
|
||||
txFrom: txFrom,
|
||||
txTo: txTo,
|
||||
transactionTo: transactionTo,
|
||||
eventLogAddress: eventLogAddress,
|
||||
}
|
||||
txsDBFields = append(txsDBFields, dbFields)
|
||||
}
|
||||
|
@ -469,15 +489,18 @@ type transferDBFields struct {
|
|||
tokenID *big.Int
|
||||
txFrom *common.Address
|
||||
txTo *common.Address
|
||||
transactionTo *common.Address
|
||||
eventLogAddress *common.Address
|
||||
}
|
||||
|
||||
func updateOrInsertTransfersDBFields(creator statementCreator, transfers []transferDBFields) error {
|
||||
insert, err := creator.Prepare(`INSERT OR REPLACE INTO transfers
|
||||
(network_id, hash, blk_hash, blk_number, timestamp, address, tx, sender, receipt, log, type, loaded, base_gas_fee, multi_transaction_id,
|
||||
status, receipt_type, tx_hash, log_index, block_hash, cumulative_gas_used, contract_address, gas_used, tx_index,
|
||||
tx_type, protected, gas_limit, gas_price_clamped64, gas_tip_cap_clamped64, gas_fee_cap_clamped64, amount_padded128hex, account_nonce, size, token_address, token_id, tx_from_address, tx_to_address)
|
||||
tx_type, protected, gas_limit, gas_price_clamped64, gas_tip_cap_clamped64, gas_fee_cap_clamped64, amount_padded128hex, account_nonce, size, token_address, token_id, tx_from_address, tx_to_address,
|
||||
transaction_to, event_log_address)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -489,7 +512,8 @@ func updateOrInsertTransfersDBFields(creator statementCreator, transfers []trans
|
|||
|
||||
_, err = insert.Exec(t.chainID, t.id, t.blockHash, (*bigint.SQLBigInt)(t.blockNumber), t.timestamp, t.address, &JSONBlob{t.transaction}, t.sender, &JSONBlob{t.receipt}, &JSONBlob{t.log}, t.transferType, t.baseGasFees, t.multiTransactionID,
|
||||
t.receiptStatus, t.receiptType, t.txHash, t.logIndex, t.receiptBlockHash, t.cumulativeGasUsed, t.contractAddress, t.gasUsed, t.transactionIndex,
|
||||
t.txType, t.txProtected, t.txGas, txGasPrice, txGasTipCap, txGasFeeCap, txValue, t.txNonce, t.txSize, t.tokenAddress, (*bigint.SQLBigIntBytes)(t.tokenID), t.txFrom, t.txTo)
|
||||
t.txType, t.txProtected, t.txGas, txGasPrice, txGasTipCap, txGasFeeCap, txValue, t.txNonce, t.txSize, t.tokenAddress, (*bigint.SQLBigIntBytes)(t.tokenID), t.txFrom, t.txTo,
|
||||
t.transactionTo, t.eventLogAddress)
|
||||
if err != nil {
|
||||
log.Error("can't save transfer", "b-hash", t.blockHash, "b-n", t.blockNumber, "a", t.address, "h", t.id)
|
||||
return err
|
||||
|
|
|
@ -434,7 +434,8 @@ func (d *ETHDownloader) subTransactionsFromTransactionData(address, from common.
|
|||
for _, txLog := range receipt.Logs {
|
||||
eventType := w_common.GetEventType(txLog)
|
||||
switch eventType {
|
||||
case w_common.UniswapV2SwapEventType, w_common.UniswapV3SwapEventType,
|
||||
case w_common.Erc20ApprovalEventType,
|
||||
w_common.UniswapV2SwapEventType, w_common.UniswapV3SwapEventType,
|
||||
w_common.HopBridgeTransferSentToL2EventType, w_common.HopBridgeTransferFromL1CompletedEventType,
|
||||
w_common.HopBridgeWithdrawalBondedEventType, w_common.HopBridgeTransferSentEventType:
|
||||
transfer := Transfer{
|
||||
|
|
|
@ -154,13 +154,16 @@ func updateDataFromMultiTx(data []*pathprocessor.MultipathProcessorTxArgs, multi
|
|||
}
|
||||
}
|
||||
|
||||
func sendTransactions(data []*pathprocessor.MultipathProcessorTxArgs, pathProcessors map[string]pathprocessor.PathProcessor, account *account.SelectedExtKey) (
|
||||
func sendTransactions(
|
||||
data []*pathprocessor.MultipathProcessorTxArgs,
|
||||
pathProcessors map[string]pathprocessor.PathProcessor,
|
||||
account *account.SelectedExtKey,
|
||||
txInputDataStorage TransactionInputDataStorage) (
|
||||
map[uint64][]types.Hash, error) {
|
||||
|
||||
hashes := make(map[uint64][]types.Hash)
|
||||
usedNonces := make(map[uint64]int64)
|
||||
for _, tx := range data {
|
||||
|
||||
lastUsedNonce := int64(-1)
|
||||
if nonce, ok := usedNonces[tx.ChainID]; ok {
|
||||
lastUsedNonce = nonce
|
||||
|
@ -171,6 +174,18 @@ func sendTransactions(data []*pathprocessor.MultipathProcessorTxArgs, pathProces
|
|||
return nil, err // TODO: One of transfers within transaction could have been sent. Need to notify user about it
|
||||
}
|
||||
|
||||
// Store transaction input data
|
||||
txInputData, err := pathProcessors[tx.Name].GetTransactionInputData(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if txInputData != nil {
|
||||
err = txInputDataStorage.UpsertInputData(wallet_common.ChainID(tx.ChainID), hash, *txInputData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
hashes[tx.ChainID] = append(hashes[tx.ChainID], hash)
|
||||
usedNonces[tx.ChainID] = int64(usedNonce)
|
||||
}
|
||||
|
|
|
@ -9,9 +9,12 @@ import (
|
|||
eth_common "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
eth_types "github.com/status-im/status-go/eth-node/types"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/common"
|
||||
wallet_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
||||
"github.com/status-im/status-go/services/wallet/testutils"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
|
||||
|
@ -42,6 +45,13 @@ type TestCollectibleTransfer struct {
|
|||
TestCollectible
|
||||
}
|
||||
|
||||
type TestApprove struct {
|
||||
TestTransaction
|
||||
Spender eth_common.Address // [address]
|
||||
Amount int64
|
||||
Token *token.Token
|
||||
}
|
||||
|
||||
func SeedToToken(seed int) *token.Token {
|
||||
tokenIndex := seed % len(TestTokens)
|
||||
return TestTokens[tokenIndex]
|
||||
|
@ -106,6 +116,17 @@ func generateTestCollectibleTransfer(seed int) TestCollectibleTransfer {
|
|||
return tr
|
||||
}
|
||||
|
||||
func generateTestApprove(seed int) TestApprove {
|
||||
tokenIndex := seed % len(TestTokens)
|
||||
token := TestTokens[tokenIndex]
|
||||
return TestApprove{
|
||||
TestTransaction: generateTestTransaction(seed),
|
||||
Spender: eth_common.HexToAddress(fmt.Sprintf("0x3%d", seed)),
|
||||
Amount: int64(seed),
|
||||
Token: token,
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateTestSendMultiTransaction(tr TestTransfer) MultiTransaction {
|
||||
return MultiTransaction{
|
||||
ID: multiTransactionIDGenerator(),
|
||||
|
@ -148,15 +169,15 @@ func GenerateTestBridgeMultiTransaction(fromTr, toTr TestTransfer) MultiTransact
|
|||
}
|
||||
}
|
||||
|
||||
func GenerateTestApproveMultiTransaction(tr TestTransfer) MultiTransaction {
|
||||
func GenerateTestApproveMultiTransaction(tr TestApprove) MultiTransaction {
|
||||
return MultiTransaction{
|
||||
ID: multiTransactionIDGenerator(),
|
||||
Type: MultiTransactionApprove,
|
||||
FromAddress: tr.From,
|
||||
ToAddress: tr.To,
|
||||
ToAddress: tr.Spender,
|
||||
FromAsset: tr.Token.Symbol,
|
||||
ToAsset: tr.Token.Symbol,
|
||||
FromAmount: (*hexutil.Big)(big.NewInt(tr.Value)),
|
||||
FromAmount: (*hexutil.Big)(big.NewInt(tr.Amount)),
|
||||
ToAmount: (*hexutil.Big)(big.NewInt(0)),
|
||||
Timestamp: uint64(tr.Timestamp),
|
||||
}
|
||||
|
@ -174,6 +195,17 @@ func GenerateTestTransfers(tb testing.TB, db *sql.DB, firstStartIndex int, count
|
|||
return
|
||||
}
|
||||
|
||||
// GenerateTesApproves will generate transaction based on the TestTokens index and roll over if there are more than
|
||||
// len(TestTokens) transactions
|
||||
func GenerateTestApproves(tb testing.TB, db *sql.DB, firstStartIndex int, count int) (result []TestApprove, fromAddresses []eth_common.Address) {
|
||||
for i := firstStartIndex; i < (firstStartIndex + count); i++ {
|
||||
tr := generateTestApprove(i)
|
||||
fromAddresses = append(fromAddresses, tr.From)
|
||||
result = append(result, tr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type TestCollectible struct {
|
||||
TokenAddress eth_common.Address
|
||||
TokenID *big.Int
|
||||
|
@ -361,6 +393,14 @@ func InsertTestTransferWithOptions(tb testing.TB, db *sql.DB, address eth_common
|
|||
}
|
||||
}
|
||||
|
||||
var transactionTo *eth_common.Address
|
||||
if opt.Tx != nil {
|
||||
transactionTo = opt.Tx.To()
|
||||
}
|
||||
var eventLogAddress *eth_common.Address
|
||||
if opt.Receipt != nil {
|
||||
eventLogAddress = &opt.Receipt.Logs[0].Address
|
||||
}
|
||||
transfer := transferDBFields{
|
||||
chainID: uint64(tr.ChainID),
|
||||
id: tr.Hash,
|
||||
|
@ -383,6 +423,8 @@ func InsertTestTransferWithOptions(tb testing.TB, db *sql.DB, address eth_common
|
|||
tokenID: opt.TokenID,
|
||||
transaction: opt.Tx,
|
||||
receipt: opt.Receipt,
|
||||
transactionTo: transactionTo,
|
||||
eventLogAddress: eventLogAddress,
|
||||
}
|
||||
err = updateOrInsertTransfersDBFields(tx, []transferDBFields{transfer})
|
||||
require.NoError(tb, err)
|
||||
|
@ -391,9 +433,9 @@ func InsertTestTransferWithOptions(tb testing.TB, db *sql.DB, address eth_common
|
|||
func InsertTestPendingTransaction(tb testing.TB, db *sql.DB, tr *TestTransfer) {
|
||||
_, err := db.Exec(`
|
||||
INSERT INTO pending_transactions (network_id, hash, timestamp, from_address, to_address,
|
||||
symbol, gas_price, gas_limit, value, data, type, additional_data, multi_transaction_id
|
||||
) VALUES (?, ?, ?, ?, ?, 'ETH', 0, 0, ?, '', 'eth', '', ?)`,
|
||||
tr.ChainID, tr.Hash, tr.Timestamp, tr.From, tr.To, (*bigint.SQLBigIntBytes)(big.NewInt(tr.Value)), tr.MultiTransactionID)
|
||||
symbol, gas_price, gas_limit, value, data, type, additional_data, multi_transaction_id, transaction_to,
|
||||
) VALUES (?, ?, ?, ?, ?, 'ETH', 0, 0, ?, '', 'eth', '', ?, ?)`,
|
||||
tr.ChainID, tr.Hash, tr.Timestamp, tr.From, tr.To, (*bigint.SQLBigIntBytes)(big.NewInt(tr.Value)), tr.MultiTransactionID, tr.Token.Address)
|
||||
require.NoError(tb, err)
|
||||
}
|
||||
|
||||
|
@ -499,3 +541,19 @@ func (s *InMemMultiTransactionStorage) ReadMultiTransactions(details *MultiTxDet
|
|||
}
|
||||
return multiTxs, nil
|
||||
}
|
||||
|
||||
type InMemTransactionInputDataStorage struct {
|
||||
storage map[string]*pathprocessor.TransactionInputData
|
||||
}
|
||||
|
||||
func NewInMemTransactionInputDataStorage() *InMemTransactionInputDataStorage {
|
||||
return &InMemTransactionInputDataStorage{
|
||||
storage: make(map[string]*pathprocessor.TransactionInputData),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *InMemTransactionInputDataStorage) UpsertInputData(chainID wallet_common.ChainID, txHash eth_types.Hash, inputData pathprocessor.TransactionInputData) error {
|
||||
key := fmt.Sprintf("%d-%s", chainID, txHash.String())
|
||||
s.storage[key] = &inputData
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ type TransactionDescription struct {
|
|||
|
||||
type TransactionManager struct {
|
||||
storage MultiTransactionStorage
|
||||
txInputDataStorage TransactionInputDataStorage
|
||||
gethManager *account.GethManager
|
||||
transactor transactions.TransactorIface
|
||||
config *params.NodeConfig
|
||||
|
@ -54,8 +55,13 @@ type MultiTransactionStorage interface {
|
|||
DeleteMultiTransaction(id wallet_common.MultiTransactionIDType) error
|
||||
}
|
||||
|
||||
type TransactionInputDataStorage interface {
|
||||
UpsertInputData(chainID wallet_common.ChainID, txHash types.Hash, inputData pathprocessor.TransactionInputData) error
|
||||
}
|
||||
|
||||
func NewTransactionManager(
|
||||
storage MultiTransactionStorage,
|
||||
txInputDataStorage TransactionInputDataStorage,
|
||||
gethManager *account.GethManager,
|
||||
transactor transactions.TransactorIface,
|
||||
config *params.NodeConfig,
|
||||
|
@ -65,6 +71,7 @@ func NewTransactionManager(
|
|||
) *TransactionManager {
|
||||
return &TransactionManager{
|
||||
storage: storage,
|
||||
txInputDataStorage: txInputDataStorage,
|
||||
gethManager: gethManager,
|
||||
transactor: transactor,
|
||||
config: config,
|
||||
|
|
|
@ -90,7 +90,7 @@ func (tm *TransactionManager) SendTransactionForSigningToKeycard(ctx context.Con
|
|||
|
||||
func (tm *TransactionManager) SendTransactions(ctx context.Context, multiTransaction *MultiTransaction, data []*pathprocessor.MultipathProcessorTxArgs, pathProcessors map[string]pathprocessor.PathProcessor, account *account.SelectedExtKey) (*MultiTransactionCommandResult, error) {
|
||||
updateDataFromMultiTx(data, multiTransaction)
|
||||
hashes, err := sendTransactions(data, pathProcessors, account)
|
||||
hashes, err := sendTransactions(data, pathProcessors, account, tm.txInputDataStorage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ func setupTransactionManager(t *testing.T) (*TransactionManager, *mock_transacto
|
|||
// Create a mock transactor
|
||||
transactor := mock_transactor.NewMockTransactorIface(ctrl)
|
||||
// Create a new instance of the TransactionManager
|
||||
tm := NewTransactionManager(NewInMemMultiTransactionStorage(), nil, transactor, nil, nil, nil, nil)
|
||||
tm := NewTransactionManager(NewInMemMultiTransactionStorage(), NewInMemTransactionInputDataStorage(), nil, transactor, nil, nil, nil, nil)
|
||||
|
||||
return tm, transactor, ctrl
|
||||
}
|
||||
|
|
|
@ -343,7 +343,7 @@ func (tm *PendingTxTracker) emitNotifications(chainID common.ChainID, changes []
|
|||
}
|
||||
|
||||
// PendingTransaction called with autoDelete = false will keep the transaction in the database until it is confirmed by the caller using Delete
|
||||
func (tm *PendingTxTracker) TrackPendingTransaction(chainID common.ChainID, hash eth.Hash, from eth.Address, to eth.Address, trType PendingTrxType, autoDelete AutoDeleteType, additionalData string) error {
|
||||
func (tm *PendingTxTracker) TrackPendingTransaction(chainID common.ChainID, hash eth.Hash, from eth.Address, to eth.Address, trType PendingTrxType, autoDelete AutoDeleteType, additionalData string, transactionTo *eth.Address) error {
|
||||
err := tm.addPending(&PendingTransaction{
|
||||
ChainID: chainID,
|
||||
Hash: hash,
|
||||
|
@ -353,6 +353,7 @@ func (tm *PendingTxTracker) TrackPendingTransaction(chainID common.ChainID, hash
|
|||
Type: trType,
|
||||
AutoDelete: &autoDelete,
|
||||
AdditionalData: additionalData,
|
||||
TransactionTo: transactionTo,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -419,6 +420,7 @@ type PendingTransaction struct {
|
|||
GasLimit bigint.BigInt `json:"gasLimit"`
|
||||
Type PendingTrxType `json:"type"`
|
||||
AdditionalData string `json:"additionalData"`
|
||||
TransactionTo *eth.Address `json:"transactionTo"`
|
||||
ChainID common.ChainID `json:"network_id"`
|
||||
MultiTransactionID wallet_common.MultiTransactionIDType `json:"multi_transaction_id"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
|
@ -430,7 +432,7 @@ type PendingTransaction struct {
|
|||
}
|
||||
|
||||
const selectFromPending = `SELECT hash, timestamp, value, from_address, to_address, data,
|
||||
symbol, gas_price, gas_limit, type, additional_data,
|
||||
symbol, gas_price, gas_limit, type, additional_data, transaction_to,
|
||||
network_id, COALESCE(multi_transaction_id, 0), status, auto_delete, nonce
|
||||
FROM pending_transactions
|
||||
`
|
||||
|
@ -456,6 +458,7 @@ func rowsToTransactions(rows *sql.Rows) (transactions []*PendingTransaction, err
|
|||
(*bigint.SQLBigIntBytes)(transaction.GasLimit.Int),
|
||||
&transaction.Type,
|
||||
&transaction.AdditionalData,
|
||||
&transaction.TransactionTo,
|
||||
&transaction.ChainID,
|
||||
&transaction.MultiTransactionID,
|
||||
transaction.Status,
|
||||
|
@ -608,10 +611,10 @@ func (tm *PendingTxTracker) addPending(transaction *PendingTransaction) error {
|
|||
var insert *sql.Stmt
|
||||
insert, err = tx.Prepare(`INSERT OR REPLACE INTO pending_transactions
|
||||
(network_id, hash, timestamp, value, from_address, to_address,
|
||||
data, symbol, gas_price, gas_limit, type, additional_data, multi_transaction_id, status,
|
||||
data, symbol, gas_price, gas_limit, type, additional_data, transaction_to, multi_transaction_id, status,
|
||||
auto_delete, nonce)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? , ?, ?)`)
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? , ?, ?, ?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -630,6 +633,7 @@ func (tm *PendingTxTracker) addPending(transaction *PendingTransaction) error {
|
|||
(*bigint.SQLBigIntBytes)(transaction.GasLimit.Int),
|
||||
transaction.Type,
|
||||
transaction.AdditionalData,
|
||||
transaction.TransactionTo,
|
||||
transaction.MultiTransactionID,
|
||||
transaction.Status,
|
||||
transaction.AutoDelete,
|
||||
|
|
|
@ -302,7 +302,7 @@ func TestPendingTxTracker_MultipleClients(t *testing.T) {
|
|||
sub := eventFeed.Subscribe(eventChan)
|
||||
|
||||
for i := range txs {
|
||||
err := m.TrackPendingTransaction(txs[i].ChainID, txs[i].Hash, txs[i].From, txs[i].To, txs[i].Type, AutoDelete, "")
|
||||
err := m.TrackPendingTransaction(txs[i].ChainID, txs[i].Hash, txs[i].From, txs[i].To, txs[i].Type, AutoDelete, "", txs[i].TransactionTo)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
@ -377,7 +377,7 @@ func TestPendingTxTracker_Watch(t *testing.T) {
|
|||
sub := eventFeed.Subscribe(eventChan)
|
||||
|
||||
// Track the first transaction
|
||||
err := m.TrackPendingTransaction(txs[1].ChainID, txs[1].Hash, txs[1].From, txs[1].To, txs[1].Type, Keep, "")
|
||||
err := m.TrackPendingTransaction(txs[1].ChainID, txs[1].Hash, txs[1].From, txs[1].To, txs[1].Type, Keep, "", txs[1].TransactionTo)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Store the confirmed already
|
||||
|
@ -476,7 +476,7 @@ func TestPendingTxTracker_Watch_StatusChangeIncrementally(t *testing.T) {
|
|||
|
||||
for i := range txs {
|
||||
// Track the first transaction
|
||||
err := m.TrackPendingTransaction(txs[i].ChainID, txs[i].Hash, txs[i].From, txs[i].To, txs[i].Type, Keep, "")
|
||||
err := m.TrackPendingTransaction(txs[i].ChainID, txs[i].Hash, txs[i].From, txs[i].To, txs[i].Type, Keep, "", txs[i].TransactionTo)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -67,12 +67,14 @@ func GenerateTestPendingTransactions(start int, count int) []PendingTransaction
|
|||
|
||||
txs := make([]PendingTransaction, count)
|
||||
for i := start; i < count; i++ {
|
||||
transactionTo := eth.HexToAddress(fmt.Sprintf("0x4%d", i))
|
||||
txs[i] = PendingTransaction{
|
||||
Hash: eth.HexToHash(fmt.Sprintf("0x1%d", i)),
|
||||
From: eth.HexToAddress(fmt.Sprintf("0x2%d", i)),
|
||||
To: eth.HexToAddress(fmt.Sprintf("0x3%d", i)),
|
||||
Type: RegisterENS,
|
||||
AdditionalData: "someuser.stateofus.eth",
|
||||
TransactionTo: &transactionTo,
|
||||
Value: bigint.BigInt{Int: big.NewInt(int64(i))},
|
||||
GasLimit: bigint.BigInt{Int: big.NewInt(21000)},
|
||||
GasPrice: bigint.BigInt{Int: big.NewInt(int64(i))},
|
||||
|
|
|
@ -197,6 +197,7 @@ func createPendingTransaction(from common.Address, symbol string, chainID uint64
|
|||
MultiTransactionID: multiTransactionID,
|
||||
Symbol: symbol,
|
||||
AutoDelete: new(bool),
|
||||
TransactionTo: tx.To(),
|
||||
}
|
||||
// Transaction downloader will delete pending transaction as soon as it is confirmed
|
||||
*pTx.AutoDelete = false
|
||||
|
|
|
@ -2,11 +2,19 @@ package walletdatabase
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/status-im/status-go/sqlite"
|
||||
"github.com/status-im/status-go/walletdatabase/migrations"
|
||||
)
|
||||
|
||||
const (
|
||||
batchSize = 1000
|
||||
)
|
||||
|
||||
type DbInitializer struct {
|
||||
}
|
||||
|
||||
|
@ -14,7 +22,9 @@ func (a DbInitializer) Initialize(path, password string, kdfIterationsNumber int
|
|||
return InitializeDB(path, password, kdfIterationsNumber)
|
||||
}
|
||||
|
||||
var walletCustomSteps = []*sqlite.PostStep{}
|
||||
var walletCustomSteps = []*sqlite.PostStep{
|
||||
{Version: 1721166023, CustomMigration: migrateWalletTransactionToAndEventLogAddress, RollBackVersion: 1720206965},
|
||||
}
|
||||
|
||||
func doMigration(db *sql.DB) error {
|
||||
// Run all the new migrations
|
||||
|
@ -35,3 +45,105 @@ func InitializeDB(path, password string, kdfIterationsNumber int) (*sql.DB, erro
|
|||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func migrateWalletTransactionToAndEventLogAddress(sqlTx *sql.Tx) error {
|
||||
var batchEntries [][]interface{}
|
||||
|
||||
// Extract Transaction To addresses and Event Log Address and
|
||||
// add the information into the new columns
|
||||
newColumnsAndIndexSetup := `
|
||||
ALTER TABLE transfers ADD COLUMN transaction_to BLOB;
|
||||
ALTER TABLE transfers ADD COLUMN event_log_address BLOB;`
|
||||
|
||||
rowIndex := 0
|
||||
mightHaveRows := true
|
||||
|
||||
_, err := sqlTx.Exec(newColumnsAndIndexSetup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for mightHaveRows {
|
||||
var chainID uint64
|
||||
var hash common.Hash
|
||||
var address common.Address
|
||||
|
||||
rows, err := sqlTx.Query(`SELECT hash, address, network_id, tx, log FROM transfers WHERE tx IS NOT NULL LIMIT ? OFFSET ?`, batchSize, rowIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curProcessed := 0
|
||||
for rows.Next() {
|
||||
tx := &types.Transaction{}
|
||||
l := &types.Log{}
|
||||
|
||||
// Scan row data into the transaction and log objects
|
||||
nullableTx := sqlite.JSONBlob{Data: tx}
|
||||
nullableL := sqlite.JSONBlob{Data: l}
|
||||
err = rows.Scan(&hash, &address, &chainID, &nullableTx, &nullableL)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "missing required field") {
|
||||
// Some Arb and Opt transaction types don't contain all required fields
|
||||
continue
|
||||
}
|
||||
rows.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
var currentRow []interface{}
|
||||
|
||||
var transactionTo *common.Address
|
||||
var eventLogAddress *common.Address
|
||||
|
||||
if nullableTx.Valid {
|
||||
transactionTo = tx.To()
|
||||
currentRow = append(currentRow, transactionTo)
|
||||
} else {
|
||||
currentRow = append(currentRow, nil)
|
||||
}
|
||||
|
||||
if nullableL.Valid {
|
||||
eventLogAddress = &l.Address
|
||||
currentRow = append(currentRow, eventLogAddress)
|
||||
} else {
|
||||
currentRow = append(currentRow, nil)
|
||||
}
|
||||
|
||||
currentRow = append(currentRow, hash, address, chainID)
|
||||
batchEntries = append(batchEntries, currentRow)
|
||||
|
||||
curProcessed++
|
||||
}
|
||||
rowIndex += curProcessed
|
||||
|
||||
// Check if there was an error in the last rows.Next()
|
||||
rows.Close()
|
||||
if err = rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
mightHaveRows = (curProcessed == batchSize)
|
||||
|
||||
// insert extracted data into the new columns
|
||||
if len(batchEntries) > 0 {
|
||||
var stmt *sql.Stmt
|
||||
stmt, err = sqlTx.Prepare(`UPDATE transfers SET transaction_to = ?, event_log_address = ?
|
||||
WHERE hash = ? AND address = ? AND network_id = ?`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, dataEntry := range batchEntries {
|
||||
_, err = stmt.Exec(dataEntry...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Reset placeHolders and batchEntries for the next batch
|
||||
batchEntries = [][]interface{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
package walletdatabase
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/status-im/status-go/common/dbsetup"
|
||||
w_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/sqlite"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/walletdatabase/migrations"
|
||||
)
|
||||
|
||||
const (
|
||||
erc20ReceiptTestDataTemplate = `{"type":"0x2","root":"0x","status":"0x%d","cumulativeGasUsed":"0x10f8d2c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000001008000000000000000000000000000000000000002000000000020000000000000000000800000000000000000000000010000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000800000000000000000000","logs":[{"address":"0x98339d8c260052b7ad81c28c16c0b98420f2b46a","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000e2d622c817878da5143bbe06866ca8e35273ba8a"],"data":"0x0000000000000000000000000000000000000000000000000000000000989680","blockNumber":"0x825527","transactionHash":"0xdcaa0fc7fe2e0d1f1343d1f36807344bb4fd26cda62ad8f9d8700e2c458cc79a","transactionIndex":"0x6c","blockHash":"0x69e0f829a557052c134cd7e21c220507d91bc35c316d3c47217e9bd362270274","logIndex":"0xcd","removed":false}],"transactionHash":"0xdcaa0fc7fe2e0d1f1343d1f36807344bb4fd26cda62ad8f9d8700e2c458cc79a","contractAddress":"0x0000000000000000000000000000000000000000","gasUsed":"0x8623","blockHash":"0x69e0f829a557052c134cd7e21c220507d91bc35c316d3c47217e9bd362270274","blockNumber":"0x825527","transactionIndex":"0x6c"}`
|
||||
erc20TxTestData = `{"type":"0x2","nonce":"0x3d","gasPrice":"0x0","maxPriorityFeePerGas":"0x8c347c90","maxFeePerGas":"0x45964d43a4","gas":"0x8623","value":"0x0","input":"0x40c10f19000000000000000000000000e2d622c817878da5143bbe06866ca8e35273ba8a0000000000000000000000000000000000000000000000000000000000989680","v":"0x0","r":"0xbcac4bb290d48b467bb18ac67e98050b5f316d2c66b2f75dcc1d63a45c905d21","s":"0x10c15517ea9cabd7fe134b270daabf5d2e8335e935d3e021f54a4efaffb37cd2","to":"0x98339d8c260052b7ad81c28c16c0b98420f2b46a","chainId":"0x5","accessList":[],"hash":"0xdcaa0fc7fe2e0d1f1343d1f36807344bb4fd26cda62ad8f9d8700e2c458cc79a"}`
|
||||
erc20LogTestData = `{"address":"0x98339d8c260052b7ad81c28c16c0b98420f2b46a","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000e2d622c817878da5143bbe06866ca8e35273ba8a"],"data":"0x0000000000000000000000000000000000000000000000000000000000989680","blockNumber":"0x825527","transactionHash":"0xdcaa0fc7fe2e0d1f1343d1f36807344bb4fd26cda62ad8f9d8700e2c458cc79a","transactionIndex":"0x6c","blockHash":"0x69e0f829a557052c134cd7e21c220507d91bc35c316d3c47217e9bd362270274","logIndex":"0xcd","removed":false}`
|
||||
|
||||
ethReceiptTestData = `{
|
||||
"type": "0x2",
|
||||
"root": "0x",
|
||||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x2b461",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": [],
|
||||
"transactionHash": "0x4ac700ee2a1702f82b3cfdc88fd4d91f767b87fea9b929bd6223c6471a5e05b4",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x5208",
|
||||
"blockHash": "0x25fe164361c1cb4ed1b46996f7b5236d3118144529b31fca037fcda1d8ee684d",
|
||||
"blockNumber": "0x5e3294",
|
||||
"transactionIndex": "0x3"
|
||||
}`
|
||||
ethTxTestData = `{
|
||||
"type": "0x2",
|
||||
"nonce": "0x1",
|
||||
"gasPrice": "0x0",
|
||||
"maxPriorityFeePerGas": "0x33",
|
||||
"maxFeePerGas": "0x3b9aca00",
|
||||
"gas": "0x55f0",
|
||||
"value": "0x%s",
|
||||
"input": "0x",
|
||||
"v": "0x0",
|
||||
"r": "0xacc277ce156382d6f333cc8d75a56250778b17f1c6d1676af63cf68d53713986",
|
||||
"s": "0x32417261484e9796390abb8db13f993965d917836be5cd96df25b9b581de91ec",
|
||||
"to": "0xbd54a96c0ae19a220c8e1234f54c940dfab34639",
|
||||
"chainId": "0x1a4",
|
||||
"accessList": [],
|
||||
"hash": "0x4ac700ee2a1702f82b3cfdc88fd4d91f767b87fea9b929bd6223c6471a5e05b4"
|
||||
}`
|
||||
)
|
||||
|
||||
func TestMigrateWalletTransactionToAndEventLogAddress(t *testing.T) {
|
||||
openDB := func() (*sql.DB, error) {
|
||||
return sqlite.OpenDB(sqlite.InMemoryPath, "1234567890", dbsetup.ReducedKDFIterationsNumber)
|
||||
}
|
||||
db, err := openDB()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Migrate until before custom step
|
||||
err = migrations.MigrateTo(db, walletCustomSteps, 1720206965)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Validate that transfers table has no status column
|
||||
exists, err := helpers.ColumnExists(db, "transfers", "transaction_to")
|
||||
require.NoError(t, err)
|
||||
require.False(t, exists)
|
||||
|
||||
exists, err = helpers.ColumnExists(db, "transfers", "event_log_address")
|
||||
require.NoError(t, err)
|
||||
require.False(t, exists)
|
||||
|
||||
insertTestTransaction := func(index int, txBlob string, receiptBlob string, logBlob string, ethType bool) error {
|
||||
indexStr := strconv.Itoa(index)
|
||||
senderStr := strconv.Itoa(index + 1)
|
||||
|
||||
var txValue *string
|
||||
if txBlob != "" {
|
||||
txValue = &txBlob
|
||||
}
|
||||
var receiptValue *string
|
||||
if receiptBlob != "" {
|
||||
receiptValue = &receiptBlob
|
||||
}
|
||||
var logValue *string
|
||||
if logBlob != "" {
|
||||
logValue = &logBlob
|
||||
}
|
||||
entryType := "eth"
|
||||
if !ethType {
|
||||
entryType = "erc20"
|
||||
}
|
||||
_, err = db.Exec(`INSERT OR IGNORE INTO blocks(network_id, address, blk_number, blk_hash) VALUES (?, ?, ?, ?);
|
||||
INSERT INTO transfers (hash, address, sender, network_id, tx, receipt, log, blk_hash, type, blk_number, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
index, common.HexToAddress(indexStr), index, common.HexToHash(indexStr),
|
||||
common.HexToHash(indexStr), common.HexToAddress(indexStr), common.HexToAddress(senderStr), index, txValue, receiptValue, logValue, common.HexToHash(indexStr), entryType, index, index)
|
||||
return err
|
||||
}
|
||||
|
||||
// Empty transaction, found the usecase in the test DB
|
||||
erc20FailReceiptJSON := fmt.Sprintf(erc20ReceiptTestDataTemplate, 0)
|
||||
erc20SuccessReceiptJSON := fmt.Sprintf(erc20ReceiptTestDataTemplate, 1)
|
||||
err = insertTestTransaction(2, erc20TxTestData, erc20FailReceiptJSON, erc20LogTestData, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = insertTestTransaction(3, erc20TxTestData, erc20SuccessReceiptJSON, erc20LogTestData, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
ethZeroValueTxTestData := fmt.Sprintf(ethTxTestData, "0")
|
||||
ethVeryBigValueTxTestData := fmt.Sprintf(ethTxTestData, "12345678901234567890")
|
||||
ethOriginalTxTestData := fmt.Sprintf(ethTxTestData, "2386f26fc10000")
|
||||
|
||||
err = insertTestTransaction(4, ethZeroValueTxTestData, ethReceiptTestData, "", true)
|
||||
require.NoError(t, err)
|
||||
err = insertTestTransaction(5, ethVeryBigValueTxTestData, "", "", true)
|
||||
require.NoError(t, err)
|
||||
err = insertTestTransaction(6, ethOriginalTxTestData, ethReceiptTestData, "", true)
|
||||
require.NoError(t, err)
|
||||
|
||||
failMigrationSteps := []*sqlite.PostStep{
|
||||
{
|
||||
Version: walletCustomSteps[0].Version,
|
||||
CustomMigration: func(sqlTx *sql.Tx) error {
|
||||
return errors.New("failed to run custom migration")
|
||||
},
|
||||
RollBackVersion: walletCustomSteps[0].RollBackVersion,
|
||||
},
|
||||
}
|
||||
|
||||
// Attempt to run test migration 1721166023 and fail in custom step
|
||||
err = migrations.MigrateTo(db, failMigrationSteps, walletCustomSteps[0].Version)
|
||||
require.Error(t, err)
|
||||
|
||||
exists, err = helpers.ColumnExists(db, "transfers", "transaction_to")
|
||||
require.NoError(t, err)
|
||||
require.False(t, exists)
|
||||
|
||||
// Run test migration 1721166023_extract_tx_to_and_event_log_address.<up/down>.sql
|
||||
err = migrations.MigrateTo(db, walletCustomSteps, walletCustomSteps[0].Version)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Validate that the migration was run and transfers table has now new columns
|
||||
exists, err = helpers.ColumnExists(db, "transfers", "transaction_to")
|
||||
require.NoError(t, err)
|
||||
require.True(t, exists)
|
||||
|
||||
exists, err = helpers.ColumnExists(db, "transfers", "event_log_address")
|
||||
require.NoError(t, err)
|
||||
require.True(t, exists)
|
||||
|
||||
var (
|
||||
transactionTo sql.RawBytes
|
||||
eventLogAddress sql.RawBytes
|
||||
)
|
||||
|
||||
rows, err := db.Query(`SELECT transaction_to, event_log_address
|
||||
FROM transfers ORDER BY timestamp ASC`)
|
||||
require.NoError(t, err)
|
||||
|
||||
scanNextData := func() error {
|
||||
rows.Next()
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
}
|
||||
err := rows.Scan(&transactionTo, &eventLogAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
validateTransaction := func(tt *types.Transaction, expectedEntryType w_common.Type, tl *types.Log) {
|
||||
if tt == nil {
|
||||
require.Empty(t, transactionTo)
|
||||
require.Empty(t, eventLogAddress)
|
||||
} else {
|
||||
if expectedEntryType == w_common.EthTransfer {
|
||||
require.NotEmpty(t, transactionTo)
|
||||
parsedTransactionTo := common.BytesToAddress(transactionTo)
|
||||
require.Equal(t, *tt.To(), parsedTransactionTo)
|
||||
} else {
|
||||
require.NotEmpty(t, transactionTo)
|
||||
parsedTransactionTo := common.BytesToAddress(transactionTo)
|
||||
require.Equal(t, *tt.To(), parsedTransactionTo)
|
||||
|
||||
require.NotEmpty(t, eventLogAddress)
|
||||
parsedEventLogAddress := common.BytesToAddress(eventLogAddress)
|
||||
require.Equal(t, tl.Address, parsedEventLogAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var successReceipt types.Receipt
|
||||
err = json.Unmarshal([]byte(erc20SuccessReceiptJSON), &successReceipt)
|
||||
require.NoError(t, err)
|
||||
|
||||
var failReceipt types.Receipt
|
||||
err = json.Unmarshal([]byte(erc20FailReceiptJSON), &failReceipt)
|
||||
require.NoError(t, err)
|
||||
|
||||
var erc20Log types.Log
|
||||
err = json.Unmarshal([]byte(erc20LogTestData), &erc20Log)
|
||||
require.NoError(t, err)
|
||||
|
||||
var erc20Tx types.Transaction
|
||||
err = json.Unmarshal([]byte(erc20TxTestData), &erc20Tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = scanNextData()
|
||||
require.NoError(t, err)
|
||||
validateTransaction(&erc20Tx, w_common.Erc20Transfer, &erc20Log)
|
||||
|
||||
err = scanNextData()
|
||||
require.NoError(t, err)
|
||||
validateTransaction(&erc20Tx, w_common.Erc20Transfer, &erc20Log)
|
||||
|
||||
var zeroTestTx types.Transaction
|
||||
err = json.Unmarshal([]byte(ethZeroValueTxTestData), &zeroTestTx)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ethReceipt types.Receipt
|
||||
err = json.Unmarshal([]byte(ethReceiptTestData), ðReceipt)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = scanNextData()
|
||||
require.NoError(t, err)
|
||||
validateTransaction(&zeroTestTx, w_common.EthTransfer, nil)
|
||||
|
||||
var bigTestTx types.Transaction
|
||||
err = json.Unmarshal([]byte(ethVeryBigValueTxTestData), &bigTestTx)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = scanNextData()
|
||||
require.NoError(t, err)
|
||||
validateTransaction(&bigTestTx, w_common.EthTransfer, nil)
|
||||
|
||||
var ethOriginalTestTx types.Transaction
|
||||
err = json.Unmarshal([]byte(ethOriginalTxTestData), ðOriginalTestTx)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = scanNextData()
|
||||
require.NoError(t, err)
|
||||
validateTransaction(ðOriginalTestTx, w_common.EthTransfer, nil)
|
||||
|
||||
err = scanNextData()
|
||||
// Validate that we processed all data (no more rows expected)
|
||||
require.Error(t, err)
|
||||
|
||||
db.Close()
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
-- transaction_input_data table stores data for each transaction generated by the wallet
|
||||
CREATE TABLE IF NOT EXISTS transaction_input_data (
|
||||
chain_id UNSIGNED BIGINT NOT NULL,
|
||||
tx_hash BLOB NOT NULL,
|
||||
processor_name TEXT NOT NULL,
|
||||
from_asset TEXT,
|
||||
from_amount BLOB,
|
||||
to_asset TEXT,
|
||||
to_amount BLOB,
|
||||
side INTEGER,
|
||||
slippage_bps INTEGER,
|
||||
approval_amount BLOB,
|
||||
approval_spender BLOB
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS transaction_input_data_identify_entry ON transaction_input_data (chain_id, tx_hash);
|
||||
|
||||
-- ALTER TABLE transfers ADD COLUMN approval_amount BLOB;
|
||||
-- ALTER TABLE transfers ADD COLUMN approval_spender BLOB;
|
|
@ -0,0 +1 @@
|
|||
-- This migration is in GO code. If GO migration fails this entry serves as rollback to the previous one
|
|
@ -0,0 +1,13 @@
|
|||
-- This migration is done in GO code as a custom step.
|
||||
-- This file serves as an anchor for the migration system
|
||||
-- Check migrateWalletTransactionToAndEventLogAddress from walletdatabase/database.go
|
||||
|
||||
-- The following steps are done in GO code:
|
||||
|
||||
-- ALTER TABLE transfers ADD COLUMN transaction_to BLOB;
|
||||
-- ALTER TABLE transfers ADD COLUMN event_log_address BLOB;
|
||||
|
||||
-- Extract the following:
|
||||
-- 1) Transaction To field (Receiver address for ETH transfers or
|
||||
-- address of the contract interacted with)
|
||||
-- 2) Event Log Address (Address of the contract that emitted the event)
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE pending_transactions ADD COLUMN transaction_to BLOB;
|
Loading…
Reference in New Issue