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.DeployCommunityToken,
|
||||||
transactions.Keep,
|
transactions.Keep,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
@ -242,6 +243,7 @@ func (api *API) DeployOwnerToken(ctx context.Context, chainID uint64,
|
||||||
transactions.DeployOwnerToken,
|
transactions.DeployOwnerToken,
|
||||||
transactions.Keep,
|
transactions.Keep,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
@ -305,6 +307,7 @@ func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentPara
|
||||||
transactions.DeployCommunityToken,
|
transactions.DeployCommunityToken,
|
||||||
transactions.Keep,
|
transactions.Keep,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
@ -402,6 +405,7 @@ func (api *API) MintTokens(ctx context.Context, chainID uint64, contractAddress
|
||||||
transactions.AirdropCommunityToken,
|
transactions.AirdropCommunityToken,
|
||||||
transactions.Keep,
|
transactions.Keep,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
@ -469,6 +473,7 @@ func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress
|
||||||
transactions.RemoteDestructCollectible,
|
transactions.RemoteDestructCollectible,
|
||||||
transactions.Keep,
|
transactions.Keep,
|
||||||
additionalData,
|
additionalData,
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
@ -521,6 +526,7 @@ func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string
|
||||||
transactions.BurnCommunityToken,
|
transactions.BurnCommunityToken,
|
||||||
transactions.Keep,
|
transactions.Keep,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
|
|
@ -526,6 +526,7 @@ func (s *Service) SetSignerPubKey(ctx context.Context, chainID uint64, contractA
|
||||||
transactions.SetSignerPublicKey,
|
transactions.SetSignerPublicKey,
|
||||||
transactions.Keep,
|
transactions.Keep,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
@ -711,17 +712,19 @@ func (s *Service) ReTrackOwnerTokenDeploymentTransaction(ctx context.Context, ch
|
||||||
transactionType = transactions.DeployOwnerToken
|
transactionType = transactions.DeployOwnerToken
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deployerAddress := common.HexToAddress(communityToken.Deployer)
|
||||||
_, err = s.pendingTracker.GetPendingEntry(wcommon.ChainID(chainID), common.HexToHash(hashString))
|
_, err = s.pendingTracker.GetPendingEntry(wcommon.ChainID(chainID), common.HexToHash(hashString))
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
// start only if no pending transaction in database
|
// start only if no pending transaction in database
|
||||||
err = s.pendingTracker.TrackPendingTransaction(
|
err = s.pendingTracker.TrackPendingTransaction(
|
||||||
wcommon.ChainID(chainID),
|
wcommon.ChainID(chainID),
|
||||||
common.HexToHash(hashString),
|
common.HexToHash(hashString),
|
||||||
common.HexToAddress(communityToken.Deployer),
|
deployerAddress,
|
||||||
common.Address{},
|
common.Address{},
|
||||||
transactionType,
|
transactionType,
|
||||||
transactions.Keep,
|
transactions.Keep,
|
||||||
"",
|
"",
|
||||||
|
&deployerAddress,
|
||||||
)
|
)
|
||||||
log.Debug("retracking pending transaction with hashId ", hashString)
|
log.Debug("retracking pending transaction with hashId ", hashString)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -361,6 +361,7 @@ func (api *API) Release(ctx context.Context, chainID uint64, txArgs transactions
|
||||||
transactions.ReleaseENS,
|
transactions.ReleaseENS,
|
||||||
transactions.AutoDelete,
|
transactions.AutoDelete,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
@ -490,6 +491,7 @@ func (api *API) Register(ctx context.Context, chainID uint64, txArgs transaction
|
||||||
transactions.RegisterENS,
|
transactions.RegisterENS,
|
||||||
transactions.AutoDelete,
|
transactions.AutoDelete,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
@ -608,6 +610,7 @@ func (api *API) SetPubKey(ctx context.Context, chainID uint64, txArgs transactio
|
||||||
transactions.SetPubKey,
|
transactions.SetPubKey,
|
||||||
transactions.AutoDelete,
|
transactions.AutoDelete,
|
||||||
"",
|
"",
|
||||||
|
tx.To(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("TrackPendingTransaction error", "error", err)
|
log.Error("TrackPendingTransaction error", "error", err)
|
||||||
|
|
|
@ -67,8 +67,9 @@ type Entry struct {
|
||||||
chainIDOut *common.ChainID
|
chainIDOut *common.ChainID
|
||||||
chainIDIn *common.ChainID
|
chainIDIn *common.ChainID
|
||||||
transferType *TransferType
|
transferType *TransferType
|
||||||
contractAddress *eth.Address
|
deployedContractAddress *eth.Address
|
||||||
communityID *string
|
communityID *string
|
||||||
|
interactedContractAddress *eth.Address
|
||||||
|
|
||||||
isNew bool // isNew is used to indicate if the entry is newer than session start (changed state also)
|
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"`
|
ChainIDOut *common.ChainID `json:"chainIdOut,omitempty"`
|
||||||
ChainIDIn *common.ChainID `json:"chainIdIn,omitempty"`
|
ChainIDIn *common.ChainID `json:"chainIdIn,omitempty"`
|
||||||
TransferType *TransferType `json:"transferType,omitempty"`
|
TransferType *TransferType `json:"transferType,omitempty"`
|
||||||
ContractAddress *eth.Address `json:"contractAddress,omitempty"`
|
DeployedContractAddress *eth.Address `json:"contractAddress,omitempty"`
|
||||||
CommunityID *string `json:"communityId,omitempty"`
|
CommunityID *string `json:"communityId,omitempty"`
|
||||||
|
InteractedContractAddress *eth.Address `json:"interactedContractAddress,omitempty"`
|
||||||
|
|
||||||
IsNew *bool `json:"isNew,omitempty"`
|
IsNew *bool `json:"isNew,omitempty"`
|
||||||
|
|
||||||
|
@ -117,8 +119,9 @@ func (e *Entry) MarshalJSON() ([]byte, error) {
|
||||||
ChainIDOut: e.chainIDOut,
|
ChainIDOut: e.chainIDOut,
|
||||||
ChainIDIn: e.chainIDIn,
|
ChainIDIn: e.chainIDIn,
|
||||||
TransferType: e.transferType,
|
TransferType: e.transferType,
|
||||||
ContractAddress: e.contractAddress,
|
DeployedContractAddress: e.deployedContractAddress,
|
||||||
CommunityID: e.communityID,
|
CommunityID: e.communityID,
|
||||||
|
InteractedContractAddress: e.interactedContractAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.payloadType == MultiTransactionPT {
|
if e.payloadType == MultiTransactionPT {
|
||||||
|
@ -486,6 +489,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||||
startFilterDisabled, filter.Period.StartTimestamp, endFilterDisabled, filter.Period.EndTimestamp,
|
startFilterDisabled, filter.Period.StartTimestamp, endFilterDisabled, filter.Period.EndTimestamp,
|
||||||
filterActivityTypeAll, sliceContains(filter.Types, SendAT), sliceContains(filter.Types, ReceiveAT),
|
filterActivityTypeAll, sliceContains(filter.Types, SendAT), sliceContains(filter.Types, ReceiveAT),
|
||||||
sliceContains(filter.Types, ContractDeploymentAT), sliceContains(filter.Types, MintAT),
|
sliceContains(filter.Types, ContractDeploymentAT), sliceContains(filter.Types, MintAT),
|
||||||
|
sliceContains(filter.Types, SwapAT), sliceContains(filter.Types, ApproveAT),
|
||||||
transfer.MultiTransactionSend,
|
transfer.MultiTransactionSend,
|
||||||
fromTrType, toTrType,
|
fromTrType, toTrType,
|
||||||
allAddresses, filterAllToAddresses,
|
allAddresses, filterAllToAddresses,
|
||||||
|
@ -511,21 +515,36 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||||
var timestamp int64
|
var timestamp int64
|
||||||
var dbMtType, dbTrType sql.NullByte
|
var dbMtType, dbTrType sql.NullByte
|
||||||
var toAddress, fromAddress eth.Address
|
var toAddress, fromAddress eth.Address
|
||||||
var toAddressDB, ownerAddressDB, contractAddressDB, dbTokenID sql.RawBytes
|
var toAddressDB, ownerAddressDB, deployedContractAddressDB, dbTokenID sql.RawBytes
|
||||||
var tokenAddress, contractAddress *eth.Address
|
var tokenAddress, deployedContractAddress, interactedContractAddress *eth.Address
|
||||||
var aggregatedStatus int
|
var aggregatedStatus int
|
||||||
var dbTrAmount sql.NullString
|
var dbTrAmount sql.NullString
|
||||||
dbPTrAmount := new(big.Int)
|
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 tokenCode, fromTokenCode, toTokenCode sql.NullString
|
||||||
var methodHash, communityID sql.NullString
|
var methodHash, communityID sql.NullString
|
||||||
var transferType *TransferType
|
var transferType *TransferType
|
||||||
var communityMintEventDB sql.NullBool
|
var communityMintEventDB sql.NullBool
|
||||||
var communityMintEvent bool
|
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,
|
err := rows.Scan(&transferHash, &pendingHash, &chainID, &multiTxID, ×tamp, &dbMtType, &dbTrType, &fromAddress,
|
||||||
&toAddressDB, &ownerAddressDB, &dbTrAmount, (*bigint.SQLBigIntBytes)(dbPTrAmount), &dbMtFromAmount, &dbMtToAmount, &aggregatedStatus, &aggregatedCount,
|
&toAddressDB, &ownerAddressDB, &dbTrAmount, (*bigint.SQLBigIntBytes)(dbPTrAmount), &dbMtFromAmount, &dbMtToAmount, &aggregatedStatus, &aggregatedCount,
|
||||||
&tokenAddress, &dbTokenID, &tokenCode, &fromTokenCode, &toTokenCode, &outChainIDDB, &inChainIDDB, &contractType,
|
&tokenAddress, &dbTokenID, &tokenCode, &fromTokenCode, &toTokenCode, &outChainIDDB, &inChainIDDB, &dbContractType,
|
||||||
&contractAddressDB, &methodHash, &communityMintEventDB, &communityID)
|
&deployedContractAddressDB, &methodHash, &communityMintEventDB, &communityID,
|
||||||
|
&dbTrInputApprovalSpender, (*bigint.SQLBigIntBytes)(dbTrInputApprovalAmount), &dbTrInputFromAsset, (*bigint.SQLBigIntBytes)(dbTrInputFromAmount),
|
||||||
|
&dbTrInputToAsset, (*bigint.SQLBigIntBytes)(dbTrInputToAmount), &dbTrInputSide, &dbTrInputSlippageBps,
|
||||||
|
&dbInteractedContractAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -534,24 +553,32 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||||
toAddress = eth.BytesToAddress(toAddressDB)
|
toAddress = eth.BytesToAddress(toAddressDB)
|
||||||
}
|
}
|
||||||
|
|
||||||
if contractType.Valid {
|
if dbContractType.Valid {
|
||||||
transferType = contractTypeFromDBType(contractType.String)
|
contractType = dbContractType.String
|
||||||
|
transferType = contractTypeFromDBType(contractType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if communityMintEventDB.Valid {
|
if communityMintEventDB.Valid {
|
||||||
communityMintEvent = communityMintEventDB.Bool
|
communityMintEvent = communityMintEventDB.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(contractAddressDB) > 0 {
|
if len(deployedContractAddressDB) > 0 {
|
||||||
contractAddress = new(eth.Address)
|
deployedContractAddress = new(eth.Address)
|
||||||
*contractAddress = eth.BytesToAddress(contractAddressDB)
|
*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) {
|
getActivityType := func(trType sql.NullByte) (activityType Type, filteredAddress eth.Address) {
|
||||||
if trType.Valid {
|
if trType.Valid {
|
||||||
if trType.Byte == fromTrType {
|
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
|
return ContractDeploymentAT, fromAddress
|
||||||
|
} else if contractType == string(common.Erc20Approval) {
|
||||||
|
return ApproveAT, fromAddress
|
||||||
}
|
}
|
||||||
return SendAT, fromAddress
|
return SendAT, fromAddress
|
||||||
} else if trType.Byte == toTrType {
|
} else if trType.Byte == toTrType {
|
||||||
|
@ -624,15 +651,12 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||||
entry.amountIn = inAmount
|
entry.amountIn = inAmount
|
||||||
} else if pendingHash != nil && chainID.Valid {
|
} else if pendingHash != nil && chainID.Valid {
|
||||||
// Process `pending_transactions` row
|
// Process `pending_transactions` row
|
||||||
|
|
||||||
// Extract activity type: SendAT/ReceiveAT
|
|
||||||
activityType, _ := getActivityType(dbTrType)
|
|
||||||
|
|
||||||
inAmount, outAmount := getTrInAndOutAmounts(activityType, dbTrAmount, dbPTrAmount)
|
|
||||||
|
|
||||||
outChainID = new(common.ChainID)
|
outChainID = new(common.ChainID)
|
||||||
*outChainID = common.ChainID(chainID.Int64)
|
*outChainID = common.ChainID(chainID.Int64)
|
||||||
|
|
||||||
|
// Extract activity type: SendAT/ReceiveAT/ApproveAT
|
||||||
|
activityType, _ := getActivityType(dbTrType)
|
||||||
|
|
||||||
entry = newActivityEntryWithPendingTransaction(
|
entry = newActivityEntryWithPendingTransaction(
|
||||||
&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64),
|
&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64),
|
||||||
Hash: eth.BytesToHash(pendingHash),
|
Hash: eth.BytesToHash(pendingHash),
|
||||||
|
@ -640,6 +664,25 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||||
timestamp, activityType, activityStatus,
|
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
|
// Extract tokens
|
||||||
if tokenCode.Valid {
|
if tokenCode.Valid {
|
||||||
cID := common.ChainID(chainID.Int64)
|
cID := common.ChainID(chainID.Int64)
|
||||||
|
@ -650,7 +693,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||||
// Complete the data
|
// Complete the data
|
||||||
entry.amountOut = outAmount
|
entry.amountOut = outAmount
|
||||||
entry.amountIn = inAmount
|
entry.amountIn = inAmount
|
||||||
|
}
|
||||||
} else if multiTxID.Valid {
|
} else if multiTxID.Valid {
|
||||||
// Process `multi_transactions` row
|
// Process `multi_transactions` row
|
||||||
|
|
||||||
|
@ -693,12 +736,12 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete common data
|
// Complete common data
|
||||||
entry.recipient = &toAddress
|
|
||||||
entry.sender = &fromAddress
|
entry.sender = &fromAddress
|
||||||
entry.recipient = &toAddress
|
entry.recipient = &toAddress
|
||||||
entry.chainIDOut = outChainID
|
entry.chainIDOut = outChainID
|
||||||
entry.chainIDIn = inChainID
|
entry.chainIDIn = inChainID
|
||||||
entry.transferType = transferType
|
entry.transferType = transferType
|
||||||
|
entry.interactedContractAddress = interactedContractAddress
|
||||||
|
|
||||||
entries = append(entries, entry)
|
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
|
// Add 6 extractable transactions: one MultiTransactionSwap, two MultiTransactionBridge, two MultiTransactionSend and one MultiTransactionApprove
|
||||||
multiTxs := make([]transfer.MultiTransaction, 6)
|
multiTxs := make([]transfer.MultiTransaction, 6)
|
||||||
trs, fromAddrs, toAddrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, len(multiTxs)*2)
|
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[0] = transfer.GenerateTestBridgeMultiTransaction(trs[0], trs[1])
|
||||||
multiTxs[1] = transfer.GenerateTestSwapMultiTransaction(trs[2], testutils.SntSymbol, 100) // trs[3]
|
multiTxs[1] = transfer.GenerateTestSwapMultiTransaction(trs[2], testutils.SntSymbol, 100) // trs[3]
|
||||||
multiTxs[2] = transfer.GenerateTestSendMultiTransaction(trs[4]) // trs[5]
|
multiTxs[2] = transfer.GenerateTestSendMultiTransaction(trs[4]) // trs[5]
|
||||||
multiTxs[3] = transfer.GenerateTestBridgeMultiTransaction(trs[6], trs[7])
|
multiTxs[3] = transfer.GenerateTestBridgeMultiTransaction(trs[6], trs[7])
|
||||||
multiTxs[4] = transfer.GenerateTestSendMultiTransaction(trs[8]) // trs[9]
|
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
|
var lastMT common.MultiTransactionIDType
|
||||||
for i := range trs {
|
for i := range trs {
|
||||||
|
@ -695,6 +697,7 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
|
||||||
|
|
||||||
// Here not to include the modified To and From addresses
|
// Here not to include the modified To and From addresses
|
||||||
allAddresses := append(append(append(append(append(tdFromAdds, tdToAddrs...), fromAddrs...), toAddrs...), fromSpecial...), toSpecial...)
|
allAddresses := append(append(append(append(append(tdFromAdds, tdToAddrs...), fromAddrs...), toAddrs...), fromSpecial...), toSpecial...)
|
||||||
|
allAddresses = append(allAddresses, approveFromAddrs...)
|
||||||
|
|
||||||
// Insert MintAT Collectible
|
// Insert MintAT Collectible
|
||||||
trsSpecial[0].From = eth.HexToAddress("0x0")
|
trsSpecial[0].From = eth.HexToAddress("0x0")
|
||||||
|
|
|
@ -23,6 +23,8 @@ WITH filter_conditions AS (
|
||||||
? AS filterActivityTypeReceive,
|
? AS filterActivityTypeReceive,
|
||||||
? AS filterActivityTypeContractDeployment,
|
? AS filterActivityTypeContractDeployment,
|
||||||
? AS filterActivityTypeMint,
|
? AS filterActivityTypeMint,
|
||||||
|
? AS filterActivityTypeSwap,
|
||||||
|
? AS filterActivityTypeApprove,
|
||||||
? AS mTTypeSend,
|
? AS mTTypeSend,
|
||||||
? AS fromTrType,
|
? AS fromTrType,
|
||||||
? AS toTrType,
|
? 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 = '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)
|
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
|
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
|
FROM
|
||||||
transfers
|
transfers
|
||||||
CROSS JOIN filter_conditions
|
CROSS JOIN filter_conditions
|
||||||
LEFT JOIN filter_addresses from_join ON transfers.tx_from_address = from_join.address
|
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 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
|
WHERE
|
||||||
transfers.loaded == 1
|
transfers.loaded == 1
|
||||||
AND transfers.multi_transaction_id = 0
|
AND transfers.multi_transaction_id = 0
|
||||||
|
@ -273,6 +285,11 @@ WHERE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
OR (
|
||||||
|
filterActivityTypeApprove
|
||||||
|
AND tr_type = fromTrType
|
||||||
|
AND transfers.type = 'Erc20Approval'
|
||||||
|
)
|
||||||
OR (
|
OR (
|
||||||
filterActivityTypeContractDeployment
|
filterActivityTypeContractDeployment
|
||||||
AND tr_type = fromTrType
|
AND tr_type = fromTrType
|
||||||
|
@ -391,12 +408,22 @@ SELECT
|
||||||
NULL as contract_address,
|
NULL as contract_address,
|
||||||
NULL AS method_hash,
|
NULL AS method_hash,
|
||||||
NULL AS community_mint_event,
|
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
|
FROM
|
||||||
pending_transactions
|
pending_transactions
|
||||||
CROSS JOIN filter_conditions
|
CROSS JOIN filter_conditions
|
||||||
LEFT JOIN filter_addresses from_join ON pending_transactions.from_address = from_join.address
|
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 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
|
WHERE
|
||||||
pending_transactions.multi_transaction_id = 0
|
pending_transactions.multi_transaction_id = 0
|
||||||
AND pending_transactions.status = pendingStatus
|
AND pending_transactions.status = pendingStatus
|
||||||
|
@ -484,7 +511,16 @@ SELECT
|
||||||
NULL as contract_address,
|
NULL as contract_address,
|
||||||
NULL AS method_hash,
|
NULL AS method_hash,
|
||||||
NULL AS community_mint_event,
|
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
|
FROM
|
||||||
multi_transactions
|
multi_transactions
|
||||||
CROSS JOIN filter_conditions
|
CROSS JOIN filter_conditions
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
||||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
"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/async"
|
||||||
"github.com/status-im/status-go/services/wallet/collectibles"
|
"github.com/status-im/status-go/services/wallet/collectibles"
|
||||||
w_common "github.com/status-im/status-go/services/wallet/common"
|
w_common "github.com/status-im/status-go/services/wallet/common"
|
||||||
|
@ -74,13 +75,22 @@ type Service struct {
|
||||||
debounceDuration time.Duration
|
debounceDuration time.Duration
|
||||||
|
|
||||||
pendingTracker *transactions.PendingTxTracker
|
pendingTracker *transactions.PendingTxTracker
|
||||||
|
|
||||||
|
featureFlags *protocolCommon.FeatureFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) nextSessionID() SessionID {
|
func (s *Service) nextSessionID() SessionID {
|
||||||
return SessionID(s.lastSessionID.Add(1))
|
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{
|
return &Service{
|
||||||
db: db,
|
db: db,
|
||||||
accountsDB: accountsDB,
|
accountsDB: accountsDB,
|
||||||
|
@ -94,6 +104,8 @@ func NewService(db *sql.DB, accountsDB *accounts.Database, tokenManager token.Ma
|
||||||
debounceDuration: 1 * time.Second,
|
debounceDuration: 1 * time.Second,
|
||||||
|
|
||||||
pendingTracker: pendingTracker,
|
pendingTracker: pendingTracker,
|
||||||
|
|
||||||
|
featureFlags: featureFlags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -468,7 +468,7 @@ func TestService_IncrementalUpdateOnTop(t *testing.T) {
|
||||||
ChainID: 5,
|
ChainID: 5,
|
||||||
}, newTx.tokenOut)
|
}, newTx.tokenOut)
|
||||||
require.Equal(t, (*Token)(nil), newTx.tokenIn)
|
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
|
// Check the order of the following transaction data
|
||||||
require.Equal(t, SimpleTransactionPT, payload.Activities[1].payloadType)
|
require.Equal(t, SimpleTransactionPT, payload.Activities[1].payloadType)
|
||||||
|
|
|
@ -29,9 +29,11 @@ const (
|
||||||
UniswapV3Swap Type = "uniswapV3Swap"
|
UniswapV3Swap Type = "uniswapV3Swap"
|
||||||
HopBridgeFrom Type = "HopBridgeFrom"
|
HopBridgeFrom Type = "HopBridgeFrom"
|
||||||
HopBridgeTo Type = "HopBridgeTo"
|
HopBridgeTo Type = "HopBridgeTo"
|
||||||
|
Erc20Approval Type = "Erc20Approval"
|
||||||
unknownTransaction Type = "unknown"
|
unknownTransaction Type = "unknown"
|
||||||
|
|
||||||
// Event types
|
// Event types
|
||||||
|
Erc20ApprovalEventType EventType = "erc20ApprovalEvent"
|
||||||
WETHDepositEventType EventType = "wethDepositEvent"
|
WETHDepositEventType EventType = "wethDepositEvent"
|
||||||
WETHWithdrawalEventType EventType = "wethWithdrawalEvent"
|
WETHWithdrawalEventType EventType = "wethWithdrawalEvent"
|
||||||
Erc20TransferEventType EventType = "erc20Event"
|
Erc20TransferEventType EventType = "erc20Event"
|
||||||
|
@ -46,6 +48,10 @@ const (
|
||||||
HopBridgeTransferSentEventType EventType = "hopBridgeTransferSentEvent"
|
HopBridgeTransferSentEventType EventType = "hopBridgeTransferSentEvent"
|
||||||
UnknownEventType EventType = "unknownEvent"
|
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)
|
// Deposit (index_topic_1 address dst, uint256 wad)
|
||||||
wethDepositEventSignature = "Deposit(address,uint256)"
|
wethDepositEventSignature = "Deposit(address,uint256)"
|
||||||
// Withdrawal (index_topic_1 address src, uint256 wad)
|
// Withdrawal (index_topic_1 address src, uint256 wad)
|
||||||
|
@ -83,6 +89,7 @@ var (
|
||||||
|
|
||||||
// Detect event type for a cetain item from the Events Log
|
// Detect event type for a cetain item from the Events Log
|
||||||
func GetEventType(log *types.Log) EventType {
|
func GetEventType(log *types.Log) EventType {
|
||||||
|
erc20_721ApprovalEventSignatureHash := GetEventSignatureHash(Erc20_721ApprovalEventSignature)
|
||||||
wethDepositEventSignatureHash := GetEventSignatureHash(wethDepositEventSignature)
|
wethDepositEventSignatureHash := GetEventSignatureHash(wethDepositEventSignature)
|
||||||
wethWithdrawalEventSignatureHash := GetEventSignatureHash(wethWithdrawalEventSignature)
|
wethWithdrawalEventSignatureHash := GetEventSignatureHash(wethWithdrawalEventSignature)
|
||||||
erc20_721TransferEventSignatureHash := GetEventSignatureHash(Erc20_721TransferEventSignature)
|
erc20_721TransferEventSignatureHash := GetEventSignatureHash(Erc20_721TransferEventSignature)
|
||||||
|
@ -97,6 +104,11 @@ func GetEventType(log *types.Log) EventType {
|
||||||
|
|
||||||
if len(log.Topics) > 0 {
|
if len(log.Topics) > 0 {
|
||||||
switch log.Topics[0] {
|
switch log.Topics[0] {
|
||||||
|
case erc20_721ApprovalEventSignatureHash:
|
||||||
|
switch len(log.Topics) {
|
||||||
|
case erc20ApprovalEventIndexedParameters:
|
||||||
|
return Erc20ApprovalEventType
|
||||||
|
}
|
||||||
case wethDepositEventSignatureHash:
|
case wethDepositEventSignatureHash:
|
||||||
return WETHDepositEventType
|
return WETHDepositEventType
|
||||||
case wethWithdrawalEventSignatureHash:
|
case wethWithdrawalEventSignatureHash:
|
||||||
|
@ -132,6 +144,8 @@ func GetEventType(log *types.Log) EventType {
|
||||||
|
|
||||||
func EventTypeToSubtransactionType(eventType EventType) Type {
|
func EventTypeToSubtransactionType(eventType EventType) Type {
|
||||||
switch eventType {
|
switch eventType {
|
||||||
|
case Erc20ApprovalEventType:
|
||||||
|
return Erc20Approval
|
||||||
case Erc20TransferEventType:
|
case Erc20TransferEventType:
|
||||||
return Erc20Transfer
|
return Erc20Transfer
|
||||||
case Erc721TransferEventType:
|
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)
|
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) {
|
func ParseTransferLog(ethlog types.Log) (from, to common.Address, txIDs []common.Hash, tokenIDs, values []*big.Int, err error) {
|
||||||
eventType := GetEventType(ðlog)
|
eventType := GetEventType(ðlog)
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ var (
|
||||||
ZeroBigIntValue = big.NewInt(0)
|
ZeroBigIntValue = big.NewInt(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SwapSide uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IncreaseEstimatedGasFactor = 1.1
|
IncreaseEstimatedGasFactor = 1.1
|
||||||
SevenDaysInSeconds = 60 * 60 * 24 * 7
|
SevenDaysInSeconds = 60 * 60 * 24 * 7
|
||||||
|
@ -31,6 +33,9 @@ const (
|
||||||
ProcessorENSReleaseName = "ENSRelease"
|
ProcessorENSReleaseName = "ENSRelease"
|
||||||
ProcessorENSPublicKeyName = "ENSPublicKey"
|
ProcessorENSPublicKeyName = "ENSPublicKey"
|
||||||
ProcessorStickersBuyName = "StickersBuy"
|
ProcessorStickersBuyName = "StickersBuy"
|
||||||
|
|
||||||
|
SwapSideBuy SwapSide = 0
|
||||||
|
SwapSideSell SwapSide = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsProcessorBridge(name string) bool {
|
func IsProcessorBridge(name string) bool {
|
||||||
|
|
|
@ -47,6 +47,7 @@ var (
|
||||||
ErrPriceTimeout = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-037"), Details: "price timeout"}
|
ErrPriceTimeout = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-037"), Details: "price timeout"}
|
||||||
ErrNotEnoughLiquidity = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-038"), Details: "not enough liquidity"}
|
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"}
|
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 {
|
func createErrorResponse(processorName string, err error) error {
|
||||||
|
|
|
@ -211,3 +211,18 @@ func (mr *MockPathProcessorClearableMockRecorder) Clear() *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockPathProcessorClearable)(nil).Clear))
|
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"`
|
Name string `json:"bridgeName"`
|
||||||
ChainID uint64
|
ChainID uint64
|
||||||
TransferTx *transactions.SendTxArgs
|
TransferTx *transactions.SendTxArgs
|
||||||
|
ApproveTx *ApproveTxArgs
|
||||||
HopTx *HopBridgeTxArgs
|
HopTx *HopBridgeTxArgs
|
||||||
CbridgeTx *CelerBridgeTxArgs
|
CbridgeTx *CelerBridgeTxArgs
|
||||||
ERC721TransferTx *ERC721TxArgs
|
ERC721TransferTx *ERC721TxArgs
|
||||||
|
|
|
@ -31,6 +31,8 @@ type PathProcessor interface {
|
||||||
GetContractAddress(params ProcessorInputParams) (common.Address, error)
|
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 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)
|
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 {
|
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)
|
amountOut, _ := new(big.Int).SetString(amt.EqValueTokenAmt, 10)
|
||||||
return amountOut, nil
|
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
|
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
|
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) {
|
func (s *ENSRegisterProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||||
return snt.ContractAddress(params.FromChain.ChainID)
|
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
|
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) {
|
func (s *ERC1155Processor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||||
return params.FromToken.Address, nil
|
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) {
|
func (s *ERC721Processor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||||
return params.FromToken.Address, nil
|
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) {
|
func (s *StickersBuyProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||||
return snt.ContractAddress(params.FromChain.ChainID)
|
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
|
return priceRoute.TokenTransferProxy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SwapParaswapProcessor) prepareTransaction(sendArgs *MultipathProcessorTxArgs) error {
|
func (s *SwapParaswapProcessor) getPriceRoute(sendArgs *MultipathProcessorTxArgs) (*paraswap.Route, error) {
|
||||||
slippageBP := uint(sendArgs.SwapTx.SlippagePercentage * 100) // convert to basis points
|
|
||||||
|
|
||||||
key := makeKey(sendArgs.SwapTx.ChainID, sendArgs.SwapTx.ChainIDTo, sendArgs.SwapTx.TokenIDFrom, sendArgs.SwapTx.TokenIDTo)
|
key := makeKey(sendArgs.SwapTx.ChainID, sendArgs.SwapTx.ChainIDTo, sendArgs.SwapTx.TokenIDFrom, sendArgs.SwapTx.TokenIDTo)
|
||||||
priceRouteIns, ok := s.priceRoute.Load(key)
|
priceRouteIns, ok := s.priceRoute.Load(key)
|
||||||
if !ok {
|
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,
|
tx, err := s.paraswapClient.BuildTransaction(context.Background(), priceRoute.SrcTokenAddress, priceRoute.SrcTokenDecimals, priceRoute.SrcAmount.Int,
|
||||||
priceRoute.DestTokenAddress, priceRoute.DestTokenDecimals, priceRoute.DestAmount.Int, slippageBP,
|
priceRoute.DestTokenAddress, priceRoute.DestTokenDecimals, priceRoute.DestAmount.Int, slippageBP,
|
||||||
|
@ -284,3 +294,31 @@ func (s *SwapParaswapProcessor) CalculateAmountOut(params ProcessorInputParams)
|
||||||
|
|
||||||
return destAmount, nil
|
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) {
|
func (s *TransferProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
|
||||||
return common.Address{}, nil
|
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/history"
|
||||||
"github.com/status-im/status-go/services/wallet/market"
|
"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/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"
|
||||||
"github.com/status-im/status-go/services/wallet/thirdparty/alchemy"
|
"github.com/status-im/status-go/services/wallet/thirdparty/alchemy"
|
||||||
"github.com/status-im/status-go/services/wallet/thirdparty/coingecko"
|
"github.com/status-im/status-go/services/wallet/thirdparty/coingecko"
|
||||||
|
@ -112,7 +113,16 @@ func NewService(
|
||||||
cryptoOnRampManager := onramp.NewManager(cryptoOnRampProviders)
|
cryptoOnRampManager := onramp.NewManager(cryptoOnRampProviders)
|
||||||
|
|
||||||
savedAddressesManager := &SavedAddressesManager{db: db}
|
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()
|
blockChainState := blockchainstate.NewBlockChainState()
|
||||||
transferController := transfer.NewTransferController(db, accountsDB, rpcClient, accountFeed, feed, transactionManager, pendingTxManager,
|
transferController := transfer.NewTransferController(db, accountsDB, rpcClient, accountFeed, feed, transactionManager, pendingTxManager,
|
||||||
tokenManager, balanceCacher, blockChainState)
|
tokenManager, balanceCacher, blockChainState)
|
||||||
|
@ -181,12 +191,7 @@ func NewService(
|
||||||
)
|
)
|
||||||
collectibles := collectibles.NewService(db, feed, accountsDB, accountFeed, settingsFeed, communityManager, rpcClient.NetworkManager, collectiblesManager)
|
collectibles := collectibles.NewService(db, feed, accountsDB, accountFeed, settingsFeed, communityManager, rpcClient.NetworkManager, collectiblesManager)
|
||||||
|
|
||||||
activity := activity.NewService(db, accountsDB, tokenManager, collectiblesManager, feed, pendingTxManager)
|
activity := activity.NewService(db, accountsDB, tokenManager, collectiblesManager, feed, pendingTxManager, featureFlags)
|
||||||
|
|
||||||
featureFlags := &protocolCommon.FeatureFlags{}
|
|
||||||
if config.WalletConfig.EnableCelerBridge {
|
|
||||||
featureFlags.EnableCelerBridge = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Service{
|
return &Service{
|
||||||
db: db,
|
db: db,
|
||||||
|
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"github.com/status-im/status-go/services/wallet/balance"
|
"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/blockchainstate"
|
||||||
"github.com/status-im/status-go/services/wallet/community"
|
"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/helpers"
|
||||||
"github.com/status-im/status-go/t/utils"
|
"github.com/status-im/status-go/t/utils"
|
||||||
|
|
||||||
|
@ -1322,7 +1323,7 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) {
|
||||||
|
|
||||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||||
require.NoError(t, err)
|
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)
|
mediaServer, err := server.NewMediaServer(appdb, nil, nil, db)
|
||||||
require.NoError(t, err)
|
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/accounts/accountsevent"
|
||||||
"github.com/status-im/status-go/services/wallet/blockchainstate"
|
"github.com/status-im/status-go/services/wallet/blockchainstate"
|
||||||
wallet_common "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/t/helpers"
|
"github.com/status-im/status-go/t/helpers"
|
||||||
"github.com/status-im/status-go/walletdatabase"
|
"github.com/status-im/status-go/walletdatabase"
|
||||||
)
|
)
|
||||||
|
@ -36,7 +37,7 @@ func TestController_watchAccountsChanges(t *testing.T) {
|
||||||
|
|
||||||
bcstate := blockchainstate.NewBlockChainState()
|
bcstate := blockchainstate.NewBlockChainState()
|
||||||
SetMultiTransactionIDGenerator(StaticIDCounter()) // to have different multi-transaction IDs even with fast execution
|
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(
|
c := NewTransferController(
|
||||||
walletDB,
|
walletDB,
|
||||||
accountsDB,
|
accountsDB,
|
||||||
|
@ -239,7 +240,7 @@ func TestController_cleanupAccountLeftovers(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, storedAccs, 1)
|
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()
|
bcstate := blockchainstate.NewBlockChainState()
|
||||||
c := NewTransferController(
|
c := NewTransferController(
|
||||||
walletDB,
|
walletDB,
|
||||||
|
|
|
@ -359,8 +359,23 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
|
||||||
var tokenID *big.Int
|
var tokenID *big.Int
|
||||||
var txFrom *common.Address
|
var txFrom *common.Address
|
||||||
var txTo *common.Address
|
var txTo *common.Address
|
||||||
|
var transactionTo *common.Address
|
||||||
|
var eventLogAddress *common.Address
|
||||||
if t.Transaction != nil {
|
if t.Transaction != nil {
|
||||||
if t.Log != 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)
|
_, tokenAddress, txFrom, txTo = w_common.ExtractTokenTransferData(t.Type, t.Log, t.Transaction)
|
||||||
tokenID = t.TokenID
|
tokenID = t.TokenID
|
||||||
// Zero tokenID can be used for ERC721 and ERC1155 transfers but when serialzed/deserialized it becomes nil
|
// 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)
|
tokenID = big.NewInt(0)
|
||||||
}
|
}
|
||||||
txValue = t.TokenValue
|
txValue = t.TokenValue
|
||||||
|
}
|
||||||
|
eventLogAddress = &t.Log.Address
|
||||||
} else {
|
} else {
|
||||||
txValue = new(big.Int).Set(t.Transaction.Value())
|
txValue = new(big.Int).Set(t.Transaction.Value())
|
||||||
txFrom = &t.From
|
txFrom = &t.From
|
||||||
|
@ -388,6 +405,7 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
|
||||||
*txNonce = t.Transaction.Nonce()
|
*txNonce = t.Transaction.Nonce()
|
||||||
txSize = new(uint64)
|
txSize = new(uint64)
|
||||||
*txSize = t.Transaction.Size()
|
*txSize = t.Transaction.Size()
|
||||||
|
transactionTo = t.Transaction.To()
|
||||||
}
|
}
|
||||||
|
|
||||||
dbFields := transferDBFields{
|
dbFields := transferDBFields{
|
||||||
|
@ -426,6 +444,8 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
|
||||||
tokenID: tokenID,
|
tokenID: tokenID,
|
||||||
txFrom: txFrom,
|
txFrom: txFrom,
|
||||||
txTo: txTo,
|
txTo: txTo,
|
||||||
|
transactionTo: transactionTo,
|
||||||
|
eventLogAddress: eventLogAddress,
|
||||||
}
|
}
|
||||||
txsDBFields = append(txsDBFields, dbFields)
|
txsDBFields = append(txsDBFields, dbFields)
|
||||||
}
|
}
|
||||||
|
@ -469,15 +489,18 @@ type transferDBFields struct {
|
||||||
tokenID *big.Int
|
tokenID *big.Int
|
||||||
txFrom *common.Address
|
txFrom *common.Address
|
||||||
txTo *common.Address
|
txTo *common.Address
|
||||||
|
transactionTo *common.Address
|
||||||
|
eventLogAddress *common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateOrInsertTransfersDBFields(creator statementCreator, transfers []transferDBFields) error {
|
func updateOrInsertTransfersDBFields(creator statementCreator, transfers []transferDBFields) error {
|
||||||
insert, err := creator.Prepare(`INSERT OR REPLACE INTO transfers
|
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,
|
(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,
|
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
|
VALUES
|
||||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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,
|
_, 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.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 {
|
if err != nil {
|
||||||
log.Error("can't save transfer", "b-hash", t.blockHash, "b-n", t.blockNumber, "a", t.address, "h", t.id)
|
log.Error("can't save transfer", "b-hash", t.blockHash, "b-n", t.blockNumber, "a", t.address, "h", t.id)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -434,7 +434,8 @@ func (d *ETHDownloader) subTransactionsFromTransactionData(address, from common.
|
||||||
for _, txLog := range receipt.Logs {
|
for _, txLog := range receipt.Logs {
|
||||||
eventType := w_common.GetEventType(txLog)
|
eventType := w_common.GetEventType(txLog)
|
||||||
switch eventType {
|
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.HopBridgeTransferSentToL2EventType, w_common.HopBridgeTransferFromL1CompletedEventType,
|
||||||
w_common.HopBridgeWithdrawalBondedEventType, w_common.HopBridgeTransferSentEventType:
|
w_common.HopBridgeWithdrawalBondedEventType, w_common.HopBridgeTransferSentEventType:
|
||||||
transfer := Transfer{
|
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) {
|
map[uint64][]types.Hash, error) {
|
||||||
|
|
||||||
hashes := make(map[uint64][]types.Hash)
|
hashes := make(map[uint64][]types.Hash)
|
||||||
usedNonces := make(map[uint64]int64)
|
usedNonces := make(map[uint64]int64)
|
||||||
for _, tx := range data {
|
for _, tx := range data {
|
||||||
|
|
||||||
lastUsedNonce := int64(-1)
|
lastUsedNonce := int64(-1)
|
||||||
if nonce, ok := usedNonces[tx.ChainID]; ok {
|
if nonce, ok := usedNonces[tx.ChainID]; ok {
|
||||||
lastUsedNonce = nonce
|
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
|
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)
|
hashes[tx.ChainID] = append(hashes[tx.ChainID], hash)
|
||||||
usedNonces[tx.ChainID] = int64(usedNonce)
|
usedNonces[tx.ChainID] = int64(usedNonce)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,12 @@ import (
|
||||||
eth_common "github.com/ethereum/go-ethereum/common"
|
eth_common "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/bigint"
|
||||||
"github.com/status-im/status-go/services/wallet/common"
|
"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/testutils"
|
||||||
"github.com/status-im/status-go/services/wallet/token"
|
"github.com/status-im/status-go/services/wallet/token"
|
||||||
|
|
||||||
|
@ -42,6 +45,13 @@ type TestCollectibleTransfer struct {
|
||||||
TestCollectible
|
TestCollectible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TestApprove struct {
|
||||||
|
TestTransaction
|
||||||
|
Spender eth_common.Address // [address]
|
||||||
|
Amount int64
|
||||||
|
Token *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
func SeedToToken(seed int) *token.Token {
|
func SeedToToken(seed int) *token.Token {
|
||||||
tokenIndex := seed % len(TestTokens)
|
tokenIndex := seed % len(TestTokens)
|
||||||
return TestTokens[tokenIndex]
|
return TestTokens[tokenIndex]
|
||||||
|
@ -106,6 +116,17 @@ func generateTestCollectibleTransfer(seed int) TestCollectibleTransfer {
|
||||||
return tr
|
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 {
|
func GenerateTestSendMultiTransaction(tr TestTransfer) MultiTransaction {
|
||||||
return MultiTransaction{
|
return MultiTransaction{
|
||||||
ID: multiTransactionIDGenerator(),
|
ID: multiTransactionIDGenerator(),
|
||||||
|
@ -148,15 +169,15 @@ func GenerateTestBridgeMultiTransaction(fromTr, toTr TestTransfer) MultiTransact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateTestApproveMultiTransaction(tr TestTransfer) MultiTransaction {
|
func GenerateTestApproveMultiTransaction(tr TestApprove) MultiTransaction {
|
||||||
return MultiTransaction{
|
return MultiTransaction{
|
||||||
ID: multiTransactionIDGenerator(),
|
ID: multiTransactionIDGenerator(),
|
||||||
Type: MultiTransactionApprove,
|
Type: MultiTransactionApprove,
|
||||||
FromAddress: tr.From,
|
FromAddress: tr.From,
|
||||||
ToAddress: tr.To,
|
ToAddress: tr.Spender,
|
||||||
FromAsset: tr.Token.Symbol,
|
FromAsset: tr.Token.Symbol,
|
||||||
ToAsset: 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)),
|
ToAmount: (*hexutil.Big)(big.NewInt(0)),
|
||||||
Timestamp: uint64(tr.Timestamp),
|
Timestamp: uint64(tr.Timestamp),
|
||||||
}
|
}
|
||||||
|
@ -174,6 +195,17 @@ func GenerateTestTransfers(tb testing.TB, db *sql.DB, firstStartIndex int, count
|
||||||
return
|
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 {
|
type TestCollectible struct {
|
||||||
TokenAddress eth_common.Address
|
TokenAddress eth_common.Address
|
||||||
TokenID *big.Int
|
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{
|
transfer := transferDBFields{
|
||||||
chainID: uint64(tr.ChainID),
|
chainID: uint64(tr.ChainID),
|
||||||
id: tr.Hash,
|
id: tr.Hash,
|
||||||
|
@ -383,6 +423,8 @@ func InsertTestTransferWithOptions(tb testing.TB, db *sql.DB, address eth_common
|
||||||
tokenID: opt.TokenID,
|
tokenID: opt.TokenID,
|
||||||
transaction: opt.Tx,
|
transaction: opt.Tx,
|
||||||
receipt: opt.Receipt,
|
receipt: opt.Receipt,
|
||||||
|
transactionTo: transactionTo,
|
||||||
|
eventLogAddress: eventLogAddress,
|
||||||
}
|
}
|
||||||
err = updateOrInsertTransfersDBFields(tx, []transferDBFields{transfer})
|
err = updateOrInsertTransfersDBFields(tx, []transferDBFields{transfer})
|
||||||
require.NoError(tb, err)
|
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) {
|
func InsertTestPendingTransaction(tb testing.TB, db *sql.DB, tr *TestTransfer) {
|
||||||
_, err := db.Exec(`
|
_, err := db.Exec(`
|
||||||
INSERT INTO pending_transactions (network_id, hash, timestamp, from_address, to_address,
|
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
|
symbol, gas_price, gas_limit, value, data, type, additional_data, multi_transaction_id, transaction_to,
|
||||||
) VALUES (?, ?, ?, ?, ?, 'ETH', 0, 0, ?, '', 'eth', '', ?)`,
|
) VALUES (?, ?, ?, ?, ?, 'ETH', 0, 0, ?, '', 'eth', '', ?, ?)`,
|
||||||
tr.ChainID, tr.Hash, tr.Timestamp, tr.From, tr.To, (*bigint.SQLBigIntBytes)(big.NewInt(tr.Value)), tr.MultiTransactionID)
|
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)
|
require.NoError(tb, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,3 +541,19 @@ func (s *InMemMultiTransactionStorage) ReadMultiTransactions(details *MultiTxDet
|
||||||
}
|
}
|
||||||
return multiTxs, nil
|
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 {
|
type TransactionManager struct {
|
||||||
storage MultiTransactionStorage
|
storage MultiTransactionStorage
|
||||||
|
txInputDataStorage TransactionInputDataStorage
|
||||||
gethManager *account.GethManager
|
gethManager *account.GethManager
|
||||||
transactor transactions.TransactorIface
|
transactor transactions.TransactorIface
|
||||||
config *params.NodeConfig
|
config *params.NodeConfig
|
||||||
|
@ -54,8 +55,13 @@ type MultiTransactionStorage interface {
|
||||||
DeleteMultiTransaction(id wallet_common.MultiTransactionIDType) error
|
DeleteMultiTransaction(id wallet_common.MultiTransactionIDType) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TransactionInputDataStorage interface {
|
||||||
|
UpsertInputData(chainID wallet_common.ChainID, txHash types.Hash, inputData pathprocessor.TransactionInputData) error
|
||||||
|
}
|
||||||
|
|
||||||
func NewTransactionManager(
|
func NewTransactionManager(
|
||||||
storage MultiTransactionStorage,
|
storage MultiTransactionStorage,
|
||||||
|
txInputDataStorage TransactionInputDataStorage,
|
||||||
gethManager *account.GethManager,
|
gethManager *account.GethManager,
|
||||||
transactor transactions.TransactorIface,
|
transactor transactions.TransactorIface,
|
||||||
config *params.NodeConfig,
|
config *params.NodeConfig,
|
||||||
|
@ -65,6 +71,7 @@ func NewTransactionManager(
|
||||||
) *TransactionManager {
|
) *TransactionManager {
|
||||||
return &TransactionManager{
|
return &TransactionManager{
|
||||||
storage: storage,
|
storage: storage,
|
||||||
|
txInputDataStorage: txInputDataStorage,
|
||||||
gethManager: gethManager,
|
gethManager: gethManager,
|
||||||
transactor: transactor,
|
transactor: transactor,
|
||||||
config: config,
|
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) {
|
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)
|
updateDataFromMultiTx(data, multiTransaction)
|
||||||
hashes, err := sendTransactions(data, pathProcessors, account)
|
hashes, err := sendTransactions(data, pathProcessors, account, tm.txInputDataStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ func setupTransactionManager(t *testing.T) (*TransactionManager, *mock_transacto
|
||||||
// Create a mock transactor
|
// Create a mock transactor
|
||||||
transactor := mock_transactor.NewMockTransactorIface(ctrl)
|
transactor := mock_transactor.NewMockTransactorIface(ctrl)
|
||||||
// Create a new instance of the TransactionManager
|
// 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
|
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
|
// 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{
|
err := tm.addPending(&PendingTransaction{
|
||||||
ChainID: chainID,
|
ChainID: chainID,
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
|
@ -353,6 +353,7 @@ func (tm *PendingTxTracker) TrackPendingTransaction(chainID common.ChainID, hash
|
||||||
Type: trType,
|
Type: trType,
|
||||||
AutoDelete: &autoDelete,
|
AutoDelete: &autoDelete,
|
||||||
AdditionalData: additionalData,
|
AdditionalData: additionalData,
|
||||||
|
TransactionTo: transactionTo,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -419,6 +420,7 @@ type PendingTransaction struct {
|
||||||
GasLimit bigint.BigInt `json:"gasLimit"`
|
GasLimit bigint.BigInt `json:"gasLimit"`
|
||||||
Type PendingTrxType `json:"type"`
|
Type PendingTrxType `json:"type"`
|
||||||
AdditionalData string `json:"additionalData"`
|
AdditionalData string `json:"additionalData"`
|
||||||
|
TransactionTo *eth.Address `json:"transactionTo"`
|
||||||
ChainID common.ChainID `json:"network_id"`
|
ChainID common.ChainID `json:"network_id"`
|
||||||
MultiTransactionID wallet_common.MultiTransactionIDType `json:"multi_transaction_id"`
|
MultiTransactionID wallet_common.MultiTransactionIDType `json:"multi_transaction_id"`
|
||||||
Nonce uint64 `json:"nonce"`
|
Nonce uint64 `json:"nonce"`
|
||||||
|
@ -430,7 +432,7 @@ type PendingTransaction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectFromPending = `SELECT hash, timestamp, value, from_address, to_address, data,
|
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
|
network_id, COALESCE(multi_transaction_id, 0), status, auto_delete, nonce
|
||||||
FROM pending_transactions
|
FROM pending_transactions
|
||||||
`
|
`
|
||||||
|
@ -456,6 +458,7 @@ func rowsToTransactions(rows *sql.Rows) (transactions []*PendingTransaction, err
|
||||||
(*bigint.SQLBigIntBytes)(transaction.GasLimit.Int),
|
(*bigint.SQLBigIntBytes)(transaction.GasLimit.Int),
|
||||||
&transaction.Type,
|
&transaction.Type,
|
||||||
&transaction.AdditionalData,
|
&transaction.AdditionalData,
|
||||||
|
&transaction.TransactionTo,
|
||||||
&transaction.ChainID,
|
&transaction.ChainID,
|
||||||
&transaction.MultiTransactionID,
|
&transaction.MultiTransactionID,
|
||||||
transaction.Status,
|
transaction.Status,
|
||||||
|
@ -608,10 +611,10 @@ func (tm *PendingTxTracker) addPending(transaction *PendingTransaction) error {
|
||||||
var insert *sql.Stmt
|
var insert *sql.Stmt
|
||||||
insert, err = tx.Prepare(`INSERT OR REPLACE INTO pending_transactions
|
insert, err = tx.Prepare(`INSERT OR REPLACE INTO pending_transactions
|
||||||
(network_id, hash, timestamp, value, from_address, to_address,
|
(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)
|
auto_delete, nonce)
|
||||||
VALUES
|
VALUES
|
||||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? , ?, ?)`)
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? , ?, ?, ?)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -630,6 +633,7 @@ func (tm *PendingTxTracker) addPending(transaction *PendingTransaction) error {
|
||||||
(*bigint.SQLBigIntBytes)(transaction.GasLimit.Int),
|
(*bigint.SQLBigIntBytes)(transaction.GasLimit.Int),
|
||||||
transaction.Type,
|
transaction.Type,
|
||||||
transaction.AdditionalData,
|
transaction.AdditionalData,
|
||||||
|
transaction.TransactionTo,
|
||||||
transaction.MultiTransactionID,
|
transaction.MultiTransactionID,
|
||||||
transaction.Status,
|
transaction.Status,
|
||||||
transaction.AutoDelete,
|
transaction.AutoDelete,
|
||||||
|
|
|
@ -302,7 +302,7 @@ func TestPendingTxTracker_MultipleClients(t *testing.T) {
|
||||||
sub := eventFeed.Subscribe(eventChan)
|
sub := eventFeed.Subscribe(eventChan)
|
||||||
|
|
||||||
for i := range txs {
|
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)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +377,7 @@ func TestPendingTxTracker_Watch(t *testing.T) {
|
||||||
sub := eventFeed.Subscribe(eventChan)
|
sub := eventFeed.Subscribe(eventChan)
|
||||||
|
|
||||||
// Track the first transaction
|
// 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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Store the confirmed already
|
// Store the confirmed already
|
||||||
|
@ -476,7 +476,7 @@ func TestPendingTxTracker_Watch_StatusChangeIncrementally(t *testing.T) {
|
||||||
|
|
||||||
for i := range txs {
|
for i := range txs {
|
||||||
// Track the first transaction
|
// 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)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,12 +67,14 @@ func GenerateTestPendingTransactions(start int, count int) []PendingTransaction
|
||||||
|
|
||||||
txs := make([]PendingTransaction, count)
|
txs := make([]PendingTransaction, count)
|
||||||
for i := start; i < count; i++ {
|
for i := start; i < count; i++ {
|
||||||
|
transactionTo := eth.HexToAddress(fmt.Sprintf("0x4%d", i))
|
||||||
txs[i] = PendingTransaction{
|
txs[i] = PendingTransaction{
|
||||||
Hash: eth.HexToHash(fmt.Sprintf("0x1%d", i)),
|
Hash: eth.HexToHash(fmt.Sprintf("0x1%d", i)),
|
||||||
From: eth.HexToAddress(fmt.Sprintf("0x2%d", i)),
|
From: eth.HexToAddress(fmt.Sprintf("0x2%d", i)),
|
||||||
To: eth.HexToAddress(fmt.Sprintf("0x3%d", i)),
|
To: eth.HexToAddress(fmt.Sprintf("0x3%d", i)),
|
||||||
Type: RegisterENS,
|
Type: RegisterENS,
|
||||||
AdditionalData: "someuser.stateofus.eth",
|
AdditionalData: "someuser.stateofus.eth",
|
||||||
|
TransactionTo: &transactionTo,
|
||||||
Value: bigint.BigInt{Int: big.NewInt(int64(i))},
|
Value: bigint.BigInt{Int: big.NewInt(int64(i))},
|
||||||
GasLimit: bigint.BigInt{Int: big.NewInt(21000)},
|
GasLimit: bigint.BigInt{Int: big.NewInt(21000)},
|
||||||
GasPrice: bigint.BigInt{Int: big.NewInt(int64(i))},
|
GasPrice: bigint.BigInt{Int: big.NewInt(int64(i))},
|
||||||
|
|
|
@ -197,6 +197,7 @@ func createPendingTransaction(from common.Address, symbol string, chainID uint64
|
||||||
MultiTransactionID: multiTransactionID,
|
MultiTransactionID: multiTransactionID,
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
AutoDelete: new(bool),
|
AutoDelete: new(bool),
|
||||||
|
TransactionTo: tx.To(),
|
||||||
}
|
}
|
||||||
// Transaction downloader will delete pending transaction as soon as it is confirmed
|
// Transaction downloader will delete pending transaction as soon as it is confirmed
|
||||||
*pTx.AutoDelete = false
|
*pTx.AutoDelete = false
|
||||||
|
|
|
@ -2,11 +2,19 @@ package walletdatabase
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"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/sqlite"
|
||||||
"github.com/status-im/status-go/walletdatabase/migrations"
|
"github.com/status-im/status-go/walletdatabase/migrations"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
batchSize = 1000
|
||||||
|
)
|
||||||
|
|
||||||
type DbInitializer struct {
|
type DbInitializer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +22,9 @@ func (a DbInitializer) Initialize(path, password string, kdfIterationsNumber int
|
||||||
return InitializeDB(path, password, kdfIterationsNumber)
|
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 {
|
func doMigration(db *sql.DB) error {
|
||||||
// Run all the new migrations
|
// Run all the new migrations
|
||||||
|
@ -35,3 +45,105 @@ func InitializeDB(path, password string, kdfIterationsNumber int) (*sql.DB, erro
|
||||||
|
|
||||||
return db, nil
|
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