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: add Whitelist message ability to whitelist SPL tokens on Solana #2984

Merged
merged 26 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

* [2984](https://github.com/zeta-chain/node/pull/2984) - add Whitelist message ability to whitelist SPL tokens on Solana

## v21.0.0

### Features
Expand Down
3 changes: 3 additions & 0 deletions cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
e2etests.TestSolanaDepositAndCallRefundName,
e2etests.TestSolanaDepositRestrictedName,
e2etests.TestSolanaWithdrawRestrictedName,
// TODO move under admin tests
// https://github.com/zeta-chain/node/issues/3085
e2etests.TestSolanaWhitelistSPLName,
}
eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...))
}
Expand Down
1 change: 1 addition & 0 deletions cmd/zetae2e/local/solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func solanaTestRoutine(
deployerRunner,
conf.AdditionalAccounts.UserSolana,
runner.NewLogger(verbose, color.FgCyan, "solana"),
runner.WithZetaTxServer(deployerRunner.ZetaTxServer),
)
if err != nil {
return err
Expand Down
Binary file modified contrib/localnet/solana/gateway.so
Binary file not shown.
7 changes: 7 additions & 0 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ const (
TestPauseERC20CustodyName = "pause_erc20_custody"
TestMigrateERC20CustodyFundsName = "migrate_erc20_custody_funds"
TestMigrateTSSName = "migrate_TSS"
TestSolanaWhitelistSPLName = "solana_whitelist_spl"

/*
V2 smart contract tests
Expand Down Expand Up @@ -453,6 +454,12 @@ var AllE2ETests = []runner.E2ETest{
},
TestSolanaWithdrawRestricted,
),
runner.NewE2ETest(
TestSolanaWhitelistSPLName,
"whitelist SPL",
[]runner.ArgDefinition{},
TestSolanaWhitelistSPL,
),
/*
TON tests
*/
Expand Down
68 changes: 68 additions & 0 deletions e2e/e2etests/test_solana_whitelist_spl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package e2etests

import (
"github.com/gagliardetto/solana-go"
"github.com/stretchr/testify/require"

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

func TestSolanaWhitelistSPL(r *runner.E2ERunner, _ []string) {
// Deploy a new SPL
r.Logger.Info("Deploying new SPL")

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

spl := r.DeploySPL(&privkey)

// check that whitelist entry doesn't exist for this spl
seed := [][]byte{[]byte("whitelist"), spl.PublicKey().Bytes()}
whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, r.GatewayProgram)
require.NoError(r, err)

whitelistEntryInfo, err := r.SolanaClient.GetAccountInfo(r.Ctx, whitelistEntryPDA)
require.Error(r, err)
require.Nil(r, whitelistEntryInfo)
skosito marked this conversation as resolved.
Show resolved Hide resolved

// whitelist sol zrc20
r.Logger.Info("whitelisting spl on new network")
res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, crosschaintypes.NewMsgWhitelistERC20(
r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName),
spl.PublicKey().String(),
chains.SolanaLocalnet.ChainId,
"TESTSPL",
"TESTSPL",
6,
100000,
))
require.NoError(r, err)

event, ok := txserver.EventOfType[*crosschaintypes.EventERC20Whitelist](res.Events)
require.True(r, ok, "no EventERC20Whitelist in %s", res.TxHash)
erc20zrc20Addr := event.Zrc20Address
whitelistCCTXIndex := event.WhitelistCctxIndex

err = r.ZetaTxServer.InitializeLiquidityCaps(erc20zrc20Addr)
require.NoError(r, err)

// ensure CCTX created
resCCTX, err := r.CctxClient.Cctx(r.Ctx, &crosschaintypes.QueryGetCctxRequest{Index: whitelistCCTXIndex})
require.NoError(r, err)

cctx := resCCTX.CrossChainTx
r.Logger.CCTX(*cctx, "whitelist_cctx")

// wait for the whitelist cctx to be mined
r.WaitForMinedCCTXFromIndex(whitelistCCTXIndex)

// check that whitelist entry exists for this spl
whitelistEntryInfo, err = r.SolanaClient.GetAccountInfo(r.Ctx, whitelistEntryPDA)
require.NoError(r, err)
require.NotNil(r, whitelistEntryInfo)
}
skosito marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 2 additions & 3 deletions e2e/runner/setup_solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,19 @@ func (r *E2ERunner) SetupSolana(deployerPrivateKey string) {
accountSlice = append(accountSlice, solana.Meta(privkey.PublicKey()).WRITE().SIGNER())
accountSlice = append(accountSlice, solana.Meta(pdaComputed).WRITE())
accountSlice = append(accountSlice, solana.Meta(solana.SystemProgramID))
accountSlice = append(accountSlice, solana.Meta(r.GatewayProgram))
inst.ProgID = r.GatewayProgram
inst.AccountValues = accountSlice

inst.DataBytes, err = borsh.Serialize(solanacontracts.InitializeParams{
Discriminator: solanacontracts.DiscriminatorInitialize(),
Discriminator: solanacontracts.DiscriminatorInitialize,
TssAddress: r.TSSAddress,
// #nosec G115 chain id always positive
ChainID: uint64(chains.SolanaLocalnet.ChainId),
})
require.NoError(r, err)

// create and sign the transaction
signedTx := r.CreateSignedTransaction([]solana.Instruction{&inst}, privkey)
signedTx := r.CreateSignedTransaction([]solana.Instruction{&inst}, privkey, []solana.PrivateKey{})

// broadcast the transaction and wait for finalization
_, out := r.BroadcastTxSync(signedTx)
Expand Down
46 changes: 44 additions & 2 deletions e2e/runner/solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/programs/system"
"github.com/gagliardetto/solana-go/programs/token"
"github.com/gagliardetto/solana-go/rpc"
"github.com/near/borsh-go"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -49,7 +51,7 @@ func (r *E2ERunner) CreateDepositInstruction(

var err error
inst.DataBytes, err = borsh.Serialize(solanacontract.DepositInstructionParams{
Discriminator: solanacontract.DiscriminatorDeposit(),
Discriminator: solanacontract.DiscriminatorDeposit,
Amount: amount,
Memo: append(receiver.Bytes(), data...),
})
Expand All @@ -62,6 +64,7 @@ func (r *E2ERunner) CreateDepositInstruction(
func (r *E2ERunner) CreateSignedTransaction(
instructions []solana.Instruction,
privateKey solana.PrivateKey,
additionalPrivateKeys []solana.PrivateKey,
) *solana.Transaction {
// get a recent blockhash
recent, err := r.SolanaClient.GetLatestBlockhash(r.Ctx, rpc.CommitmentFinalized)
Expand All @@ -81,6 +84,11 @@ func (r *E2ERunner) CreateSignedTransaction(
if privateKey.PublicKey().Equals(key) {
return &privateKey
}
for _, apk := range additionalPrivateKeys {
if apk.PublicKey().Equals(key) {
return &apk
}
}
return nil
},
)
Expand All @@ -89,6 +97,40 @@ func (r *E2ERunner) CreateSignedTransaction(
return tx
}

func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey) *solana.Wallet {
lamport, err := r.SolanaClient.GetMinimumBalanceForRentExemption(r.Ctx, token.MINT_SIZE, rpc.CommitmentFinalized)
require.NoError(r, err)

// to deploy new spl token, create account instruction and initialize mint instruction have to be in the same transaction
tokenAccount := solana.NewWallet()
createAccountInstruction := system.NewCreateAccountInstruction(
skosito marked this conversation as resolved.
Show resolved Hide resolved
lamport,
token.MINT_SIZE,
solana.TokenProgramID,
privateKey.PublicKey(),
tokenAccount.PublicKey(),
).Build()

initializeMintInstruction := token.NewInitializeMint2Instruction(
6,
privateKey.PublicKey(),
privateKey.PublicKey(),
tokenAccount.PublicKey(),
).Build()

signedTx := r.CreateSignedTransaction(
[]solana.Instruction{createAccountInstruction, initializeMintInstruction},
*privateKey,
[]solana.PrivateKey{tokenAccount.PrivateKey},
)

// broadcast the transaction and wait for finalization
_, out := r.BroadcastTxSync(signedTx)
r.Logger.Info("create spl logs: %v", out.Meta.LogMessages)

return tokenAccount
}
skosito marked this conversation as resolved.
Show resolved Hide resolved

// BroadcastTxSync broadcasts a transaction and waits for it to be finalized
func (r *E2ERunner) BroadcastTxSync(tx *solana.Transaction) (solana.Signature, *rpc.GetTransactionResult) {
// broadcast the transaction
Expand Down Expand Up @@ -134,7 +176,7 @@ func (r *E2ERunner) SOLDepositAndCall(
instruction := r.CreateDepositInstruction(signerPrivKey.PublicKey(), receiver, data, amount.Uint64())

// create and sign the transaction
signedTx := r.CreateSignedTransaction([]solana.Instruction{instruction}, *signerPrivKey)
signedTx := r.CreateSignedTransaction([]solana.Instruction{instruction}, *signerPrivKey, []solana.PrivateKey{})

// broadcast the transaction and wait for finalization
sig, out := r.BroadcastTxSync(signedTx)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ require (
github.com/bnb-chain/tss-lib v1.5.0
github.com/showa-93/go-mask v0.6.2
github.com/tonkeeper/tongo v1.9.3
github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241025181051-d8d49e4fc85b
skosito marked this conversation as resolved.
Show resolved Hide resolved
)

require (
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2186,6 +2186,8 @@ github.com/fzipp/gocyclo v0.5.1/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlya
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
github.com/gagliardetto/solana-go v1.10.0 h1:lDuHGC+XLxw9j8fCHBZM9tv4trI0PVhev1m9NAMaIdM=
github.com/gagliardetto/solana-go v1.10.0/go.mod h1:afBEcIRrDLJst3lvAahTr63m6W2Ns6dajZxe2irF7Jg=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
Expand Down Expand Up @@ -4212,6 +4214,8 @@ github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138 h1:vck/Fc
github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138/go.mod h1:U494OsZTWsU75hqoriZgMdSsgSGP1mUL1jX+wN/Aez8=
github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c h1:ZoFxMMZtivRLquXVq1sEVlT45UnTPMO1MSXtc88nDv4=
github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w=
github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241025181051-d8d49e4fc85b h1:w4YVBbWxk9TI+7HM8hTvK66IgOo5XvEFsmH7n6WgW50=
github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241025181051-d8d49e4fc85b/go.mod h1:DcDY828o773soiU/h0XpC+naxitrIMFVZqEvq/EJxMA=
github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901 h1:9whtN5fjYHfk4yXIuAsYP2EHxImwDWDVUOnZJ2pfL3w=
github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901/go.mod h1:d2iTC62s9JwKiCMPhcDDXbIZmuzAyJ4lwso0H5QyRbk=
github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
Expand Down
4 changes: 4 additions & 0 deletions pkg/chains/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@
return chain.Vm == Vm_evm
}

func (chain Chain) IsSolanaChain() bool {
return chain.Consensus == Consensus_solana_consensus

Check warning on line 100 in pkg/chains/chain.go

View check run for this annotation

Codecov / codecov/patch

pkg/chains/chain.go#L99-L100

Added lines #L99 - L100 were not covered by tests
}

func (chain Chain) IsBitcoinChain() bool {
return chain.Consensus == Consensus_bitcoin
}
Expand Down
39 changes: 15 additions & 24 deletions pkg/contracts/solana/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package solana
import (
"github.com/gagliardetto/solana-go"
"github.com/pkg/errors"
idlgateway "github.com/zeta-chain/protocol-contracts-solana/go-idl/generated"
)

const (
Expand All @@ -18,30 +19,20 @@ const (
AccountsNumDeposit = 3
)

// DiscriminatorInitialize returns the discriminator for Solana gateway 'initialize' instruction
func DiscriminatorInitialize() [8]byte {
return [8]byte{175, 175, 109, 31, 13, 152, 155, 237}
}

// DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit' instruction
func DiscriminatorDeposit() [8]byte {
return [8]byte{242, 35, 198, 137, 82, 225, 242, 182}
}

// DiscriminatorDepositSPL returns the discriminator for Solana gateway 'deposit_spl_token' instruction
func DiscriminatorDepositSPL() [8]byte {
return [8]byte{86, 172, 212, 121, 63, 233, 96, 144}
}

// DiscriminatorWithdraw returns the discriminator for Solana gateway 'withdraw' instruction
func DiscriminatorWithdraw() [8]byte {
return [8]byte{183, 18, 70, 156, 148, 109, 161, 34}
}

// DiscriminatorWithdrawSPL returns the discriminator for Solana gateway 'withdraw_spl_token' instruction
func DiscriminatorWithdrawSPL() [8]byte {
return [8]byte{156, 234, 11, 89, 235, 246, 32}
}
var (
// DiscriminatorInitialize returns the discriminator for Solana gateway 'initialize' instruction
skosito marked this conversation as resolved.
Show resolved Hide resolved
DiscriminatorInitialize = idlgateway.IDLGateway.GetDiscriminator("initialize")
// DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit' instruction
DiscriminatorDeposit = idlgateway.IDLGateway.GetDiscriminator("deposit")
// DiscriminatorDepositSPL returns the discriminator for Solana gateway 'deposit_spl_token' instruction
DiscriminatorDepositSPL = idlgateway.IDLGateway.GetDiscriminator("deposit_spl_token")
// DiscriminatorWithdraw returns the discriminator for Solana gateway 'withdraw' instruction
DiscriminatorWithdraw = idlgateway.IDLGateway.GetDiscriminator("withdraw")
// DiscriminatorWithdrawSPL returns the discriminator for Solana gateway 'withdraw_spl_token' instruction
DiscriminatorWithdrawSPL = idlgateway.IDLGateway.GetDiscriminator("withdraw_spl_token")
// DiscriminatorWhitelist returns the discriminator for Solana gateway 'whitelist_spl_mint' instruction
DiscriminatorWhitelistSplMint = idlgateway.IDLGateway.GetDiscriminator("whitelist_spl_mint")
)

// ParseGatewayAddressAndPda parses the gateway id and program derived address from the given string
func ParseGatewayIDAndPda(address string) (solana.PublicKey, solana.PublicKey, error) {
Expand Down
Loading
Loading