solana.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package e2e
  2. import (
  3. "context"
  4. "fmt"
  5. "math"
  6. "math/big"
  7. "math/rand"
  8. "regexp"
  9. "strconv"
  10. "testing"
  11. "time"
  12. "github.com/ethereum/go-ethereum/common"
  13. "github.com/ethereum/go-ethereum/ethclient"
  14. "k8s.io/apimachinery/pkg/util/wait"
  15. "k8s.io/client-go/kubernetes"
  16. "github.com/certusone/wormhole/node/pkg/devnet"
  17. "github.com/certusone/wormhole/node/pkg/ethereum/erc20"
  18. "github.com/certusone/wormhole/node/pkg/vaa"
  19. )
  20. func getSPLBalance(ctx context.Context, c *kubernetes.Clientset, hexAddr string) (*big.Int, error) {
  21. b, err := executeCommandInPod(ctx, c, "solana-devnet-0", "setup",
  22. []string{"cli", "balance", hexAddr})
  23. if err != nil {
  24. return nil, fmt.Errorf("error running 'cli balance': %w", err)
  25. }
  26. re := regexp.MustCompile("(?m)^amount: (.*)$")
  27. m := re.FindStringSubmatch(string(b))
  28. if len(m) == 0 {
  29. return nil, fmt.Errorf("invalid 'cli balance' output: %s", string(b))
  30. }
  31. n, ok := new(big.Int).SetString(m[1], 10)
  32. if !ok {
  33. return nil, fmt.Errorf("invalid int: %s", m[1])
  34. }
  35. return n, nil
  36. }
  37. func waitSPLBalance(t *testing.T, ctx context.Context, c *kubernetes.Clientset, hexAddr string, before *big.Int, target int64) {
  38. // Wait for target account balance to increase.
  39. ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
  40. defer cancel()
  41. err := wait.PollUntil(1*time.Second, func() (bool, error) {
  42. after, err := getSPLBalance(ctx, c, hexAddr)
  43. if err != nil {
  44. t.Fatal(err)
  45. }
  46. d := new(big.Int).Sub(after, before)
  47. t.Logf("SPL balance after: %d -> %d, delta %d", before, after, d)
  48. if after.Cmp(before) != 0 {
  49. if d.Cmp(new(big.Int).SetInt64(target)) != 0 {
  50. t.Errorf("expected SPL delta of %v, got: %v", target, d)
  51. }
  52. return true, nil
  53. }
  54. return false, nil
  55. }, ctx.Done())
  56. if err != nil {
  57. t.Error(err)
  58. }
  59. }
  60. func testSolanaLockup(t *testing.T, ctx context.Context, ec *ethclient.Client, c *kubernetes.Clientset,
  61. sourceAcct string, tokenAddr string, destination common.Address, amount int, precisionGain int) {
  62. token, err := erc20.NewErc20(destination, ec)
  63. if err != nil {
  64. panic(err)
  65. }
  66. // Store balance of wrapped destination token
  67. beforeErc20, err := token.BalanceOf(nil, devnet.GanacheClientDefaultAccountAddress)
  68. if err != nil {
  69. beforeErc20 = new(big.Int)
  70. t.Log(err) // account may not yet exist, defaults to 0
  71. }
  72. t.Logf("ERC20 balance: %v", beforeErc20)
  73. // Store balance of source SPL token
  74. beforeSPL, err := getSPLBalance(ctx, c, sourceAcct)
  75. if err != nil {
  76. t.Fatal(err)
  77. }
  78. t.Logf("SPL balance: %d", beforeSPL)
  79. _, err = executeCommandInPod(ctx, c, "solana-devnet-0", "setup",
  80. []string{"cli", "lock",
  81. // Address of the Wormhole bridge.
  82. devnet.SolanaBridgeContract,
  83. // Account which holds the SPL tokens to be sent.
  84. sourceAcct,
  85. // The SPL token.
  86. tokenAddr,
  87. // Token amount.
  88. strconv.Itoa(amount),
  89. // Destination chain ID.
  90. strconv.Itoa(vaa.ChainIDEthereum),
  91. // Random nonce.
  92. strconv.Itoa(int(rand.Uint32())),
  93. // Destination account on Ethereum
  94. devnet.GanacheClientDefaultAccountAddress.Hex()[2:],
  95. })
  96. if err != nil {
  97. t.Fatal(err)
  98. }
  99. // Destination account increases by the full amount.
  100. waitEthBalance(t, ctx, token, beforeErc20, int64(float64(amount)*math.Pow10(precisionGain)))
  101. // Source account decreases by full amount.
  102. waitSPLBalance(t, ctx, c, sourceAcct, beforeSPL, -int64(amount))
  103. }