status-im/status-go

View on GitHub
services/wallet/transfer/bridge_identifier.go

Summary

Maintainability
A
0 mins
Test Coverage
F
0%
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
}