diff --git a/zetaclient/chains/base/amount.go b/zetaclient/chains/base/amount.go new file mode 100644 index 0000000000..e9221d80b8 --- /dev/null +++ b/zetaclient/chains/base/amount.go @@ -0,0 +1,17 @@ +package base + +import ( + cosmoserrors "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + + cctypes "github.com/zeta-chain/node/x/crosschain/types" +) + +// OutboundAmountUint64 validates an amount can fit into uint64 and returns it. +func OutboundAmountUint64(amount sdkmath.Uint) (uint64, error) { + if amount.BigInt().BitLen() > 64 { + return 0, cosmoserrors.Wrap(cctypes.ErrInvalidWithdrawalAmount, "amount exceeds uint64 range") + } + + return amount.Uint64(), nil +} diff --git a/zetaclient/chains/base/amount_test.go b/zetaclient/chains/base/amount_test.go new file mode 100644 index 0000000000..a61d33e905 --- /dev/null +++ b/zetaclient/chains/base/amount_test.go @@ -0,0 +1,27 @@ +package base + +import ( + "math/big" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/require" + + cctypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestOutboundAmountUint64(t *testing.T) { + t.Run("returns amount within uint64 range", func(t *testing.T) { + maxUint64 := ^uint64(0) + amount, err := OutboundAmountUint64(sdkmath.NewUint(maxUint64)) + require.NoError(t, err) + require.Equal(t, maxUint64, amount) + }) + + t.Run("fails if amount exceeds uint64 range", func(t *testing.T) { + amount := sdkmath.NewUintFromBigInt(new(big.Int).Lsh(big.NewInt(1), 64)) + + _, err := OutboundAmountUint64(amount) + require.ErrorIs(t, err, cctypes.ErrInvalidWithdrawalAmount) + }) +} diff --git a/zetaclient/chains/solana/signer/execute.go b/zetaclient/chains/solana/signer/execute.go index d2a6db12aa..18294eda60 100644 --- a/zetaclient/chains/solana/signer/execute.go +++ b/zetaclient/chains/solana/signer/execute.go @@ -17,6 +17,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" contracts "github.com/zeta-chain/node/pkg/contracts/solana" "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" zctx "github.com/zeta-chain/node/zetaclient/context" ) @@ -156,11 +157,15 @@ func (signer *Signer) createMsgExecute( // #nosec G115 always positive chainID := uint64(signer.Chain().ChainId) nonce := params.TssNonce - amount := params.Amount.Uint64() + amount := uint64(0) // zero out the amount if cancelTx is set. It's legal to withdraw 0 lamports through the gateway. - if cancelTx { - amount = 0 + if !cancelTx { + var err error + amount, err = base.OutboundAmountUint64(params.Amount) + if err != nil { + return nil, nil, err + } } // prepare data for msg execute diff --git a/zetaclient/chains/solana/signer/execute_spl.go b/zetaclient/chains/solana/signer/execute_spl.go index 563a42a768..98c0838091 100644 --- a/zetaclient/chains/solana/signer/execute_spl.go +++ b/zetaclient/chains/solana/signer/execute_spl.go @@ -12,6 +12,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" contracts "github.com/zeta-chain/node/pkg/contracts/solana" "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" ) // prepareExecuteSPLTx prepares execute spl outbound @@ -63,11 +64,15 @@ func (signer *Signer) createMsgExecuteSPL( // #nosec G115 always positive chainID := uint64(signer.Chain().ChainId) nonce := params.TssNonce - amount := params.Amount.Uint64() + amount := uint64(0) // zero out the amount if cancelTx is set. It's legal to withdraw 0 spl through the gateway. - if cancelTx { - amount = 0 + if !cancelTx { + var err error + amount, err = base.OutboundAmountUint64(params.Amount) + if err != nil { + return nil, nil, err + } } // get mint details to get decimals diff --git a/zetaclient/chains/solana/signer/increment_nonce.go b/zetaclient/chains/solana/signer/increment_nonce.go index 5520e043cc..9ebb400ce2 100644 --- a/zetaclient/chains/solana/signer/increment_nonce.go +++ b/zetaclient/chains/solana/signer/increment_nonce.go @@ -11,6 +11,7 @@ import ( "github.com/zeta-chain/node/pkg/coin" contracts "github.com/zeta-chain/node/pkg/contracts/solana" "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" "github.com/zeta-chain/node/zetaclient/compliance" ) @@ -70,11 +71,15 @@ func (signer *Signer) createAndSignMsgIncrementNonce( // #nosec G115 always positive chainID := uint64(signer.Chain().ChainId) nonce := params.TssNonce - amount := params.Amount.Uint64() + amount := uint64(0) // zero out the amount if cancelTx is set. It's legal to withdraw 0 lamports through the gateway. - if cancelTx { - amount = 0 + if !cancelTx { + var err error + amount, err = base.OutboundAmountUint64(params.Amount) + if err != nil { + return nil, err + } } // prepare increment_nonce msg and compute hash diff --git a/zetaclient/chains/solana/signer/withdraw.go b/zetaclient/chains/solana/signer/withdraw.go index 40296e728b..a8c5f29fea 100644 --- a/zetaclient/chains/solana/signer/withdraw.go +++ b/zetaclient/chains/solana/signer/withdraw.go @@ -11,6 +11,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" contracts "github.com/zeta-chain/node/pkg/contracts/solana" "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" ) // prepareWithdrawTx prepares withdraw outbound @@ -53,11 +54,15 @@ func (signer *Signer) createMsgWithdraw( // #nosec G115 always positive chainID := uint64(signer.Chain().ChainId) nonce := params.TssNonce - amount := params.Amount.Uint64() + amount := uint64(0) // zero out the amount if cancelTx is set. It's legal to withdraw 0 lamports through the gateway. - if cancelTx { - amount = 0 + if !cancelTx { + var err error + amount, err = base.OutboundAmountUint64(params.Amount) + if err != nil { + return nil, nil, err + } } // check receiver address diff --git a/zetaclient/chains/solana/signer/withdraw_spl.go b/zetaclient/chains/solana/signer/withdraw_spl.go index 5d07bc807c..bbfa862fc0 100644 --- a/zetaclient/chains/solana/signer/withdraw_spl.go +++ b/zetaclient/chains/solana/signer/withdraw_spl.go @@ -12,6 +12,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" contracts "github.com/zeta-chain/node/pkg/contracts/solana" "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" ) // prepareWithdrawSPLTx prepares withdraw spl outbound @@ -60,11 +61,15 @@ func (signer *Signer) createMsgWithdrawSPL( // #nosec G115 always positive chainID := uint64(signer.Chain().ChainId) nonce := params.TssNonce - amount := params.Amount.Uint64() + amount := uint64(0) // zero out the amount if cancelTx is set. It's legal to withdraw 0 spl through the gateway. - if cancelTx { - amount = 0 + if !cancelTx { + var err error + amount, err = base.OutboundAmountUint64(params.Amount) + if err != nil { + return nil, nil, err + } } // get mint details to get decimals diff --git a/zetaclient/chains/sui/signer/signer_tx.go b/zetaclient/chains/sui/signer/signer_tx.go index ea130f8219..aa8ad94a39 100644 --- a/zetaclient/chains/sui/signer/signer_tx.go +++ b/zetaclient/chains/sui/signer/signer_tx.go @@ -15,6 +15,7 @@ import ( "github.com/zeta-chain/node/pkg/coin" zetasui "github.com/zeta-chain/node/pkg/contracts/sui" cctypes "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" "github.com/zeta-chain/node/zetaclient/chains/sui/client" "github.com/zeta-chain/node/zetaclient/logs" ) @@ -159,6 +160,10 @@ func (s *Signer) buildWithdrawAndCallTx( payloadHex string, ) (models.TxnMetaData, error) { params := cctx.GetCurrentOutboundParam() + amount, err := base.OutboundAmountUint64(params.Amount) + if err != nil { + return models.TxnMetaData{}, err + } // decode and parse the payload into object IDs and on_call arguments payloadBytes, err := hex.DecodeString(payloadHex) @@ -181,7 +186,7 @@ func (s *Signer) buildWithdrawAndCallTx( args := withdrawAndCallPTBArgs{ withdrawAndCallObjRefs: wacRefs, coinType: coinType, - amount: params.Amount.Uint64(), + amount: amount, nonce: params.TssNonce, gasBudget: gasBudget, sender: ethcommon.HexToAddress(cctx.InboundParams.Sender).Hex(),