Skip to content

Commit

Permalink
TON withdrawal E2E
Browse files Browse the repository at this point in the history
  • Loading branch information
swift1337 committed Oct 25, 2024
1 parent 0ca1d0d commit d942a26
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 32 deletions.
1 change: 1 addition & 0 deletions cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
tonTests := []string{
e2etests.TestTONDepositName,
e2etests.TestTONDepositAndCallName,
e2etests.TestTONWithdrawName,
}

eg.Go(tonTestRoutine(conf, deployerRunner, verbose, tonTests...))
Expand Down
9 changes: 9 additions & 0 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const (
*/
TestTONDepositName = "ton_deposit"
TestTONDepositAndCallName = "ton_deposit_and_call"
TestTONWithdrawName = "ton_withdraw"

/*
Bitcoin tests
Expand Down Expand Up @@ -458,6 +459,14 @@ var AllE2ETests = []runner.E2ETest{
},
TestTONDepositAndCall,
),
runner.NewE2ETest(
TestTONWithdrawName,
"withdraw TON from ZEVM",
[]runner.ArgDefinition{
{Description: "amount in nano tons", DefaultValue: "2000000000"}, // 2.0 TON
},
TestTONWithdraw,
),
/*
Bitcoin tests
*/
Expand Down
16 changes: 2 additions & 14 deletions e2e/e2etests/test_ton_deposit.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package e2etests

import (
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"

"github.com/zeta-chain/node/e2e/runner"
"github.com/zeta-chain/node/pkg/chains"
toncontracts "github.com/zeta-chain/node/pkg/contracts/ton"
"github.com/zeta-chain/node/testutil/sample"
cctypes "github.com/zeta-chain/node/x/crosschain/types"
)

func TestTONDeposit(r *runner.E2ERunner, args []string) {
require.Len(r, args, 1)

// Given deployer
ctx, deployer, chain := r.Ctx, r.TONDeployer, chains.TONLocalnet
ctx, deployer := r.Ctx, r.TONDeployer

// Given amount
amount := parseUint(r, args[0])
Expand All @@ -34,19 +30,11 @@ func TestTONDeposit(r *runner.E2ERunner, args []string) {
recipient := sample.EthAddress()

// ACT
err = r.TONDeposit(sender, amount, recipient)
cctx, err := r.TONDeposit(sender, amount, recipient)

// ASSERT
require.NoError(r, err)

// Wait for CCTX mining
filter := func(cctx *cctypes.CrossChainTx) bool {
return cctx.InboundParams.SenderChainId == chain.ChainId &&
cctx.InboundParams.Sender == sender.GetAddress().ToRaw()
}

cctx := r.WaitForSpecificCCTX(filter, time.Minute)

// Check CCTX
expectedDeposit := amount.Sub(depositFee)

Expand Down
16 changes: 2 additions & 14 deletions e2e/e2etests/test_ton_deposit_and_call.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
package e2etests

import (
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"

"github.com/zeta-chain/node/e2e/runner"
"github.com/zeta-chain/node/e2e/utils"
"github.com/zeta-chain/node/pkg/chains"
toncontracts "github.com/zeta-chain/node/pkg/contracts/ton"
testcontract "github.com/zeta-chain/node/testutil/contracts"
cctypes "github.com/zeta-chain/node/x/crosschain/types"
)

func TestTONDepositAndCall(r *runner.E2ERunner, args []string) {
require.Len(r, args, 1)

// Given deployer
ctx, deployer, chain := r.Ctx, r.TONDeployer, chains.TONLocalnet
ctx, deployer := r.Ctx, r.TONDeployer

// Given amount
amount := parseUint(r, args[0])
Expand All @@ -40,19 +36,11 @@ func TestTONDepositAndCall(r *runner.E2ERunner, args []string) {
callData := []byte("hello from TON!")

// ACT
err = r.TONDepositAndCall(sender, amount, contractAddr, callData)
_, err = r.TONDepositAndCall(sender, amount, contractAddr, callData)

// ASSERT
require.NoError(r, err)

// Wait for CCTX mining
filter := func(cctx *cctypes.CrossChainTx) bool {
return cctx.InboundParams.SenderChainId == chain.ChainId &&
cctx.InboundParams.Sender == sender.GetAddress().ToRaw()
}

r.WaitForSpecificCCTX(filter, time.Minute)

expectedDeposit := amount.Sub(depositFee)

// check if example contract has been called, bar value should be set to amount
Expand Down
95 changes: 95 additions & 0 deletions e2e/e2etests/test_ton_withdrawals.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package e2etests

import (
"math/big"

"cosmossdk.io/math"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"

"github.com/zeta-chain/node/e2e/runner"
toncontracts "github.com/zeta-chain/node/pkg/contracts/ton"
"github.com/zeta-chain/node/zetaclient/chains/ton/liteapi"
)

func TestTONWithdraw(r *runner.E2ERunner, args []string) {
// ARRANGE
require.Len(r, args, 1)

// Given a deployer
_, deployer := r.Ctx, r.TONDeployer

// That donates 100 TON to some zEVM sender
zevmSender := r.ZEVMAuth.From

_, err := r.TONDeposit(&deployer.Wallet, toncontracts.Coins(100), zevmSender)
require.NoError(r, err)

// Given his ZRC-20 balance
senderZRC20BalanceBefore, err := r.TONZRC20.BalanceOf(&bind.CallOpts{}, zevmSender)
require.NoError(r, err)
r.Logger.Print("zEVM sender's ZRC20 TON balance before withdraw: %d", senderZRC20BalanceBefore)

// Given another TON wallet
tonRecipient, err := deployer.CreateWallet(r.Ctx, toncontracts.Coins(1))
require.NoError(r, err)

tonRecipientBalanceBefore, err := deployer.GetBalanceOf(r.Ctx, tonRecipient.GetAddress())
require.NoError(r, err)

r.Logger.Print("Recipient's TON balance before withdrawal: %s", toncontracts.FormatCoins(tonRecipientBalanceBefore))

// Given amount to withdraw (and approved amount in TON ZRC20 to cover the gas fee)
amount := parseUint(r, args[0])
approvedAmount := amount.Add(toncontracts.Coins(1))

// ACT
cctx := r.WithdrawTONZRC20(tonRecipient.GetAddress(), amount.BigInt(), approvedAmount.BigInt())

// ASSERT
r.Logger.Print(
"Withdraw TON ZRC20 transaction (with %s) sent: %+v",
toncontracts.FormatCoins(amount),
map[string]any{
"zevm_sender": zevmSender.Hex(),
"ton_recipient": tonRecipient.GetAddress().ToRaw(),
"ton_amount": toncontracts.FormatCoins(amount),
"cctx_index": cctx.Index,
"ton_hash": cctx.GetCurrentOutboundParam().Hash,
"zevm_hash": cctx.InboundParams.ObservedHash,
},
)

// Make sure that recipient's TON balance has increased
tonRecipientBalanceAfter, err := deployer.GetBalanceOf(r.Ctx, tonRecipient.GetAddress())
require.NoError(r, err)

r.Logger.Print("Recipient's balance after withdrawal: %s", toncontracts.FormatCoins(tonRecipientBalanceAfter))

// Make sure that sender's ZRC20 balance has decreased
senderZRC20BalanceAfter, err := r.TONZRC20.BalanceOf(&bind.CallOpts{}, zevmSender)
require.NoError(r, err)
r.Logger.Print("zEVM sender's ZRC20 TON balance after withdraw: %d", senderZRC20BalanceAfter)
r.Logger.Print(
"zEVM sender's ZRC20 TON balance diff: %d",
big.NewInt(0).Sub(senderZRC20BalanceBefore, senderZRC20BalanceAfter),
)

// Make sure that TON withdrawal CCTX contain outgoing message with exact withdrawal amount
lt, hash, err := liteapi.TransactionHashFromString(cctx.GetCurrentOutboundParam().Hash)
require.NoError(r, err)

txs, err := r.Clients.TON.GetTransactions(r.Ctx, 1, r.TONGateway.AccountID(), lt, hash)
require.NoError(r, err)
require.Len(r, txs, 1)

// TON coins that were withdrawn from GW to the recipient
inMsgAmount := math.NewUint(
uint64(txs[0].Msgs.OutMsgs.Values()[0].Value.Info.IntMsgInfo.Value.Grams),
)

require.Equal(r, int(amount.Uint64()), int(inMsgAmount.Uint64()))
}

// TODO: Add "withdraw_many_concurrent" test
// https://github.com/zeta-chain/node/issues/3044
5 changes: 5 additions & 0 deletions e2e/runner/setup_ton.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ func (r *E2ERunner) ensureTONChainParams(gw *ton.AccountInit) error {
return errors.Wrap(err, "unable to broadcast TON chain params tx")
}

resetMsg := observertypes.NewMsgResetChainNonces(creator, chainID, 0, 0)
if _, err := r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, resetMsg); err != nil {
return errors.Wrap(err, "unable to broadcast TON chain nonce reset tx")
}

r.Logger.Print("💎Voted for adding TON chain params (localnet). Waiting for confirmation")

query := &observertypes.QueryGetChainParamsForChainRequest{ChainId: chainID}
Expand Down
76 changes: 72 additions & 4 deletions e2e/runner/ton.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package runner

import (
"math/big"
"time"

"cosmossdk.io/math"
eth "github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"github.com/tonkeeper/tongo/ton"
"github.com/tonkeeper/tongo/wallet"

"github.com/zeta-chain/node/e2e/utils"
"github.com/zeta-chain/node/pkg/chains"
toncontracts "github.com/zeta-chain/node/pkg/contracts/ton"
cctypes "github.com/zeta-chain/node/x/crosschain/types"
)

// we need to use this send mode due to how wallet V5 works
Expand All @@ -16,7 +24,13 @@ import (
const tonDepositSendCode = toncontracts.SendFlagSeparateFees + toncontracts.SendFlagIgnoreErrors

// TONDeposit deposit TON to Gateway contract
func (r *E2ERunner) TONDeposit(sender *wallet.Wallet, amount math.Uint, zevmRecipient eth.Address) error {
func (r *E2ERunner) TONDeposit(
sender *wallet.Wallet,
amount math.Uint,
zevmRecipient eth.Address,
) (*cctypes.CrossChainTx, error) {
chain := chains.TONLocalnet

require.NotNil(r, r.TONGateway, "TON Gateway is not initialized")

require.NotNil(r, sender, "Sender wallet is nil")
Expand All @@ -30,7 +44,21 @@ func (r *E2ERunner) TONDeposit(sender *wallet.Wallet, amount math.Uint, zevmReci
zevmRecipient.Hex(),
)

return r.TONGateway.SendDeposit(r.Ctx, sender, amount, zevmRecipient, tonDepositSendCode)
// Send TX
err := r.TONGateway.SendDeposit(r.Ctx, sender, amount, zevmRecipient, tonDepositSendCode)
if err != nil {
return nil, errors.Wrap(err, "failed to send TON deposit")
}

filter := func(cctx *cctypes.CrossChainTx) bool {
return cctx.InboundParams.SenderChainId == chain.ChainId &&
cctx.InboundParams.Sender == sender.GetAddress().ToRaw()
}

// Wait for cctx
cctx := r.WaitForSpecificCCTX(filter, time.Minute)

return cctx, nil
}

// TONDepositAndCall deposit TON to Gateway contract with call data.
Expand All @@ -39,7 +67,9 @@ func (r *E2ERunner) TONDepositAndCall(
amount math.Uint,
zevmRecipient eth.Address,
callData []byte,
) error {
) (*cctypes.CrossChainTx, error) {
chain := chains.TONLocalnet

require.NotNil(r, r.TONGateway, "TON Gateway is not initialized")

require.NotNil(r, sender, "Sender wallet is nil")
Expand All @@ -55,5 +85,43 @@ func (r *E2ERunner) TONDepositAndCall(
string(callData),
)

return r.TONGateway.SendDepositAndCall(r.Ctx, sender, amount, zevmRecipient, callData, tonDepositSendCode)
err := r.TONGateway.SendDepositAndCall(r.Ctx, sender, amount, zevmRecipient, callData, tonDepositSendCode)
if err != nil {
return nil, errors.Wrap(err, "failed to send TON deposit and call")
}

filter := func(cctx *cctypes.CrossChainTx) bool {
return cctx.InboundParams.SenderChainId == chain.ChainId &&
cctx.InboundParams.Sender == sender.GetAddress().ToRaw()
}

// Wait for cctx
cctx := r.WaitForSpecificCCTX(filter, time.Minute)

return cctx, nil
}

// WithdrawTONZRC20 withdraws an amount of ZRC20 TON tokens
func (r *E2ERunner) WithdrawTONZRC20(to ton.AccountID, amount *big.Int, approveAmount *big.Int) *cctypes.CrossChainTx {
// approve
tx, err := r.TONZRC20.Approve(r.ZEVMAuth, r.TONZRC20Addr, approveAmount)
require.NoError(r, err)
receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt, "approve")

// withdraw
tx, err = r.TONZRC20.Withdraw(r.ZEVMAuth, []byte(to.ToRaw()), amount)
require.NoError(r, err)
r.Logger.EVMTransaction(*tx, "withdraw")

// wait for tx receipt
receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt, "withdraw")
r.Logger.Info("Receipt txhash %s status %d", receipt.TxHash, receipt.Status)

// wait for the cctx to be mined
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout)
utils.RequireCCTXStatus(r, cctx, cctypes.CctxStatus_OutboundMined)

return cctx
}
6 changes: 6 additions & 0 deletions pkg/chains/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ func (chain Chain) EncodeAddress(b []byte) (string, error) {
return "", err
}
return pk.String(), nil
case Consensus_catchain_consensus:
acc, err := ton.ParseAccountID(string(b))
if err != nil {
return "", err
}
return acc.ToRaw(), nil
default:
return "", fmt.Errorf("chain id %d not supported", chain.ChainId)
}
Expand Down

0 comments on commit d942a26

Please sign in to comment.