status-go/services/wallet/transfer/bridge_identifier.go

222 lines
7.8 KiB
Go

package transfer
import (
"context"
"encoding/hex"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/rpc/chain"
w_common "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/token"
)
// TODO: Find proper way to uniquely match Origin and Destination transactions (some sort of hash or uniqueID)
// Current approach is not failsafe (for example, if multiple identical bridge operations are triggered
// at the same time)
// Recipient + Relayer + Data should match in both Origin and Destination transactions
func getHopBridgeFromL1CrossTxID(recipient common.Address, relayer common.Address, logData []byte) string {
return fmt.Sprintf("FromL1_%s_%s_%s", recipient.String(), relayer.String(), hex.EncodeToString(logData))
}
func getHopBridgeFromL2CrossTxID(transferID *big.Int) string {
return fmt.Sprintf("FromL2_0x%s", transferID.Text(16))
}
type originTxParams struct {
fromNetworkID uint64
fromTxHash common.Hash
fromAddress common.Address
fromAsset string
fromAmount *big.Int
toNetworkID uint64
toAddress common.Address
crossTxID string
timestamp uint64
}
func upsertHopBridgeOriginTx(ctx context.Context, transactionManager *TransactionManager, params originTxParams) (*MultiTransaction, error) {
// Try to find "destination" half of the multiTx
multiTx, err := transactionManager.GetBridgeDestinationMultiTransaction(ctx, params.toNetworkID, params.crossTxID)
if err != nil {
return nil, err
}
if multiTx == nil {
multiTx = NewMultiTransaction(
/* Timestamp: */ params.timestamp, // Common data
/* FromNetworkID: */ params.fromNetworkID, // Data from "origin" transaction
/* ToNetworkID: */ params.toNetworkID, // Data from "origin" transaction
/* FromTxHash: */ params.fromTxHash, // Data from "origin" transaction
/* ToTxHash: */ common.Hash{},
/* FromAddress: */ params.fromAddress, // Data from "origin" transaction
/* ToAddress: */ params.toAddress, // Data from "origin" transaction
/* FromAsset: */ params.fromAsset, // Data from "origin" transaction
/* ToAsset: */ params.fromAsset, // To be replaced by "destination" transaction, need to be non-null
/* FromAmount: */ (*hexutil.Big)(params.fromAmount), // Data from "origin" transaction
/* ToAmount: */ (*hexutil.Big)(params.fromAmount), // To be replaced by "destination" transaction, need to be non-null
/* Type: */ MultiTransactionBridge, // Common data
/* CrossTxID: */ params.crossTxID, // Common data
)
_, err := transactionManager.InsertMultiTransaction(multiTx)
if err != nil {
return nil, err
}
} else {
multiTx.FromNetworkID = params.fromNetworkID
multiTx.FromTxHash = params.fromTxHash
multiTx.FromAddress = params.fromAddress
multiTx.FromAsset = params.fromAsset
multiTx.FromAmount = (*hexutil.Big)(params.fromAmount)
multiTx.Timestamp = params.timestamp
err := transactionManager.UpdateMultiTransaction(multiTx)
if err != nil {
return nil, err
}
}
return multiTx, nil
}
type destinationTxParams struct {
toNetworkID uint64
toTxHash common.Hash
toAddress common.Address
toAsset string
toAmount *big.Int
crossTxID string
timestamp uint64
}
func upsertHopBridgeDestinationTx(ctx context.Context, transactionManager *TransactionManager, params destinationTxParams) (*MultiTransaction, error) {
// Try to find "origin" half of the multiTx
multiTx, err := transactionManager.GetBridgeOriginMultiTransaction(ctx, params.toNetworkID, params.crossTxID)
if err != nil {
return nil, err
}
if multiTx == nil {
multiTx = NewMultiTransaction(
/* Timestamp: */ params.timestamp, // Common data
/* FromNetworkID: */ 0, // not set
/* ToNetworkID: */ params.toNetworkID, // Data from "destination" transaction
/* FromTxHash: */ common.Hash{},
/* ToTxHash: */ params.toTxHash, // Data from "destination" transaction
/* FromAddress: */ params.toAddress, // To be replaced by "origin" transaction, need to be non-null
/* ToAddress: */ params.toAddress, // Data from "destination" transaction
/* FromAsset: */ params.toAsset, // To be replaced by "origin" transaction, need to be non-null
/* ToAsset: */ params.toAsset, // Data from "destination" transaction
/* FromAmount: */ (*hexutil.Big)(params.toAmount), // To be replaced by "origin" transaction, need to be non-null
/* ToAmount: */ (*hexutil.Big)(params.toAmount), // Data from "destination" transaction
/* Type: */ MultiTransactionBridge, // Common data
/* CrossTxID: */ params.crossTxID, // Common data
)
_, err := transactionManager.InsertMultiTransaction(multiTx)
if err != nil {
return nil, err
}
} else {
multiTx.ToTxHash = params.toTxHash
multiTx.ToAsset = params.toAsset
multiTx.ToAmount = (*hexutil.Big)(params.toAmount)
multiTx.Timestamp = params.timestamp
err := transactionManager.UpdateMultiTransaction(multiTx)
if err != nil {
return nil, err
}
}
return multiTx, nil
}
func buildHopBridgeMultitransaction(ctx context.Context, client chain.ClientInterface, transactionManager *TransactionManager, tokenManager *token.Manager, subTx *Transfer) (*MultiTransaction, error) {
// Identify if it's from/to transaction
switch w_common.GetEventType(subTx.Log) {
case w_common.HopBridgeTransferSentToL2EventType:
// L1->L2 Origin transaction
toChainID, recipient, relayer, fromAmount, err := w_common.ParseHopBridgeTransferSentToL2Log(subTx.Log)
if err != nil {
return nil, err
}
params := originTxParams{
fromNetworkID: subTx.NetworkID,
fromTxHash: subTx.Receipt.TxHash,
fromAddress: subTx.From,
fromAsset: "ETH",
fromAmount: fromAmount,
toNetworkID: toChainID,
toAddress: recipient,
crossTxID: getHopBridgeFromL1CrossTxID(recipient, relayer, subTx.Log.Data),
timestamp: subTx.Timestamp,
}
return upsertHopBridgeOriginTx(ctx, transactionManager, params)
case w_common.HopBridgeTransferFromL1CompletedEventType:
// L1->L2 Destination transaction
recipient, relayer, toAmount, err := w_common.ParseHopBridgeTransferFromL1CompletedLog(subTx.Log)
if err != nil {
return nil, err
}
params := destinationTxParams{
toNetworkID: subTx.NetworkID,
toTxHash: subTx.Receipt.TxHash,
toAddress: recipient,
toAsset: "ETH",
toAmount: toAmount,
crossTxID: getHopBridgeFromL1CrossTxID(recipient, relayer, subTx.Log.Data),
timestamp: subTx.Timestamp,
}
return upsertHopBridgeDestinationTx(ctx, transactionManager, params)
case w_common.HopBridgeTransferSentEventType:
// L2->L1 / L2->L2 Origin transaction
transferID, toChainID, recipient, fromAmount, _, _, _, _, _, err := w_common.ParseHopBridgeTransferSentLog(subTx.Log)
if err != nil {
return nil, err
}
params := originTxParams{
fromNetworkID: subTx.NetworkID,
fromTxHash: subTx.Receipt.TxHash,
fromAddress: subTx.From,
fromAsset: "ETH",
fromAmount: fromAmount,
toNetworkID: toChainID,
toAddress: recipient,
crossTxID: getHopBridgeFromL2CrossTxID(transferID),
timestamp: subTx.Timestamp,
}
return upsertHopBridgeOriginTx(ctx, transactionManager, params)
case w_common.HopBridgeWithdrawalBondedEventType:
// L2->L1 / L2->L2 Destination transaction
transferID, toAmount, err := w_common.ParseHopWithdrawalBondedLog(subTx.Log)
if err != nil {
return nil, err
}
params := destinationTxParams{
toNetworkID: subTx.NetworkID,
toTxHash: subTx.Receipt.TxHash,
toAddress: subTx.Address,
toAsset: "ETH",
toAmount: toAmount,
crossTxID: getHopBridgeFromL2CrossTxID(transferID),
timestamp: subTx.Timestamp,
}
return upsertHopBridgeDestinationTx(ctx, transactionManager, params)
}
return nil, nil
}