Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: integrate withdraw SPL #3134

Merged
merged 14 commits into from
Nov 12, 2024
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* [2984](https://github.com/zeta-chain/node/pull/2984) - add Whitelist message ability to whitelist SPL tokens on Solana
* [3091](https://github.com/zeta-chain/node/pull/3091) - improve build reproducability. `make release{,-build-only}` checksums should now be stable.
* [3124](https://github.com/zeta-chain/node/pull/3124) - integrate SPL deposits
* [3134](https://github.com/zeta-chain/node/pull/3134) - integrate SPL tokens withdraw to Solana

### Tests

Expand Down
4 changes: 3 additions & 1 deletion cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,9 +438,11 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
e2etests.TestSolanaWithdrawRestrictedName,
// TODO move under admin tests
// https://github.com/zeta-chain/node/issues/3085
e2etests.TestSolanaWhitelistSPLName,
e2etests.TestSPLDepositName,
e2etests.TestSPLDepositAndCallName,
e2etests.TestSPLWithdrawName,
e2etests.TestSPLWithdrawAndCreateReceiverAtaName,
e2etests.TestSolanaWhitelistSPLName,
}
eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...))
}
Expand Down
3 changes: 2 additions & 1 deletion contrib/localnet/solana/start-solana.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ echo "starting solana test validator..."
solana-test-validator &

sleep 5
# airdrop to e2e sol account
# airdrop to e2e sol account and rent payer (used to generate atas for withdraw spl receivers if they don't exist)
solana airdrop 100
solana airdrop 100 37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ
solana airdrop 100 C6KPvGDYfNusoE4yfRP21F8wK35bxCBMT69xk4xo3X79
skosito marked this conversation as resolved.
Show resolved Hide resolved
solana program deploy gateway.so


Expand Down
38 changes: 28 additions & 10 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,16 @@ const (
/*
* Solana tests
*/
TestSolanaDepositName = "solana_deposit"
TestSolanaWithdrawName = "solana_withdraw"
TestSolanaDepositAndCallName = "solana_deposit_and_call"
TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund"
TestSolanaDepositRestrictedName = "solana_deposit_restricted"
TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted"
TestSPLDepositName = "spl_deposit"
TestSPLDepositAndCallName = "spl_deposit_and_call"
TestSolanaDepositName = "solana_deposit"
TestSolanaWithdrawName = "solana_withdraw"
TestSolanaDepositAndCallName = "solana_deposit_and_call"
TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund"
TestSolanaDepositRestrictedName = "solana_deposit_restricted"
TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted"
TestSPLDepositName = "spl_deposit"
TestSPLDepositAndCallName = "spl_deposit_and_call"
TestSPLWithdrawName = "spl_withdraw"
TestSPLWithdrawAndCreateReceiverAtaName = "spl_withdraw_and_create_receiver_ata"

/**
* TON tests
Expand Down Expand Up @@ -434,6 +436,22 @@ var AllE2ETests = []runner.E2ETest{
},
TestSolanaDepositAndCall,
),
runner.NewE2ETest(
TestSPLWithdrawName,
"withdraw SPL from ZEVM",
[]runner.ArgDefinition{
{Description: "amount in spl tokens", DefaultValue: "1000000"},
},
TestSPLWithdraw,
),
runner.NewE2ETest(
TestSPLWithdrawAndCreateReceiverAtaName,
"withdraw SPL from ZEVM and create receiver ata",
[]runner.ArgDefinition{
{Description: "amount in spl tokens", DefaultValue: "1000000"},
},
TestSPLWithdrawAndCreateReceiverAta,
),
runner.NewE2ETest(
TestSolanaDepositAndCallRefundName,
"deposit SOL into ZEVM and call a contract that reverts; should refund",
Expand Down Expand Up @@ -470,15 +488,15 @@ var AllE2ETests = []runner.E2ETest{
TestSPLDepositName,
"deposit SPL into ZEVM",
[]runner.ArgDefinition{
{Description: "amount of spl tokens", DefaultValue: "500000"},
{Description: "amount of spl tokens", DefaultValue: "12000000"},
},
TestSPLDeposit,
),
runner.NewE2ETest(
TestSPLDepositAndCallName,
"deposit SPL into ZEVM and call",
[]runner.ArgDefinition{
{Description: "amount of spl tokens", DefaultValue: "500000"},
{Description: "amount of spl tokens", DefaultValue: "12000000"},
},
TestSPLDepositAndCall,
),
Expand Down
3 changes: 1 addition & 2 deletions e2e/e2etests/test_solana_whitelist_spl.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ func TestSolanaWhitelistSPL(r *runner.E2ERunner, _ []string) {
r.Logger.Info("Deploying new SPL")

// load deployer private key
privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String())
require.NoError(r, err)
privkey := r.GetSolanaPrivKey()

// deploy SPL token, but don't whitelist in gateway
spl := r.DeploySPL(&privkey, false)
Expand Down
14 changes: 10 additions & 4 deletions e2e/e2etests/test_solana_withdraw.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/stretchr/testify/require"

"github.com/zeta-chain/node/e2e/runner"
"github.com/zeta-chain/node/e2e/utils"
crosschaintypes "github.com/zeta-chain/node/x/crosschain/types"
)

func TestSolanaWithdraw(r *runner.E2ERunner, args []string) {
Expand All @@ -28,15 +30,19 @@ func TestSolanaWithdraw(r *runner.E2ERunner, args []string) {
r,
-1,
withdrawAmount.Cmp(approvedAmount),
"Withdrawal amount must be less than the approved amount (1e9)",
"Withdrawal amount must be less than the approved amount: %v",
approvedAmount,
)

// load deployer private key
privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String())
require.NoError(r, err)
privkey := r.GetSolanaPrivKey()

// withdraw
r.WithdrawSOLZRC20(privkey.PublicKey(), withdrawAmount, approvedAmount)
tx := r.WithdrawSOLZRC20(privkey.PublicKey(), withdrawAmount, approvedAmount)

// 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, crosschaintypes.CctxStatus_OutboundMined)

// get ERC20 SOL balance after withdraw
balanceAfter, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
Expand Down
8 changes: 7 additions & 1 deletion e2e/e2etests/test_solana_withdraw_restricted_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"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"
crosschaintypes "github.com/zeta-chain/node/x/crosschain/types"
)

func TestSolanaWithdrawRestricted(r *runner.E2ERunner, args []string) {
Expand All @@ -29,7 +31,11 @@ func TestSolanaWithdrawRestricted(r *runner.E2ERunner, args []string) {
)

// withdraw
cctx := r.WithdrawSOLZRC20(receiverRestricted, withdrawAmount, approvedAmount)
tx := r.WithdrawSOLZRC20(receiverRestricted, withdrawAmount, approvedAmount)

// 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, crosschaintypes.CctxStatus_OutboundMined)

// the cctx should be cancelled with zero value
verifySolanaWithdrawalAmountFromCCTX(r, cctx, 0)
Expand Down
16 changes: 7 additions & 9 deletions e2e/e2etests/test_spl_deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"math/big"

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

Expand All @@ -18,18 +17,17 @@ func TestSPLDeposit(r *runner.E2ERunner, args []string) {
amount := parseInt(r, args[0])

// load deployer private key
privKey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String())
require.NoError(r, err)
privKey := r.GetSolanaPrivKey()

// get SPL balance for pda and sender atas
pda := r.ComputePdaAddress()
pdaAta := r.FindOrCreateAssociatedTokenAccount(privKey, pda, r.SPLAddr)
pdaAta := r.ResolveSolanaATA(privKey, pda, r.SPLAddr)

pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed)
pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized)
require.NoError(r, err)

senderAta := r.FindOrCreateAssociatedTokenAccount(privKey, privKey.PublicKey(), r.SPLAddr)
senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed)
senderAta := r.ResolveSolanaATA(privKey, privKey.PublicKey(), r.SPLAddr)
senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentFinalized)
require.NoError(r, err)

// get zrc20 balance for recipient
Expand All @@ -46,10 +44,10 @@ func TestSPLDeposit(r *runner.E2ERunner, args []string) {
utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined)

// verify balances are updated
pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed)
pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized)
require.NoError(r, err)

senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed)
senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentFinalized)
require.NoError(r, err)

zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
Expand Down
16 changes: 7 additions & 9 deletions e2e/e2etests/test_spl_deposit_and_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"math/big"

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

Expand All @@ -24,18 +23,17 @@ func TestSPLDepositAndCall(r *runner.E2ERunner, args []string) {
r.Logger.Info("Example contract deployed at: %s", contractAddr.String())

// load deployer private key
privKey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String())
require.NoError(r, err)
privKey := r.GetSolanaPrivKey()

// get SPL balance for pda and sender atas
pda := r.ComputePdaAddress()
pdaAta := r.FindOrCreateAssociatedTokenAccount(privKey, pda, r.SPLAddr)
pdaAta := r.ResolveSolanaATA(privKey, pda, r.SPLAddr)

pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed)
pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized)
require.NoError(r, err)

senderAta := r.FindOrCreateAssociatedTokenAccount(privKey, privKey.PublicKey(), r.SPLAddr)
senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed)
senderAta := r.ResolveSolanaATA(privKey, privKey.PublicKey(), r.SPLAddr)
senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentFinalized)
require.NoError(r, err)

// get zrc20 balance for recipient
Expand All @@ -56,10 +54,10 @@ func TestSPLDepositAndCall(r *runner.E2ERunner, args []string) {
utils.MustHaveCalledExampleContract(r, contract, big.NewInt(int64(amount)))

// verify balances are updated
pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed)
pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized)
require.NoError(r, err)

senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed)
senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentFinalized)
require.NoError(r, err)

zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, contractAddr)
Expand Down
73 changes: 73 additions & 0 deletions e2e/e2etests/test_spl_withdraw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package e2etests

import (
"math/big"

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

"github.com/zeta-chain/node/e2e/runner"
"github.com/zeta-chain/node/e2e/utils"
crosschaintypes "github.com/zeta-chain/node/x/crosschain/types"
)

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

withdrawAmount := parseBigInt(r, args[0])

skosito marked this conversation as resolved.
Show resolved Hide resolved
// get SPL ZRC20 balance before withdraw
zrc20BalanceBefore, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
require.NoError(r, err)
r.Logger.Info("runner balance of SPL before withdraw: %d", zrc20BalanceBefore)

require.Equal(r, 1, zrc20BalanceBefore.Cmp(withdrawAmount), "Insufficient balance for withdrawal")

// parse withdraw amount (in lamports), approve amount is 1 SOL
approvedAmount := new(big.Int).SetUint64(solana.LAMPORTS_PER_SOL)
require.Equal(
r,
-1,
withdrawAmount.Cmp(approvedAmount),
"Withdrawal amount must be less than the %v",
approvedAmount,
)

// load deployer private key
privkey := r.GetSolanaPrivKey()

// get receiver ata balance before withdraw
receiverAta := r.ResolveSolanaATA(privkey, privkey.PublicKey(), r.SPLAddr)
receiverBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, receiverAta, rpc.CommitmentFinalized)
require.NoError(r, err)
r.Logger.Info("receiver balance of SPL before withdraw: %s", receiverBalanceBefore.Value.Amount)

// withdraw
tx := r.WithdrawSPLZRC20(privkey.PublicKey(), withdrawAmount, approvedAmount)

// 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, crosschaintypes.CctxStatus_OutboundMined)

// get SPL ZRC20 balance after withdraw
zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
require.NoError(r, err)
r.Logger.Info("runner balance of SPL after withdraw: %d", zrc20BalanceAfter)

// verify balances are updated
receiverBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, receiverAta, rpc.CommitmentFinalized)
require.NoError(r, err)
r.Logger.Info("receiver balance of SPL after withdraw: %s", receiverBalanceAfter.Value.Amount)

// verify amount is added to receiver ata
require.EqualValues(
r,
new(big.Int).Add(withdrawAmount, parseBigInt(r, receiverBalanceBefore.Value.Amount)).String(),
parseBigInt(r, receiverBalanceAfter.Value.Amount).String(),
)

// verify amount is subtracted on zrc20
require.EqualValues(r, new(big.Int).Sub(zrc20BalanceBefore, withdrawAmount).String(), zrc20BalanceAfter.String())
}
Loading
Loading