-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
287 additions
and
242 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package evm | ||
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
|
||
ethcommon "github.com/ethereum/go-ethereum/common" | ||
"github.com/rs/zerolog" | ||
"github.com/zeta-chain/zetacore/common" | ||
"github.com/zeta-chain/zetacore/x/crosschain/types" | ||
"github.com/zeta-chain/zetacore/zetaclient/interfaces" | ||
) | ||
|
||
const ( | ||
MinGasLimit = 100_000 | ||
MaxGasLimit = 1_000_000 | ||
) | ||
|
||
// BaseTransactionData is a data structure containing input fields used to construct each type of transaction. | ||
// This is populated using cctx and other input parameters passed to TryProcessOutTx | ||
type BaseTransactionData struct { | ||
srcChainID *big.Int | ||
toChainID *big.Int | ||
sender ethcommon.Address | ||
to ethcommon.Address | ||
asset ethcommon.Address | ||
amount *big.Int | ||
gasPrice *big.Int | ||
gasLimit uint64 | ||
message []byte | ||
nonce uint64 | ||
height uint64 | ||
|
||
// cmd field is used to determine whether to execute ERC20 whitelist or migrate TSS funds given that the coin type | ||
// from the cctx is CMD | ||
cmd string | ||
|
||
// params field is used to pass input parameters for command requests, currently it is used to pass the ERC20 | ||
// contract address when a whitelist command is requested | ||
params string | ||
|
||
// sendHash field is the inbound message digest that is sent to the destination contract | ||
sendHash [32]byte | ||
|
||
// outboundParams field contains data detailing the receiver chain and outbound transaction | ||
outboundParams *types.OutboundTxParams | ||
} | ||
|
||
// SetChainAndSender populates the destination address and Chain ID based on the status of the cross chain tx | ||
// returns true if transaction should be skipped | ||
// returns false otherwise | ||
func (txData *BaseTransactionData) SetChainAndSender(cctx *types.CrossChainTx, logger zerolog.Logger) bool { | ||
switch cctx.CctxStatus.Status { | ||
case types.CctxStatus_PendingRevert: | ||
txData.to = ethcommon.HexToAddress(cctx.InboundTxParams.Sender) | ||
txData.toChainID = big.NewInt(cctx.InboundTxParams.SenderChainId) | ||
logger.Info().Msgf("Abort: reverting inbound") | ||
case types.CctxStatus_PendingOutbound: | ||
txData.to = ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver) | ||
txData.toChainID = big.NewInt(cctx.GetCurrentOutTxParam().ReceiverChainId) | ||
default: | ||
logger.Info().Msgf("Transaction doesn't need to be processed status: %d", cctx.CctxStatus.Status) | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
// SetupGas sets the gas limit and price | ||
func (txData *BaseTransactionData) SetupGas( | ||
cctx *types.CrossChainTx, | ||
logger zerolog.Logger, | ||
client interfaces.EVMRPCClient, | ||
chain *common.Chain, | ||
) error { | ||
|
||
txData.gasLimit = cctx.GetCurrentOutTxParam().OutboundTxGasLimit | ||
if txData.gasLimit < MinGasLimit { | ||
txData.gasLimit = MinGasLimit | ||
logger.Warn().Msgf("gasLimit %d is too low; set to %d", cctx.GetCurrentOutTxParam().OutboundTxGasLimit, txData.gasLimit) | ||
} | ||
if txData.gasLimit > MaxGasLimit { | ||
txData.gasLimit = MaxGasLimit | ||
logger.Warn().Msgf("gasLimit %d is too high; set to %d", cctx.GetCurrentOutTxParam().OutboundTxGasLimit, txData.gasLimit) | ||
} | ||
|
||
// use dynamic gas price for ethereum chains. | ||
// The code below is a fix for https://github.com/zeta-chain/node/issues/1085 | ||
// doesn't close directly the issue because we should determine if we want to keep using SuggestGasPrice if no OutboundTxGasPrice | ||
// we should possibly remove it completely and return an error if no OutboundTxGasPrice is provided because it means no fee is processed on ZetaChain | ||
specified, ok := new(big.Int).SetString(cctx.GetCurrentOutTxParam().OutboundTxGasPrice, 10) | ||
if !ok { | ||
if common.IsEthereumChain(chain.ChainId) { | ||
suggested, err := client.SuggestGasPrice(context.Background()) | ||
if err != nil { | ||
return errors.Join(err, fmt.Errorf("cannot get gas price from chain %s ", chain)) | ||
} | ||
txData.gasPrice = roundUpToNearestGwei(suggested) | ||
} else { | ||
return fmt.Errorf("cannot convert gas price %s ", cctx.GetCurrentOutTxParam().OutboundTxGasPrice) | ||
} | ||
} else { | ||
txData.gasPrice = specified | ||
} | ||
return nil | ||
} | ||
|
||
// SetTransactionData populates transaction input fields parsed from the cctx and other parameters | ||
// returns | ||
// bool (skipTx) - if the transaction doesn't qualify to be processed the function will return true, meaning that this | ||
// | ||
// cctx will be skipped and false otherwise. | ||
// | ||
// error | ||
func (txData *BaseTransactionData) SetTransactionData( | ||
cctx *types.CrossChainTx, | ||
evmClient *ChainClient, | ||
evmRPC interfaces.EVMRPCClient, | ||
logger zerolog.Logger, | ||
) (bool, error) { | ||
|
||
txData.outboundParams = cctx.GetCurrentOutTxParam() | ||
txData.amount = cctx.GetCurrentOutTxParam().Amount.BigInt() | ||
txData.nonce = cctx.GetCurrentOutTxParam().OutboundTxTssNonce | ||
txData.sender = ethcommon.HexToAddress(cctx.InboundTxParams.Sender) | ||
txData.srcChainID = big.NewInt(cctx.InboundTxParams.SenderChainId) | ||
txData.asset = ethcommon.HexToAddress(cctx.InboundTxParams.Asset) | ||
|
||
skipTx := txData.SetChainAndSender(cctx, logger) | ||
if skipTx { | ||
return true, nil | ||
} | ||
|
||
toChain := common.GetChainFromChainID(txData.toChainID.Int64()) | ||
if toChain == nil { | ||
return true, fmt.Errorf("unknown chain: %d", txData.toChainID.Int64()) | ||
} | ||
|
||
// Get nonce, Early return if the cctx is already processed | ||
nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce | ||
included, confirmed, err := evmClient.IsSendOutTxProcessed(cctx, logger) | ||
if err != nil { | ||
return true, errors.New("IsSendOutTxProcessed failed") | ||
} | ||
if included || confirmed { | ||
logger.Info().Msgf("CCTX already processed; exit signer") | ||
return true, nil | ||
} | ||
|
||
// Set up gas limit and gas price | ||
err = txData.SetupGas(cctx, logger, evmRPC, toChain) | ||
if err != nil { | ||
return true, err | ||
} | ||
|
||
// Get sendHash | ||
logger.Info().Msgf("chain %s minting %d to %s, nonce %d, finalized zeta bn %d", toChain, cctx.InboundTxParams.Amount, txData.to.Hex(), nonce, cctx.InboundTxParams.InboundTxFinalizedZetaHeight) | ||
sendHash, err := hex.DecodeString(cctx.Index[2:]) // remove the leading 0x | ||
if err != nil || len(sendHash) != 32 { | ||
return true, fmt.Errorf("decode CCTX %s error", cctx.Index) | ||
} | ||
copy(txData.sendHash[:32], sendHash[:32]) | ||
|
||
// In case there is a pending transaction, make sure this keysign is a transaction replacement | ||
pendingTx := evmClient.GetPendingTx(nonce) | ||
if pendingTx != nil { | ||
if txData.gasPrice.Cmp(pendingTx.GasPrice()) > 0 { | ||
logger.Info().Msgf("replace pending outTx %s nonce %d using gas price %d", pendingTx.Hash().Hex(), nonce, txData.gasPrice) | ||
} else { | ||
logger.Info().Msgf("please wait for pending outTx %s nonce %d to be included", pendingTx.Hash().Hex(), nonce) | ||
return true, nil | ||
} | ||
} | ||
|
||
// Base64 decode message | ||
if cctx.GetCurrentOutTxParam().CoinType != common.CoinType_Cmd { | ||
txData.message, err = base64.StdEncoding.DecodeString(cctx.RelayedMessage) | ||
if err != nil { | ||
logger.Err(err).Msgf("decode CCTX.Message %s error", cctx.RelayedMessage) | ||
} | ||
} | ||
|
||
return false, nil | ||
} |
Oops, something went wrong.