From af26d743492beab80a0c6723acc361975a837a89 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 4 Apr 2024 23:36:39 -0500 Subject: [PATCH 1/6] initial commit to simply IsSendOutTxProcessed func --- cmd/zetaclientd/start.go | 2 +- zetaclient/evm/evm_client.go | 338 +++++------------------ zetaclient/evm/outbounds.go | 125 +++++++++ zetaclient/zetabridge/tx.go | 4 +- zetaclient/zetabridge/zetacore_bridge.go | 3 +- 5 files changed, 195 insertions(+), 277 deletions(-) create mode 100644 zetaclient/evm/outbounds.go diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 56d77e0950..de1633ad6f 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -124,7 +124,7 @@ func start(_ *cobra.Command, _ []string) error { // Initialize core parameters from zetacore appContext := appcontext.NewAppContext(corecontext.NewZetaCoreContext(cfg), cfg) - err = zetaBridge.UpdateZetaCoreContext(appContext.ZetaCoreContext(), true) + err = zetaBridge.UpdateZetaCoreContext(appContext.ZetaCoreContext(), true, startLogger) if err != nil { startLogger.Error().Err(err).Msg("Error getting core parameters") return err diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index f94536be25..cc7e4c74d3 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -317,298 +317,90 @@ func (ob *ChainClient) Stop() { ob.logger.Chain.Info().Msgf("%s observer stopped", ob.chain.String()) } -// returns: isIncluded, isConfirmed, Error -// If isConfirmed, it also post to ZetaCore +// IsSendOutTxProcessed checks outtx status and returns (isIncluded, isConfirmed, error) +// It also posts vote to zetacore if the tx is confirmed func (ob *ChainClient) IsSendOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { sendHash := cctx.Index cointype := cctx.InboundTxParams.CoinType nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce // skip if outtx is not confirmed - params := ob.GetChainParams() - receipt, transaction := ob.GetTxNReceipt(nonce) - if receipt == nil || transaction == nil { // not confirmed yet + if !ob.isTxConfirmed(nonce) { return false, false, nil } - - sendID := fmt.Sprintf("%s-%d", ob.chain.String(), nonce) + receipt, transaction := ob.GetTxNReceipt(nonce) + sendID := fmt.Sprintf("%d-%d", ob.chain.ChainId, nonce) logger = logger.With().Str("sendID", sendID).Logger() + // get connector and erce20Custody contracts + connectorAddr, connector, err := ob.GetConnectorContract() + if err != nil { + return false, false, errors.Wrapf(err, "error getting zeta connector for chain %d", ob.chain.ChainId) + } + custodyAddr, custody, err := ob.GetERC20CustodyContract() + if err != nil { + return false, false, errors.Wrapf(err, "error getting erc20 custody for chain %d", ob.chain.ChainId) + } + + // define a few common variables + txHash := receipt.TxHash.Hex() + var receiveValue *big.Int + var receiveStatus chains.ReceiveStatus + // compliance check, special handling the cancelled cctx if compliance.IsCctxRestricted(cctx) { - recvStatus := chains.ReceiveStatus_Failed + // use cctx's amount to bypass the amount check in zetacore + receiveValue = cctx.GetCurrentOutTxParam().Amount.BigInt() + receiveStatus := chains.ReceiveStatus_Failed if receipt.Status == 1 { - recvStatus = chains.ReceiveStatus_Success - } - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendHash, - receipt.TxHash.Hex(), - receipt.BlockNumber.Uint64(), - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - // use cctx's amount to bypass the amount check in zetacore - cctx.GetCurrentOutTxParam().Amount.BigInt(), - recvStatus, - ob.chain, - nonce, - coin.CoinType_Cmd, - ) - if err != nil { - logger.Error().Err(err).Msgf("error posting confirmation to meta core for cctx %s nonce %d", sendHash, nonce) - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) + receiveStatus = chains.ReceiveStatus_Success } + ob.PostVoteOutbound(sendHash, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) return true, true, nil } - if cointype == coin.CoinType_Cmd { - recvStatus := chains.ReceiveStatus_Failed - if receipt.Status == 1 { - recvStatus = chains.ReceiveStatus_Success - } - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendHash, - receipt.TxHash.Hex(), - receipt.BlockNumber.Uint64(), - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - transaction.Value(), - recvStatus, - ob.chain, - nonce, - coin.CoinType_Cmd, - ) - if err != nil { - logger.Error().Err(err).Msgf("error posting confirmation to meta core for cctx %s nonce %d", sendHash, nonce) - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) - } - return true, true, nil - - } else if cointype == coin.CoinType_Gas { // the outbound is a regular Ether/BNB/Matic transfer; no need to check events - if receipt.Status == 1 { - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendHash, - receipt.TxHash.Hex(), - receipt.BlockNumber.Uint64(), - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - transaction.Value(), - chains.ReceiveStatus_Success, - ob.chain, - nonce, - coin.CoinType_Gas, - ) - if err != nil { - logger.Error().Err(err).Msgf("error posting confirmation to meta core for cctx %s nonce %d", sendHash, nonce) - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) - } - return true, true, nil - } else if receipt.Status == 0 { // the same as below events flow - logger.Info().Msgf("Found (failed tx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), receipt.TxHash.Hex()) - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendHash, - receipt.TxHash.Hex(), - receipt.BlockNumber.Uint64(), - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - big.NewInt(0), - chains.ReceiveStatus_Failed, - ob.chain, - nonce, - coin.CoinType_Gas, - ) - if err != nil { - logger.Error().Err(err).Msgf("PostVoteOutbound error in WatchTxHashWithTimeout; zeta tx hash %s cctx %s nonce %d", zetaTxHash, sendHash, nonce) - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) - } - return true, true, nil - } - } else if cointype == coin.CoinType_Zeta { // the outbound is a Zeta transfer; need to check events ZetaReceived - if receipt.Status == 1 { - logs := receipt.Logs - for _, vLog := range logs { - confHeight := vLog.BlockNumber + params.ConfirmationCount - // TODO rewrite this to return early if not confirmed - connectorAddr, connector, err := ob.GetConnectorContract() - if err != nil { - return false, false, fmt.Errorf("error getting connector contract: %w", err) - } - receivedLog, err := connector.ZetaConnectorNonEthFilterer.ParseZetaReceived(*vLog) - if err == nil { - logger.Info().Msgf("Found (outTx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), vLog.TxHash.Hex()) - if confHeight <= ob.GetLastBlockHeight() { - logger.Info().Msg("Confirmed! Sending PostConfirmation to zetabridge...") - // sanity check tx event - err = ValidateEvmTxLog(vLog, connectorAddr, transaction.Hash().Hex(), TopicsZetaReceived) - if err != nil { - logger.Error().Err(err).Msgf("CheckEvmTxLog error on ZetaReceived event, chain %d nonce %d txhash %s", ob.chain.ChainId, nonce, transaction.Hash().Hex()) - return false, false, err - } - sendhash := vLog.Topics[3].Hex() - //var rxAddress string = ethcommon.HexToAddress(vLog.Topics[1].Hex()).Hex() - mMint := receivedLog.ZetaValue - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendhash, - vLog.TxHash.Hex(), - vLog.BlockNumber, - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - mMint, - chains.ReceiveStatus_Success, - ob.chain, - nonce, - coin.CoinType_Zeta, - ) - if err != nil { - logger.Error().Err(err).Msgf("error posting confirmation to meta core for cctx %s nonce %d", sendHash, nonce) - continue - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) - } - return true, true, nil - } - logger.Info().Msgf("Included; %d blocks before confirmed! chain %s nonce %d", confHeight-ob.GetLastBlockHeight(), ob.chain.String(), nonce) - return true, false, nil - } - revertedLog, err := connector.ZetaConnectorNonEthFilterer.ParseZetaReverted(*vLog) - if err == nil { - logger.Info().Msgf("Found (revertTx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), vLog.TxHash.Hex()) - if confHeight <= ob.GetLastBlockHeight() { - logger.Info().Msg("Confirmed! Sending PostConfirmation to zetabridge...") - // sanity check tx event - err = ValidateEvmTxLog(vLog, connectorAddr, transaction.Hash().Hex(), TopicsZetaReverted) - if err != nil { - logger.Error().Err(err).Msgf("CheckEvmTxLog error on ZetaReverted event, chain %d nonce %d txhash %s", ob.chain.ChainId, nonce, transaction.Hash().Hex()) - return false, false, err - } - sendhash := vLog.Topics[2].Hex() - mMint := revertedLog.RemainingZetaValue - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendhash, - vLog.TxHash.Hex(), - vLog.BlockNumber, - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - mMint, - chains.ReceiveStatus_Success, - ob.chain, - nonce, - coin.CoinType_Zeta, - ) - if err != nil { - logger.Err(err).Msgf("error posting confirmation to meta core for cctx %s nonce %d", sendHash, nonce) - continue - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) - } - return true, true, nil - } - logger.Info().Msgf("Included; %d blocks before confirmed! chain %s nonce %d", confHeight-ob.GetLastBlockHeight(), ob.chain.String(), nonce) - return true, false, nil - } - } - } else if receipt.Status == 0 { - //FIXME: check nonce here by getTransaction RPC - logger.Info().Msgf("Found (failed tx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), receipt.TxHash.Hex()) - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendHash, - receipt.TxHash.Hex(), - receipt.BlockNumber.Uint64(), - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - big.NewInt(0), - chains.ReceiveStatus_Failed, - ob.chain, - nonce, - coin.CoinType_Zeta, - ) - if err != nil { - logger.Error().Err(err).Msgf("error posting confirmation to meta core for cctx %s nonce %d", sendHash, nonce) - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) - } - return true, true, nil - } - } else if cointype == coin.CoinType_ERC20 { - if receipt.Status == 1 { - logs := receipt.Logs - addrCustody, ERC20Custody, err := ob.GetERC20CustodyContract() - if err != nil { - logger.Warn().Msgf("NewERC20Custody err: %s", err) - } - for _, vLog := range logs { - event, err := ERC20Custody.ParseWithdrawn(*vLog) - confHeight := vLog.BlockNumber + params.ConfirmationCount - if err == nil { - logger.Info().Msgf("Found (ERC20Custody.Withdrawn Event) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), vLog.TxHash.Hex()) - // sanity check tx event - err = ValidateEvmTxLog(vLog, addrCustody, transaction.Hash().Hex(), TopicsWithdrawn) - if err != nil { - logger.Error().Err(err).Msgf("CheckEvmTxLog error on Withdrawn event, chain %d nonce %d txhash %s", ob.chain.ChainId, nonce, transaction.Hash().Hex()) - return false, false, err - } - if confHeight <= ob.GetLastBlockHeight() { - logger.Info().Msg("Confirmed! Sending PostConfirmation to zetabridge...") - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendHash, - vLog.TxHash.Hex(), - vLog.BlockNumber, - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - event.Amount, - chains.ReceiveStatus_Success, - ob.chain, - nonce, - coin.CoinType_ERC20, - ) - if err != nil { - logger.Error().Err(err).Msgf("error posting confirmation to meta core for cctx %s nonce %d", sendHash, nonce) - continue - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) - } - return true, true, nil - } - logger.Info().Msgf("Included; %d blocks before confirmed! chain %s nonce %d", confHeight-ob.GetLastBlockHeight(), ob.chain.String(), nonce) - return true, false, nil - } - } - } else { - logger.Info().Msgf("Found (failed tx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), receipt.TxHash.Hex()) - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendHash, - receipt.TxHash.Hex(), - receipt.BlockNumber.Uint64(), - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - big.NewInt(0), - chains.ReceiveStatus_Failed, - ob.chain, - nonce, - coin.CoinType_ERC20, - ) - if err != nil { - logger.Error().Err(err).Msgf("PostVoteOutbound error in WatchTxHashWithTimeout; zeta tx hash %s", zetaTxHash) - } else if zetaTxHash != "" { - logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) - } - return true, true, nil - } + // parse the received value from the outtx receipt + receiveStatus, receiveValue, err = ParseOuttxReceivedValue(receipt, transaction, cointype, connectorAddr, connector, custodyAddr, custody, sendHash, txHash) + if err != nil { + logger.Error().Err(err).Msgf("IsSendOutTxProcessed: error parsing outtx event for chain %d txhash %s", ob.chain.ChainId, txHash) + return false, false, err } - return false, false, nil + // post vote to zetacore + ob.PostVoteOutbound(sendHash, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) + return true, true, nil +} + +// PostVoteOutbound posts vote to zetacore for the confirmed outtx +func (ob *ChainClient) PostVoteOutbound( + sendHash string, + receipt *ethtypes.Receipt, + transaction *ethtypes.Transaction, + receiveValue *big.Int, + receiveStatus chains.ReceiveStatus, + nonce uint64, + cointype coin.CoinType, + logger zerolog.Logger, +) { + chainID := ob.chain.ChainId + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( + sendHash, + receipt.TxHash.Hex(), + receipt.BlockNumber.Uint64(), + receipt.GasUsed, + transaction.GasPrice(), + transaction.Gas(), + receiveValue, + receiveStatus, + ob.chain, + nonce, + cointype, + ) + if err != nil { + logger.Error().Err(err).Msgf("PostVoteOutbound: error posting vote for chain %d nonce %d outtx %s ", chainID, nonce, receipt.TxHash) + } else if zetaTxHash != "" { + logger.Info().Msgf("PostVoteOutbound: posted vote for chain %d nonce %d outtx %s vote %s ballot %s", chainID, nonce, receipt.TxHash, zetaTxHash, ballot) + } } // WatchOutTx watches evm chain for outgoing txs status diff --git a/zetaclient/evm/outbounds.go b/zetaclient/evm/outbounds.go new file mode 100644 index 0000000000..2420919b23 --- /dev/null +++ b/zetaclient/evm/outbounds.go @@ -0,0 +1,125 @@ +package evm + +import ( + "fmt" + "math/big" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" +) + +// ParseZetaEvent either returns a parsed ZetaReceived or ZetaReverted event +func ParseZetaEvent( + receipt *ethtypes.Receipt, + connectorAddr ethcommon.Address, + connector *zetaconnector.ZetaConnectorNonEth, + sendHash string, + txHash string) (*zetaconnector.ZetaConnectorNonEthZetaReceived, *zetaconnector.ZetaConnectorNonEthZetaReverted, error) { + for _, vLog := range receipt.Logs { + receivedEvent, err := connector.ZetaConnectorNonEthFilterer.ParseZetaReceived(*vLog) + if err == nil { + // sanity check tx event + err = ValidateEvmTxLog(vLog, connectorAddr, txHash, TopicsZetaReceived) + if err != nil { + return nil, nil, errors.Wrapf(err, "error validating ZetaReceived event from outtx %s", txHash) + } + if vLog.Topics[3].Hex() != sendHash { + return nil, nil, fmt.Errorf("cctx index mismatch in ZetaReceived event from outtx %s, want %s got %s", txHash, sendHash, vLog.Topics[3].Hex()) + } + return receivedEvent, nil, nil + } + revertedEvent, err := connector.ZetaConnectorNonEthFilterer.ParseZetaReverted(*vLog) + if err == nil { + // sanity check tx event + err = ValidateEvmTxLog(vLog, connectorAddr, receipt.TxHash.Hex(), TopicsZetaReverted) + if err != nil { + return nil, nil, errors.Wrapf(err, "error validating ZetaReverted event from outtx %s", txHash) + } + return nil, revertedEvent, nil + } + } + return nil, nil, fmt.Errorf("no ZetaReceived/ZetaReverted event found in outtx %s", txHash) +} + +// ParseERC20WithdrawnEvent returns a parsed ERC20CustodyWithdrawn event from the outtx receipt +func ParseERC20WithdrawnEvent( + receipt *ethtypes.Receipt, + custodyAddr ethcommon.Address, + custody *erc20custody.ERC20Custody, + txHash string) (*erc20custody.ERC20CustodyWithdrawn, error) { + for _, vLog := range receipt.Logs { + withdrawnEvent, err := custody.ParseWithdrawn(*vLog) + if err == nil { + // sanity check tx event + err = ValidateEvmTxLog(vLog, custodyAddr, receipt.TxHash.Hex(), TopicsWithdrawn) + if err != nil { + return nil, errors.Wrapf(err, "error validating ERC20CustodyWithdrawn event from outtx %s", txHash) + } + return withdrawnEvent, nil + } + } + return nil, fmt.Errorf("no ERC20CustodyWithdrawn event found in outtx %s", txHash) +} + +// ParseOuttxReceivedValue parses the received value from the outtx receipt +func ParseOuttxReceivedValue( + receipt *ethtypes.Receipt, + transaction *ethtypes.Transaction, + cointype coin.CoinType, + connectorAddress ethcommon.Address, + connector *zetaconnector.ZetaConnectorNonEth, + custodyAddress ethcommon.Address, + custody *erc20custody.ERC20Custody, + sendHash string, + txHash string) (chains.ReceiveStatus, *big.Int, error) { + // determine the receive status and value + var receiveStatus chains.ReceiveStatus + var receiveValue *big.Int + switch receipt.Status { + case ethtypes.ReceiptStatusSuccessful: + receiveStatus = chains.ReceiveStatus_Success + receiveValue = transaction.Value() + case ethtypes.ReceiptStatusFailed: + receiveStatus = chains.ReceiveStatus_Failed + receiveValue = big.NewInt(0) + default: + // https://docs.nethereum.com/en/latest/nethereum-receipt-status/ + return chains.ReceiveStatus_Failed, nil, fmt.Errorf("unknown tx receipt status %d for outtx %s", receipt.Status, receipt.TxHash) + } + + // parse receive value from the outtx receipt for Zeta and ERC20 + switch cointype { + case coin.CoinType_Zeta: + if receipt.Status == ethtypes.ReceiptStatusSuccessful { + receivedLog, revertedLog, err := ParseZetaEvent(receipt, connectorAddress, connector, sendHash, txHash) + if err != nil { + return chains.ReceiveStatus_Failed, nil, err + } + // use the value in ZetaReceived/ZetaReverted event for vote message + if receivedLog != nil { + receiveValue = receivedLog.ZetaValue + } else if revertedLog != nil { + receiveValue = revertedLog.RemainingZetaValue + } + } + case coin.CoinType_ERC20: + if receipt.Status == ethtypes.ReceiptStatusSuccessful { + withdrawn, err := ParseERC20WithdrawnEvent(receipt, custodyAddress, custody, txHash) + if err != nil { + return chains.ReceiveStatus_Failed, nil, err + } + // use the value in Withdrawn event for vote message + receiveValue = withdrawn.Amount + } + case coin.CoinType_Gas, coin.CoinType_Cmd: + // nothing to do for CoinType_Gas/CoinType_Cmd, no need to parse event + default: + return chains.ReceiveStatus_Failed, nil, fmt.Errorf("unknown coin type %s for outtx %s", cointype, txHash) + } + return receiveStatus, receiveValue, nil +} diff --git a/zetaclient/zetabridge/tx.go b/zetaclient/zetabridge/tx.go index 90e44b16f5..9eef0007bd 100644 --- a/zetaclient/zetabridge/tx.go +++ b/zetaclient/zetabridge/tx.go @@ -10,6 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz" "github.com/pkg/errors" + "github.com/rs/zerolog" "github.com/zeta-chain/go-tss/blame" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" @@ -130,11 +131,12 @@ func (b *ZetaCoreBridge) SetTSS(tssPubkey string, keyGenZetaHeight int64, status func (b *ZetaCoreBridge) CoreContextUpdater(appContext *appcontext.AppContext) { b.logger.Info().Msg("CoreContextUpdater started") ticker := time.NewTicker(time.Duration(appContext.Config().ConfigUpdateTicker) * time.Second) + sampledLogger := b.logger.Sample(&zerolog.BasicSampler{N: 10}) for { select { case <-ticker.C: b.logger.Debug().Msg("Running Updater") - err := b.UpdateZetaCoreContext(appContext.ZetaCoreContext(), false) + err := b.UpdateZetaCoreContext(appContext.ZetaCoreContext(), false, sampledLogger) if err != nil { b.logger.Err(err).Msg("CoreContextUpdater failed to update config") } diff --git a/zetaclient/zetabridge/zetacore_bridge.go b/zetaclient/zetabridge/zetacore_bridge.go index d8a29fa665..e7be6fd8c1 100644 --- a/zetaclient/zetabridge/zetacore_bridge.go +++ b/zetaclient/zetabridge/zetacore_bridge.go @@ -195,7 +195,7 @@ func (b *ZetaCoreBridge) GetKeys() *keys.Keys { // UpdateZetaCoreContext updates core context // zetacore stores core context for all clients -func (b *ZetaCoreBridge) UpdateZetaCoreContext(coreContext *corecontext.ZetaCoreContext, init bool) error { +func (b *ZetaCoreBridge) UpdateZetaCoreContext(coreContext *corecontext.ZetaCoreContext, init bool, sampledLogger zerolog.Logger) error { bn, err := b.GetZetaBlockHeight() if err != nil { return err @@ -220,7 +220,6 @@ func (b *ZetaCoreBridge) UpdateZetaCoreContext(coreContext *corecontext.ZetaCore var newBTCParams *observertypes.ChainParams // check and update chain params for each chain - sampledLogger := b.logger.Sample(&zerolog.BasicSampler{N: 10}) for _, chainParam := range chainParams { if !chainParam.GetIsSupported() { sampledLogger.Info().Msgf("Chain %d is not supported yet", chainParam.ChainId) From 4e6943fddbaeda008aa619b0b82b506150289e36 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 10 Apr 2024 16:48:43 -0500 Subject: [PATCH 2/6] simplified IsSendOutTxProcessed() method and added unit tests --- changelog.md | 1 + cmd/zetaclientd/debug.go | 2 +- zetaclient/bitcoin/bitcoin_client.go | 14 +- zetaclient/evm/evm_client.go | 104 +---- zetaclient/evm/evm_client_test.go | 70 ++++ zetaclient/evm/inbounds.go | 14 +- zetaclient/evm/inbounds_test.go | 77 +--- zetaclient/evm/outbound_transaction_data.go | 4 +- zetaclient/evm/outbounds.go | 204 +++++++-- zetaclient/evm/outbounds_test.go | 390 ++++++++++++++++++ zetaclient/interfaces/interfaces.go | 2 +- zetaclient/testdata/cctx/cctx_1337_14.json | 55 +++ zetaclient/testdata/cctx/cctx_1_8014.json | 35 ++ zetaclient/testdata/cctx/cctx_1_9718.json | 32 ++ ...f15b8390b73108b69f3de5c1b2efe456036a7.json | 15 + ...f15b8390b73108b69f3de5c1b2efe456036a7.json | 57 +++ ...bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json | 15 + ...7c7bdb5318e8caf37f5a687b7a91e50a7257f.json | 15 + ...bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json | 44 ++ ...7c7bdb5318e8caf37f5a687b7a91e50a7257f.json | 45 ++ zetaclient/testutils/constant.go | 20 +- zetaclient/testutils/stub/chain_client.go | 4 +- zetaclient/testutils/stub/core_bridge.go | 3 +- zetaclient/testutils/testdata.go | 31 +- zetaclient/testutils/testdata_naming.go | 8 +- zetaclient/zetabridge/tx.go | 4 +- zetaclient/zetacore_observer.go | 8 +- 27 files changed, 1037 insertions(+), 236 deletions(-) create mode 100644 zetaclient/evm/outbounds_test.go create mode 100644 zetaclient/testdata/cctx/cctx_1337_14.json create mode 100644 zetaclient/testdata/cctx/cctx_1_8014.json create mode 100644 zetaclient/testdata/cctx/cctx_1_9718.json create mode 100644 zetaclient/testdata/evm/chain_1337_outtx_Zeta_0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7.json create mode 100644 zetaclient/testdata/evm/chain_1337_outtx_receipt_Zeta_ZetaReverted_0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7.json create mode 100644 zetaclient/testdata/evm/chain_1_outtx_ERC20_0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json create mode 100644 zetaclient/testdata/evm/chain_1_outtx_Zeta_0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f.json create mode 100644 zetaclient/testdata/evm/chain_1_outtx_receipt_ERC20_Withdrawn_0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json create mode 100644 zetaclient/testdata/evm/chain_1_outtx_receipt_Zeta_ZetaReceived_0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f.json diff --git a/changelog.md b/changelog.md index 1fa1f6d681..9341006cf4 100644 --- a/changelog.md +++ b/changelog.md @@ -26,6 +26,7 @@ * [1936](https://github.com/zeta-chain/node/pull/1936) - refactor common package into subpackages and rename to pkg * [1966](https://github.com/zeta-chain/node/pull/1966) - move TSS vote message from crosschain to observer * [1853](https://github.com/zeta-chain/node/pull/1853) - refactor vote inbound tx and vote outbound tx +* [1989](https://github.com/zeta-chain/node/pull/1989) - simplify `IsSendOutTxProcessed` method and add unit tests ### Features diff --git a/cmd/zetaclientd/debug.go b/cmd/zetaclientd/debug.go index 32cb32c772..b784488ee0 100644 --- a/cmd/zetaclientd/debug.go +++ b/cmd/zetaclientd/debug.go @@ -100,7 +100,7 @@ func DebugCmd() *cobra.Command { ob := evm.ChainClient{ Mu: &sync.Mutex{}, } - ob.WithZetaClient(bridge) + ob.WithZetaBridge(bridge) ob.WithLogger(chainLogger) var ethRPC *ethrpc.EthRPC var client *ethclient.Client diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index 110c89c474..f1124014e0 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -504,15 +504,15 @@ func (ob *BTCChainClient) ConfirmationsThreshold(amount *big.Int) int64 { return int64(ob.GetChainParams().ConfirmationCount) } -// IsSendOutTxProcessed returns isIncluded(or inMempool), isConfirmed, Error -func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { +// IsCctxOutTxProcessed returns isIncluded(or inMempool), isConfirmed, Error +func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { params := *cctx.GetCurrentOutTxParam() sendHash := cctx.Index nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce // get broadcasted outtx and tx result outTxID := ob.GetTxID(nonce) - logger.Info().Msgf("IsSendOutTxProcessed %s", outTxID) + logger.Info().Msgf("IsCctxOutTxProcessed %s", outTxID) ob.Mu.Lock() txnHash, broadcasted := ob.broadcastedTx[outTxID] @@ -537,7 +537,7 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger if txResult == nil { // check failed, try again next time return false, false, nil } else if inMempool { // still in mempool (should avoid unnecessary Tss keysign) - ob.logger.OutTx.Info().Msgf("IsSendOutTxProcessed: outTx %s is still in mempool", outTxID) + ob.logger.OutTx.Info().Msgf("IsCctxOutTxProcessed: outTx %s is still in mempool", outTxID) return true, false, nil } // included @@ -548,7 +548,7 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger if res == nil { return false, false, nil } - ob.logger.OutTx.Info().Msgf("IsSendOutTxProcessed: setIncludedTx succeeded for outTx %s", outTxID) + ob.logger.OutTx.Info().Msgf("IsCctxOutTxProcessed: setIncludedTx succeeded for outTx %s", outTxID) } // It's safe to use cctx's amount to post confirmation because it has already been verified in observeOutTx() @@ -573,9 +573,9 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger coin.CoinType_Gas, ) if err != nil { - logger.Error().Err(err).Msgf("IsSendOutTxProcessed: error confirming bitcoin outTx %s, nonce %d ballot %s", res.TxID, nonce, ballot) + logger.Error().Err(err).Msgf("IsCctxOutTxProcessed: error confirming bitcoin outTx %s, nonce %d ballot %s", res.TxID, nonce, ballot) } else if zetaHash != "" { - logger.Info().Msgf("IsSendOutTxProcessed: confirmed Bitcoin outTx %s, zeta tx hash %s nonce %d ballot %s", res.TxID, zetaHash, nonce, ballot) + logger.Info().Msgf("IsCctxOutTxProcessed: confirmed Bitcoin outTx %s, zeta tx hash %s nonce %d ballot %s", res.TxID, zetaHash, nonce, ballot) } return true, true, nil } diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index dc77b682ba..c62bc39c44 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -31,11 +31,9 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/proofs" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" - "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" "github.com/zeta-chain/zetacore/zetaclient/interfaces" @@ -72,7 +70,7 @@ type ChainClient struct { chain chains.Chain evmClient interfaces.EVMRPCClient evmJSONRPC interfaces.EVMJSONRPCClient - zetaClient interfaces.ZetaCoreBridger + zetaBridge interfaces.ZetaCoreBridger Tss interfaces.TSSSigner lastBlockScanned uint64 lastBlock uint64 @@ -123,7 +121,7 @@ func NewEVMChainClient( ob.stop = make(chan struct{}) ob.chain = evmCfg.Chain ob.Mu = &sync.Mutex{} - ob.zetaClient = bridge + ob.zetaBridge = bridge ob.txWatchList = make(map[ethcommon.Hash]string) ob.Tss = tss ob.outTxPendingTransactions = make(map[string]*ethtypes.Transaction) @@ -188,10 +186,10 @@ func (ob *ChainClient) WithEvmJSONRPC(client interfaces.EVMJSONRPCClient) { ob.evmJSONRPC = client } -func (ob *ChainClient) WithZetaClient(bridge interfaces.ZetaCoreBridger) { +func (ob *ChainClient) WithZetaBridge(bridge interfaces.ZetaCoreBridger) { ob.Mu.Lock() defer ob.Mu.Unlock() - ob.zetaClient = bridge + ob.zetaBridge = bridge } func (ob *ChainClient) WithBlockCache(cache *lru.Cache) { @@ -317,92 +315,6 @@ func (ob *ChainClient) Stop() { ob.logger.Chain.Info().Msgf("%s observer stopped", ob.chain.String()) } -// IsSendOutTxProcessed checks outtx status and returns (isIncluded, isConfirmed, error) -// It also posts vote to zetacore if the tx is confirmed -func (ob *ChainClient) IsSendOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { - sendHash := cctx.Index - cointype := cctx.InboundTxParams.CoinType - nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce - - // skip if outtx is not confirmed - if !ob.isTxConfirmed(nonce) { - return false, false, nil - } - receipt, transaction := ob.GetTxNReceipt(nonce) - sendID := fmt.Sprintf("%d-%d", ob.chain.ChainId, nonce) - logger = logger.With().Str("sendID", sendID).Logger() - - // get connector and erce20Custody contracts - connectorAddr, connector, err := ob.GetConnectorContract() - if err != nil { - return false, false, errors.Wrapf(err, "error getting zeta connector for chain %d", ob.chain.ChainId) - } - custodyAddr, custody, err := ob.GetERC20CustodyContract() - if err != nil { - return false, false, errors.Wrapf(err, "error getting erc20 custody for chain %d", ob.chain.ChainId) - } - - // define a few common variables - txHash := receipt.TxHash.Hex() - var receiveValue *big.Int - var receiveStatus chains.ReceiveStatus - - // compliance check, special handling the cancelled cctx - if compliance.IsCctxRestricted(cctx) { - // use cctx's amount to bypass the amount check in zetacore - receiveValue = cctx.GetCurrentOutTxParam().Amount.BigInt() - receiveStatus := chains.ReceiveStatus_Failed - if receipt.Status == 1 { - receiveStatus = chains.ReceiveStatus_Success - } - ob.PostVoteOutbound(sendHash, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) - return true, true, nil - } - - // parse the received value from the outtx receipt - receiveStatus, receiveValue, err = ParseOuttxReceivedValue(receipt, transaction, cointype, connectorAddr, connector, custodyAddr, custody, sendHash, txHash) - if err != nil { - logger.Error().Err(err).Msgf("IsSendOutTxProcessed: error parsing outtx event for chain %d txhash %s", ob.chain.ChainId, txHash) - return false, false, err - } - - // post vote to zetacore - ob.PostVoteOutbound(sendHash, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) - return true, true, nil -} - -// PostVoteOutbound posts vote to zetacore for the confirmed outtx -func (ob *ChainClient) PostVoteOutbound( - sendHash string, - receipt *ethtypes.Receipt, - transaction *ethtypes.Transaction, - receiveValue *big.Int, - receiveStatus chains.ReceiveStatus, - nonce uint64, - cointype coin.CoinType, - logger zerolog.Logger, -) { - chainID := ob.chain.ChainId - zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( - sendHash, - receipt.TxHash.Hex(), - receipt.BlockNumber.Uint64(), - receipt.GasUsed, - transaction.GasPrice(), - transaction.Gas(), - receiveValue, - receiveStatus, - ob.chain, - nonce, - cointype, - ) - if err != nil { - logger.Error().Err(err).Msgf("PostVoteOutbound: error posting vote for chain %d nonce %d outtx %s ", chainID, nonce, receipt.TxHash) - } else if zetaTxHash != "" { - logger.Info().Msgf("PostVoteOutbound: posted vote for chain %d nonce %d outtx %s vote %s ballot %s", chainID, nonce, receipt.TxHash, zetaTxHash, ballot) - } -} - // WatchOutTx watches evm chain for outgoing txs status func (ob *ChainClient) WatchOutTx() { ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_WatchOutTx_%d", ob.chain.ChainId), ob.GetChainParams().OutTxTicker) @@ -421,7 +333,7 @@ func (ob *ChainClient) WatchOutTx() { sampledLogger.Info().Msgf("WatchOutTx: outbound observation is disabled for chain %d", ob.chain.ChainId) continue } - trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) + trackers, err := ob.zetaBridge.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) if err != nil { continue } @@ -662,7 +574,7 @@ func (ob *ChainClient) calcBlockRangeToScan(latestConfirmed, lastScanned, batchS func (ob *ChainClient) postBlockHeader(tip uint64) error { bn := tip - res, err := ob.zetaClient.GetBlockHeaderStateByChain(ob.chain.ChainId) + res, err := ob.zetaBridge.GetBlockHeaderStateByChain(ob.chain.ChainId) if err == nil && res.BlockHeaderState != nil && res.BlockHeaderState.EarliestHeight > 0 { // #nosec G701 always positive bn = uint64(res.BlockHeaderState.LatestHeight) + 1 // the next header to post @@ -683,7 +595,7 @@ func (ob *ChainClient) postBlockHeader(tip uint64) error { return err } - _, err = ob.zetaClient.PostAddBlockHeader( + _, err = ob.zetaBridge.PostAddBlockHeader( ob.chain.ChainId, header.Hash().Bytes(), header.Number.Int64(), @@ -991,7 +903,7 @@ func (ob *ChainClient) PostGasPrice() error { // SUPPLY supply := "100" // lockedAmount on ETH, totalSupply on other chains - zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, gasPrice.Uint64(), supply, blockNum) + zetaHash, err := ob.zetaBridge.PostGasPrice(ob.chain, gasPrice.Uint64(), supply, blockNum) if err != nil { ob.logger.GasPrice.Err(err).Msg("PostGasPrice to zetabridge failed") return err diff --git a/zetaclient/evm/evm_client_test.go b/zetaclient/evm/evm_client_test.go index 7596931f3e..4fc947288e 100644 --- a/zetaclient/evm/evm_client_test.go +++ b/zetaclient/evm/evm_client_test.go @@ -7,14 +7,84 @@ import ( "cosmossdk.io/math" lru "github.com/hashicorp/golang-lru" "github.com/onrik/ethrpc" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" + "github.com/zeta-chain/zetacore/zetaclient/common" + "github.com/zeta-chain/zetacore/zetaclient/config" + corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" "github.com/zeta-chain/zetacore/zetaclient/evm" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" "github.com/zeta-chain/zetacore/zetaclient/testutils" + "github.com/zeta-chain/zetacore/zetaclient/testutils/stub" ) +// getAppContext creates an app context for unit tests +func getAppContext(evmChain chains.Chain, evmChainParams *observertypes.ChainParams) (*appcontext.AppContext, config.EVMConfig) { + // create config + cfg := config.NewConfig() + cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ + Chain: evmChain, + Endpoint: "http://localhost:8545", + } + // create core context + coreCtx := corecontext.NewZetaCoreContext(cfg) + evmChainParamsMap := make(map[int64]*observertypes.ChainParams) + evmChainParamsMap[evmChain.ChainId] = evmChainParams + ccFlags := *sample.CrosschainFlags() + + // feed chain params + coreCtx.Update( + &observertypes.Keygen{}, + []chains.Chain{evmChain}, + evmChainParamsMap, + nil, + "", + ccFlags, + true, + zerolog.Logger{}, + ) + // create app context + appCtx := appcontext.NewAppContext(coreCtx, cfg) + return appCtx, cfg.EVMChainConfigs[evmChain.ChainId] +} + +// MockEVMClient creates a mock ChainClient with custom chain, TSS, params etc +func MockEVMClient( + t *testing.T, + chain chains.Chain, + evmClient interfaces.EVMRPCClient, + evmJSONRPC interfaces.EVMJSONRPCClient, + zetaBridge interfaces.ZetaCoreBridger, + tss interfaces.TSSSigner, + lastBlock uint64, + params observertypes.ChainParams) *evm.ChainClient { + // use default mock bridge if not provided + if zetaBridge == nil { + zetaBridge = stub.NewMockZetaCoreBridge() + } + // use default mock tss if not provided + if tss == nil { + tss = stub.NewTSSMainnet() + } + // create app context + appCtx, evmCfg := getAppContext(chain, ¶ms) + + // create chain client + client, err := evm.NewEVMChainClient(appCtx, zetaBridge, tss, "", common.ClientLogger{}, evmCfg, nil) + require.NoError(t, err) + client.WithEvmClient(evmClient) + client.WithEvmJSONRPC(evmJSONRPC) + client.SetLastBlockHeight(lastBlock) + + return client +} + func TestEVM_BlockCache(t *testing.T) { // create client blockCache, err := lru.New(1000) diff --git a/zetaclient/evm/inbounds.go b/zetaclient/evm/inbounds.go index 819ab09f77..0ee0e0457f 100644 --- a/zetaclient/evm/inbounds.go +++ b/zetaclient/evm/inbounds.go @@ -60,7 +60,7 @@ func (ob *ChainClient) WatchIntxTracker() { // ObserveIntxTrackers observes the inbound trackers for the chain func (ob *ChainClient) ObserveIntxTrackers() error { - trackers, err := ob.zetaClient.GetInboundTrackersForChain(ob.chain.ChainId) + trackers, err := ob.zetaBridge.GetInboundTrackersForChain(ob.chain.ChainId) if err != nil { return err } @@ -206,7 +206,7 @@ func (ob *ChainClient) CheckAndVoteInboundTokenGas(tx *ethrpc.Transaction, recei func (ob *ChainClient) PostVoteInbound(msg *types.MsgVoteOnObservedInboundTx, coinType coin.CoinType, retryGasLimit uint64) (string, error) { txHash := msg.InTxHash chainID := ob.chain.ChainId - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, retryGasLimit, msg) + zetaHash, ballot, err := ob.zetaBridge.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, retryGasLimit, msg) if err != nil { ob.logger.InTx.Err(err).Msgf("intx detected: error posting vote for chain %d token %s intx %s", chainID, coinType, txHash) return "", err @@ -252,7 +252,7 @@ func (ob *ChainClient) BuildInboundVoteMsgForDepositedEvent(event *erc20custody. ob.chain.ChainId, "", clienttypes.BytesToEthHex(event.Recipient), - ob.zetaClient.ZetaChain().ChainId, + ob.zetaBridge.ZetaChain().ChainId, sdkmath.NewUintFromBigInt(event.Amount), hex.EncodeToString(event.Message), event.Raw.TxHash.Hex(), @@ -260,7 +260,7 @@ func (ob *ChainClient) BuildInboundVoteMsgForDepositedEvent(event *erc20custody. 1_500_000, coin.CoinType_ERC20, event.Asset.String(), - ob.zetaClient.GetKeys().GetOperatorAddress().String(), + ob.zetaBridge.GetKeys().GetOperatorAddress().String(), event.Raw.Index, ) } @@ -311,7 +311,7 @@ func (ob *ChainClient) BuildInboundVoteMsgForZetaSentEvent(event *zetaconnector. event.DestinationGasLimit.Uint64(), coin.CoinType_Zeta, "", - ob.zetaClient.GetKeys().GetOperatorAddress().String(), + ob.zetaBridge.GetKeys().GetOperatorAddress().String(), event.Raw.Index, ) } @@ -347,7 +347,7 @@ func (ob *ChainClient) BuildInboundVoteMsgForTokenSentToTSS(tx *ethrpc.Transacti ob.chain.ChainId, sender.Hex(), sender.Hex(), - ob.zetaClient.ZetaChain().ChainId, + ob.zetaBridge.ZetaChain().ChainId, sdkmath.NewUintFromBigInt(&tx.Value), message, tx.Hash, @@ -355,7 +355,7 @@ func (ob *ChainClient) BuildInboundVoteMsgForTokenSentToTSS(tx *ethrpc.Transacti 90_000, coin.CoinType_Gas, "", - ob.zetaClient.GetKeys().GetOperatorAddress().String(), + ob.zetaBridge.GetKeys().GetOperatorAddress().String(), 0, // not a smart contract call ) } diff --git a/zetaclient/evm/inbounds_test.go b/zetaclient/evm/inbounds_test.go index e24c66b4d5..1b5a10fc82 100644 --- a/zetaclient/evm/inbounds_test.go +++ b/zetaclient/evm/inbounds_test.go @@ -2,55 +2,22 @@ package evm_test import ( "encoding/hex" - "sync" "testing" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - lru "github.com/hashicorp/golang-lru" "github.com/onrik/ethrpc" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/constant" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/evm" - "github.com/zeta-chain/zetacore/zetaclient/interfaces" "github.com/zeta-chain/zetacore/zetaclient/testutils" "github.com/zeta-chain/zetacore/zetaclient/testutils/stub" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" ) -// MockEVMClient creates a mock ChainClient with custom chain, TSS, params etc -func MockEVMClient( - chain chains.Chain, - evmClient interfaces.EVMRPCClient, - evmJSONRPC interfaces.EVMJSONRPCClient, - zetClient interfaces.ZetaCoreBridger, - tss interfaces.TSSSigner, - lastBlock uint64, - params observertypes.ChainParams) *evm.ChainClient { - client := &evm.ChainClient{ - Tss: tss, - Mu: &sync.Mutex{}, - } - client.WithChain(chain) - client.WithEvmClient(evmClient) - client.WithEvmJSONRPC(evmJSONRPC) - if zetClient != nil { - client.WithZetaClient(zetClient) - } else { - client.WithZetaClient(stub.NewMockZetaCoreBridge()) - } - client.SetLastBlockHeight(lastBlock) - client.SetChainParams(params) - blockCache, _ := lru.New(1000) - client.WithBlockCache(blockCache) - - return client -} - func TestEVM_CheckAndVoteInboundTokenZeta(t *testing.T) { // load archived ZetaSent intx, receipt and cctx // https://etherscan.io/tx/0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76 @@ -65,7 +32,7 @@ func TestEVM_CheckAndVoteInboundTokenZeta(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundTxParams.InboundTxBallotIndex, ballot) @@ -75,7 +42,7 @@ func TestEVM_CheckAndVoteInboundTokenZeta(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) _, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) @@ -85,7 +52,7 @@ func TestEVM_CheckAndVoteInboundTokenZeta(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, true) require.NoError(t, err) require.Equal(t, "", ballot) @@ -96,7 +63,7 @@ func TestEVM_CheckAndVoteInboundTokenZeta(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation chainID = 56 // use BSC chain connector - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, stub.MockChainParams(chainID, confirmation)) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, stub.MockChainParams(chainID, confirmation)) _, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, true) require.ErrorContains(t, err, "emitter address mismatch") }) @@ -116,7 +83,7 @@ func TestEVM_CheckAndVoteInboundTokenERC20(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundTxParams.InboundTxBallotIndex, ballot) @@ -126,7 +93,7 @@ func TestEVM_CheckAndVoteInboundTokenERC20(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) _, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) @@ -136,7 +103,7 @@ func TestEVM_CheckAndVoteInboundTokenERC20(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, true) require.NoError(t, err) require.Equal(t, "", ballot) @@ -147,7 +114,7 @@ func TestEVM_CheckAndVoteInboundTokenERC20(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation chainID = 56 // use BSC chain ERC20 custody - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, stub.MockChainParams(chainID, confirmation)) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, stub.MockChainParams(chainID, confirmation)) _, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, true) require.ErrorContains(t, err, "emitter address mismatch") }) @@ -167,7 +134,7 @@ func TestEVM_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundTxParams.InboundTxBallotIndex, ballot) @@ -177,7 +144,7 @@ func TestEVM_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) _, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) @@ -187,7 +154,7 @@ func TestEVM_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.ErrorContains(t, err, "not TSS address") require.Equal(t, "", ballot) @@ -198,7 +165,7 @@ func TestEVM_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.ErrorContains(t, err, "not a successful tx") require.Equal(t, "", ballot) @@ -209,7 +176,7 @@ func TestEVM_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMClient(chain, nil, nil, nil, stub.NewTSSMainnet(), lastBlock, chainParam) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.NoError(t, err) require.Equal(t, "", ballot) @@ -226,7 +193,7 @@ func TestEVM_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { cctx := testutils.LoadEVMIntxCctx(t, chainID, intxHash, coin.CoinType_Zeta) // parse ZetaSent event - ob := MockEVMClient(chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1)) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1)) connector := stub.MockConnectorNonEth(chainID) event := testutils.ParseReceiptZetaSent(receipt, connector) @@ -273,7 +240,7 @@ func TestEVM_BuildInboundVoteMsgForDepositedEvent(t *testing.T) { cctx := testutils.LoadEVMIntxCctx(t, chainID, intxHash, coin.CoinType_ERC20) // parse Deposited event - ob := MockEVMClient(chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1)) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1)) custody := stub.MockERC20Custody(chainID) event := testutils.ParseReceiptERC20Deposited(receipt, custody) sender := ethcommon.HexToAddress(tx.From) @@ -325,7 +292,7 @@ func TestEVM_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(txDonation)) // create test compliance config - ob := MockEVMClient(chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1)) + ob := MockEVMClient(t, chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1)) cfg := config.Config{ ComplianceConfig: config.ComplianceConfig{}, } @@ -378,12 +345,12 @@ func TestEVM_ObserveTSSReceiveInBlock(t *testing.T) { // create mock client evmClient := stub.NewMockEvmClient() evmJSONRPC := stub.NewMockJSONRPCClient() - zetaClient := stub.NewMockZetaCoreBridge() + zetaBridge := stub.NewMockZetaCoreBridge() tss := stub.NewTSSMainnet() lastBlock := receipt.BlockNumber.Uint64() + confirmation t.Run("should observe TSS receive in block", func(t *testing.T) { - ob := MockEVMClient(chain, evmClient, evmJSONRPC, zetaClient, tss, lastBlock, chainParam) + ob := MockEVMClient(t, chain, evmClient, evmJSONRPC, zetaBridge, tss, lastBlock, chainParam) // feed archived block and receipt evmJSONRPC.WithBlock(block) @@ -392,25 +359,25 @@ func TestEVM_ObserveTSSReceiveInBlock(t *testing.T) { require.NoError(t, err) }) t.Run("should not observe on error getting block", func(t *testing.T) { - ob := MockEVMClient(chain, evmClient, evmJSONRPC, zetaClient, tss, lastBlock, chainParam) + ob := MockEVMClient(t, chain, evmClient, evmJSONRPC, zetaBridge, tss, lastBlock, chainParam) err := ob.ObserveTSSReceiveInBlock(blockNumber) // error getting block is expected because the mock JSONRPC contains no block require.ErrorContains(t, err, "error getting block") }) t.Run("should not observe on error getting receipt", func(t *testing.T) { - ob := MockEVMClient(chain, evmClient, evmJSONRPC, zetaClient, tss, lastBlock, chainParam) + ob := MockEVMClient(t, chain, evmClient, evmJSONRPC, zetaBridge, tss, lastBlock, chainParam) evmJSONRPC.WithBlock(block) err := ob.ObserveTSSReceiveInBlock(blockNumber) // error getting block is expected because the mock evmClient contains no receipt require.ErrorContains(t, err, "error getting receipt") }) t.Run("should not observe on error posting vote", func(t *testing.T) { - ob := MockEVMClient(chain, evmClient, evmJSONRPC, zetaClient, tss, lastBlock, chainParam) + ob := MockEVMClient(t, chain, evmClient, evmJSONRPC, zetaBridge, tss, lastBlock, chainParam) // feed archived block and pause zeta bridge evmJSONRPC.WithBlock(block) evmClient.WithReceipt(receipt) - zetaClient.Pause() + zetaBridge.Pause() err := ob.ObserveTSSReceiveInBlock(blockNumber) // error posting vote is expected because the mock zetaClient is paused require.ErrorContains(t, err, "error checking and voting") diff --git a/zetaclient/evm/outbound_transaction_data.go b/zetaclient/evm/outbound_transaction_data.go index 6734926f0d..25f59e5e2e 100644 --- a/zetaclient/evm/outbound_transaction_data.go +++ b/zetaclient/evm/outbound_transaction_data.go @@ -135,9 +135,9 @@ func NewOutBoundTransactionData( // Get nonce, Early return if the cctx is already processed nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce - included, confirmed, err := evmClient.IsSendOutTxProcessed(cctx, logger) + included, confirmed, err := evmClient.IsCctxOutTxProcessed(cctx, logger) if err != nil { - return nil, true, errors.New("IsSendOutTxProcessed failed") + return nil, true, errors.New("IsCctxOutTxProcessed failed") } if included || confirmed { logger.Info().Msgf("CCTX already processed; exit signer") diff --git a/zetaclient/evm/outbounds.go b/zetaclient/evm/outbounds.go index 2420919b23..cda7403828 100644 --- a/zetaclient/evm/outbounds.go +++ b/zetaclient/evm/outbounds.go @@ -1,104 +1,220 @@ package evm import ( + "encoding/hex" "fmt" "math/big" + "strings" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/rs/zerolog" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/compliance" ) -// ParseZetaEvent either returns a parsed ZetaReceived or ZetaReverted event -func ParseZetaEvent( +// PostVoteOutbound posts vote to zetacore for the confirmed outtx +func (ob *ChainClient) PostVoteOutbound( + cctxIndex string, + receipt *ethtypes.Receipt, + transaction *ethtypes.Transaction, + receiveValue *big.Int, + receiveStatus chains.ReceiveStatus, + nonce uint64, + cointype coin.CoinType, + logger zerolog.Logger, +) { + chainID := ob.chain.ChainId + zetaTxHash, ballot, err := ob.zetaBridge.PostVoteOutbound( + cctxIndex, + receipt.TxHash.Hex(), + receipt.BlockNumber.Uint64(), + receipt.GasUsed, + transaction.GasPrice(), + transaction.Gas(), + receiveValue, + receiveStatus, + ob.chain, + nonce, + cointype, + ) + if err != nil { + logger.Error().Err(err).Msgf("PostVoteOutbound: error posting vote for chain %d nonce %d outtx %s ", chainID, nonce, receipt.TxHash) + } else if zetaTxHash != "" { + logger.Info().Msgf("PostVoteOutbound: posted vote for chain %d nonce %d outtx %s vote %s ballot %s", chainID, nonce, receipt.TxHash, zetaTxHash, ballot) + } +} + +// IsCctxOutTxProcessed checks outtx status and returns (isIncluded, isConfirmed, error) +// It also posts vote to zetacore if the tx is confirmed +func (ob *ChainClient) IsCctxOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { + // skip if outtx is not confirmed + nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce + if !ob.isTxConfirmed(nonce) { + return false, false, nil + } + receipt, transaction := ob.GetTxNReceipt(nonce) + sendID := fmt.Sprintf("%d-%d", ob.chain.ChainId, nonce) + logger = logger.With().Str("sendID", sendID).Logger() + + // get connector and erce20Custody contracts + connectorAddr, connector, err := ob.GetConnectorContract() + if err != nil { + return false, false, errors.Wrapf(err, "error getting zeta connector for chain %d", ob.chain.ChainId) + } + custodyAddr, custody, err := ob.GetERC20CustodyContract() + if err != nil { + return false, false, errors.Wrapf(err, "error getting erc20 custody for chain %d", ob.chain.ChainId) + } + + // define a few common variables + var receiveValue *big.Int + var receiveStatus chains.ReceiveStatus + cointype := cctx.InboundTxParams.CoinType + + // compliance check, special handling the cancelled cctx + if compliance.IsCctxRestricted(cctx) { + // use cctx's amount to bypass the amount check in zetacore + receiveValue = cctx.GetCurrentOutTxParam().Amount.BigInt() + receiveStatus := chains.ReceiveStatus_Failed + if receipt.Status == ethtypes.ReceiptStatusSuccessful { + receiveStatus = chains.ReceiveStatus_Success + } + ob.PostVoteOutbound(cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) + return true, true, nil + } + + // parse the received value from the outtx receipt + receiveValue, receiveStatus, err = ParseOuttxReceivedValue(cctx, receipt, transaction, cointype, connectorAddr, connector, custodyAddr, custody) + if err != nil { + logger.Error().Err(err).Msgf("IsCctxOutTxProcessed: error parsing outtx event for chain %d txhash %s", ob.chain.ChainId, receipt.TxHash) + return false, false, err + } + + // post vote to zetacore + ob.PostVoteOutbound(cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) + return true, true, nil +} + +// ParseAndCheckZetaEvent parses and checks ZetaReceived/ZetaReverted event from the outtx receipt +func ParseAndCheckZetaEvent( + cctx *crosschaintypes.CrossChainTx, receipt *ethtypes.Receipt, connectorAddr ethcommon.Address, connector *zetaconnector.ZetaConnectorNonEth, - sendHash string, - txHash string) (*zetaconnector.ZetaConnectorNonEthZetaReceived, *zetaconnector.ZetaConnectorNonEthZetaReverted, error) { +) (*zetaconnector.ZetaConnectorNonEthZetaReceived, *zetaconnector.ZetaConnectorNonEthZetaReverted, error) { + params := cctx.GetCurrentOutTxParam() for _, vLog := range receipt.Logs { - receivedEvent, err := connector.ZetaConnectorNonEthFilterer.ParseZetaReceived(*vLog) + // try parsing ZetaReceived event + received, err := connector.ZetaConnectorNonEthFilterer.ParseZetaReceived(*vLog) if err == nil { - // sanity check tx event - err = ValidateEvmTxLog(vLog, connectorAddr, txHash, TopicsZetaReceived) + err = ValidateEvmTxLog(vLog, connectorAddr, receipt.TxHash.Hex(), TopicsZetaReceived) if err != nil { - return nil, nil, errors.Wrapf(err, "error validating ZetaReceived event from outtx %s", txHash) + return nil, nil, errors.Wrap(err, "error validating ZetaReceived event") + } + if !strings.EqualFold(received.DestinationAddress.Hex(), params.Receiver) { + return nil, nil, fmt.Errorf("receiver address mismatch in ZetaReceived event, want %s got %s", + params.Receiver, received.DestinationAddress.Hex()) } - if vLog.Topics[3].Hex() != sendHash { - return nil, nil, fmt.Errorf("cctx index mismatch in ZetaReceived event from outtx %s, want %s got %s", txHash, sendHash, vLog.Topics[3].Hex()) + if received.ZetaValue.Cmp(params.Amount.BigInt()) != 0 { + return nil, nil, fmt.Errorf("amount mismatch in ZetaReceived event, want %s got %s", + params.Amount.String(), received.ZetaValue.String()) } - return receivedEvent, nil, nil + if ethcommon.BytesToHash(received.InternalSendHash[:]).Hex() != cctx.Index { + return nil, nil, fmt.Errorf("cctx index mismatch in ZetaReceived event, want %s got %s", + cctx.Index, hex.EncodeToString(received.InternalSendHash[:])) + } + return received, nil, nil } - revertedEvent, err := connector.ZetaConnectorNonEthFilterer.ParseZetaReverted(*vLog) + // try parsing ZetaReverted event + reverted, err := connector.ZetaConnectorNonEthFilterer.ParseZetaReverted(*vLog) if err == nil { - // sanity check tx event err = ValidateEvmTxLog(vLog, connectorAddr, receipt.TxHash.Hex(), TopicsZetaReverted) if err != nil { - return nil, nil, errors.Wrapf(err, "error validating ZetaReverted event from outtx %s", txHash) + return nil, nil, errors.Wrap(err, "error validating ZetaReverted event") + } + if !strings.EqualFold(ethcommon.BytesToAddress(reverted.DestinationAddress[:]).Hex(), cctx.InboundTxParams.Sender) { + return nil, nil, fmt.Errorf("receiver address mismatch in ZetaReverted event, want %s got %s", + cctx.InboundTxParams.Sender, ethcommon.BytesToAddress(reverted.DestinationAddress[:]).Hex()) + } + if reverted.RemainingZetaValue.Cmp(params.Amount.BigInt()) != 0 { + return nil, nil, fmt.Errorf("amount mismatch in ZetaReverted event, want %s got %s", + params.Amount.String(), reverted.RemainingZetaValue.String()) + } + if ethcommon.BytesToHash(reverted.InternalSendHash[:]).Hex() != cctx.Index { + return nil, nil, fmt.Errorf("cctx index mismatch in ZetaReverted event, want %s got %s", + cctx.Index, hex.EncodeToString(reverted.InternalSendHash[:])) } - return nil, revertedEvent, nil + return nil, reverted, nil } } - return nil, nil, fmt.Errorf("no ZetaReceived/ZetaReverted event found in outtx %s", txHash) + return nil, nil, errors.New("no ZetaReceived/ZetaReverted event found") } -// ParseERC20WithdrawnEvent returns a parsed ERC20CustodyWithdrawn event from the outtx receipt -func ParseERC20WithdrawnEvent( +// ParseAndCheckWithdrawnEvent parses and checks erc20 Withdrawn event from the outtx receipt +func ParseAndCheckWithdrawnEvent( + cctx *crosschaintypes.CrossChainTx, receipt *ethtypes.Receipt, custodyAddr ethcommon.Address, custody *erc20custody.ERC20Custody, - txHash string) (*erc20custody.ERC20CustodyWithdrawn, error) { +) (*erc20custody.ERC20CustodyWithdrawn, error) { + params := cctx.GetCurrentOutTxParam() for _, vLog := range receipt.Logs { - withdrawnEvent, err := custody.ParseWithdrawn(*vLog) + withdrawn, err := custody.ParseWithdrawn(*vLog) if err == nil { - // sanity check tx event err = ValidateEvmTxLog(vLog, custodyAddr, receipt.TxHash.Hex(), TopicsWithdrawn) if err != nil { - return nil, errors.Wrapf(err, "error validating ERC20CustodyWithdrawn event from outtx %s", txHash) + return nil, errors.Wrap(err, "error validating Withdrawn event") } - return withdrawnEvent, nil + if !strings.EqualFold(withdrawn.Recipient.Hex(), params.Receiver) { + return nil, fmt.Errorf("receiver address mismatch in Withdrawn event, want %s got %s", + params.Receiver, withdrawn.Recipient.Hex()) + } + if !strings.EqualFold(withdrawn.Asset.Hex(), cctx.InboundTxParams.Asset) { + return nil, fmt.Errorf("asset mismatch in Withdrawn event, want %s got %s", + cctx.InboundTxParams.Asset, withdrawn.Asset.Hex()) + } + if withdrawn.Amount.Cmp(params.Amount.BigInt()) != 0 { + return nil, fmt.Errorf("amount mismatch in Withdrawn event, want %s got %s", + params.Amount.String(), withdrawn.Amount.String()) + } + return withdrawn, nil } } - return nil, fmt.Errorf("no ERC20CustodyWithdrawn event found in outtx %s", txHash) + return nil, errors.New("no ERC20 Withdrawn event found") } // ParseOuttxReceivedValue parses the received value from the outtx receipt func ParseOuttxReceivedValue( + cctx *crosschaintypes.CrossChainTx, receipt *ethtypes.Receipt, transaction *ethtypes.Transaction, cointype coin.CoinType, connectorAddress ethcommon.Address, connector *zetaconnector.ZetaConnectorNonEth, custodyAddress ethcommon.Address, - custody *erc20custody.ERC20Custody, - sendHash string, - txHash string) (chains.ReceiveStatus, *big.Int, error) { + custody *erc20custody.ERC20Custody) (*big.Int, chains.ReceiveStatus, error) { // determine the receive status and value - var receiveStatus chains.ReceiveStatus - var receiveValue *big.Int - switch receipt.Status { - case ethtypes.ReceiptStatusSuccessful: - receiveStatus = chains.ReceiveStatus_Success + // https://docs.nethereum.com/en/latest/nethereum-receipt-status/ + receiveValue := big.NewInt(0) + receiveStatus := chains.ReceiveStatus_Failed + if receipt.Status == ethtypes.ReceiptStatusSuccessful { receiveValue = transaction.Value() - case ethtypes.ReceiptStatusFailed: - receiveStatus = chains.ReceiveStatus_Failed - receiveValue = big.NewInt(0) - default: - // https://docs.nethereum.com/en/latest/nethereum-receipt-status/ - return chains.ReceiveStatus_Failed, nil, fmt.Errorf("unknown tx receipt status %d for outtx %s", receipt.Status, receipt.TxHash) + receiveStatus = chains.ReceiveStatus_Success } // parse receive value from the outtx receipt for Zeta and ERC20 switch cointype { case coin.CoinType_Zeta: if receipt.Status == ethtypes.ReceiptStatusSuccessful { - receivedLog, revertedLog, err := ParseZetaEvent(receipt, connectorAddress, connector, sendHash, txHash) + receivedLog, revertedLog, err := ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) if err != nil { - return chains.ReceiveStatus_Failed, nil, err + return nil, chains.ReceiveStatus_Failed, err } // use the value in ZetaReceived/ZetaReverted event for vote message if receivedLog != nil { @@ -109,9 +225,9 @@ func ParseOuttxReceivedValue( } case coin.CoinType_ERC20: if receipt.Status == ethtypes.ReceiptStatusSuccessful { - withdrawn, err := ParseERC20WithdrawnEvent(receipt, custodyAddress, custody, txHash) + withdrawn, err := ParseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) if err != nil { - return chains.ReceiveStatus_Failed, nil, err + return nil, chains.ReceiveStatus_Failed, err } // use the value in Withdrawn event for vote message receiveValue = withdrawn.Amount @@ -119,7 +235,7 @@ func ParseOuttxReceivedValue( case coin.CoinType_Gas, coin.CoinType_Cmd: // nothing to do for CoinType_Gas/CoinType_Cmd, no need to parse event default: - return chains.ReceiveStatus_Failed, nil, fmt.Errorf("unknown coin type %s for outtx %s", cointype, txHash) + return nil, chains.ReceiveStatus_Failed, fmt.Errorf("unknown coin type %s", cointype) } - return receiveStatus, receiveValue, nil + return receiveValue, receiveStatus, nil } diff --git a/zetaclient/evm/outbounds_test.go b/zetaclient/evm/outbounds_test.go new file mode 100644 index 0000000000..df167b1fc9 --- /dev/null +++ b/zetaclient/evm/outbounds_test.go @@ -0,0 +1,390 @@ +package evm_test + +import ( + "testing" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/testutil/sample" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/evm" + "github.com/zeta-chain/zetacore/zetaclient/testutils" + "github.com/zeta-chain/zetacore/zetaclient/testutils/stub" +) + +// getContractsByChainID is a helper func to get contracts and addresses by chainID +func getContractsByChainID(chainID int64) (*zetaconnector.ZetaConnectorNonEth, ethcommon.Address, *erc20custody.ERC20Custody, ethcommon.Address) { + connector := stub.MockConnectorNonEth(chainID) + connectorAddress := testutils.ConnectorAddresses[chainID] + custody := stub.MockERC20Custody(chainID) + custodyAddress := testutils.CustodyAddresses[chainID] + return connector, connectorAddress, custody, custodyAddress +} + +func Test_PostVoteOutbound(t *testing.T) { + // Note: outtx of Gas/ERC20 token can also be used for this test + // load archived cctx, outtx and receipt for a ZetaReceived event + // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f + chain := chains.EthChain() + nonce := uint64(9718) + coinType := coin.CoinType_Zeta + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chain.ChainId, nonce, testutils.EventZetaReceived) + + t.Run("post vote outbound successfully", func(t *testing.T) { + // the amount and status to be used for vote + receiveValue := cctx.GetCurrentOutTxParam().Amount.BigInt() + receiveStatus := chains.ReceiveStatus_Success + + // create evm client using mock zetaBridge and post outbound vote + zetaBridge := stub.NewMockZetaCoreBridge() + client := MockEVMClient(t, chain, nil, nil, zetaBridge, nil, 1, observertypes.ChainParams{}) + client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{}) + + // pause the mock zetaBridge to simulate error posting vote + zetaBridge.Pause() + client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{}) + }) +} + +func Test_IsCctxOutTxProcessed(t *testing.T) { + // load archived outtx receipt that contains ZetaReceived event + // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f + chain := chains.EthChain() + chainID := chains.EthChain().ChainId + nonce := uint64(9718) + chainParam := stub.MockChainParams(chain.ChainId, 1) + outtxHash := "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f" + cctx := testutils.LoadCctxByNonce(t, chainID, nonce) + receipt := testutils.LoadEVMOuttxReceipt(t, chainID, outtxHash, coin.CoinType_Zeta, testutils.EventZetaReceived) + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, testutils.EventZetaReceived) + + t.Run("should post vote and return true if outtx is processed", func(t *testing.T) { + // create evm client and set outtx and receipt + client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam) + client.SetTxNReceipt(nonce, receipt, outtx) + // post outbound vote + isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{}) + require.NoError(t, err) + require.True(t, isIncluded) + require.True(t, isConfirmed) + }) + t.Run("should post vote and return true on restricted address", func(t *testing.T) { + // load cctx and modify sender address to arbitrary address + // Note: other tests cases will fail if we use the original sender address because the + // compliance config is globally set and will impact other tests when running in parallel + cctx := testutils.LoadCctxByNonce(t, chainID, nonce) + cctx.InboundTxParams.Sender = sample.EthAddress().Hex() + + // create evm client and set outtx and receipt + client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam) + client.SetTxNReceipt(nonce, receipt, outtx) + + // modify compliance config to restrict sender address + cfg := config.Config{ + ComplianceConfig: config.ComplianceConfig{}, + } + cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundTxParams.Sender} + config.LoadComplianceConfig(cfg) + + // post outbound vote + isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{}) + require.NoError(t, err) + require.True(t, isIncluded) + require.True(t, isConfirmed) + }) + t.Run("should return false if outtx is not confirmed", func(t *testing.T) { + // create evm client and DO NOT set outtx as confirmed + client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam) + isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{}) + require.NoError(t, err) + require.False(t, isIncluded) + require.False(t, isConfirmed) + }) + t.Run("should fail if unable to parse ZetaReceived event", func(t *testing.T) { + // create evm client and set outtx and receipt + client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam) + client.SetTxNReceipt(nonce, receipt, outtx) + + // set connector contract address to an arbitrary address to make event parsing fail + chainParamsNew := client.GetChainParams() + chainParamsNew.ConnectorContractAddress = sample.EthAddress().Hex() + client.SetChainParams(chainParamsNew) + isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{}) + require.Error(t, err) + require.False(t, isIncluded) + require.False(t, isConfirmed) + }) +} + +func Test_ParseZetaReceived(t *testing.T) { + // load archived outtx receipt that contains ZetaReceived event + // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f + chainID := chains.EthChain().ChainId + nonce := uint64(9718) + outtxHash := "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f" + connector := stub.MockConnectorNonEth(chainID) + connectorAddress := testutils.ConnectorAddresses[chainID] + cctx := testutils.LoadCctxByNonce(t, chainID, nonce) + receipt := testutils.LoadEVMOuttxReceipt(t, chainID, outtxHash, coin.CoinType_Zeta, testutils.EventZetaReceived) + + t.Run("should parse ZetaReceived event from archived outtx receipt", func(t *testing.T) { + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + require.NoError(t, err) + require.NotNil(t, receivedLog) + require.Nil(t, revertedLog) + }) + t.Run("should fail on connector address mismatch", func(t *testing.T) { + // use an arbitrary address to make validation fail + fakeConnectorAddress := sample.EthAddress() + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(cctx, receipt, fakeConnectorAddress, connector) + require.ErrorContains(t, err, "error validating ZetaReceived event") + require.Nil(t, revertedLog) + require.Nil(t, receivedLog) + }) + t.Run("should fail on receiver address mismatch", func(t *testing.T) { + // load cctx and set receiver address to an arbitrary address + fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) + fakeCctx.GetCurrentOutTxParam().Receiver = sample.EthAddress().Hex() + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) + require.ErrorContains(t, err, "receiver address mismatch") + require.Nil(t, revertedLog) + require.Nil(t, receivedLog) + }) + t.Run("should fail on amount mismatch", func(t *testing.T) { + // load cctx and set amount to an arbitrary wrong value + fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) + fakeAmount := sample.UintInRange(0, fakeCctx.GetCurrentOutTxParam().Amount.Uint64()-1) + fakeCctx.GetCurrentOutTxParam().Amount = fakeAmount + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) + require.ErrorContains(t, err, "amount mismatch") + require.Nil(t, revertedLog) + require.Nil(t, receivedLog) + }) + t.Run("should fail on cctx index mismatch", func(t *testing.T) { + cctx.Index = sample.Hash().Hex() // use an arbitrary index + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + require.ErrorContains(t, err, "cctx index mismatch") + require.Nil(t, revertedLog) + require.Nil(t, receivedLog) + }) + t.Run("should fail if no event found in receipt", func(t *testing.T) { + // load receipt and remove ZetaReceived event from logs + receipt := testutils.LoadEVMOuttxReceipt(t, chainID, outtxHash, coin.CoinType_Zeta, testutils.EventZetaReceived) + receipt.Logs = receipt.Logs[:1] // the 2nd log is ZetaReceived event + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + require.ErrorContains(t, err, "no ZetaReceived/ZetaReverted event") + require.Nil(t, revertedLog) + require.Nil(t, receivedLog) + }) +} + +func Test_ParseZetaReverted(t *testing.T) { + // load archived outtx receipt that contains ZetaReverted event + chainID := chains.GoerliLocalnetChain().ChainId + nonce := uint64(14) + outtxHash := "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7" + connector := stub.MockConnectorNonEth(chainID) + connectorAddress := testutils.ConnectorAddresses[chainID] + cctx := testutils.LoadCctxByNonce(t, chainID, nonce) + receipt := testutils.LoadEVMOuttxReceipt(t, chainID, outtxHash, coin.CoinType_Zeta, testutils.EventZetaReverted) + + t.Run("should parse ZetaReverted event from archived outtx receipt", func(t *testing.T) { + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + require.NoError(t, err) + require.Nil(t, receivedLog) + require.NotNil(t, revertedLog) + }) + t.Run("should fail on connector address mismatch", func(t *testing.T) { + // use an arbitrary address to make validation fail + fakeConnectorAddress := sample.EthAddress() + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(cctx, receipt, fakeConnectorAddress, connector) + require.ErrorContains(t, err, "error validating ZetaReverted event") + require.Nil(t, receivedLog) + require.Nil(t, revertedLog) + }) + t.Run("should fail on receiver address mismatch", func(t *testing.T) { + // load cctx and set receiver address to an arbitrary address + fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) + fakeCctx.InboundTxParams.Sender = sample.EthAddress().Hex() // the receiver is the sender for reverted ccxt + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) + require.ErrorContains(t, err, "receiver address mismatch") + require.Nil(t, revertedLog) + require.Nil(t, receivedLog) + }) + t.Run("should fail on amount mismatch", func(t *testing.T) { + // load cctx and set amount to an arbitrary wrong value + fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) + fakeAmount := sample.UintInRange(0, fakeCctx.GetCurrentOutTxParam().Amount.Uint64()-1) + fakeCctx.GetCurrentOutTxParam().Amount = fakeAmount + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) + require.ErrorContains(t, err, "amount mismatch") + require.Nil(t, revertedLog) + require.Nil(t, receivedLog) + }) + t.Run("should fail on cctx index mismatch", func(t *testing.T) { + cctx.Index = sample.Hash().Hex() // use an arbitrary index to make validation fail + receivedLog, revertedLog, err := evm.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + require.ErrorContains(t, err, "cctx index mismatch") + require.Nil(t, receivedLog) + require.Nil(t, revertedLog) + }) +} + +func Test_ParseERC20WithdrawnEvent(t *testing.T) { + // load archived outtx receipt that contains ERC20 Withdrawn event + chainID := chains.EthChain().ChainId + nonce := uint64(8014) + outtxHash := "0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146" + custody := stub.MockERC20Custody(chainID) + custodyAddress := testutils.CustodyAddresses[chainID] + cctx := testutils.LoadCctxByNonce(t, chainID, nonce) + receipt := testutils.LoadEVMOuttxReceipt(t, chainID, outtxHash, coin.CoinType_ERC20, testutils.EventERC20Withdraw) + + t.Run("should parse ERC20 Withdrawn event from archived outtx receipt", func(t *testing.T) { + withdrawn, err := evm.ParseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) + require.NoError(t, err) + require.NotNil(t, withdrawn) + }) + t.Run("should fail on erc20 custody address mismatch", func(t *testing.T) { + // use an arbitrary address to make validation fail + fakeCustodyAddress := sample.EthAddress() + withdrawn, err := evm.ParseAndCheckWithdrawnEvent(cctx, receipt, fakeCustodyAddress, custody) + require.ErrorContains(t, err, "error validating Withdrawn event") + require.Nil(t, withdrawn) + }) + t.Run("should fail on receiver address mismatch", func(t *testing.T) { + // load cctx and set receiver address to an arbitrary address + fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) + fakeCctx.GetCurrentOutTxParam().Receiver = sample.EthAddress().Hex() + withdrawn, err := evm.ParseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) + require.ErrorContains(t, err, "receiver address mismatch") + require.Nil(t, withdrawn) + }) + t.Run("should fail on asset mismatch", func(t *testing.T) { + // load cctx and set asset to an arbitrary address + fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) + fakeCctx.InboundTxParams.Asset = sample.EthAddress().Hex() + withdrawn, err := evm.ParseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) + require.ErrorContains(t, err, "asset mismatch") + require.Nil(t, withdrawn) + }) + t.Run("should fail on amount mismatch", func(t *testing.T) { + // load cctx and set amount to an arbitrary wrong value + fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) + fakeAmount := sample.UintInRange(0, fakeCctx.GetCurrentOutTxParam().Amount.Uint64()-1) + fakeCctx.GetCurrentOutTxParam().Amount = fakeAmount + withdrawn, err := evm.ParseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) + require.ErrorContains(t, err, "amount mismatch") + require.Nil(t, withdrawn) + }) + t.Run("should fail if no Withdrawn event found in receipt", func(t *testing.T) { + // load receipt and remove Withdrawn event from logs + receipt := testutils.LoadEVMOuttxReceipt(t, chainID, outtxHash, coin.CoinType_ERC20, testutils.EventERC20Withdraw) + receipt.Logs = receipt.Logs[:1] // the 2nd log is Withdrawn event + withdrawn, err := evm.ParseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) + require.ErrorContains(t, err, "no ERC20 Withdrawn event") + require.Nil(t, withdrawn) + }) +} + +func Test_ParseOuttxReceivedValue(t *testing.T) { + chainID := chains.EthChain().ChainId + connector, connectorAddr, custody, custodyAddr := getContractsByChainID(chainID) + + t.Run("should parse and check ZetaReceived event from archived outtx receipt", func(t *testing.T) { + // load archived outtx receipt that contains ZetaReceived event + // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f + nonce := uint64(9718) + coinType := coin.CoinType_Zeta + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, testutils.EventZetaReceived) + params := cctx.GetCurrentOutTxParam() + value, status, err := evm.ParseOuttxReceivedValue(cctx, receipt, outtx, coinType, connectorAddr, connector, custodyAddr, custody) + require.NoError(t, err) + require.True(t, params.Amount.BigInt().Cmp(value) == 0) + require.Equal(t, chains.ReceiveStatus_Success, status) + }) + t.Run("should parse and check ZetaReverted event from archived outtx receipt", func(t *testing.T) { + // load archived outtx receipt that contains ZetaReverted event + // use local network tx: 0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7 + localChainID := chains.GoerliLocalnetChain().ChainId + nonce := uint64(14) + coinType := coin.CoinType_Zeta + connectorLocal, connectorAddrLocal, custodyLocal, custodyAddrLocal := getContractsByChainID(localChainID) + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, localChainID, nonce, testutils.EventZetaReverted) + params := cctx.GetCurrentOutTxParam() + value, status, err := evm.ParseOuttxReceivedValue( + cctx, receipt, outtx, coinType, connectorAddrLocal, connectorLocal, custodyAddrLocal, custodyLocal) + require.NoError(t, err) + require.True(t, params.Amount.BigInt().Cmp(value) == 0) + require.Equal(t, chains.ReceiveStatus_Success, status) + }) + t.Run("should parse and check ERC20 Withdrawn event from archived outtx receipt", func(t *testing.T) { + // load archived outtx receipt that contains ERC20 Withdrawn event + // https://etherscan.io/tx/0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146 + nonce := uint64(8014) + coinType := coin.CoinType_ERC20 + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, testutils.EventERC20Withdraw) + params := cctx.GetCurrentOutTxParam() + value, status, err := evm.ParseOuttxReceivedValue(cctx, receipt, outtx, coinType, connectorAddr, connector, custodyAddr, custody) + require.NoError(t, err) + require.True(t, params.Amount.BigInt().Cmp(value) == 0) + require.Equal(t, chains.ReceiveStatus_Success, status) + }) + t.Run("nothing to parse if coinType is Gas", func(t *testing.T) { + // load archived outtx receipt of Gas token transfer + // https://etherscan.io/tx/0xd13b593eb62b5500a00e288cc2fb2c8af1339025c0e6bc6183b8bef2ebbed0d3 + nonce := uint64(7260) + coinType := coin.CoinType_Gas + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, "") + params := cctx.GetCurrentOutTxParam() + value, status, err := evm.ParseOuttxReceivedValue(cctx, receipt, outtx, coinType, connectorAddr, connector, custodyAddr, custody) + require.NoError(t, err) + require.True(t, params.Amount.BigInt().Cmp(value) == 0) + require.Equal(t, chains.ReceiveStatus_Success, status) + }) + t.Run("should fail on unknown coin type", func(t *testing.T) { + // load archived outtx receipt that contains ZetaReceived event + // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f + nonce := uint64(9718) + coinType := coin.CoinType(5) // unknown coin type + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, testutils.EventZetaReceived) + value, status, err := evm.ParseOuttxReceivedValue(cctx, receipt, outtx, coinType, connectorAddr, connector, custodyAddr, custody) + require.ErrorContains(t, err, "unknown coin type") + require.Nil(t, value) + require.Equal(t, chains.ReceiveStatus_Failed, status) + }) + t.Run("should fail if unable to parse ZetaReceived event", func(t *testing.T) { + // load archived outtx receipt that contains ZetaReceived event + // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f + nonce := uint64(9718) + coinType := coin.CoinType_Zeta + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, testutils.EventZetaReceived) + + // use an arbitrary address to make event parsing fail + fakeConnectorAddress := sample.EthAddress() + value, status, err := evm.ParseOuttxReceivedValue(cctx, receipt, outtx, coinType, fakeConnectorAddress, connector, custodyAddr, custody) + require.Error(t, err) + require.Nil(t, value) + require.Equal(t, chains.ReceiveStatus_Failed, status) + }) + t.Run("should fail if unable to parse ERC20 Withdrawn event", func(t *testing.T) { + // load archived outtx receipt that contains ERC20 Withdrawn event + // https://etherscan.io/tx/0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146 + nonce := uint64(8014) + coinType := coin.CoinType_ERC20 + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, testutils.EventERC20Withdraw) + + // use an arbitrary address to make event parsing fail + fakeCustodyAddress := sample.EthAddress() + value, status, err := evm.ParseOuttxReceivedValue(cctx, receipt, outtx, coinType, connectorAddr, connector, fakeCustodyAddress, custody) + require.Error(t, err) + require.Nil(t, value) + require.Equal(t, chains.ReceiveStatus_Failed, status) + }) +} diff --git a/zetaclient/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index 1c01b7e486..a938d6af68 100644 --- a/zetaclient/interfaces/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -39,7 +39,7 @@ const ( type ChainClient interface { Start() Stop() - IsSendOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) + IsCctxOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) SetChainParams(observertypes.ChainParams) GetChainParams() observertypes.ChainParams GetTxID(nonce uint64) string diff --git a/zetaclient/testdata/cctx/cctx_1337_14.json b/zetaclient/testdata/cctx/cctx_1337_14.json new file mode 100644 index 0000000000..058849a351 --- /dev/null +++ b/zetaclient/testdata/cctx/cctx_1337_14.json @@ -0,0 +1,55 @@ +{ + "creator": "zeta1plfrp7ejn0s9tmwufuxvsyn8nlf6a7u9ndgk9m", + "index": "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df", + "zeta_fees": "4000000000009027082", + "relayed_message": "bgGCGUux3roBhJr9PgNaC3DOfLBp5ILuZjUZx2z1abQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ==", + "cctx_status": { + "status": 5, + "status_message": "Outbound failed, start revert : Outbound succeeded, revert executed", + "lastUpdate_timestamp": 1712705995 + }, + "inbound_tx_params": { + "sender": "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed", + "sender_chain_id": 1337, + "tx_origin": "0x5cC2fBb200A929B372e3016F1925DcF988E081fd", + "amount": "10000000000000000000", + "inbound_tx_observed_hash": "0xa5589bf24eca8f108ca35048adc9d5582a303d416c01319391159269ae7e4e6f", + "inbound_tx_observed_external_height": 177, + "inbound_tx_ballot_index": "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df", + "inbound_tx_finalized_zeta_height": 150, + "tx_finalization_status": 2 + }, + "outbound_tx_params": [ + { + "receiver": "0xbff76e77d56b3c1202107f059425d56f0aef87ed", + "receiver_chainId": 1337, + "amount": "7999999999995486459", + "outbound_tx_tss_nonce": 13, + "outbound_tx_gas_limit": 250000, + "outbound_tx_gas_price": "18", + "outbound_tx_hash": "0x19f99459da6cb08f917f9b0ee2dac94a7be328371dff788ad46e64a24e8c06c9", + "outbound_tx_observed_external_height": 187, + "outbound_tx_gas_used": 67852, + "outbound_tx_effective_gas_price": "18", + "outbound_tx_effective_gas_limit": 250000, + "tss_pubkey": "zetapub1addwnpepqggky6z958k7hhxs6k5quuvs27uv5vtmlv330ppt2362p8ejct88w4g64jv", + "tx_finalization_status": 2 + }, + { + "receiver": "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed", + "receiver_chainId": 1337, + "amount": "5999999999990972918", + "outbound_tx_tss_nonce": 14, + "outbound_tx_gas_limit": 250000, + "outbound_tx_gas_price": "18", + "outbound_tx_hash": "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7", + "outbound_tx_ballot_index": "0xc36c689fdaf09a9b80a614420cd4fea4fec15044790df60080cdefca0090a9dc", + "outbound_tx_observed_external_height": 201, + "outbound_tx_gas_used": 76128, + "outbound_tx_effective_gas_price": "18", + "outbound_tx_effective_gas_limit": 250000, + "tss_pubkey": "zetapub1addwnpepqggky6z958k7hhxs6k5quuvs27uv5vtmlv330ppt2362p8ejct88w4g64jv", + "tx_finalization_status": 2 + } + ] +} diff --git a/zetaclient/testdata/cctx/cctx_1_8014.json b/zetaclient/testdata/cctx/cctx_1_8014.json new file mode 100644 index 0000000000..b702f8d7f6 --- /dev/null +++ b/zetaclient/testdata/cctx/cctx_1_8014.json @@ -0,0 +1,35 @@ +{ + "index": "0x5a100fdb426da35ad4c95520d7a4f1fd2f38c88067c9e80ba209d3a655c6e06e", + "zeta_fees": "0", + "cctx_status": { "status": 3, "lastUpdate_timestamp": 1710834402 }, + "inbound_tx_params": { + "sender": "0x7c8dDa80bbBE1254a7aACf3219EBe1481c6E01d7", + "sender_chain_id": 7000, + "tx_origin": "0x8d8D67A8B71c141492825CAE5112Ccd8581073f2", + "coin_type": 2, + "asset": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "amount": "23726342442", + "inbound_tx_observed_hash": "0x114ed9d327b6afc068c3fa891b82f7c7f2d42ae25a571f7dc004c05e77af592a", + "inbound_tx_observed_external_height": 2241077, + "inbound_tx_ballot_index": "0x5a100fdb426da35ad4c95520d7a4f1fd2f38c88067c9e80ba209d3a655c6e06e" + }, + "outbound_tx_params": [ + { + "receiver": "0x8d8D67A8B71c141492825CAE5112Ccd8581073f2", + "receiver_chainId": 1, + "coin_type": 2, + "amount": "23726342442", + "outbound_tx_tss_nonce": 8014, + "outbound_tx_gas_limit": 100000, + "outbound_tx_gas_price": "58619665744", + "outbound_tx_hash": "0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146", + "outbound_tx_ballot_index": "0x4213f2c335758301b8bbb09d9891949ed6ffeea5dd95e5d9eaa8d410baaa0884", + "outbound_tx_observed_external_height": 19467367, + "outbound_tx_gas_used": 60625, + "outbound_tx_effective_gas_price": "58619665744", + "outbound_tx_effective_gas_limit": 100000, + "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + "tx_finalization_status": 2 + } + ] +} diff --git a/zetaclient/testdata/cctx/cctx_1_9718.json b/zetaclient/testdata/cctx/cctx_1_9718.json new file mode 100644 index 0000000000..ebd71301e2 --- /dev/null +++ b/zetaclient/testdata/cctx/cctx_1_9718.json @@ -0,0 +1,32 @@ +{ + "index": "0xbf7a214cf9868e1c618123ab4df0081da87bade74eeb5aef37843e35f25e67b7", + "zeta_fees": "19525506001302763608", + "cctx_status": { "status": 3, "lastUpdate_timestamp": 1712336965 }, + "inbound_tx_params": { + "sender": "0xF0a3F93Ed1B126142E61423F9546bf1323Ff82DF", + "sender_chain_id": 7000, + "tx_origin": "0x87257C910a19a3fe64AfFAbFe8cF9AAF2ab148BF", + "amount": "20000000000000000000", + "inbound_tx_observed_hash": "0xb136652cd58fb6a537b0a1677965983059a2004d98919cdacd52551f877cc44f", + "inbound_tx_observed_external_height": 2492552, + "inbound_tx_ballot_index": "0xbf7a214cf9868e1c618123ab4df0081da87bade74eeb5aef37843e35f25e67b7" + }, + "outbound_tx_params": [ + { + "receiver": "0x30735c88fa430f11499b0edcfcc25246fb9182e3", + "receiver_chainId": 1, + "amount": "474493998697236392", + "outbound_tx_tss_nonce": 9718, + "outbound_tx_gas_limit": 90000, + "outbound_tx_gas_price": "112217884384", + "outbound_tx_hash": "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f", + "outbound_tx_ballot_index": "0xff07eaa34ca02a08bca1558e5f6220cbfc734061f083622b24923e032f0c480f", + "outbound_tx_observed_external_height": 19590894, + "outbound_tx_gas_used": 64651, + "outbound_tx_effective_gas_price": "112217884384", + "outbound_tx_effective_gas_limit": 100000, + "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + "tx_finalization_status": 2 + } + ] +} diff --git a/zetaclient/testdata/evm/chain_1337_outtx_Zeta_0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7.json b/zetaclient/testdata/evm/chain_1337_outtx_Zeta_0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7.json new file mode 100644 index 0000000000..69fad53b1f --- /dev/null +++ b/zetaclient/testdata/evm/chain_1337_outtx_Zeta_0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7.json @@ -0,0 +1,15 @@ +{ + "type": "0x0", + "nonce": "0xe", + "gasPrice": "0x12", + "maxPriorityFeePerGas": null, + "maxFeePerGas": null, + "gas": "0x3d090", + "value": "0x0", + "input": "0x942a5e16000000000000000000000000bff76e77d56b3c1202107f059425d56f0aef87ed000000000000000000000000000000000000000000000000000000000000053900000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000053900000000000000000000000000000000000000000000000053444835ebce41f6000000000000000000000000000000000000000000000000000000000000012085d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df0000000000000000000000000000000000000000000000000000000000000014bff76e77d56b3c1202107f059425d56f0aef87ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000406e0182194bb1deba01849afd3e035a0b70ce7cb069e482ee663519c76cf569b40000000000000000000000000000000000000000000000000000000000000001", + "v": "0xa95", + "r": "0x79f72c2cbd8a0ce55d1130f11ea175c8fd02e93e6063f747ef70ac3cc74c3777", + "s": "0x6131d906f55dc4e6ca9a6f795307d75e9447b740a04fd32cdb4f7865e4521822", + "to": "0xd28d6a0b8189305551a0a8bd247a6eca9ce781ca", + "hash": "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7" +} diff --git a/zetaclient/testdata/evm/chain_1337_outtx_receipt_Zeta_ZetaReverted_0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7.json b/zetaclient/testdata/evm/chain_1337_outtx_receipt_Zeta_ZetaReverted_0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7.json new file mode 100644 index 0000000000..6e07f707bf --- /dev/null +++ b/zetaclient/testdata/evm/chain_1337_outtx_receipt_Zeta_ZetaReverted_0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7.json @@ -0,0 +1,57 @@ +{ + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x12960", + "logsBloom": "0x00000000000000000000000010400000000010000000000000000000000000000000200000200000000100000040000000020000000000800000000000000000000000000000000008000008000000000000000000000000000000000000000000000100000020000000000000000000000000000004020000000010000000000000000000000000020800000000010400800000000000000400000000000000000000000000000001000000000000000000000000000000000020000000000000000006000000000000000000002000000000000800000000000000000000004000001000000000000000000000000000000000000000000000000000000000", + "logs": [ + { + "address": "0x733ab8b06dddef27eaa72294b0d7c9cef7f12db9", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000d28d6a0b8189305551a0a8bd247a6eca9ce781ca", + "0x000000000000000000000000bff76e77d56b3c1202107f059425d56f0aef87ed" + ], + "data": "0x00000000000000000000000000000000000000000000000053444835ebce41f6", + "blockNumber": "0xc9", + "transactionHash": "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7", + "transactionIndex": "0x0", + "blockHash": "0x4d34bc01ece5cf216cb9817ba3e99a8383434aa4a3f20eef8bc19f6b691eed2b", + "logIndex": "0x0", + "removed": false + }, + { + "address": "0xbff76e77d56b3c1202107f059425d56f0aef87ed", + "topics": [ + "0x4f30bf4846ce4cde02361b3232cd2287313384a7b8e60161a1b2818b6905a521" + ], + "data": "0x", + "blockNumber": "0xc9", + "transactionHash": "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7", + "transactionIndex": "0x0", + "blockHash": "0x4d34bc01ece5cf216cb9817ba3e99a8383434aa4a3f20eef8bc19f6b691eed2b", + "logIndex": "0x1", + "removed": false + }, + { + "address": "0xd28d6a0b8189305551a0a8bd247a6eca9ce781ca", + "topics": [ + "0x521fb0b407c2eb9b1375530e9b9a569889992140a688bc076aa72c1712012c88", + "0x0000000000000000000000000000000000000000000000000000000000000539", + "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df" + ], + "data": "0x000000000000000000000000bff76e77d56b3c1202107f059425d56f0aef87ed000000000000000000000000000000000000000000000000000000000000053900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000053444835ebce41f600000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000014bff76e77d56b3c1202107f059425d56f0aef87ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000406e0182194bb1deba01849afd3e035a0b70ce7cb069e482ee663519c76cf569b40000000000000000000000000000000000000000000000000000000000000001", + "blockNumber": "0xc9", + "transactionHash": "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7", + "transactionIndex": "0x0", + "blockHash": "0x4d34bc01ece5cf216cb9817ba3e99a8383434aa4a3f20eef8bc19f6b691eed2b", + "logIndex": "0x2", + "removed": false + } + ], + "transactionHash": "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x12960", + "blockHash": "0x4d34bc01ece5cf216cb9817ba3e99a8383434aa4a3f20eef8bc19f6b691eed2b", + "blockNumber": "0xc9", + "transactionIndex": "0x0" +} diff --git a/zetaclient/testdata/evm/chain_1_outtx_ERC20_0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json b/zetaclient/testdata/evm/chain_1_outtx_ERC20_0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json new file mode 100644 index 0000000000..88141e8967 --- /dev/null +++ b/zetaclient/testdata/evm/chain_1_outtx_ERC20_0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json @@ -0,0 +1,15 @@ +{ + "type": "0x0", + "nonce": "0x1f4e", + "gasPrice": "0xda6011d50", + "maxPriorityFeePerGas": null, + "maxFeePerGas": null, + "gas": "0x186a0", + "value": "0x0", + "input": "0xd9caed120000000000000000000000008d8d67a8b71c141492825cae5112ccd8581073f2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000058633412a", + "v": "0x25", + "r": "0x7e05da110efc400146318c287f630812117eab9225baa7181f783861f088ab83", + "s": "0x560380aacfe105b0922483594c9f5e046c372dc266c54b61e37168a956b7d42b", + "to": "0x0000030ec64df25301d8414ee5a29588c4b0de10", + "hash": "0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146" +} diff --git a/zetaclient/testdata/evm/chain_1_outtx_Zeta_0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f.json b/zetaclient/testdata/evm/chain_1_outtx_Zeta_0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f.json new file mode 100644 index 0000000000..662139e1d7 --- /dev/null +++ b/zetaclient/testdata/evm/chain_1_outtx_Zeta_0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f.json @@ -0,0 +1,15 @@ +{ + "type": "0x0", + "nonce": "0x25f6", + "gasPrice": "0x1a20b506e0", + "maxPriorityFeePerGas": null, + "maxFeePerGas": null, + "gas": "0x186a0", + "value": "0x0", + "input": "0x29dd214d00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000001b5800000000000000000000000030735c88fa430f11499b0edcfcc25246fb9182e30000000000000000000000000000000000000000000000000695bdc7206747a80000000000000000000000000000000000000000000000000000000000000100bf7a214cf9868e1c618123ab4df0081da87bade74eeb5aef37843e35f25e67b70000000000000000000000000000000000000000000000000000000000000014f0a3f93ed1b126142e61423f9546bf1323ff82df0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "v": "0x26", + "r": "0x4691ef5c56d90ae924c07505da70eae69bf172c885b7cd6fbe660f8d0d679f49", + "s": "0x47cd582bf871d63e3ef93924ec45a94a9077721dc1a470dc6aa04c93496c5841", + "to": "0x000007cf399229b2f5a4d043f20e90c9c98b7c6a", + "hash": "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f" +} diff --git a/zetaclient/testdata/evm/chain_1_outtx_receipt_ERC20_Withdrawn_0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json b/zetaclient/testdata/evm/chain_1_outtx_receipt_ERC20_Withdrawn_0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json new file mode 100644 index 0000000000..02da3f6ae9 --- /dev/null +++ b/zetaclient/testdata/evm/chain_1_outtx_receipt_ERC20_Withdrawn_0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146.json @@ -0,0 +1,44 @@ +{ + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x26b5b5", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000010000000000000000000000400000000000000000000000000000000008000000000000000000000000000002001000000000000000000000000000800000000000000000200000000000000010000000000000000020000000000000000000000000000000000000000000000200100000000000000000000002000080000000000000000000000000040000000000004000000002000000000000000000000000000000020000000010000000000000000000000800000000000000080000000000000000000000000000000000000000", + "logs": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000030ec64df25301d8414ee5a29588c4b0de10", + "0x0000000000000000000000008d8d67a8b71c141492825cae5112ccd8581073f2" + ], + "data": "0x000000000000000000000000000000000000000000000000000000058633412a", + "blockNumber": "0x1290c67", + "transactionHash": "0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146", + "transactionIndex": "0x15", + "blockHash": "0x599baa79442ea7b4958fc9c24f71269fd7a9dfe7bef8c7f7b6f382afacf06164", + "logIndex": "0x55", + "removed": false + }, + { + "address": "0x0000030ec64df25301d8414ee5a29588c4b0de10", + "topics": [ + "0xd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb", + "0x0000000000000000000000008d8d67a8b71c141492825cae5112ccd8581073f2", + "0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7" + ], + "data": "0x000000000000000000000000000000000000000000000000000000058633412a", + "blockNumber": "0x1290c67", + "transactionHash": "0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146", + "transactionIndex": "0x15", + "blockHash": "0x599baa79442ea7b4958fc9c24f71269fd7a9dfe7bef8c7f7b6f382afacf06164", + "logIndex": "0x56", + "removed": false + } + ], + "transactionHash": "0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0xecd1", + "blockHash": "0x599baa79442ea7b4958fc9c24f71269fd7a9dfe7bef8c7f7b6f382afacf06164", + "blockNumber": "0x1290c67", + "transactionIndex": "0x15" +} diff --git a/zetaclient/testdata/evm/chain_1_outtx_receipt_Zeta_ZetaReceived_0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f.json b/zetaclient/testdata/evm/chain_1_outtx_receipt_Zeta_ZetaReceived_0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f.json new file mode 100644 index 0000000000..b0b3070c3a --- /dev/null +++ b/zetaclient/testdata/evm/chain_1_outtx_receipt_Zeta_ZetaReceived_0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f.json @@ -0,0 +1,45 @@ +{ + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x18d886", + "logsBloom": "0x00000000000000000000000000000000001000000000000000000000080000000000000000000000000000000000002008041000000000000000000000000000800000008000000000000008000000000000000000000000000000000000080000000000100000000000040000000000000000000000000000000010000000000000000000000100000000010000000000000002000000000000000000000000000000000000000000000000000000000000000000000002000000000000200000000002000000000000000000000080000000000000000000200000000000000000100000000000080000000000000000000000000000000000002000000000", + "logs": [ + { + "address": "0xf091867ec603a6628ed83d274e835539d82e9cc8", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000000007cf399229b2f5a4d043f20e90c9c98b7c6a", + "0x00000000000000000000000030735c88fa430f11499b0edcfcc25246fb9182e3" + ], + "data": "0x0000000000000000000000000000000000000000000000000695bdc7206747a8", + "blockNumber": "0x12aeeee", + "transactionHash": "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f", + "transactionIndex": "0xc", + "blockHash": "0xd27850dcfe8026052a2d89122c910a909062458374c870a3f3b3dbf7d5f5e6a0", + "logIndex": "0x3d", + "removed": false + }, + { + "address": "0x000007cf399229b2f5a4d043f20e90c9c98b7c6a", + "topics": [ + "0xf1302855733b40d8acb467ee990b6d56c05c80e28ebcabfa6e6f3f57cb50d698", + "0x0000000000000000000000000000000000000000000000000000000000001b58", + "0x00000000000000000000000030735c88fa430f11499b0edcfcc25246fb9182e3", + "0xbf7a214cf9868e1c618123ab4df0081da87bade74eeb5aef37843e35f25e67b7" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000695bdc7206747a800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000014f0a3f93ed1b126142e61423f9546bf1323ff82df0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x12aeeee", + "transactionHash": "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f", + "transactionIndex": "0xc", + "blockHash": "0xd27850dcfe8026052a2d89122c910a909062458374c870a3f3b3dbf7d5f5e6a0", + "logIndex": "0x3e", + "removed": false + } + ], + "transactionHash": "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0xfc8b", + "blockHash": "0xd27850dcfe8026052a2d89122c910a909062458374c870a3f3b3dbf7d5f5e6a0", + "blockNumber": "0x12aeeee", + "transactionIndex": "0xc" +} diff --git a/zetaclient/testutils/constant.go b/zetaclient/testutils/constant.go index 05cab82256..380c3ab501 100644 --- a/zetaclient/testutils/constant.go +++ b/zetaclient/testutils/constant.go @@ -16,6 +16,13 @@ const ( OtherAddress1 = "0x21248Decd0B7EcB0F30186297766b8AB6496265b" OtherAddress2 = "0x33A351C90aF486AebC35042Bb0544123cAed26AB" OtherAddress3 = "0x86B77E4fBd07CFdCc486cAe4F2787fB5C5a62cd3" + + // evm event names for test data naming + EventZetaSent = "ZetaSent" + EventZetaReceived = "ZetaReceived" + EventZetaReverted = "ZetaReverted" + EventERC20Deposit = "Deposited" + EventERC20Withdraw = "Withdrawn" ) // ConnectorAddresses contains constants ERC20 connector addresses for testing @@ -25,8 +32,12 @@ var ConnectorAddresses = map[int64]ethcommon.Address{ 56: ethcommon.HexToAddress("0x000063A6e758D9e2f438d430108377564cf4077D"), // testnet - 5: ethcommon.HexToAddress("0x00005E3125aBA53C5652f9F0CE1a4Cf91D8B15eA"), - 97: ethcommon.HexToAddress("0x0000ecb8cdd25a18F12DAA23f6422e07fBf8B9E1"), + 5: ethcommon.HexToAddress("0x00005E3125aBA53C5652f9F0CE1a4Cf91D8B15eA"), + 97: ethcommon.HexToAddress("0x0000ecb8cdd25a18F12DAA23f6422e07fBf8B9E1"), + 11155111: ethcommon.HexToAddress("0x3963341dad121c9CD33046089395D66eBF20Fb03"), + + // localnet + 1337: ethcommon.HexToAddress("0xD28D6A0b8189305551a0A8bd247a6ECa9CE781Ca"), } // CustodyAddresses contains constants ERC20 custody addresses for testing @@ -36,6 +47,7 @@ var CustodyAddresses = map[int64]ethcommon.Address{ 56: ethcommon.HexToAddress("0x00000fF8fA992424957F97688015814e707A0115"), // testnet - 5: ethcommon.HexToAddress("0x000047f11C6E42293F433C82473532E869Ce4Ec5"), - 97: ethcommon.HexToAddress("0x0000a7Db254145767262C6A81a7eE1650684258e"), + 5: ethcommon.HexToAddress("0x000047f11C6E42293F433C82473532E869Ce4Ec5"), + 97: ethcommon.HexToAddress("0x0000a7Db254145767262C6A81a7eE1650684258e"), + 11155111: ethcommon.HexToAddress("0x84725b70a239d3Faa7C6EF0C6C8E8b6c8e28338b"), } diff --git a/zetaclient/testutils/stub/chain_client.go b/zetaclient/testutils/stub/chain_client.go index f5a5368511..e6bcdf03b9 100644 --- a/zetaclient/testutils/stub/chain_client.go +++ b/zetaclient/testutils/stub/chain_client.go @@ -29,7 +29,7 @@ func (s *EVMClient) Start() { func (s *EVMClient) Stop() { } -func (s *EVMClient) IsSendOutTxProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { +func (s *EVMClient) IsCctxOutTxProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { return false, false, nil } @@ -70,7 +70,7 @@ func (s *BTCClient) Start() { func (s *BTCClient) Stop() { } -func (s *BTCClient) IsSendOutTxProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { +func (s *BTCClient) IsCctxOutTxProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { return false, false, nil } diff --git a/zetaclient/testutils/stub/core_bridge.go b/zetaclient/testutils/stub/core_bridge.go index 111d73983a..fe785741ae 100644 --- a/zetaclient/testutils/stub/core_bridge.go +++ b/zetaclient/testutils/stub/core_bridge.go @@ -10,6 +10,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/proofs" + "github.com/zeta-chain/zetacore/testutil/sample" cctxtypes "github.com/zeta-chain/zetacore/x/crosschain/types" observerTypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/interfaces" @@ -48,7 +49,7 @@ func (z *MockZetaCoreBridge) PostVoteOutbound(_ string, _ string, _ uint64, _ ui if z.paused { return "", "", errors.New(ErrMsgPaused) } - return "", "", nil + return sample.Hash().Hex(), "", nil } func (z *MockZetaCoreBridge) PostGasPrice(_ chains.Chain, _ uint64, _ string, _ uint64) (string, error) { diff --git a/zetaclient/testutils/testdata.go b/zetaclient/testutils/testdata.go index 37b6cbab14..d9e1981c6e 100644 --- a/zetaclient/testutils/testdata.go +++ b/zetaclient/testutils/testdata.go @@ -226,9 +226,9 @@ func LoadEVMIntxNReceiptNCctx( func LoadEVMOuttx( t *testing.T, chainID int64, - intxHash string, + txHash string, coinType coin.CoinType) *ethtypes.Transaction { - nameTx := path.Join("../", TestDataPathEVM, FileNameEVMOuttx(chainID, intxHash, coinType)) + nameTx := path.Join("../", TestDataPathEVM, FileNameEVMOuttx(chainID, txHash, coinType)) tx := ðtypes.Transaction{} LoadObjectFromJSONFile(t, &tx, nameTx) @@ -239,9 +239,10 @@ func LoadEVMOuttx( func LoadEVMOuttxReceipt( t *testing.T, chainID int64, - intxHash string, - coinType coin.CoinType) *ethtypes.Receipt { - nameReceipt := path.Join("../", TestDataPathEVM, FileNameEVMOuttxReceipt(chainID, intxHash, coinType)) + txHash string, + coinType coin.CoinType, + eventName string) *ethtypes.Receipt { + nameReceipt := path.Join("../", TestDataPathEVM, FileNameEVMOuttxReceipt(chainID, txHash, coinType, eventName)) receipt := ðtypes.Receipt{} LoadObjectFromJSONFile(t, &receipt, nameReceipt) @@ -252,11 +253,25 @@ func LoadEVMOuttxReceipt( func LoadEVMOuttxNReceipt( t *testing.T, chainID int64, - intxHash string, + txHash string, coinType coin.CoinType) (*ethtypes.Transaction, *ethtypes.Receipt) { // load archived evm outtx and receipt - tx := LoadEVMOuttx(t, chainID, intxHash, coinType) - receipt := LoadEVMOuttxReceipt(t, chainID, intxHash, coinType) + tx := LoadEVMOuttx(t, chainID, txHash, coinType) + receipt := LoadEVMOuttxReceipt(t, chainID, txHash, coinType, "") return tx, receipt } + +// LoadEVMOuttxNReceiptNEvent loads archived cctx, outtx and receipt from file +func LoadEVMCctxNOuttxNReceipt( + t *testing.T, + chainID int64, + nonce uint64, + eventName string) (*crosschaintypes.CrossChainTx, *ethtypes.Transaction, *ethtypes.Receipt) { + cctx := LoadCctxByNonce(t, chainID, nonce) + coinType := cctx.GetCurrentOutTxParam().CoinType + txHash := cctx.GetCurrentOutTxParam().OutboundTxHash + outtx := LoadEVMOuttx(t, chainID, txHash, coinType) + receipt := LoadEVMOuttxReceipt(t, chainID, txHash, coinType, eventName) + return cctx, outtx, receipt +} diff --git a/zetaclient/testutils/testdata_naming.go b/zetaclient/testutils/testdata_naming.go index a920680335..47928ff934 100644 --- a/zetaclient/testutils/testdata_naming.go +++ b/zetaclient/testutils/testdata_naming.go @@ -70,6 +70,10 @@ func FileNameEVMOuttx(chainID int64, txHash string, coinType coin.CoinType) stri } // FileNameEVMOuttxReceipt returns unified archive file name for outbound tx receipt -func FileNameEVMOuttxReceipt(chainID int64, txHash string, coinType coin.CoinType) string { - return fmt.Sprintf("chain_%d_outtx_receipt_%s_%s.json", chainID, coinType, txHash) +func FileNameEVMOuttxReceipt(chainID int64, txHash string, coinType coin.CoinType, eventName string) string { + // empty eventName is for regular transfer receipt, no event + if eventName == "" { + return fmt.Sprintf("chain_%d_outtx_receipt_%s_%s.json", chainID, coinType, txHash) + } + return fmt.Sprintf("chain_%d_outtx_receipt_%s_%s_%s.json", chainID, coinType, eventName, txHash) } diff --git a/zetaclient/zetabridge/tx.go b/zetaclient/zetabridge/tx.go index e920367f82..9b08ab3fb9 100644 --- a/zetaclient/zetabridge/tx.go +++ b/zetaclient/zetabridge/tx.go @@ -283,7 +283,7 @@ func (b *ZetaCoreBridge) MonitorVoteInboundTxResult(zetaTxHash string, retryGasL // PostVoteOutbound posts a vote on an observed outbound tx func (b *ZetaCoreBridge) PostVoteOutbound( - sendHash string, + cctxIndex string, outTxHash string, outBlockHeight uint64, outTxGasUsed uint64, @@ -298,7 +298,7 @@ func (b *ZetaCoreBridge) PostVoteOutbound( signerAddress := b.keys.GetOperatorAddress().String() msg := types.NewMsgVoteOnObservedOutboundTx( signerAddress, - sendHash, + cctxIndex, outTxHash, outBlockHeight, outTxGasUsed, diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 415b5d5d21..6c3ce5beae 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -218,9 +218,9 @@ func (co *CoreObserver) scheduleCctxEVM( } // try confirming the outtx - included, _, err := ob.IsSendOutTxProcessed(cctx, co.logger.ZetaChainWatcher) + included, _, err := ob.IsCctxOutTxProcessed(cctx, co.logger.ZetaChainWatcher) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxEVM: IsSendOutTxProcessed faild for chain %d nonce %d", chainID, nonce) + co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxEVM: IsCctxOutTxProcessed faild for chain %d nonce %d", chainID, nonce) continue } if included { @@ -298,9 +298,9 @@ func (co *CoreObserver) scheduleCctxBTC( continue } // try confirming the outtx - included, confirmed, err := btcClient.IsSendOutTxProcessed(cctx, co.logger.ZetaChainWatcher) + included, confirmed, err := btcClient.IsCctxOutTxProcessed(cctx, co.logger.ZetaChainWatcher) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxBTC: IsSendOutTxProcessed faild for chain %d nonce %d", chainID, nonce) + co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxBTC: IsCctxOutTxProcessed faild for chain %d nonce %d", chainID, nonce) continue } if included || confirmed { From be565b394a1fda79ccceb4ff7dbbffa053a375ba Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 10 Apr 2024 16:58:45 -0500 Subject: [PATCH 3/6] fix unit test --- zetaclient/zetabridge/tx_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zetaclient/zetabridge/tx_test.go b/zetaclient/zetabridge/tx_test.go index 20da71f1d2..8dc6668384 100644 --- a/zetaclient/zetabridge/tx_test.go +++ b/zetaclient/zetabridge/tx_test.go @@ -13,6 +13,7 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/zeta-chain/go-tss/blame" "github.com/zeta-chain/zetacore/pkg/chains" @@ -302,7 +303,7 @@ func TestZetaCoreBridge_UpdateZetaCoreContext(t *testing.T) { cfg := config.NewConfig() coreCtx := corecontext.NewZetaCoreContext(cfg) zetaBridgeBroadcast = ZetaBridgeBroadcastTest - err := zetabridge.UpdateZetaCoreContext(coreCtx, false) + err := zetabridge.UpdateZetaCoreContext(coreCtx, false, zerolog.Logger{}) require.NoError(t, err) }) } From 9f61d5014fcbde886a962b2a93b7cb74f6177c47 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 11 Apr 2024 17:11:31 -0500 Subject: [PATCH 4/6] converted archived cctxs JSON files to GoLang variables; resolved a few PR comments --- zetaclient/bitcoin/bitcoin_client.go | 14 +- zetaclient/evm/evm_client.go | 16 ++- zetaclient/evm/evm_signer_test.go | 12 +- zetaclient/evm/inbounds_test.go | 6 +- zetaclient/evm/outbound_transaction_data.go | 4 +- zetaclient/evm/outbounds.go | 14 +- zetaclient/evm/outbounds_test.go | 104 ++++++++++----- zetaclient/interfaces/interfaces.go | 2 +- zetaclient/testdata/cctx/all_cctxs.go | 49 +++++++ zetaclient/testdata/cctx/cctx_1337_14.json | 55 -------- zetaclient/testdata/cctx/cctx_1_6270.json | 34 ----- zetaclient/testdata/cctx/cctx_1_7260.json | 34 ----- zetaclient/testdata/cctx/cctx_1_8014.json | 35 ----- zetaclient/testdata/cctx/cctx_1_9718.json | 32 ----- ...650a5f0e8fca6f5b76044a6cf106d21f27098.json | 35 ----- zetaclient/testdata/cctx/cctx_56_68270.json | 43 ------ .../cctx/cctx_56_68270_invalidChainID.json | 43 ------ zetaclient/testdata/cctx/cctx_8332_148.json | 32 ----- ...b990e076e2a4bffeb616035b239b7d33843da.json | 36 ------ ...1cbdf59154fd793ea7a22c297f7c3a722c532.json | 35 ----- ...ffc40ca898e134525c42c2ae3cbc5725f9d76.json | 33 ----- .../testdata/cctx/chain_1337_cctx_14.go | 122 ++++++++++++++++++ zetaclient/testdata/cctx/chain_1_cctx_6270.go | 53 ++++++++ zetaclient/testdata/cctx/chain_1_cctx_7260.go | 53 ++++++++ zetaclient/testdata/cctx/chain_1_cctx_8014.go | 53 ++++++++ zetaclient/testdata/cctx/chain_1_cctx_9718.go | 53 ++++++++ ...c3b990e076e2a4bffeb616035b239b7d33843da.go | 53 ++++++++ ...e01cbdf59154fd793ea7a22c297f7c3a722c532.go | 53 ++++++++ ...71ffc40ca898e134525c42c2ae3cbc5725f9d76.go | 53 ++++++++ .../testdata/cctx/chain_56_cctx_68270.go | 53 ++++++++ .../testdata/cctx/chain_8332_cctx_148.go | 52 ++++++++ zetaclient/testutils/stub/chain_client.go | 4 +- zetaclient/testutils/testdata.go | 84 +++++++----- zetaclient/testutils/testdata_naming.go | 20 +-- zetaclient/zetacore_observer.go | 8 +- 35 files changed, 831 insertions(+), 551 deletions(-) create mode 100644 zetaclient/testdata/cctx/all_cctxs.go delete mode 100644 zetaclient/testdata/cctx/cctx_1337_14.json delete mode 100644 zetaclient/testdata/cctx/cctx_1_6270.json delete mode 100644 zetaclient/testdata/cctx/cctx_1_7260.json delete mode 100644 zetaclient/testdata/cctx/cctx_1_8014.json delete mode 100644 zetaclient/testdata/cctx/cctx_1_9718.json delete mode 100644 zetaclient/testdata/cctx/cctx_1_intx_Gas_0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098.json delete mode 100644 zetaclient/testdata/cctx/cctx_56_68270.json delete mode 100644 zetaclient/testdata/cctx/cctx_56_68270_invalidChainID.json delete mode 100644 zetaclient/testdata/cctx/cctx_8332_148.json delete mode 100644 zetaclient/testdata/cctx/cctx_intx_1_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.json delete mode 100644 zetaclient/testdata/cctx/cctx_intx_1_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.json delete mode 100644 zetaclient/testdata/cctx/cctx_intx_1_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json create mode 100644 zetaclient/testdata/cctx/chain_1337_cctx_14.go create mode 100644 zetaclient/testdata/cctx/chain_1_cctx_6270.go create mode 100644 zetaclient/testdata/cctx/chain_1_cctx_7260.go create mode 100644 zetaclient/testdata/cctx/chain_1_cctx_8014.go create mode 100644 zetaclient/testdata/cctx/chain_1_cctx_9718.go create mode 100644 zetaclient/testdata/cctx/chain_1_cctx_intx_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.go create mode 100644 zetaclient/testdata/cctx/chain_1_cctx_intx_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.go create mode 100644 zetaclient/testdata/cctx/chain_1_cctx_intx_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.go create mode 100644 zetaclient/testdata/cctx/chain_56_cctx_68270.go create mode 100644 zetaclient/testdata/cctx/chain_8332_cctx_148.go diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index f1124014e0..defb5bd054 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -504,15 +504,15 @@ func (ob *BTCChainClient) ConfirmationsThreshold(amount *big.Int) int64 { return int64(ob.GetChainParams().ConfirmationCount) } -// IsCctxOutTxProcessed returns isIncluded(or inMempool), isConfirmed, Error -func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { +// IsOutboundProcessed returns isIncluded(or inMempool), isConfirmed, Error +func (ob *BTCChainClient) IsOutboundProcessed(cctx *types.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { params := *cctx.GetCurrentOutTxParam() sendHash := cctx.Index nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce // get broadcasted outtx and tx result outTxID := ob.GetTxID(nonce) - logger.Info().Msgf("IsCctxOutTxProcessed %s", outTxID) + logger.Info().Msgf("IsOutboundProcessed %s", outTxID) ob.Mu.Lock() txnHash, broadcasted := ob.broadcastedTx[outTxID] @@ -537,7 +537,7 @@ func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger if txResult == nil { // check failed, try again next time return false, false, nil } else if inMempool { // still in mempool (should avoid unnecessary Tss keysign) - ob.logger.OutTx.Info().Msgf("IsCctxOutTxProcessed: outTx %s is still in mempool", outTxID) + ob.logger.OutTx.Info().Msgf("IsOutboundProcessed: outTx %s is still in mempool", outTxID) return true, false, nil } // included @@ -548,7 +548,7 @@ func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger if res == nil { return false, false, nil } - ob.logger.OutTx.Info().Msgf("IsCctxOutTxProcessed: setIncludedTx succeeded for outTx %s", outTxID) + ob.logger.OutTx.Info().Msgf("IsOutboundProcessed: setIncludedTx succeeded for outTx %s", outTxID) } // It's safe to use cctx's amount to post confirmation because it has already been verified in observeOutTx() @@ -573,9 +573,9 @@ func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger coin.CoinType_Gas, ) if err != nil { - logger.Error().Err(err).Msgf("IsCctxOutTxProcessed: error confirming bitcoin outTx %s, nonce %d ballot %s", res.TxID, nonce, ballot) + logger.Error().Err(err).Msgf("IsOutboundProcessed: error confirming bitcoin outTx %s, nonce %d ballot %s", res.TxID, nonce, ballot) } else if zetaHash != "" { - logger.Info().Msgf("IsCctxOutTxProcessed: confirmed Bitcoin outTx %s, zeta tx hash %s nonce %d ballot %s", res.TxID, zetaHash, nonce, ballot) + logger.Info().Msgf("IsOutboundProcessed: confirmed Bitcoin outTx %s, zeta tx hash %s nonce %d ballot %s", res.TxID, zetaHash, nonce, ballot) } return true, true, nil } diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index c62bc39c44..916f9e636f 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -158,11 +158,15 @@ func NewEVMChainClient( return &ob, nil } + +// WithChain attaches a new chain to the chain client func (ob *ChainClient) WithChain(chain chains.Chain) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.chain = chain } + +// WithLogger attaches a new logger to the chain client func (ob *ChainClient) WithLogger(logger zerolog.Logger) { ob.Mu.Lock() defer ob.Mu.Unlock() @@ -174,36 +178,42 @@ func (ob *ChainClient) WithLogger(logger zerolog.Logger) { } } +// WithEvmClient attaches a new evm client to the chain client func (ob *ChainClient) WithEvmClient(client interfaces.EVMRPCClient) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.evmClient = client } +// WithEvmJSONRPC attaches a new evm json rpc client to the chain client func (ob *ChainClient) WithEvmJSONRPC(client interfaces.EVMJSONRPCClient) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.evmJSONRPC = client } +// WithZetaBridge attaches a new bridge to interact with ZetaCore to the chain client func (ob *ChainClient) WithZetaBridge(bridge interfaces.ZetaCoreBridger) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.zetaBridge = bridge } +// WithBlockCache attaches a new block cache to the chain client func (ob *ChainClient) WithBlockCache(cache *lru.Cache) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.blockCache = cache } +// SetChainParams sets the chain params for the chain client func (ob *ChainClient) SetChainParams(params observertypes.ChainParams) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.chainParams = params } +// GetChainParams returns the chain params for the chain client func (ob *ChainClient) GetChainParams() observertypes.ChainParams { ob.Mu.Lock() defer ob.Mu.Unlock() @@ -339,7 +349,7 @@ func (ob *ChainClient) WatchOutTx() { } for _, tracker := range trackers { nonceInt := tracker.Nonce - if ob.isTxConfirmed(nonceInt) { // Go to next tracker if this one already has a confirmed tx + if ob.IsTxConfirmed(nonceInt) { // Go to next tracker if this one already has a confirmed tx continue } txCount := 0 @@ -403,8 +413,8 @@ func (ob *ChainClient) GetTxNReceipt(nonce uint64) (*ethtypes.Receipt, *ethtypes return receipt, transaction } -// isTxConfirmed returns true if there is a confirmed tx for 'nonce' -func (ob *ChainClient) isTxConfirmed(nonce uint64) bool { +// IsTxConfirmed returns true if there is a confirmed tx for 'nonce' +func (ob *ChainClient) IsTxConfirmed(nonce uint64) bool { ob.Mu.Lock() defer ob.Mu.Unlock() return ob.outTXConfirmedReceipts[ob.GetTxID(nonce)] != nil && ob.outTXConfirmedTransactions[ob.GetTxID(nonce)] != nil diff --git a/zetaclient/evm/evm_signer_test.go b/zetaclient/evm/evm_signer_test.go index eace105ff2..03e014adde 100644 --- a/zetaclient/evm/evm_signer_test.go +++ b/zetaclient/evm/evm_signer_test.go @@ -1,7 +1,6 @@ package evm import ( - "path" "testing" sdktypes "github.com/cosmos/cosmos-sdk/types" @@ -67,15 +66,14 @@ func getNewOutTxProcessor() *outtxprocessor.Processor { } func getCCTX(t *testing.T) *crosschaintypes.CrossChainTx { - var cctx crosschaintypes.CrossChainTx - testutils.LoadObjectFromJSONFile(t, &cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_56_68270.json")) - return &cctx + return testutils.LoadCctxByNonce(t, 56, 68270) } func getInvalidCCTX(t *testing.T) *crosschaintypes.CrossChainTx { - var cctx crosschaintypes.CrossChainTx - testutils.LoadObjectFromJSONFile(t, &cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_56_68270_invalidChainID.json")) - return &cctx + cctx := getCCTX(t) + // modify receiver chain id to make it invalid + cctx.GetCurrentOutTxParam().ReceiverChainId = 13378337 + return cctx } func TestSigner_SetGetConnectorAddress(t *testing.T) { diff --git a/zetaclient/evm/inbounds_test.go b/zetaclient/evm/inbounds_test.go index 1b5a10fc82..645a87d108 100644 --- a/zetaclient/evm/inbounds_test.go +++ b/zetaclient/evm/inbounds_test.go @@ -190,7 +190,7 @@ func TestEVM_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { chain := chains.EthChain() intxHash := "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76" receipt := testutils.LoadEVMIntxReceipt(t, chainID, intxHash, coin.CoinType_Zeta) - cctx := testutils.LoadEVMIntxCctx(t, chainID, intxHash, coin.CoinType_Zeta) + cctx := testutils.LoadCctxByIntx(t, chainID, coin.CoinType_Zeta, intxHash) // parse ZetaSent event ob := MockEVMClient(t, chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1)) @@ -237,7 +237,7 @@ func TestEVM_BuildInboundVoteMsgForDepositedEvent(t *testing.T) { chainID := chain.ChainId intxHash := "0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da" tx, receipt := testutils.LoadEVMIntxNReceipt(t, chainID, intxHash, coin.CoinType_ERC20) - cctx := testutils.LoadEVMIntxCctx(t, chainID, intxHash, coin.CoinType_ERC20) + cctx := testutils.LoadCctxByIntx(t, chainID, coin.CoinType_ERC20, intxHash) // parse Deposited event ob := MockEVMClient(t, chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1)) @@ -283,7 +283,7 @@ func TestEVM_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { intxHash := "0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532" tx, receipt := testutils.LoadEVMIntxNReceipt(t, chainID, intxHash, coin.CoinType_Gas) require.NoError(t, evm.ValidateEvmTransaction(tx)) - cctx := testutils.LoadEVMIntxCctx(t, chainID, intxHash, coin.CoinType_Gas) + cctx := testutils.LoadCctxByIntx(t, chainID, coin.CoinType_Gas, intxHash) // load archived gas token donation to TSS // https://etherscan.io/tx/0x52f214cf7b10be71f4d274193287d47bc9632b976e69b9d2cdeb527c2ba32155 diff --git a/zetaclient/evm/outbound_transaction_data.go b/zetaclient/evm/outbound_transaction_data.go index 25f59e5e2e..47a7333197 100644 --- a/zetaclient/evm/outbound_transaction_data.go +++ b/zetaclient/evm/outbound_transaction_data.go @@ -135,9 +135,9 @@ func NewOutBoundTransactionData( // Get nonce, Early return if the cctx is already processed nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce - included, confirmed, err := evmClient.IsCctxOutTxProcessed(cctx, logger) + included, confirmed, err := evmClient.IsOutboundProcessed(cctx, logger) if err != nil { - return nil, true, errors.New("IsCctxOutTxProcessed failed") + return nil, true, errors.New("IsOutboundProcessed failed") } if included || confirmed { logger.Info().Msgf("CCTX already processed; exit signer") diff --git a/zetaclient/evm/outbounds.go b/zetaclient/evm/outbounds.go index cda7403828..64a04b58fd 100644 --- a/zetaclient/evm/outbounds.go +++ b/zetaclient/evm/outbounds.go @@ -50,12 +50,12 @@ func (ob *ChainClient) PostVoteOutbound( } } -// IsCctxOutTxProcessed checks outtx status and returns (isIncluded, isConfirmed, error) +// IsOutboundProcessed checks outtx status and returns (isIncluded, isConfirmed, error) // It also posts vote to zetacore if the tx is confirmed -func (ob *ChainClient) IsCctxOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { +func (ob *ChainClient) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { // skip if outtx is not confirmed nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce - if !ob.isTxConfirmed(nonce) { + if !ob.IsTxConfirmed(nonce) { return false, false, nil } receipt, transaction := ob.GetTxNReceipt(nonce) @@ -92,7 +92,7 @@ func (ob *ChainClient) IsCctxOutTxProcessed(cctx *crosschaintypes.CrossChainTx, // parse the received value from the outtx receipt receiveValue, receiveStatus, err = ParseOuttxReceivedValue(cctx, receipt, transaction, cointype, connectorAddr, connector, custodyAddr, custody) if err != nil { - logger.Error().Err(err).Msgf("IsCctxOutTxProcessed: error parsing outtx event for chain %d txhash %s", ob.chain.ChainId, receipt.TxHash) + logger.Error().Err(err).Msgf("IsOutboundProcessed: error parsing outtx event for chain %d txhash %s", ob.chain.ChainId, receipt.TxHash) return false, false, err } @@ -189,7 +189,8 @@ func ParseAndCheckWithdrawnEvent( return nil, errors.New("no ERC20 Withdrawn event found") } -// ParseOuttxReceivedValue parses the received value from the outtx receipt +// ParseOuttxReceivedValue parses the received value and status from the outtx receipt +// The receivd value is the amount of Zeta/ERC20/Gas token (released from connector/custody/TSS) sent to the receiver func ParseOuttxReceivedValue( cctx *crosschaintypes.CrossChainTx, receipt *ethtypes.Receipt, @@ -198,7 +199,8 @@ func ParseOuttxReceivedValue( connectorAddress ethcommon.Address, connector *zetaconnector.ZetaConnectorNonEth, custodyAddress ethcommon.Address, - custody *erc20custody.ERC20Custody) (*big.Int, chains.ReceiveStatus, error) { + custody *erc20custody.ERC20Custody, +) (*big.Int, chains.ReceiveStatus, error) { // determine the receive status and value // https://docs.nethereum.com/en/latest/nethereum-receipt-status/ receiveValue := big.NewInt(0) diff --git a/zetaclient/evm/outbounds_test.go b/zetaclient/evm/outbounds_test.go index df167b1fc9..30f523614f 100644 --- a/zetaclient/evm/outbounds_test.go +++ b/zetaclient/evm/outbounds_test.go @@ -27,32 +27,7 @@ func getContractsByChainID(chainID int64) (*zetaconnector.ZetaConnectorNonEth, e return connector, connectorAddress, custody, custodyAddress } -func Test_PostVoteOutbound(t *testing.T) { - // Note: outtx of Gas/ERC20 token can also be used for this test - // load archived cctx, outtx and receipt for a ZetaReceived event - // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f - chain := chains.EthChain() - nonce := uint64(9718) - coinType := coin.CoinType_Zeta - cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chain.ChainId, nonce, testutils.EventZetaReceived) - - t.Run("post vote outbound successfully", func(t *testing.T) { - // the amount and status to be used for vote - receiveValue := cctx.GetCurrentOutTxParam().Amount.BigInt() - receiveStatus := chains.ReceiveStatus_Success - - // create evm client using mock zetaBridge and post outbound vote - zetaBridge := stub.NewMockZetaCoreBridge() - client := MockEVMClient(t, chain, nil, nil, zetaBridge, nil, 1, observertypes.ChainParams{}) - client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{}) - - // pause the mock zetaBridge to simulate error posting vote - zetaBridge.Pause() - client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{}) - }) -} - -func Test_IsCctxOutTxProcessed(t *testing.T) { +func Test_IsOutboundProcessed(t *testing.T) { // load archived outtx receipt that contains ZetaReceived event // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f chain := chains.EthChain() @@ -69,7 +44,7 @@ func Test_IsCctxOutTxProcessed(t *testing.T) { client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam) client.SetTxNReceipt(nonce, receipt, outtx) // post outbound vote - isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{}) require.NoError(t, err) require.True(t, isIncluded) require.True(t, isConfirmed) @@ -93,7 +68,7 @@ func Test_IsCctxOutTxProcessed(t *testing.T) { config.LoadComplianceConfig(cfg) // post outbound vote - isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{}) require.NoError(t, err) require.True(t, isIncluded) require.True(t, isConfirmed) @@ -101,7 +76,7 @@ func Test_IsCctxOutTxProcessed(t *testing.T) { t.Run("should return false if outtx is not confirmed", func(t *testing.T) { // create evm client and DO NOT set outtx as confirmed client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam) - isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{}) require.NoError(t, err) require.False(t, isIncluded) require.False(t, isConfirmed) @@ -115,13 +90,82 @@ func Test_IsCctxOutTxProcessed(t *testing.T) { chainParamsNew := client.GetChainParams() chainParamsNew.ConnectorContractAddress = sample.EthAddress().Hex() client.SetChainParams(chainParamsNew) - isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{}) require.Error(t, err) require.False(t, isIncluded) require.False(t, isConfirmed) }) } +func Test_IsOutboundProcessed_ContractError(t *testing.T) { + // Note: this test is skipped because it will cause CI failure. + // The only way to replicate a contract error is to use an invalid ABI. + // See the code: https://github.com/ethereum/go-ethereum/blob/v1.10.26/accounts/abi/bind/base.go#L97 + // The ABI is hardcoded in the protocol-contracts package and initialized the 1st time it binds the contract. + // Any subsequent modification to the ABI will not work and therefor fail the unit test. + t.Skip("uncomment this line to run this test separately, otherwise it will fail CI") + + // load archived outtx receipt that contains ZetaReceived event + // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f + chain := chains.EthChain() + chainID := chains.EthChain().ChainId + nonce := uint64(9718) + chainParam := stub.MockChainParams(chain.ChainId, 1) + outtxHash := "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f" + cctx := testutils.LoadCctxByNonce(t, chainID, nonce) + receipt := testutils.LoadEVMOuttxReceipt(t, chainID, outtxHash, coin.CoinType_Zeta, testutils.EventZetaReceived) + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, testutils.EventZetaReceived) + + t.Run("should fail if unable to get connector/custody contract", func(t *testing.T) { + // create evm client and set outtx and receipt + client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam) + client.SetTxNReceipt(nonce, receipt, outtx) + abiConnector := zetaconnector.ZetaConnectorNonEthMetaData.ABI + abiCustody := erc20custody.ERC20CustodyMetaData.ABI + + // set invalid connector ABI + zetaconnector.ZetaConnectorNonEthMetaData.ABI = "invalid abi" + isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{}) + zetaconnector.ZetaConnectorNonEthMetaData.ABI = abiConnector // reset connector ABI + require.ErrorContains(t, err, "error getting zeta connector") + require.False(t, isIncluded) + require.False(t, isConfirmed) + + // set invalid custody ABI + erc20custody.ERC20CustodyMetaData.ABI = "invalid abi" + isIncluded, isConfirmed, err = client.IsOutboundProcessed(cctx, zerolog.Logger{}) + require.ErrorContains(t, err, "error getting erc20 custody") + require.False(t, isIncluded) + require.False(t, isConfirmed) + erc20custody.ERC20CustodyMetaData.ABI = abiCustody // reset custody ABI + }) +} + +func Test_PostVoteOutbound(t *testing.T) { + // Note: outtx of Gas/ERC20 token can also be used for this test + // load archived cctx, outtx and receipt for a ZetaReceived event + // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f + chain := chains.EthChain() + nonce := uint64(9718) + coinType := coin.CoinType_Zeta + cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chain.ChainId, nonce, testutils.EventZetaReceived) + + t.Run("post vote outbound successfully", func(t *testing.T) { + // the amount and status to be used for vote + receiveValue := cctx.GetCurrentOutTxParam().Amount.BigInt() + receiveStatus := chains.ReceiveStatus_Success + + // create evm client using mock zetaBridge and post outbound vote + zetaBridge := stub.NewMockZetaCoreBridge() + client := MockEVMClient(t, chain, nil, nil, zetaBridge, nil, 1, observertypes.ChainParams{}) + client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{}) + + // pause the mock zetaBridge to simulate error posting vote + zetaBridge.Pause() + client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{}) + }) +} + func Test_ParseZetaReceived(t *testing.T) { // load archived outtx receipt that contains ZetaReceived event // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f diff --git a/zetaclient/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index a938d6af68..20d56bc320 100644 --- a/zetaclient/interfaces/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -39,7 +39,7 @@ const ( type ChainClient interface { Start() Stop() - IsCctxOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) + IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) SetChainParams(observertypes.ChainParams) GetChainParams() observertypes.ChainParams GetTxID(nonce uint64) string diff --git a/zetaclient/testdata/cctx/all_cctxs.go b/zetaclient/testdata/cctx/all_cctxs.go new file mode 100644 index 0000000000..359a10c8dd --- /dev/null +++ b/zetaclient/testdata/cctx/all_cctxs.go @@ -0,0 +1,49 @@ +package cctx + +import ( + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// CCtxByNonceMap maps the [chainID, nonce] to the cross chain transaction +var CCtxByNonceMap = map[int64]map[uint64]*crosschaintypes.CrossChainTx{ + // Ethereum mainnet + 1: { + chain_1_cctx_6270.GetCurrentOutTxParam().OutboundTxTssNonce: chain_1_cctx_6270, + chain_1_cctx_7260.GetCurrentOutTxParam().OutboundTxTssNonce: chain_1_cctx_7260, + chain_1_cctx_8014.GetCurrentOutTxParam().OutboundTxTssNonce: chain_1_cctx_8014, + chain_1_cctx_9718.GetCurrentOutTxParam().OutboundTxTssNonce: chain_1_cctx_9718, + }, + // BSC mainnet + 56: { + chain_56_cctx_68270.GetCurrentOutTxParam().OutboundTxTssNonce: chain_56_cctx_68270, + }, + // local goerli testnet + 1337: { + chain_1337_cctx_14.GetCurrentOutTxParam().OutboundTxTssNonce: chain_1337_cctx_14, + }, + // Bitcoin mainnet + 8332: { + chain_8332_cctx_148.GetCurrentOutTxParam().OutboundTxTssNonce: chain_8332_cctx_148, + }, +} + +// CctxByIntxMap maps the [chainID, coinType, intxHash] to the cross chain transaction +var CctxByIntxMap = map[int64]map[coin.CoinType]map[string]*crosschaintypes.CrossChainTx{ + // Ethereum mainnet + 1: { + coin.CoinType_Zeta: { + chain_1_cctx_intx_Zeta_0xf393520.InboundTxParams.InboundTxObservedHash: chain_1_cctx_intx_Zeta_0xf393520, + }, + coin.CoinType_ERC20: { + chain_1_cctx_intx_ERC20_0x4ea69a0.InboundTxParams.InboundTxObservedHash: chain_1_cctx_intx_ERC20_0x4ea69a0, + }, + coin.CoinType_Gas: { + chain_1_cctx_intx_Gas_0xeaec67d.InboundTxParams.InboundTxObservedHash: chain_1_cctx_intx_Gas_0xeaec67d, + }, + }, + // BSC mainnet + 56: {}, + // Bitcoin mainnet + 8332: {}, +} diff --git a/zetaclient/testdata/cctx/cctx_1337_14.json b/zetaclient/testdata/cctx/cctx_1337_14.json deleted file mode 100644 index 058849a351..0000000000 --- a/zetaclient/testdata/cctx/cctx_1337_14.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "creator": "zeta1plfrp7ejn0s9tmwufuxvsyn8nlf6a7u9ndgk9m", - "index": "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df", - "zeta_fees": "4000000000009027082", - "relayed_message": "bgGCGUux3roBhJr9PgNaC3DOfLBp5ILuZjUZx2z1abQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ==", - "cctx_status": { - "status": 5, - "status_message": "Outbound failed, start revert : Outbound succeeded, revert executed", - "lastUpdate_timestamp": 1712705995 - }, - "inbound_tx_params": { - "sender": "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed", - "sender_chain_id": 1337, - "tx_origin": "0x5cC2fBb200A929B372e3016F1925DcF988E081fd", - "amount": "10000000000000000000", - "inbound_tx_observed_hash": "0xa5589bf24eca8f108ca35048adc9d5582a303d416c01319391159269ae7e4e6f", - "inbound_tx_observed_external_height": 177, - "inbound_tx_ballot_index": "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df", - "inbound_tx_finalized_zeta_height": 150, - "tx_finalization_status": 2 - }, - "outbound_tx_params": [ - { - "receiver": "0xbff76e77d56b3c1202107f059425d56f0aef87ed", - "receiver_chainId": 1337, - "amount": "7999999999995486459", - "outbound_tx_tss_nonce": 13, - "outbound_tx_gas_limit": 250000, - "outbound_tx_gas_price": "18", - "outbound_tx_hash": "0x19f99459da6cb08f917f9b0ee2dac94a7be328371dff788ad46e64a24e8c06c9", - "outbound_tx_observed_external_height": 187, - "outbound_tx_gas_used": 67852, - "outbound_tx_effective_gas_price": "18", - "outbound_tx_effective_gas_limit": 250000, - "tss_pubkey": "zetapub1addwnpepqggky6z958k7hhxs6k5quuvs27uv5vtmlv330ppt2362p8ejct88w4g64jv", - "tx_finalization_status": 2 - }, - { - "receiver": "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed", - "receiver_chainId": 1337, - "amount": "5999999999990972918", - "outbound_tx_tss_nonce": 14, - "outbound_tx_gas_limit": 250000, - "outbound_tx_gas_price": "18", - "outbound_tx_hash": "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7", - "outbound_tx_ballot_index": "0xc36c689fdaf09a9b80a614420cd4fea4fec15044790df60080cdefca0090a9dc", - "outbound_tx_observed_external_height": 201, - "outbound_tx_gas_used": 76128, - "outbound_tx_effective_gas_price": "18", - "outbound_tx_effective_gas_limit": 250000, - "tss_pubkey": "zetapub1addwnpepqggky6z958k7hhxs6k5quuvs27uv5vtmlv330ppt2362p8ejct88w4g64jv", - "tx_finalization_status": 2 - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_1_6270.json b/zetaclient/testdata/cctx/cctx_1_6270.json deleted file mode 100644 index 3797b52c3c..0000000000 --- a/zetaclient/testdata/cctx/cctx_1_6270.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "index": "0xe930f363591b348a07e0a6d309b4301b84f702e3e81e0d0902340c7f7da4b5af", - "zeta_fees": "0", - "cctx_status": { "status": 3, "lastUpdate_timestamp": 1708464433 }, - "inbound_tx_params": { - "sender": "0xd91b507F2A3e2D4A32d0C86Ac19FEAD2D461008D", - "sender_chain_id": 7000, - "tx_origin": "0x18D0E2c38b4188D8Ae07008C3BeeB1c80748b41c", - "coin_type": 1, - "amount": "9831832641427386", - "inbound_tx_observed_hash": "0x8bd0df31e512c472e3162a41281b740b518216cc8eb787c2eb59c81e0cffbe89", - "inbound_tx_observed_external_height": 1846989, - "inbound_tx_ballot_index": "0xe930f363591b348a07e0a6d309b4301b84f702e3e81e0d0902340c7f7da4b5af" - }, - "outbound_tx_params": [ - { - "receiver": "0x18D0E2c38b4188D8Ae07008C3BeeB1c80748b41c", - "receiver_chainId": 1, - "coin_type": 1, - "amount": "9831832641427386", - "outbound_tx_tss_nonce": 6270, - "outbound_tx_gas_limit": 21000, - "outbound_tx_gas_price": "69197693654", - "outbound_tx_hash": "0x20104d41e042db754cf7908c5441914e581b498eedbca40979c9853f4b7f8460", - "outbound_tx_ballot_index": "0x346a1d00a4d26a2065fe1dc7d5af59a49ad6a8af25853ae2ec976c07349f48c1", - "outbound_tx_observed_external_height": 19271550, - "outbound_tx_gas_used": 21000, - "outbound_tx_effective_gas_price": "69197693654", - "outbound_tx_effective_gas_limit": 21000, - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", - "tx_finalization_status": 2 - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_1_7260.json b/zetaclient/testdata/cctx/cctx_1_7260.json deleted file mode 100644 index 46cb2e2a3a..0000000000 --- a/zetaclient/testdata/cctx/cctx_1_7260.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "index": "0xbebecbf1d8c12016e38c09d095290df503fe29731722d939433fa47e3ed1f986", - "zeta_fees": "0", - "cctx_status": { "status": 3, "lastUpdate_timestamp": 1709574082 }, - "inbound_tx_params": { - "sender": "0xd91b507F2A3e2D4A32d0C86Ac19FEAD2D461008D", - "sender_chain_id": 7000, - "tx_origin": "0x8E62e3e6FbFF3E21F725395416A20EA4E2DeF015", - "coin_type": 1, - "amount": "42635427434588308", - "inbound_tx_observed_hash": "0x2720e3a98f18c288f4197d412bfce57e58f00dc4f8b31e335ffc0bf7208dd3c5", - "inbound_tx_observed_external_height": 2031411, - "inbound_tx_ballot_index": "0xbebecbf1d8c12016e38c09d095290df503fe29731722d939433fa47e3ed1f986" - }, - "outbound_tx_params": [ - { - "receiver": "0x8E62e3e6FbFF3E21F725395416A20EA4E2DeF015", - "receiver_chainId": 1, - "coin_type": 1, - "amount": "42635427434588308", - "outbound_tx_tss_nonce": 7260, - "outbound_tx_gas_limit": 21000, - "outbound_tx_gas_price": "236882693686", - "outbound_tx_hash": "0xd13b593eb62b5500a00e288cc2fb2c8af1339025c0e6bc6183b8bef2ebbed0d3", - "outbound_tx_ballot_index": "0x689d894606642a2a7964fa906ebf4998c22a00708544fa88e9c56b86c955066b", - "outbound_tx_observed_external_height": 19363323, - "outbound_tx_gas_used": 21000, - "outbound_tx_effective_gas_price": "236882693686", - "outbound_tx_effective_gas_limit": 21000, - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", - "tx_finalization_status": 2 - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_1_8014.json b/zetaclient/testdata/cctx/cctx_1_8014.json deleted file mode 100644 index b702f8d7f6..0000000000 --- a/zetaclient/testdata/cctx/cctx_1_8014.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "index": "0x5a100fdb426da35ad4c95520d7a4f1fd2f38c88067c9e80ba209d3a655c6e06e", - "zeta_fees": "0", - "cctx_status": { "status": 3, "lastUpdate_timestamp": 1710834402 }, - "inbound_tx_params": { - "sender": "0x7c8dDa80bbBE1254a7aACf3219EBe1481c6E01d7", - "sender_chain_id": 7000, - "tx_origin": "0x8d8D67A8B71c141492825CAE5112Ccd8581073f2", - "coin_type": 2, - "asset": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "amount": "23726342442", - "inbound_tx_observed_hash": "0x114ed9d327b6afc068c3fa891b82f7c7f2d42ae25a571f7dc004c05e77af592a", - "inbound_tx_observed_external_height": 2241077, - "inbound_tx_ballot_index": "0x5a100fdb426da35ad4c95520d7a4f1fd2f38c88067c9e80ba209d3a655c6e06e" - }, - "outbound_tx_params": [ - { - "receiver": "0x8d8D67A8B71c141492825CAE5112Ccd8581073f2", - "receiver_chainId": 1, - "coin_type": 2, - "amount": "23726342442", - "outbound_tx_tss_nonce": 8014, - "outbound_tx_gas_limit": 100000, - "outbound_tx_gas_price": "58619665744", - "outbound_tx_hash": "0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146", - "outbound_tx_ballot_index": "0x4213f2c335758301b8bbb09d9891949ed6ffeea5dd95e5d9eaa8d410baaa0884", - "outbound_tx_observed_external_height": 19467367, - "outbound_tx_gas_used": 60625, - "outbound_tx_effective_gas_price": "58619665744", - "outbound_tx_effective_gas_limit": 100000, - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", - "tx_finalization_status": 2 - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_1_9718.json b/zetaclient/testdata/cctx/cctx_1_9718.json deleted file mode 100644 index ebd71301e2..0000000000 --- a/zetaclient/testdata/cctx/cctx_1_9718.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "index": "0xbf7a214cf9868e1c618123ab4df0081da87bade74eeb5aef37843e35f25e67b7", - "zeta_fees": "19525506001302763608", - "cctx_status": { "status": 3, "lastUpdate_timestamp": 1712336965 }, - "inbound_tx_params": { - "sender": "0xF0a3F93Ed1B126142E61423F9546bf1323Ff82DF", - "sender_chain_id": 7000, - "tx_origin": "0x87257C910a19a3fe64AfFAbFe8cF9AAF2ab148BF", - "amount": "20000000000000000000", - "inbound_tx_observed_hash": "0xb136652cd58fb6a537b0a1677965983059a2004d98919cdacd52551f877cc44f", - "inbound_tx_observed_external_height": 2492552, - "inbound_tx_ballot_index": "0xbf7a214cf9868e1c618123ab4df0081da87bade74eeb5aef37843e35f25e67b7" - }, - "outbound_tx_params": [ - { - "receiver": "0x30735c88fa430f11499b0edcfcc25246fb9182e3", - "receiver_chainId": 1, - "amount": "474493998697236392", - "outbound_tx_tss_nonce": 9718, - "outbound_tx_gas_limit": 90000, - "outbound_tx_gas_price": "112217884384", - "outbound_tx_hash": "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f", - "outbound_tx_ballot_index": "0xff07eaa34ca02a08bca1558e5f6220cbfc734061f083622b24923e032f0c480f", - "outbound_tx_observed_external_height": 19590894, - "outbound_tx_gas_used": 64651, - "outbound_tx_effective_gas_price": "112217884384", - "outbound_tx_effective_gas_limit": 100000, - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", - "tx_finalization_status": 2 - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_1_intx_Gas_0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098.json b/zetaclient/testdata/cctx/cctx_1_intx_Gas_0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098.json deleted file mode 100644 index a167134b35..0000000000 --- a/zetaclient/testdata/cctx/cctx_1_intx_Gas_0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "creator": "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", - "index": "0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098", - "zeta_fees": "0", - "cctx_status": { - "status": 3, - "status_message": "Remote omnichain contract call completed", - "lastUpdate_timestamp": 1709177431 - }, - "inbound_tx_params": { - "sender": "0xF829fa7069680b8C37A8086b37d4a24697E5003b", - "sender_chain_id": 1, - "tx_origin": "0xF829fa7069680b8C37A8086b37d4a24697E5003b", - "coin_type": 1, - "amount": "4000000000000000", - "inbound_tx_observed_hash": "0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532", - "inbound_tx_observed_external_height": 19330473, - "inbound_tx_ballot_index": "0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098", - "inbound_tx_finalized_zeta_height": 1965579, - "tx_finalization_status": 2 - }, - "outbound_tx_params": [ - { - "receiver": "0xF829fa7069680b8C37A8086b37d4a24697E5003b", - "receiver_chainId": 7000, - "coin_type": 1, - "amount": "0", - "outbound_tx_gas_limit": 90000, - "outbound_tx_hash": "0x3b8c1dab5aa21ff90ddb569f2f962ff2d4aa8d914c9177900102e745955e6f35", - "outbound_tx_observed_external_height": 1965579, - "outbound_tx_effective_gas_price": "0", - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc" - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_56_68270.json b/zetaclient/testdata/cctx/cctx_56_68270.json deleted file mode 100644 index 1c0ae6b762..0000000000 --- a/zetaclient/testdata/cctx/cctx_56_68270.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "creator": "", - "index": "0x541b570182950809f9b9077861a0fc7038af9a14ce8af4e151a83adfa308c7a9", - "zeta_fees": "0", - "relayed_message": "", - "cctx_status": { - "status": 1, - "status_message": "", - "lastUpdate_timestamp": 1709145057 - }, - "inbound_tx_params": { - "sender": "0xd91b507F2A3e2D4A32d0C86Ac19FEAD2D461008D", - "sender_chain_id": 7000, - "tx_origin": "0xb0C04e07A301927672A8A7a874DB6930576C90B8", - "coin_type": 1, - "asset": "", - "amount": "657177295293237048", - "inbound_tx_observed_hash": "0x093f4ca4c1884df0fd9dd59b75979342ded29d3c9b6861644287a2e1417b9a39", - "inbound_tx_observed_external_height": 1960153, - "inbound_tx_ballot_index": "0x541b570182950809f9b9077861a0fc7038af9a14ce8af4e151a83adfa308c7a9", - "inbound_tx_finalized_zeta_height": 0, - "tx_finalization_status": 0 - }, - "outbound_tx_params": [ - { - "receiver": "0xb0C04e07A301927672A8A7a874DB6930576C90B8", - "receiver_chainId": 56, - "coin_type": 1, - "amount": "657177295293237048", - "outbound_tx_tss_nonce": 68270, - "outbound_tx_gas_limit": 21000, - "outbound_tx_gas_price": "6000000000", - "outbound_tx_hash": "", - "outbound_tx_ballot_index": "", - "outbound_tx_observed_external_height": 0, - "outbound_tx_gas_used": 0, - "outbound_tx_effective_gas_price": "0", - "outbound_tx_effective_gas_limit": 0, - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", - "tx_finalization_status": 0 - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_56_68270_invalidChainID.json b/zetaclient/testdata/cctx/cctx_56_68270_invalidChainID.json deleted file mode 100644 index 2e4cc4d97a..0000000000 --- a/zetaclient/testdata/cctx/cctx_56_68270_invalidChainID.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "creator": "", - "index": "0x541b570182950809f9b9077861a0fc7038af9a14ce8af4e151a83adfa308c7a9", - "zeta_fees": "0", - "relayed_message": "", - "cctx_status": { - "status": 1, - "status_message": "", - "lastUpdate_timestamp": 1709145057 - }, - "inbound_tx_params": { - "sender": "0xd91b507F2A3e2D4A32d0C86Ac19FEAD2D461008D", - "sender_chain_id": 7000, - "tx_origin": "0xb0C04e07A301927672A8A7a874DB6930576C90B8", - "coin_type": 1, - "asset": "", - "amount": "657177295293237048", - "inbound_tx_observed_hash": "0x093f4ca4c1884df0fd9dd59b75979342ded29d3c9b6861644287a2e1417b9a39", - "inbound_tx_observed_external_height": 1960153, - "inbound_tx_ballot_index": "0x541b570182950809f9b9077861a0fc7038af9a14ce8af4e151a83adfa308c7a9", - "inbound_tx_finalized_zeta_height": 0, - "tx_finalization_status": 0 - }, - "outbound_tx_params": [ - { - "receiver": "0xb0C04e07A301927672A8A7a874DB6930576C90B8", - "receiver_chainId": 13378337, - "coin_type": 1, - "amount": "657177295293237048", - "outbound_tx_tss_nonce": 68270, - "outbound_tx_gas_limit": 21000, - "outbound_tx_gas_price": "6000000000", - "outbound_tx_hash": "", - "outbound_tx_ballot_index": "", - "outbound_tx_observed_external_height": 0, - "outbound_tx_gas_used": 0, - "outbound_tx_effective_gas_price": "0", - "outbound_tx_effective_gas_limit": 0, - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", - "tx_finalization_status": 0 - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_8332_148.json b/zetaclient/testdata/cctx/cctx_8332_148.json deleted file mode 100644 index 4564858f58..0000000000 --- a/zetaclient/testdata/cctx/cctx_8332_148.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "index": "0xb3f5f3cf2ed2e0c3fa64c8297c9e50fbc07351fb2d26d8eae4cfbbd45e47a524", - "zeta_fees": "0", - "cctx_status": { "status": 3, "lastUpdate_timestamp": 1708608895 }, - "inbound_tx_params": { - "sender": "0x13A0c5930C028511Dc02665E7285134B6d11A5f4", - "sender_chain_id": 7000, - "tx_origin": "0xe99174F08e1186134830f8511De06bd010978533", - "coin_type": 1, - "amount": "12000", - "inbound_tx_observed_hash": "0x06455013319acb1b027461134853c77b003d8eab162b1f37673da5ad8a50b74f", - "inbound_tx_observed_external_height": 1870408, - "inbound_tx_ballot_index": "0xb3f5f3cf2ed2e0c3fa64c8297c9e50fbc07351fb2d26d8eae4cfbbd45e47a524" - }, - "outbound_tx_params": [ - { - "receiver": "bc1qpsdlklfcmlcfgm77c43x65ddtrt7n0z57hsyjp", - "receiver_chainId": 8332, - "coin_type": 1, - "amount": "12000", - "outbound_tx_tss_nonce": 148, - "outbound_tx_gas_limit": 254, - "outbound_tx_gas_price": "46", - "outbound_tx_hash": "030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0", - "outbound_tx_ballot_index": "0x43845693f799b7a5e84dcf11321ae681ec018d709ecc919773968018f93e21c1", - "outbound_tx_observed_external_height": 150, - "outbound_tx_effective_gas_price": "0", - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", - "tx_finalization_status": 2 - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_intx_1_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.json b/zetaclient/testdata/cctx/cctx_intx_1_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.json deleted file mode 100644 index 6df7bcfe3e..0000000000 --- a/zetaclient/testdata/cctx/cctx_intx_1_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "creator": "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", - "index": "0xd326700a1931f28853f44f8462f72588f94b1f248214d59a23c3e1b141ff5ede", - "zeta_fees": "0", - "cctx_status": { - "status": 3, - "status_message": "Remote omnichain contract call completed", - "lastUpdate_timestamp": 1709052990 - }, - "inbound_tx_params": { - "sender": "0x56BF8D4a6E7b59D2C0E40Cba2409a4a30ab4FbE2", - "sender_chain_id": 1, - "tx_origin": "0x56BF8D4a6E7b59D2C0E40Cba2409a4a30ab4FbE2", - "coin_type": 2, - "asset": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "amount": "9992000000", - "inbound_tx_observed_hash": "0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da", - "inbound_tx_observed_external_height": 19320188, - "inbound_tx_ballot_index": "0xd326700a1931f28853f44f8462f72588f94b1f248214d59a23c3e1b141ff5ede", - "inbound_tx_finalized_zeta_height": 1944675, - "tx_finalization_status": 2 - }, - "outbound_tx_params": [ - { - "receiver": "0x56bf8d4a6e7b59d2c0e40cba2409a4a30ab4fbe2", - "receiver_chainId": 7000, - "coin_type": 2, - "amount": "0", - "outbound_tx_gas_limit": 1500000, - "outbound_tx_hash": "0xf63eaa3e01af477673aa9e86fb634df15d30a00734dab7450cb0fc28dbc9d11b", - "outbound_tx_observed_external_height": 1944675, - "outbound_tx_effective_gas_price": "0", - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc" - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_intx_1_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.json b/zetaclient/testdata/cctx/cctx_intx_1_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.json deleted file mode 100644 index a167134b35..0000000000 --- a/zetaclient/testdata/cctx/cctx_intx_1_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "creator": "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", - "index": "0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098", - "zeta_fees": "0", - "cctx_status": { - "status": 3, - "status_message": "Remote omnichain contract call completed", - "lastUpdate_timestamp": 1709177431 - }, - "inbound_tx_params": { - "sender": "0xF829fa7069680b8C37A8086b37d4a24697E5003b", - "sender_chain_id": 1, - "tx_origin": "0xF829fa7069680b8C37A8086b37d4a24697E5003b", - "coin_type": 1, - "amount": "4000000000000000", - "inbound_tx_observed_hash": "0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532", - "inbound_tx_observed_external_height": 19330473, - "inbound_tx_ballot_index": "0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098", - "inbound_tx_finalized_zeta_height": 1965579, - "tx_finalization_status": 2 - }, - "outbound_tx_params": [ - { - "receiver": "0xF829fa7069680b8C37A8086b37d4a24697E5003b", - "receiver_chainId": 7000, - "coin_type": 1, - "amount": "0", - "outbound_tx_gas_limit": 90000, - "outbound_tx_hash": "0x3b8c1dab5aa21ff90ddb569f2f962ff2d4aa8d914c9177900102e745955e6f35", - "outbound_tx_observed_external_height": 1965579, - "outbound_tx_effective_gas_price": "0", - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc" - } - ] -} diff --git a/zetaclient/testdata/cctx/cctx_intx_1_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json b/zetaclient/testdata/cctx/cctx_intx_1_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json deleted file mode 100644 index b31c759c5f..0000000000 --- a/zetaclient/testdata/cctx/cctx_intx_1_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "creator": "zeta1p0uwsq4naus5r4l7l744upy0k8ezzj84mn40nf", - "index": "0x477544c4b8c8be544b23328b21286125c89cd6bb5d1d6d388d91eea8ea1a6f1f", - "zeta_fees": "0", - "cctx_status": { - "status": 3, - "status_message": "Remote omnichain contract call completed", - "lastUpdate_timestamp": 1708490549 - }, - "inbound_tx_params": { - "sender": "0x2f993766e8e1Ef9288B1F33F6aa244911A0A77a7", - "sender_chain_id": 1, - "tx_origin": "0x2f993766e8e1Ef9288B1F33F6aa244911A0A77a7", - "amount": "20000000000000000000", - "inbound_tx_observed_hash": "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76", - "inbound_tx_observed_external_height": 19273702, - "inbound_tx_ballot_index": "0x477544c4b8c8be544b23328b21286125c89cd6bb5d1d6d388d91eea8ea1a6f1f", - "inbound_tx_finalized_zeta_height": 1851403, - "tx_finalization_status": 2 - }, - "outbound_tx_params": [ - { - "receiver": "0x2f993766e8e1ef9288b1f33f6aa244911a0a77a7", - "receiver_chainId": 7000, - "amount": "0", - "outbound_tx_gas_limit": 100000, - "outbound_tx_hash": "0x947434364da7c74d7e896a389aa8cb3122faf24bbcba64b141cb5acd7838209c", - "outbound_tx_observed_external_height": 1851403, - "outbound_tx_effective_gas_price": "0", - "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc" - } - ] -} diff --git a/zetaclient/testdata/cctx/chain_1337_cctx_14.go b/zetaclient/testdata/cctx/chain_1337_cctx_14.go new file mode 100644 index 0000000000..ec4fafd8c4 --- /dev/null +++ b/zetaclient/testdata/cctx/chain_1337_cctx_14.go @@ -0,0 +1,122 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// This cctx was generated in local e2e tests, see original json text attached at end of file +var chain_1337_cctx_14 = &crosschaintypes.CrossChainTx{ + Creator: "zeta1plfrp7ejn0s9tmwufuxvsyn8nlf6a7u9ndgk9m", + Index: "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df", + ZetaFees: sdkmath.NewUintFromString("4000000000009027082"), + RelayedMessage: "bgGCGUux3roBhJr9PgNaC3DOfLBp5ILuZjUZx2z1abQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ==", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_Reverted, + StatusMessage: "Outbound failed, start revert : Outbound succeeded, revert executed", + LastUpdateTimestamp: 1712705995, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed", + SenderChainId: 1337, + TxOrigin: "0x5cC2fBb200A929B372e3016F1925DcF988E081fd", + Amount: sdkmath.NewUintFromString("10000000000000000000"), + InboundTxObservedHash: "0xa5589bf24eca8f108ca35048adc9d5582a303d416c01319391159269ae7e4e6f", + InboundTxObservedExternalHeight: 177, + InboundTxBallotIndex: "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df", + InboundTxFinalizedZetaHeight: 150, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0xbff76e77d56b3c1202107f059425d56f0aef87ed", + ReceiverChainId: 1337, + Amount: sdkmath.NewUintFromString("7999999999995486459"), + OutboundTxTssNonce: 13, + OutboundTxGasLimit: 250000, + OutboundTxGasPrice: "18", + OutboundTxHash: "0x19f99459da6cb08f917f9b0ee2dac94a7be328371dff788ad46e64a24e8c06c9", + OutboundTxObservedExternalHeight: 187, + OutboundTxGasUsed: 67852, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(18), + OutboundTxEffectiveGasLimit: 250000, + TssPubkey: "zetapub1addwnpepqggky6z958k7hhxs6k5quuvs27uv5vtmlv330ppt2362p8ejct88w4g64jv", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + { + Receiver: "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed", + ReceiverChainId: 1337, + Amount: sdkmath.NewUintFromString("5999999999990972918"), + OutboundTxTssNonce: 14, + OutboundTxGasLimit: 250000, + OutboundTxGasPrice: "18", + OutboundTxHash: "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7", + OutboundTxBallotIndex: "0xc36c689fdaf09a9b80a614420cd4fea4fec15044790df60080cdefca0090a9dc", + OutboundTxObservedExternalHeight: 201, + OutboundTxGasUsed: 76128, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(18), + OutboundTxEffectiveGasLimit: 250000, + TssPubkey: "zetapub1addwnpepqggky6z958k7hhxs6k5quuvs27uv5vtmlv330ppt2362p8ejct88w4g64jv", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + }, +} + +// Here is the original cctx json data used to create above chain_1337_cctx_14 +/* +{ + "creator": "zeta1plfrp7ejn0s9tmwufuxvsyn8nlf6a7u9ndgk9m", + "index": "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df", + "zeta_fees": "4000000000009027082", + "relayed_message": "bgGCGUux3roBhJr9PgNaC3DOfLBp5ILuZjUZx2z1abQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ==", + "cctx_status": { + "status": 5, + "status_message": "Outbound failed, start revert : Outbound succeeded, revert executed", + "lastUpdate_timestamp": 1712705995 + }, + "inbound_tx_params": { + "sender": "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed", + "sender_chain_id": 1337, + "tx_origin": "0x5cC2fBb200A929B372e3016F1925DcF988E081fd", + "amount": "10000000000000000000", + "inbound_tx_observed_hash": "0xa5589bf24eca8f108ca35048adc9d5582a303d416c01319391159269ae7e4e6f", + "inbound_tx_observed_external_height": 177, + "inbound_tx_ballot_index": "0x85d06ac908823d125a919164f0596e3496224b206ebe8125ffe7b4ab766f85df", + "inbound_tx_finalized_zeta_height": 150, + "tx_finalization_status": 2 + }, + "outbound_tx_params": [ + { + "receiver": "0xbff76e77d56b3c1202107f059425d56f0aef87ed", + "receiver_chainId": 1337, + "amount": "7999999999995486459", + "outbound_tx_tss_nonce": 13, + "outbound_tx_gas_limit": 250000, + "outbound_tx_gas_price": "18", + "outbound_tx_hash": "0x19f99459da6cb08f917f9b0ee2dac94a7be328371dff788ad46e64a24e8c06c9", + "outbound_tx_observed_external_height": 187, + "outbound_tx_gas_used": 67852, + "outbound_tx_effective_gas_price": "18", + "outbound_tx_effective_gas_limit": 250000, + "tss_pubkey": "zetapub1addwnpepqggky6z958k7hhxs6k5quuvs27uv5vtmlv330ppt2362p8ejct88w4g64jv", + "tx_finalization_status": 2 + }, + { + "receiver": "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed", + "receiver_chainId": 1337, + "amount": "5999999999990972918", + "outbound_tx_tss_nonce": 14, + "outbound_tx_gas_limit": 250000, + "outbound_tx_gas_price": "18", + "outbound_tx_hash": "0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7", + "outbound_tx_ballot_index": "0xc36c689fdaf09a9b80a614420cd4fea4fec15044790df60080cdefca0090a9dc", + "outbound_tx_observed_external_height": 201, + "outbound_tx_gas_used": 76128, + "outbound_tx_effective_gas_price": "18", + "outbound_tx_effective_gas_limit": 250000, + "tss_pubkey": "zetapub1addwnpepqggky6z958k7hhxs6k5quuvs27uv5vtmlv330ppt2362p8ejct88w4g64jv", + "tx_finalization_status": 2 + } + ] +} +*/ diff --git a/zetaclient/testdata/cctx/chain_1_cctx_6270.go b/zetaclient/testdata/cctx/chain_1_cctx_6270.go new file mode 100644 index 0000000000..31c8158f42 --- /dev/null +++ b/zetaclient/testdata/cctx/chain_1_cctx_6270.go @@ -0,0 +1,53 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/1/6270 +var chain_1_cctx_6270 = &crosschaintypes.CrossChainTx{ + Creator: "", + Index: "0xe930f363591b348a07e0a6d309b4301b84f702e3e81e0d0902340c7f7da4b5af", + ZetaFees: sdkmath.ZeroUint(), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_OutboundMined, + StatusMessage: "", + LastUpdateTimestamp: 1708464433, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0xd91b507F2A3e2D4A32d0C86Ac19FEAD2D461008D", + SenderChainId: 7000, + TxOrigin: "0x18D0E2c38b4188D8Ae07008C3BeeB1c80748b41c", + CoinType: coin.CoinType_Gas, + Asset: "", + Amount: sdkmath.NewUint(9831832641427386), + InboundTxObservedHash: "0x8bd0df31e512c472e3162a41281b740b518216cc8eb787c2eb59c81e0cffbe89", + InboundTxObservedExternalHeight: 1846989, + InboundTxBallotIndex: "0xe930f363591b348a07e0a6d309b4301b84f702e3e81e0d0902340c7f7da4b5af", + InboundTxFinalizedZetaHeight: 0, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0x18D0E2c38b4188D8Ae07008C3BeeB1c80748b41c", + ReceiverChainId: 1, + CoinType: coin.CoinType_Gas, + Amount: sdkmath.NewUint(9831832641427386), + OutboundTxTssNonce: 6270, + OutboundTxGasLimit: 21000, + OutboundTxGasPrice: "69197693654", + OutboundTxHash: "0x20104d41e042db754cf7908c5441914e581b498eedbca40979c9853f4b7f8460", + OutboundTxBallotIndex: "0x346a1d00a4d26a2065fe1dc7d5af59a49ad6a8af25853ae2ec976c07349f48c1", + OutboundTxObservedExternalHeight: 19271550, + OutboundTxGasUsed: 21000, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(69197693654), + OutboundTxEffectiveGasLimit: 21000, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + }, +} diff --git a/zetaclient/testdata/cctx/chain_1_cctx_7260.go b/zetaclient/testdata/cctx/chain_1_cctx_7260.go new file mode 100644 index 0000000000..b7e00b954f --- /dev/null +++ b/zetaclient/testdata/cctx/chain_1_cctx_7260.go @@ -0,0 +1,53 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/1/7260 +var chain_1_cctx_7260 = &crosschaintypes.CrossChainTx{ + Creator: "", + Index: "0xbebecbf1d8c12016e38c09d095290df503fe29731722d939433fa47e3ed1f986", + ZetaFees: sdkmath.ZeroUint(), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_OutboundMined, + StatusMessage: "", + LastUpdateTimestamp: 1709574082, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0xd91b507F2A3e2D4A32d0C86Ac19FEAD2D461008D", + SenderChainId: 7000, + TxOrigin: "0x8E62e3e6FbFF3E21F725395416A20EA4E2DeF015", + CoinType: coin.CoinType_Gas, + Asset: "", + Amount: sdkmath.NewUint(42635427434588308), + InboundTxObservedHash: "0x2720e3a98f18c288f4197d412bfce57e58f00dc4f8b31e335ffc0bf7208dd3c5", + InboundTxObservedExternalHeight: 2031411, + InboundTxBallotIndex: "0xbebecbf1d8c12016e38c09d095290df503fe29731722d939433fa47e3ed1f986", + InboundTxFinalizedZetaHeight: 0, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0x8E62e3e6FbFF3E21F725395416A20EA4E2DeF015", + ReceiverChainId: 1, + CoinType: coin.CoinType_Gas, + Amount: sdkmath.NewUint(42635427434588308), + OutboundTxTssNonce: 7260, + OutboundTxGasLimit: 21000, + OutboundTxGasPrice: "236882693686", + OutboundTxHash: "0xd13b593eb62b5500a00e288cc2fb2c8af1339025c0e6bc6183b8bef2ebbed0d3", + OutboundTxBallotIndex: "0x689d894606642a2a7964fa906ebf4998c22a00708544fa88e9c56b86c955066b", + OutboundTxObservedExternalHeight: 19363323, + OutboundTxGasUsed: 21000, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(236882693686), + OutboundTxEffectiveGasLimit: 21000, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + }, +} diff --git a/zetaclient/testdata/cctx/chain_1_cctx_8014.go b/zetaclient/testdata/cctx/chain_1_cctx_8014.go new file mode 100644 index 0000000000..cc6c201b67 --- /dev/null +++ b/zetaclient/testdata/cctx/chain_1_cctx_8014.go @@ -0,0 +1,53 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/1/8014 +var chain_1_cctx_8014 = &crosschaintypes.CrossChainTx{ + Creator: "", + Index: "0x5a100fdb426da35ad4c95520d7a4f1fd2f38c88067c9e80ba209d3a655c6e06e", + ZetaFees: sdkmath.ZeroUint(), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_OutboundMined, + StatusMessage: "", + LastUpdateTimestamp: 1710834402, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0x7c8dDa80bbBE1254a7aACf3219EBe1481c6E01d7", + SenderChainId: 7000, + TxOrigin: "0x8d8D67A8B71c141492825CAE5112Ccd8581073f2", + CoinType: coin.CoinType_ERC20, + Asset: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: sdkmath.NewUint(23726342442), + InboundTxObservedHash: "0x114ed9d327b6afc068c3fa891b82f7c7f2d42ae25a571f7dc004c05e77af592a", + InboundTxObservedExternalHeight: 2241077, + InboundTxBallotIndex: "0x5a100fdb426da35ad4c95520d7a4f1fd2f38c88067c9e80ba209d3a655c6e06e", + InboundTxFinalizedZetaHeight: 0, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0x8d8D67A8B71c141492825CAE5112Ccd8581073f2", + ReceiverChainId: 1, + CoinType: coin.CoinType_ERC20, + Amount: sdkmath.NewUint(23726342442), + OutboundTxTssNonce: 8014, + OutboundTxGasLimit: 100000, + OutboundTxGasPrice: "58619665744", + OutboundTxHash: "0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146", + OutboundTxBallotIndex: "0x4213f2c335758301b8bbb09d9891949ed6ffeea5dd95e5d9eaa8d410baaa0884", + OutboundTxObservedExternalHeight: 19467367, + OutboundTxGasUsed: 60625, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(58619665744), + OutboundTxEffectiveGasLimit: 100000, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + }, +} diff --git a/zetaclient/testdata/cctx/chain_1_cctx_9718.go b/zetaclient/testdata/cctx/chain_1_cctx_9718.go new file mode 100644 index 0000000000..8c42dbdb33 --- /dev/null +++ b/zetaclient/testdata/cctx/chain_1_cctx_9718.go @@ -0,0 +1,53 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/1/9718 +var chain_1_cctx_9718 = &crosschaintypes.CrossChainTx{ + Creator: "", + Index: "0xbf7a214cf9868e1c618123ab4df0081da87bade74eeb5aef37843e35f25e67b7", + ZetaFees: sdkmath.NewUintFromString("19525506001302763608"), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_OutboundMined, + StatusMessage: "", + LastUpdateTimestamp: 1712336965, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0xF0a3F93Ed1B126142E61423F9546bf1323Ff82DF", + SenderChainId: 7000, + TxOrigin: "0x87257C910a19a3fe64AfFAbFe8cF9AAF2ab148BF", + CoinType: coin.CoinType_Zeta, + Asset: "", + Amount: sdkmath.NewUintFromString("20000000000000000000"), + InboundTxObservedHash: "0xb136652cd58fb6a537b0a1677965983059a2004d98919cdacd52551f877cc44f", + InboundTxObservedExternalHeight: 2492552, + InboundTxBallotIndex: "0xbf7a214cf9868e1c618123ab4df0081da87bade74eeb5aef37843e35f25e67b7", + InboundTxFinalizedZetaHeight: 0, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0x30735c88fa430f11499b0edcfcc25246fb9182e3", + ReceiverChainId: 1, + CoinType: coin.CoinType_Zeta, + Amount: sdkmath.NewUint(474493998697236392), + OutboundTxTssNonce: 9718, + OutboundTxGasLimit: 90000, + OutboundTxGasPrice: "112217884384", + OutboundTxHash: "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f", + OutboundTxBallotIndex: "0xff07eaa34ca02a08bca1558e5f6220cbfc734061f083622b24923e032f0c480f", + OutboundTxObservedExternalHeight: 19590894, + OutboundTxGasUsed: 64651, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(112217884384), + OutboundTxEffectiveGasLimit: 100000, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + }, +} diff --git a/zetaclient/testdata/cctx/chain_1_cctx_intx_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.go b/zetaclient/testdata/cctx/chain_1_cctx_intx_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.go new file mode 100644 index 0000000000..3fcc564b22 --- /dev/null +++ b/zetaclient/testdata/cctx/chain_1_cctx_intx_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.go @@ -0,0 +1,53 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/0xd326700a1931f28853f44f8462f72588f94b1f248214d59a23c3e1b141ff5ede +var chain_1_cctx_intx_ERC20_0x4ea69a0 = &crosschaintypes.CrossChainTx{ + Creator: "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", + Index: "0xd326700a1931f28853f44f8462f72588f94b1f248214d59a23c3e1b141ff5ede", + ZetaFees: sdkmath.NewUintFromString("0"), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_OutboundMined, + StatusMessage: "Remote omnichain contract call completed", + LastUpdateTimestamp: 1709052990, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0x56BF8D4a6E7b59D2C0E40Cba2409a4a30ab4FbE2", + SenderChainId: 1, + TxOrigin: "0x56BF8D4a6E7b59D2C0E40Cba2409a4a30ab4FbE2", + CoinType: coin.CoinType_ERC20, + Asset: "0xdAC17F958D2ee523a2206206994597C13D831ec7", + Amount: sdkmath.NewUintFromString("9992000000"), + InboundTxObservedHash: "0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da", + InboundTxObservedExternalHeight: 19320188, + InboundTxBallotIndex: "0xd326700a1931f28853f44f8462f72588f94b1f248214d59a23c3e1b141ff5ede", + InboundTxFinalizedZetaHeight: 1944675, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0x56bf8d4a6e7b59d2c0e40cba2409a4a30ab4fbe2", + ReceiverChainId: 7000, + CoinType: coin.CoinType_ERC20, + Amount: sdkmath.NewUintFromString("0"), + OutboundTxTssNonce: 0, + OutboundTxGasLimit: 1500000, + OutboundTxGasPrice: "", + OutboundTxHash: "0xf63eaa3e01af477673aa9e86fb634df15d30a00734dab7450cb0fc28dbc9d11b", + OutboundTxBallotIndex: "", + OutboundTxObservedExternalHeight: 1944675, + OutboundTxGasUsed: 0, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(0), + OutboundTxEffectiveGasLimit: 0, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + }, +} diff --git a/zetaclient/testdata/cctx/chain_1_cctx_intx_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.go b/zetaclient/testdata/cctx/chain_1_cctx_intx_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.go new file mode 100644 index 0000000000..d281302309 --- /dev/null +++ b/zetaclient/testdata/cctx/chain_1_cctx_intx_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.go @@ -0,0 +1,53 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098 +var chain_1_cctx_intx_Gas_0xeaec67d = &crosschaintypes.CrossChainTx{ + Creator: "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", + Index: "0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098", + ZetaFees: sdkmath.NewUint(0), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_OutboundMined, + StatusMessage: "Remote omnichain contract call completed", + LastUpdateTimestamp: 1709177431, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0xF829fa7069680b8C37A8086b37d4a24697E5003b", + SenderChainId: 1, + TxOrigin: "0xF829fa7069680b8C37A8086b37d4a24697E5003b", + CoinType: coin.CoinType_Gas, + Asset: "", + Amount: sdkmath.NewUintFromString("4000000000000000"), + InboundTxObservedHash: "0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532", + InboundTxObservedExternalHeight: 19330473, + InboundTxBallotIndex: "0x0210925c7dceeff563e6e240d6d650a5f0e8fca6f5b76044a6cf106d21f27098", + InboundTxFinalizedZetaHeight: 1965579, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0xF829fa7069680b8C37A8086b37d4a24697E5003b", + ReceiverChainId: 7000, + CoinType: coin.CoinType_Gas, + Amount: sdkmath.NewUint(0), + OutboundTxTssNonce: 0, + OutboundTxGasLimit: 90000, + OutboundTxGasPrice: "", + OutboundTxHash: "0x3b8c1dab5aa21ff90ddb569f2f962ff2d4aa8d914c9177900102e745955e6f35", + OutboundTxBallotIndex: "", + OutboundTxObservedExternalHeight: 1965579, + OutboundTxGasUsed: 0, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(0), + OutboundTxEffectiveGasLimit: 0, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + }, +} diff --git a/zetaclient/testdata/cctx/chain_1_cctx_intx_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.go b/zetaclient/testdata/cctx/chain_1_cctx_intx_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.go new file mode 100644 index 0000000000..22162d8ab7 --- /dev/null +++ b/zetaclient/testdata/cctx/chain_1_cctx_intx_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.go @@ -0,0 +1,53 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/0x477544c4b8c8be544b23328b21286125c89cd6bb5d1d6d388d91eea8ea1a6f1f +var chain_1_cctx_intx_Zeta_0xf393520 = &crosschaintypes.CrossChainTx{ + Creator: "zeta1p0uwsq4naus5r4l7l744upy0k8ezzj84mn40nf", + Index: "0x477544c4b8c8be544b23328b21286125c89cd6bb5d1d6d388d91eea8ea1a6f1f", + ZetaFees: sdkmath.NewUintFromString("0"), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_OutboundMined, + StatusMessage: "Remote omnichain contract call completed", + LastUpdateTimestamp: 1708490549, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0x2f993766e8e1Ef9288B1F33F6aa244911A0A77a7", + SenderChainId: 1, + TxOrigin: "0x2f993766e8e1Ef9288B1F33F6aa244911A0A77a7", + CoinType: coin.CoinType_Zeta, + Asset: "", + Amount: sdkmath.NewUintFromString("20000000000000000000"), + InboundTxObservedHash: "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76", + InboundTxObservedExternalHeight: 19273702, + InboundTxBallotIndex: "0x477544c4b8c8be544b23328b21286125c89cd6bb5d1d6d388d91eea8ea1a6f1f", + InboundTxFinalizedZetaHeight: 1851403, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0x2f993766e8e1ef9288b1f33f6aa244911a0a77a7", + ReceiverChainId: 7000, + CoinType: coin.CoinType_Zeta, + Amount: sdkmath.ZeroUint(), + OutboundTxTssNonce: 0, + OutboundTxGasLimit: 100000, + OutboundTxGasPrice: "", + OutboundTxHash: "0x947434364da7c74d7e896a389aa8cb3122faf24bbcba64b141cb5acd7838209c", + OutboundTxBallotIndex: "", + OutboundTxObservedExternalHeight: 1851403, + OutboundTxGasUsed: 0, + OutboundTxEffectiveGasPrice: sdkmath.ZeroInt(), + OutboundTxEffectiveGasLimit: 0, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + }, +} diff --git a/zetaclient/testdata/cctx/chain_56_cctx_68270.go b/zetaclient/testdata/cctx/chain_56_cctx_68270.go new file mode 100644 index 0000000000..d39ddc17f1 --- /dev/null +++ b/zetaclient/testdata/cctx/chain_56_cctx_68270.go @@ -0,0 +1,53 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/56/68270 +var chain_56_cctx_68270 = &crosschaintypes.CrossChainTx{ + Creator: "", + Index: "0x541b570182950809f9b9077861a0fc7038af9a14ce8af4e151a83adfa308c7a9", + ZetaFees: sdkmath.ZeroUint(), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_PendingOutbound, + StatusMessage: "", + LastUpdateTimestamp: 1709145183, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0xd91b507F2A3e2D4A32d0C86Ac19FEAD2D461008D", + SenderChainId: 7000, + TxOrigin: "0xb0C04e07A301927672A8A7a874DB6930576C90B8", + CoinType: coin.CoinType_Gas, + Asset: "", + Amount: sdkmath.NewUint(657177295293237048), + InboundTxObservedHash: "0x093f4ca4c1884df0fd9dd59b75979342ded29d3c9b6861644287a2e1417b9a39", + InboundTxObservedExternalHeight: 1960153, + InboundTxBallotIndex: "0x541b570182950809f9b9077861a0fc7038af9a14ce8af4e151a83adfa308c7a9", + InboundTxFinalizedZetaHeight: 0, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "0xb0C04e07A301927672A8A7a874DB6930576C90B8", + ReceiverChainId: 56, + CoinType: coin.CoinType_Gas, + Amount: sdkmath.NewUint(657177295293237048), + OutboundTxTssNonce: 68270, + OutboundTxGasLimit: 21000, + OutboundTxGasPrice: "6000000000", + OutboundTxHash: "0xeb2b183ece6638688b9df9223180b13a67208cd744bbdadeab8de0482d7f4e3c", + OutboundTxBallotIndex: "0xa4600c952934f797e162d637d70859a611757407908d96bc53e45a81c80b006b", + OutboundTxObservedExternalHeight: 36537856, + OutboundTxGasUsed: 21000, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(6000000000), + OutboundTxEffectiveGasLimit: 21000, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + }, +} diff --git a/zetaclient/testdata/cctx/chain_8332_cctx_148.go b/zetaclient/testdata/cctx/chain_8332_cctx_148.go new file mode 100644 index 0000000000..f679164bda --- /dev/null +++ b/zetaclient/testdata/cctx/chain_8332_cctx_148.go @@ -0,0 +1,52 @@ +package cctx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// https://zetachain-mainnet-archive.allthatnode.com:1317/zeta-chain/crosschain/cctx/8332/148 +var chain_8332_cctx_148 = &crosschaintypes.CrossChainTx{ + Creator: "", + Index: "0xb3f5f3cf2ed2e0c3fa64c8297c9e50fbc07351fb2d26d8eae4cfbbd45e47a524", + ZetaFees: sdkmath.ZeroUint(), + RelayedMessage: "", + CctxStatus: &crosschaintypes.Status{ + Status: crosschaintypes.CctxStatus_OutboundMined, + StatusMessage: "", + LastUpdateTimestamp: 1708608895, + IsAbortRefunded: false, + }, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Sender: "0x13A0c5930C028511Dc02665E7285134B6d11A5f4", + SenderChainId: 7000, + TxOrigin: "0xe99174F08e1186134830f8511De06bd010978533", + CoinType: coin.CoinType_Gas, + Asset: "", + Amount: sdkmath.NewUint(12000), + InboundTxObservedHash: "0x06455013319acb1b027461134853c77b003d8eab162b1f37673da5ad8a50b74f", + InboundTxObservedExternalHeight: 1870408, + InboundTxBallotIndex: "0xb3f5f3cf2ed2e0c3fa64c8297c9e50fbc07351fb2d26d8eae4cfbbd45e47a524", + InboundTxFinalizedZetaHeight: 0, + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_NotFinalized, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{ + { + Receiver: "bc1qpsdlklfcmlcfgm77c43x65ddtrt7n0z57hsyjp", + ReceiverChainId: 8332, + CoinType: coin.CoinType_Gas, + Amount: sdkmath.NewUint(12000), + OutboundTxTssNonce: 148, + OutboundTxGasLimit: 254, + OutboundTxGasPrice: "46", + OutboundTxHash: "030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0", + OutboundTxObservedExternalHeight: 150, + OutboundTxGasUsed: 0, + OutboundTxEffectiveGasPrice: sdkmath.NewInt(0), + OutboundTxEffectiveGasLimit: 0, + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, + }, + }, +} diff --git a/zetaclient/testutils/stub/chain_client.go b/zetaclient/testutils/stub/chain_client.go index e6bcdf03b9..a79cc3f731 100644 --- a/zetaclient/testutils/stub/chain_client.go +++ b/zetaclient/testutils/stub/chain_client.go @@ -29,7 +29,7 @@ func (s *EVMClient) Start() { func (s *EVMClient) Stop() { } -func (s *EVMClient) IsCctxOutTxProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { +func (s *EVMClient) IsOutboundProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { return false, false, nil } @@ -70,7 +70,7 @@ func (s *BTCClient) Start() { func (s *BTCClient) Stop() { } -func (s *BTCClient) IsCctxOutTxProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { +func (s *BTCClient) IsOutboundProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { return false, false, nil } diff --git a/zetaclient/testutils/testdata.go b/zetaclient/testutils/testdata.go index d9e1981c6e..001ca2b07b 100644 --- a/zetaclient/testutils/testdata.go +++ b/zetaclient/testutils/testdata.go @@ -14,6 +14,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/config" + testcctx "github.com/zeta-chain/zetacore/zetaclient/testdata/cctx" ) const ( @@ -24,6 +25,16 @@ const ( RestrictedBtcAddressTest = "bcrt1qzp4gt6fc7zkds09kfzaf9ln9c5rvrzxmy6qmpp" ) +// cloneCctx returns a deep copy of the cctx +func cloneCctx(t *testing.T, cctx *crosschaintypes.CrossChainTx) *crosschaintypes.CrossChainTx { + data, err := cctx.Marshal() + require.NoError(t, err) + cloned := &crosschaintypes.CrossChainTx{} + err = cloned.Unmarshal(data) + require.NoError(t, err) + return cloned +} + // SaveObjectToJSONFile saves an object to a file in JSON format func SaveObjectToJSONFile(obj interface{}, filename string) error { file, err := os.Create(filepath.Clean(filename)) @@ -76,6 +87,48 @@ func SaveBTCBlockTrimTx(blockVb *btcjson.GetBlockVerboseTxResult, filename strin return SaveObjectToJSONFile(blockVb, filename) } +// LoadCctxByIntx loads archived cctx by intx +func LoadCctxByIntx( + t *testing.T, + chainID int64, + coinType coin.CoinType, + intxHash string, +) *crosschaintypes.CrossChainTx { + // nameCctx := path.Join("../", TestDataPathCctx, FileNameCctxByIntx(chainID, intxHash, coinType)) + + // cctx := &crosschaintypes.CrossChainTx{} + // LoadObjectFromJSONFile(t, &cctx, nameCctx) + // return cctx + + // get cctx + cctx, found := testcctx.CctxByIntxMap[chainID][coinType][intxHash] + require.True(t, found) + + // clone cctx for each individual test + cloned := cloneCctx(t, cctx) + return cloned +} + +// LoadCctxByNonce loads archived cctx by nonce +func LoadCctxByNonce( + t *testing.T, + chainID int64, + nonce uint64, +) *crosschaintypes.CrossChainTx { + // nameCctx := path.Join("../", TestDataPathCctx, FileNameCctxByNonce(chainID, nonce)) + + // cctx := &crosschaintypes.CrossChainTx{} + // LoadObjectFromJSONFile(t, &cctx, nameCctx) + + // get cctx + cctx, found := testcctx.CCtxByNonceMap[chainID][nonce] + require.True(t, found) + + // clone cctx for each individual test + cloned := cloneCctx(t, cctx) + return cloned +} + // LoadEVMBlock loads archived evm block from file func LoadEVMBlock(t *testing.T, chainID int64, blockNumber uint64, trimmed bool) *ethrpc.Block { name := path.Join("../", TestDataPathEVM, FileNameEVMBlock(chainID, blockNumber, trimmed)) @@ -99,9 +152,7 @@ func LoadBTCTxRawResultNCctx(t *testing.T, chainID int64, nonce uint64) (*btcjso rawResult := &btcjson.TxRawResult{} LoadObjectFromJSONFile(t, rawResult, nameTx) - nameCctx := path.Join("../", TestDataPathCctx, FileNameCctxByNonce(chainID, nonce)) - cctx := &crosschaintypes.CrossChainTx{} - LoadObjectFromJSONFile(t, cctx, nameCctx) + cctx := LoadCctxByNonce(t, chainID, nonce) return rawResult, cctx } @@ -131,31 +182,6 @@ func LoadEVMIntxReceipt( return receipt } -// LoadEVMIntxCctx loads archived intx cctx from file -func LoadEVMIntxCctx( - t *testing.T, - chainID int64, - intxHash string, - coinType coin.CoinType) *crosschaintypes.CrossChainTx { - nameCctx := path.Join("../", TestDataPathCctx, FileNameEVMIntxCctx(chainID, intxHash, coinType)) - - cctx := &crosschaintypes.CrossChainTx{} - LoadObjectFromJSONFile(t, &cctx, nameCctx) - return cctx -} - -// LoadCctxByNonce loads archived cctx by nonce from file -func LoadCctxByNonce( - t *testing.T, - chainID int64, - nonce uint64) *crosschaintypes.CrossChainTx { - nameCctx := path.Join("../", TestDataPathCctx, FileNameCctxByNonce(chainID, nonce)) - - cctx := &crosschaintypes.CrossChainTx{} - LoadObjectFromJSONFile(t, &cctx, nameCctx) - return cctx -} - // LoadEVMIntxNReceipt loads archived intx and receipt from file func LoadEVMIntxNReceipt( t *testing.T, @@ -217,7 +243,7 @@ func LoadEVMIntxNReceiptNCctx( // load archived intx, receipt and cctx tx := LoadEVMIntx(t, chainID, intxHash, coinType) receipt := LoadEVMIntxReceipt(t, chainID, intxHash, coinType) - cctx := LoadEVMIntxCctx(t, chainID, intxHash, coinType) + cctx := LoadCctxByIntx(t, chainID, coinType, intxHash) return tx, receipt, cctx } diff --git a/zetaclient/testutils/testdata_naming.go b/zetaclient/testutils/testdata_naming.go index 47928ff934..675a1a7b0b 100644 --- a/zetaclient/testutils/testdata_naming.go +++ b/zetaclient/testutils/testdata_naming.go @@ -14,6 +14,16 @@ func FileNameEVMBlock(chainID int64, blockNumber uint64, trimmed bool) string { return fmt.Sprintf("chain_%d_block_ethrpc_trimmed_%d.json", chainID, blockNumber) } +// FileNameCctxByIntx returns unified archive cctx file name by intx +func FileNameCctxByIntx(chainID int64, intxHash string, coinType coin.CoinType) string { + return fmt.Sprintf("cctx_intx_%d_%s_%s.json", chainID, coinType, intxHash) +} + +// FileNameCctxByNonce returns unified archive cctx file name by nonce +func FileNameCctxByNonce(chainID int64, nonce uint64) string { + return fmt.Sprintf("chain_%d_cctx_%d.json", chainID, nonce) +} + // FileNameEVMIntx returns unified archive file name for inbound tx func FileNameEVMIntx(chainID int64, intxHash string, coinType coin.CoinType, donation bool) string { if !donation { @@ -30,11 +40,6 @@ func FileNameEVMIntxReceipt(chainID int64, intxHash string, coinType coin.CoinTy return fmt.Sprintf("chain_%d_intx_receipt_donation_%s_%s.json", chainID, coinType, intxHash) } -// FileNameEVMIntxCctx returns unified archive file name for inbound cctx -func FileNameEVMIntxCctx(chainID int64, intxHash string, coinType coin.CoinType) string { - return fmt.Sprintf("cctx_intx_%d_%s_%s.json", chainID, coinType, intxHash) -} - // FileNameBTCIntx returns unified archive file name for inbound tx func FileNameBTCIntx(chainID int64, intxHash string, donation bool) string { if !donation { @@ -59,11 +64,6 @@ func FileNameBTCMsgTx(chainID int64, txHash string) string { return fmt.Sprintf("chain_%d_msgtx_%s.json", chainID, txHash) } -// FileNameCctxByNonce returns unified archive file name for cctx by nonce -func FileNameCctxByNonce(chainID int64, nonce uint64) string { - return fmt.Sprintf("cctx_%d_%d.json", chainID, nonce) -} - // FileNameEVMOuttx returns unified archive file name for outbound tx func FileNameEVMOuttx(chainID int64, txHash string, coinType coin.CoinType) string { return fmt.Sprintf("chain_%d_outtx_%s_%s.json", chainID, coinType, txHash) diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 6c3ce5beae..48771bc9bc 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -218,9 +218,9 @@ func (co *CoreObserver) scheduleCctxEVM( } // try confirming the outtx - included, _, err := ob.IsCctxOutTxProcessed(cctx, co.logger.ZetaChainWatcher) + included, _, err := ob.IsOutboundProcessed(cctx, co.logger.ZetaChainWatcher) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxEVM: IsCctxOutTxProcessed faild for chain %d nonce %d", chainID, nonce) + co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxEVM: IsOutboundProcessed faild for chain %d nonce %d", chainID, nonce) continue } if included { @@ -298,9 +298,9 @@ func (co *CoreObserver) scheduleCctxBTC( continue } // try confirming the outtx - included, confirmed, err := btcClient.IsCctxOutTxProcessed(cctx, co.logger.ZetaChainWatcher) + included, confirmed, err := btcClient.IsOutboundProcessed(cctx, co.logger.ZetaChainWatcher) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxBTC: IsCctxOutTxProcessed faild for chain %d nonce %d", chainID, nonce) + co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxBTC: IsOutboundProcessed faild for chain %d nonce %d", chainID, nonce) continue } if included || confirmed { From f2bb1d1ba1d9ca96a377d095b1b4ee1b721b979b Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 11 Apr 2024 17:27:26 -0500 Subject: [PATCH 5/6] fixed CI unit test failure --- zetaclient/compliance/compliance_test.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/zetaclient/compliance/compliance_test.go b/zetaclient/compliance/compliance_test.go index 001e5d9e0d..c88e3b5da8 100644 --- a/zetaclient/compliance/compliance_test.go +++ b/zetaclient/compliance/compliance_test.go @@ -1,20 +1,19 @@ package compliance import ( - "path" "testing" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/testutils" ) func TestCctxRestricted(t *testing.T) { // load archived cctx - var cctx crosschaintypes.CrossChainTx - testutils.LoadObjectFromJSONFile(t, &cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_1_6270.json")) + chain := chains.EthChain() + cctx := testutils.LoadCctxByNonce(t, chain.ChainId, 6270) // create config cfg := config.Config{ @@ -24,29 +23,29 @@ func TestCctxRestricted(t *testing.T) { t.Run("should return true if sender is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundTxParams.Sender} config.LoadComplianceConfig(cfg) - require.True(t, IsCctxRestricted(&cctx)) + require.True(t, IsCctxRestricted(cctx)) }) t.Run("should return true if receiver is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.GetCurrentOutTxParam().Receiver} config.LoadComplianceConfig(cfg) - require.True(t, IsCctxRestricted(&cctx)) + require.True(t, IsCctxRestricted(cctx)) }) t.Run("should return false if sender and receiver are not restricted", func(t *testing.T) { // restrict other address cfg.ComplianceConfig.RestrictedAddresses = []string{"0x27104b8dB4aEdDb054fCed87c346C0758Ff5dFB1"} config.LoadComplianceConfig(cfg) - require.False(t, IsCctxRestricted(&cctx)) + require.False(t, IsCctxRestricted(cctx)) }) t.Run("should be able to restrict coinbase address", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{ethcommon.Address{}.String()} config.LoadComplianceConfig(cfg) cctx.InboundTxParams.Sender = ethcommon.Address{}.String() - require.True(t, IsCctxRestricted(&cctx)) + require.True(t, IsCctxRestricted(cctx)) }) t.Run("should ignore empty address", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{""} config.LoadComplianceConfig(cfg) cctx.InboundTxParams.Sender = "" - require.False(t, IsCctxRestricted(&cctx)) + require.False(t, IsCctxRestricted(cctx)) }) } From 6c1685b677bc41243074daebfbd42de6485c752e Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 15 Apr 2024 17:37:49 -0500 Subject: [PATCH 6/6] added comment for the return values of function ParseAndCheckZetaEvent --- zetaclient/evm/outbounds.go | 1 + 1 file changed, 1 insertion(+) diff --git a/zetaclient/evm/outbounds.go b/zetaclient/evm/outbounds.go index 64a04b58fd..081616256e 100644 --- a/zetaclient/evm/outbounds.go +++ b/zetaclient/evm/outbounds.go @@ -102,6 +102,7 @@ func (ob *ChainClient) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, l } // ParseAndCheckZetaEvent parses and checks ZetaReceived/ZetaReverted event from the outtx receipt +// It either returns an ZetaReceived or an ZetaReverted event, or an error if no event found func ParseAndCheckZetaEvent( cctx *crosschaintypes.CrossChainTx, receipt *ethtypes.Receipt,