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 SPL deposits #3124

Merged
merged 16 commits into from
Nov 8, 2024
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features
* [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) - deposit spl integration
skosito marked this conversation as resolved.
Show resolved Hide resolved

### Refactor
* [3122](https://github.com/zeta-chain/node/pull/3122) - improve & refactor zetaclientd cli
Expand Down
4 changes: 3 additions & 1 deletion cmd/zetae2e/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ func RunnerFromConfig(

// ExportContractsFromRunner export contracts from the runner to config using a source config
func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.Config {
// copy contracts from deployer runner
conf.Contracts.Solana.GatewayProgramID = r.GatewayProgram.String()
conf.Contracts.Solana.SPL = config.DoubleQuotedString(r.SPLAddr.String())
skosito marked this conversation as resolved.
Show resolved Hide resolved

// copy contracts from deployer runner
conf.Contracts.EVM.ZetaEthAddr = config.DoubleQuotedString(r.ZetaEthAddr.Hex())
conf.Contracts.EVM.ConnectorEthAddr = config.DoubleQuotedString(r.ConnectorEthAddr.Hex())
conf.Contracts.EVM.CustodyAddr = config.DoubleQuotedString(r.ERC20CustodyAddr.Hex())
Expand All @@ -68,6 +69,7 @@ func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.C
conf.Contracts.ZEVM.ERC20ZRC20Addr = config.DoubleQuotedString(r.ERC20ZRC20Addr.Hex())
conf.Contracts.ZEVM.BTCZRC20Addr = config.DoubleQuotedString(r.BTCZRC20Addr.Hex())
conf.Contracts.ZEVM.SOLZRC20Addr = config.DoubleQuotedString(r.SOLZRC20Addr.Hex())
conf.Contracts.ZEVM.SPLZRC20Addr = config.DoubleQuotedString(r.SPLZRC20Addr.Hex())
conf.Contracts.ZEVM.TONZRC20Addr = config.DoubleQuotedString(r.TONZRC20Addr.Hex())
conf.Contracts.ZEVM.UniswapFactoryAddr = config.DoubleQuotedString(r.UniswapV2FactoryAddr.Hex())
conf.Contracts.ZEVM.UniswapRouterAddr = config.DoubleQuotedString(r.UniswapV2RouterAddr.Hex())
Expand Down
15 changes: 15 additions & 0 deletions cmd/zetae2e/config/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error {
r.GatewayProgram = solana.MustPublicKeyFromBase58(c)
}

if c := conf.Contracts.Solana.SPL; c != "" {
r.SPLAddr = solana.MustPublicKeyFromBase58(c.String())
}
skosito marked this conversation as resolved.
Show resolved Hide resolved

// set EVM contracts
if c := conf.Contracts.EVM.ZetaEthAddr; c != "" {
r.ZetaEthAddr, err = c.AsEVMAddress()
Expand Down Expand Up @@ -135,6 +139,17 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error {
}
}

if c := conf.Contracts.ZEVM.SPLZRC20Addr; c != "" {
r.SPLZRC20Addr, err = c.AsEVMAddress()
if err != nil {
return fmt.Errorf("invalid SPLZRC20Addr: %w", err)
}
r.SPLZRC20, err = zrc20.NewZRC20(r.SPLZRC20Addr, r.ZEVMClient)
if err != nil {
return err
}
}

if c := conf.Contracts.ZEVM.TONZRC20Addr; c != "" {
r.TONZRC20Addr, err = c.AsEVMAddress()
if err != nil {
Expand Down
10 changes: 6 additions & 4 deletions cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ func localE2ETest(cmd *cobra.Command, _ []string) {

deployerRunner.SetupEVMV2()

if testSolana {
deployerRunner.SetupSolana(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String())
}

deployerRunner.SetZEVMSystemContracts()

// NOTE: v2 (gateway) setup called here because system contract needs to be set first, then gateway, then zrc20
Expand All @@ -231,10 +235,6 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
deployerRunner.UpdateChainParamsV2Contracts()
deployerRunner.ERC20CustodyAddr = deployerRunner.ERC20CustodyV2Addr

if testSolana {
deployerRunner.SetupSolana(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String())
}

deployerRunner.MintERC20OnEvm(1000000)

logger.Print("✅ setup completed in %s", time.Since(startTime))
Expand Down Expand Up @@ -413,6 +413,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
// TODO move under admin tests
// https://github.com/zeta-chain/node/issues/3085
e2etests.TestSolanaWhitelistSPLName,
e2etests.TestSolanaDepositSPLName,
e2etests.TestSolanaDepositSPLAndCallName,
skosito marked this conversation as resolved.
Show resolved Hide resolved
}
eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...))
}
Expand Down
4 changes: 3 additions & 1 deletion e2e/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ type Contracts struct {

// Solana contains the addresses of predeployed contracts on the Solana chain
type Solana struct {
GatewayProgramID string `yaml:"gateway_program_id"`
GatewayProgramID string `yaml:"gateway_program_id"`
SPL DoubleQuotedString `yaml:"spl"`
skosito marked this conversation as resolved.
Show resolved Hide resolved
}

// EVM contains the addresses of predeployed contracts on the EVM chain
Expand All @@ -141,6 +142,7 @@ type ZEVM struct {
ERC20ZRC20Addr DoubleQuotedString `yaml:"erc20_zrc20"`
BTCZRC20Addr DoubleQuotedString `yaml:"btc_zrc20"`
SOLZRC20Addr DoubleQuotedString `yaml:"sol_zrc20"`
SPLZRC20Addr DoubleQuotedString `yaml:"spl_zrc20"`
TONZRC20Addr DoubleQuotedString `yaml:"ton_zrc20"`
UniswapFactoryAddr DoubleQuotedString `yaml:"uniswap_factory"`
UniswapRouterAddr DoubleQuotedString `yaml:"uniswap_router"`
Expand Down
18 changes: 18 additions & 0 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const (
TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund"
TestSolanaDepositRestrictedName = "solana_deposit_restricted"
TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted"
TestSolanaDepositSPLName = "solana_deposit_spl"
TestSolanaDepositSPLAndCallName = "solana_deposit_spl_and_call"
skosito marked this conversation as resolved.
Show resolved Hide resolved

/**
* TON tests
Expand Down Expand Up @@ -462,6 +464,22 @@ var AllE2ETests = []runner.E2ETest{
[]runner.ArgDefinition{},
TestSolanaWhitelistSPL,
),
runner.NewE2ETest(
TestSolanaDepositSPLName,
"deposit SPL into ZEVM",
[]runner.ArgDefinition{
{Description: "amount of spl tokens", DefaultValue: "500000"},
},
TestSolanaDepositSPL,
),
runner.NewE2ETest(
TestSolanaDepositSPLAndCallName,
"deposit SPL into ZEVM and call",
[]runner.ArgDefinition{
{Description: "amount of spl tokens", DefaultValue: "500000"},
},
TestSolanaDepositSPLAndCall,
),
/*
TON tests
*/
Expand Down
76 changes: 76 additions & 0 deletions e2e/e2etests/test_solana_deposit_and_call_spl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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"
testcontract "github.com/zeta-chain/node/testutil/contracts"
crosschaintypes "github.com/zeta-chain/node/x/crosschain/types"
)

func TestSolanaDepositSPLAndCall(r *runner.E2ERunner, args []string) {
require.Len(r, args, 1)
amount := parseInt(r, args[0])

skosito marked this conversation as resolved.
Show resolved Hide resolved
// deploy an example contract in ZEVM
contractAddr, _, contract, err := testcontract.DeployExample(r.ZEVMAuth, r.ZEVMClient)
skosito marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(r, err)
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)

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

skosito marked this conversation as resolved.
Show resolved Hide resolved
pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed)
require.NoError(r, err)

senderAta := r.FindOrCreateAssociatedTokenAccount(privKey, privKey.PublicKey(), r.SPLAddr)
senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed)
require.NoError(r, err)
skosito marked this conversation as resolved.
Show resolved Hide resolved

// get zrc20 balance for recipient
zrc20BalanceBefore, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, contractAddr)
require.NoError(r, err)

// execute the deposit transaction
data := []byte("hello spl tokens")
// #nosec G115 e2eTest - always in range
sig := r.SPLDepositAndCall(&privKey, uint64(amount), r.SPLAddr, contractAddr, data)
skosito marked this conversation as resolved.
Show resolved Hide resolved

// wait for the cctx to be mined
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout)
r.Logger.CCTX(*cctx, "solana_deposit_spl_and_call")
utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined)

// check if example contract has been called, bar value should be set to amount
utils.MustHaveCalledExampleContract(r, contract, big.NewInt(500_000))
skosito marked this conversation as resolved.
Show resolved Hide resolved

skosito marked this conversation as resolved.
Show resolved Hide resolved
// verify balances are updated
pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed)
require.NoError(r, err)

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

zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, contractAddr)
require.NoError(r, err)

// verify amount is deposited to pda ata
require.Equal(r, parseInt(r, pdaBalanceBefore.Value.Amount)+amount, parseInt(r, pdaBalanceAfter.Value.Amount))

// verify amount is subtracted from sender ata
require.Equal(r, parseInt(r, senderBalanceBefore.Value.Amount)-amount, parseInt(r, senderBalanceAfter.Value.Amount))

// verify amount is minted to receiver
require.Zero(r, zrc20BalanceBefore.Add(zrc20BalanceBefore, big.NewInt(int64(amount))).Cmp(zrc20BalanceAfter))
}
66 changes: 66 additions & 0 deletions e2e/e2etests/test_solana_deposit_spl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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 TestSolanaDepositSPL(r *runner.E2ERunner, args []string) {
require.Len(r, args, 1)
amount := parseInt(r, args[0])

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

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

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

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

// get zrc20 balance for recipient
zrc20BalanceBefore, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
require.NoError(r, err)

// deposit SPL tokens
// #nosec G115 e2eTest - always in range
sig := r.SPLDepositAndCall(&privKey, uint64(amount), r.SPLAddr, r.EVMAddress(), nil)

// wait for the cctx to be mined
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout)
r.Logger.CCTX(*cctx, "solana_deposit_spl")
utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined)

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

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

zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
require.NoError(r, err)

// verify amount is deposited to pda ata
require.Equal(r, parseInt(r, pdaBalanceBefore.Value.Amount)+amount, parseInt(r, pdaBalanceAfter.Value.Amount))

// verify amount is subtracted from sender ata
require.Equal(r, parseInt(r, senderBalanceBefore.Value.Amount)-amount, parseInt(r, senderBalanceAfter.Value.Amount))

// verify amount is minted to receiver
require.Zero(r, zrc20BalanceBefore.Add(zrc20BalanceBefore, big.NewInt(int64(amount))).Cmp(zrc20BalanceAfter))
}
3 changes: 2 additions & 1 deletion e2e/e2etests/test_solana_whitelist_spl.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ func TestSolanaWhitelistSPL(r *runner.E2ERunner, _ []string) {
privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String())
require.NoError(r, err)

spl := r.DeploySPL(&privkey)
// deploy SPL token, but don't whitelist in gateway
spl := r.DeploySPL(&privkey, false, uint64(1_000_000))
skosito marked this conversation as resolved.
Show resolved Hide resolved

// check that whitelist entry doesn't exist for this spl
seed := [][]byte{[]byte("whitelist"), spl.PublicKey().Bytes()}
Expand Down
6 changes: 6 additions & 0 deletions e2e/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ type E2ERunner struct {

// programs on Solana
GatewayProgram solana.PublicKey
SPLAddr solana.PublicKey
skosito marked this conversation as resolved.
Show resolved Hide resolved

// contracts evm
ZetaEthAddr ethcommon.Address
Expand All @@ -125,6 +126,8 @@ type E2ERunner struct {
// contracts zevm
ERC20ZRC20Addr ethcommon.Address
ERC20ZRC20 *zrc20.ZRC20
SPLZRC20Addr ethcommon.Address
SPLZRC20 *zrc20.ZRC20
ETHZRC20Addr ethcommon.Address
ETHZRC20 *zrc20.ZRC20
BTCZRC20Addr ethcommon.Address
Expand Down Expand Up @@ -366,13 +369,16 @@ func (r *E2ERunner) Unlock() {
func (r *E2ERunner) PrintContractAddresses() {
r.Logger.Print(" --- 📜Solana addresses ---")
r.Logger.Print("GatewayProgram: %s", r.GatewayProgram.String())
r.Logger.Print("SPL: %s", r.SPLAddr.String())

// zevm contracts
r.Logger.Print(" --- 📜zEVM contracts ---")
r.Logger.Print("SystemContract: %s", r.SystemContractAddr.Hex())
r.Logger.Print("ETHZRC20: %s", r.ETHZRC20Addr.Hex())
r.Logger.Print("ERC20ZRC20: %s", r.ERC20ZRC20Addr.Hex())
r.Logger.Print("BTCZRC20: %s", r.BTCZRC20Addr.Hex())
r.Logger.Print("SOLZRC20: %s", r.SOLZRC20Addr.Hex())
r.Logger.Print("SPLZRC20: %s", r.SPLZRC20Addr.Hex())
r.Logger.Print("TONZRC20: %s", r.TONZRC20Addr.Hex())
r.Logger.Print("UniswapFactory: %s", r.UniswapV2FactoryAddr.Hex())
r.Logger.Print("UniswapRouter: %s", r.UniswapV2RouterAddr.Hex())
Expand Down
4 changes: 4 additions & 0 deletions e2e/runner/setup_solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ func (r *E2ERunner) SetupSolana(deployerPrivateKey string) {

err = r.ensureSolanaChainParams()
require.NoError(r, err)

// deploy test spl
tokenAccount := r.DeploySPL(&privkey, true, uint64(1_000_000))
skosito marked this conversation as resolved.
Show resolved Hide resolved
r.SPLAddr = tokenAccount.PublicKey()
}

func (r *E2ERunner) ensureSolanaChainParams() error {
Expand Down
8 changes: 7 additions & 1 deletion e2e/runner/setup_zeta.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,11 @@ func (r *E2ERunner) SetZEVMZRC20s() {
}()

// deploy system contracts and ZRC20 contracts on ZetaChain
erc20zrc20Addr, err := r.ZetaTxServer.DeployZRC20s(
erc20zrc20Addr, splzrc20Addr, err := r.ZetaTxServer.DeployZRC20s(
e2eutils.OperationalPolicyName,
e2eutils.AdminPolicyName,
r.ERC20Addr.Hex(),
r.SPLAddr.String(),
r.skipChainOperations,
)
require.NoError(r, err)
Expand All @@ -188,6 +189,11 @@ func (r *E2ERunner) SetZEVMZRC20s() {
r.ERC20ZRC20, err = zrc20.NewZRC20(r.ERC20ZRC20Addr, r.ZEVMClient)
require.NoError(r, err)

// Set SPLZRC20Addr
r.SPLZRC20Addr = ethcommon.HexToAddress(splzrc20Addr)
r.SPLZRC20, err = zrc20.NewZRC20(r.SPLZRC20Addr, r.ZEVMClient)
require.NoError(r, err)

// set ZRC20 contracts
r.SetupETHZRC20()
r.SetupBTCZRC20()
Expand Down
Loading
Loading