e2e: remove
These were testing the token bridge and do not currently pass (or even compile). We will bring back later some of the code. Change-Id: I34e9ae5aa901c9b20572d1fc8d87a599fa49ad93
This commit is contained in:
parent
abe95522b1
commit
01564150db
|
@ -17,10 +17,6 @@ and code reviews are our most important tools to accomplish that.
|
|||
|
||||
- Releases are first tested on a testnet. This involves coordination with the mainnet DAO running the nodes.
|
||||
|
||||
- We aim for close to 100% test coverage on all critical paths. Most of Wormhole's complexity is in the
|
||||
external interfaces, therefore, we primarily rely on software-in-the-loop E2E testing that exercises
|
||||
the entire path. Where applicable, we also use faster unit tests for invariant checking.
|
||||
|
||||
- Commits should be small and have a meaningful commit message. One commit should, roughly, be "one idea" and
|
||||
be as atomic as possible. A feature can consist of many such commits.
|
||||
|
||||
|
@ -50,7 +46,7 @@ The answer is... maybe? The following things are needed in order to fully suppor
|
|||
- The smart contract needs to be built and audited. In some cases, existing contracts can be used, like with
|
||||
EVM-compatible chains.
|
||||
|
||||
- Support for observing the chain needs to be added to guardiand, along with E2E tests.
|
||||
- Support for observing the chain needs to be added to guardiand.
|
||||
|
||||
- Web wallet integration needs to be built to actually interact with Wormhole.
|
||||
|
||||
|
|
|
@ -61,11 +61,6 @@ Generate test Solana -> Ethereum transfers:
|
|||
|
||||
scripts/send-solana-lockups.sh
|
||||
|
||||
Run end-to-end tests:
|
||||
|
||||
cd bridge
|
||||
go test github.com/certusone/wormhole/bridge/e2e
|
||||
|
||||
Adjust number of nodes in running cluster: (this is only useful if you want to test scenarios where the number
|
||||
of nodes diverges from the guardian set - otherwise, `tilt down --delete-namespaces` and restart the cluster)
|
||||
|
||||
|
|
|
@ -1,262 +0,0 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/mr-tron/base58"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"github.com/certusone/wormhole/bridge/pkg/devnet"
|
||||
"github.com/certusone/wormhole/bridge/pkg/ethereum"
|
||||
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
)
|
||||
|
||||
func setup(t *testing.T) (*kubernetes.Clientset, *ethclient.Client, *bind.TransactOpts, *TerraClient) {
|
||||
// List of pods we need in a ready state before we can run tests.
|
||||
want := []string{
|
||||
// Our test guardian set.
|
||||
"guardian-0",
|
||||
//"guardian-1",
|
||||
//"guardian-2",
|
||||
//"guardian-3",
|
||||
//"guardian-4",
|
||||
//"guardian-5",
|
||||
|
||||
// Connected chains
|
||||
"solana-devnet-0",
|
||||
|
||||
"terra-terrad-0",
|
||||
"terra-lcd-0",
|
||||
|
||||
"eth-devnet-0",
|
||||
}
|
||||
|
||||
c := getk8sClient()
|
||||
|
||||
// Wait for all pods to be ready. This blocks until the bridge is ready to receive lockups.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
defer cancel()
|
||||
waitForPods(ctx, c, want)
|
||||
if ctx.Err() != nil {
|
||||
t.Fatal(ctx.Err())
|
||||
}
|
||||
|
||||
// Ethereum client.
|
||||
ec, err := ethclient.Dial(devnet.GanacheRPCURL)
|
||||
if err != nil {
|
||||
t.Fatalf("dialing devnet eth rpc failed: %v", err)
|
||||
}
|
||||
kt := devnet.GetKeyedTransactor(context.Background())
|
||||
|
||||
// Terra client
|
||||
tc, err := NewTerraClient()
|
||||
if err != nil {
|
||||
t.Fatalf("creating devnet terra client failed: %v", err)
|
||||
}
|
||||
|
||||
return c, ec, kt, tc
|
||||
}
|
||||
|
||||
// Careful about parallel tests - accounts on some chains like Ethereum cannot be
|
||||
// used concurrently as they have monotonically increasing nonces that would conflict.
|
||||
// Either use different Ethereum account, or do not run Ethereum tests in parallel.
|
||||
|
||||
func TestEndToEnd_SOL_ETH(t *testing.T) {
|
||||
c, ec, kt, _ := setup(t)
|
||||
|
||||
t.Run("[SOL] Native -> [ETH] Wrapped", func(t *testing.T) {
|
||||
testSolanaLockup(t, context.Background(), ec, c,
|
||||
// Source SPL account
|
||||
devnet.SolanaExampleTokenOwningAccount,
|
||||
// Source SPL token
|
||||
devnet.SolanaExampleToken,
|
||||
// Our wrapped destination token on Ethereum
|
||||
devnet.GanacheExampleERC20WrappedSOL,
|
||||
// Amount of SPL token value to transfer.
|
||||
50*devnet.SolanaDefaultPrecision,
|
||||
// Same precision - same amount, no precision gained.
|
||||
0,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[ETH] Wrapped -> [SOL] Native", func(t *testing.T) {
|
||||
testEthereumLockup(t, context.Background(), ec, kt, c,
|
||||
// Source ERC20 token
|
||||
devnet.GanacheExampleERC20WrappedSOL,
|
||||
// Destination SPL token account
|
||||
devnet.SolanaExampleTokenOwningAccount,
|
||||
// Amount (the reverse of what the previous test did, with the same precision because
|
||||
// the wrapped ERC20 is set to the original asset's 10**9 precision).
|
||||
50*devnet.SolanaDefaultPrecision,
|
||||
// No precision loss
|
||||
0,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[ETH] Native -> [SOL] Wrapped", func(t *testing.T) {
|
||||
testEthereumLockup(t, context.Background(), ec, kt, c,
|
||||
// Source ERC20 token
|
||||
devnet.GanacheExampleERC20Token,
|
||||
// Destination SPL token account
|
||||
devnet.SolanaExampleWrappedERCTokenOwningAccount,
|
||||
// Amount
|
||||
0.000000012*devnet.ERC20DefaultPrecision,
|
||||
// We lose 9 digits of precision on this path, as the default ERC20 token has 10**18 precision.
|
||||
9,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[SOL] Wrapped -> [ETH] Native", func(t *testing.T) {
|
||||
testSolanaLockup(t, context.Background(), ec, c,
|
||||
// Source SPL account
|
||||
devnet.SolanaExampleWrappedERCTokenOwningAccount,
|
||||
// Source SPL token
|
||||
devnet.SolanaExampleWrappedERCToken,
|
||||
// Our wrapped destination token on Ethereum
|
||||
devnet.GanacheExampleERC20Token,
|
||||
// Amount of SPL token value to transfer.
|
||||
0.000000012*devnet.SolanaDefaultPrecision,
|
||||
// We gain 9 digits of precision on Eth.
|
||||
9,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEndToEnd_SOL_Terra(t *testing.T) {
|
||||
c, _, _, tc := setup(t)
|
||||
|
||||
t.Run("[Terra] Native -> [SOL] Wrapped", func(t *testing.T) {
|
||||
testTerraLockup(t, context.Background(), tc, c,
|
||||
// Source CW20 token
|
||||
devnet.TerraTokenAddress,
|
||||
// Destination SPL token account
|
||||
devnet.SolanaExampleWrappedCWTokenOwningAccount,
|
||||
// Amount
|
||||
2*devnet.TerraDefaultPrecision,
|
||||
// Same precision - same amount, no precision gained.
|
||||
0,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[SOL] Wrapped -> [Terra] Native", func(t *testing.T) {
|
||||
testSolanaToTerraLockup(t, context.Background(), c,
|
||||
// Source SPL account
|
||||
devnet.SolanaExampleWrappedCWTokenOwningAccount,
|
||||
// Source SPL token
|
||||
devnet.SolanaExampleWrappedCWToken,
|
||||
// Wrapped
|
||||
false,
|
||||
// Amount of SPL token value to transfer.
|
||||
2*devnet.TerraDefaultPrecision,
|
||||
// Same precision - same amount, no precision gained.
|
||||
0,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[SOL] Native -> [Terra] Wrapped", func(t *testing.T) {
|
||||
testSolanaToTerraLockup(t, context.Background(), c,
|
||||
// Source SPL account
|
||||
devnet.SolanaExampleTokenOwningAccount,
|
||||
// Source SPL token
|
||||
devnet.SolanaExampleToken,
|
||||
// Native
|
||||
true,
|
||||
// Amount of SPL token value to transfer.
|
||||
50*devnet.SolanaDefaultPrecision,
|
||||
// Same precision - same amount, no precision gained.
|
||||
0,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[Terra] Wrapped -> [SOL] Native", func(t *testing.T) {
|
||||
|
||||
tokenSlice, err := base58.Decode(devnet.SolanaExampleToken)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wrappedAsset, err := waitTerraAsset(t, context.Background(), devnet.TerraBridgeAddress, vaa.ChainIDSolana, tokenSlice)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testTerraLockup(t, context.Background(), tc, c,
|
||||
// Source wrapped token
|
||||
wrappedAsset,
|
||||
// Destination SPL token account
|
||||
devnet.SolanaExampleTokenOwningAccount,
|
||||
// Amount of Terra token value to transfer.
|
||||
50*devnet.SolanaDefaultPrecision,
|
||||
// Same precision
|
||||
0,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEndToEnd_ETH_Terra(t *testing.T) {
|
||||
_, ec, kt, tc := setup(t)
|
||||
|
||||
t.Run("[Terra] Native -> [ETH] Wrapped", func(t *testing.T) {
|
||||
testTerraToEthLockup(t, context.Background(), tc, ec,
|
||||
// Source CW20 token
|
||||
devnet.TerraTokenAddress,
|
||||
// Destination ETH token
|
||||
devnet.GanacheExampleERC20WrappedTerra,
|
||||
// Amount
|
||||
2*devnet.TerraDefaultPrecision,
|
||||
// Same precision - same amount, no precision gained.
|
||||
0,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[ETH] Wrapped -> [Terra] Native", func(t *testing.T) {
|
||||
testEthereumToTerraLockup(t, context.Background(), ec, kt,
|
||||
// Source Ethereum token
|
||||
devnet.GanacheExampleERC20WrappedTerra,
|
||||
// Wrapped
|
||||
false,
|
||||
// Amount of Ethereum token value to transfer.
|
||||
2*devnet.TerraDefaultPrecision,
|
||||
// Same precision
|
||||
0,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[ETH] Native -> [Terra] Wrapped", func(t *testing.T) {
|
||||
testEthereumToTerraLockup(t, context.Background(), ec, kt,
|
||||
// Source Ethereum token
|
||||
devnet.GanacheExampleERC20Token,
|
||||
// Native
|
||||
true,
|
||||
// Amount of Ethereum token value to transfer.
|
||||
0.000000012*devnet.ERC20DefaultPrecision,
|
||||
// We lose 9 digits of precision on this path, as the default ERC20 token has 10**18 precision.
|
||||
9,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("[Terra] Wrapped -> [ETH] Native", func(t *testing.T) {
|
||||
|
||||
paddedTokenAddress := ethereum.PadAddress(devnet.GanacheExampleERC20Token)
|
||||
wrappedAsset, err := waitTerraAsset(t, context.Background(), devnet.TerraBridgeAddress, vaa.ChainIDEthereum, paddedTokenAddress[:])
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testTerraToEthLockup(t, context.Background(), tc, ec,
|
||||
// Source wrapped token
|
||||
wrappedAsset,
|
||||
// Destination ETH token
|
||||
devnet.GanacheExampleERC20Token,
|
||||
// Amount of Terra token value to transfer.
|
||||
0.000000012*1e9, // 10**9 because default ETH precision is 18 and we lost 9 digits on wrapping
|
||||
// We gain 9 digits of precision on Eth.
|
||||
9,
|
||||
)
|
||||
})
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/tendermint/tendermint/libs/rand"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"github.com/certusone/wormhole/bridge/pkg/devnet"
|
||||
"github.com/certusone/wormhole/bridge/pkg/ethereum"
|
||||
"github.com/certusone/wormhole/bridge/pkg/ethereum/abi"
|
||||
"github.com/certusone/wormhole/bridge/pkg/ethereum/erc20"
|
||||
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||
)
|
||||
|
||||
// waitEthBalance waits for target account before to increase.
|
||||
func waitEthBalance(t *testing.T, ctx context.Context, token *erc20.Erc20, before *big.Int, target int64) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := wait.PollUntil(1*time.Second, func() (bool, error) {
|
||||
after, err := token.BalanceOf(nil, devnet.GanacheClientDefaultAccountAddress)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
d := new(big.Int).Sub(after, before)
|
||||
t.Logf("ERC20 balance after: %d -> %d, delta %d", before, after, d)
|
||||
|
||||
if after.Cmp(before) != 0 {
|
||||
if d.Cmp(new(big.Int).SetInt64(target)) != 0 {
|
||||
t.Errorf("expected ERC20 delta of %v, got: %v", target, d)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}, ctx.Done())
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testEthereumLockup(t *testing.T, ctx context.Context, ec *ethclient.Client, kt *bind.TransactOpts,
|
||||
c *kubernetes.Clientset, tokenAddr common.Address, destination string, amount int64, precisionLoss int) {
|
||||
|
||||
// Bridge client
|
||||
ethBridge, err := abi.NewAbi(devnet.GanacheBridgeContractAddress, ec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Source token client
|
||||
token, err := erc20.NewErc20(tokenAddr, ec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Store balance of source ERC20 token
|
||||
beforeErc20, err := token.BalanceOf(nil, devnet.GanacheClientDefaultAccountAddress)
|
||||
if err != nil {
|
||||
t.Log(err) // account may not yet exist, defaults to 0
|
||||
}
|
||||
t.Logf("ERC20 balance: %v", beforeErc20)
|
||||
|
||||
// Store balance of destination SPL token
|
||||
beforeSPL, err := getSPLBalance(ctx, c, destination)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("SPL balance: %d", beforeSPL)
|
||||
|
||||
// Send lockup
|
||||
tx, err := ethBridge.LockAssets(kt,
|
||||
// asset address
|
||||
tokenAddr,
|
||||
// token amount
|
||||
new(big.Int).SetInt64(amount),
|
||||
// recipient address on target chain
|
||||
devnet.MustBase58ToEthAddress(destination),
|
||||
// target chain
|
||||
vaa.ChainIDSolana,
|
||||
// random nonce
|
||||
rand.Uint32(),
|
||||
// refund dust?
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("sent lockup tx: %v", tx.Hash().Hex())
|
||||
|
||||
// Destination account increases by full amount.
|
||||
waitSPLBalance(t, ctx, c, destination, beforeSPL, int64(float64(amount)/math.Pow10(precisionLoss)))
|
||||
|
||||
// Source account decreases by the full amount.
|
||||
waitEthBalance(t, ctx, token, beforeErc20, -int64(amount))
|
||||
}
|
||||
|
||||
func testEthereumToTerraLockup(t *testing.T, ctx context.Context, ec *ethclient.Client, kt *bind.TransactOpts,
|
||||
tokenAddr common.Address, isNative bool, amount int64, precisionLoss int) {
|
||||
|
||||
// Bridge client
|
||||
ethBridge, err := abi.NewAbi(devnet.GanacheBridgeContractAddress, ec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Source token client
|
||||
token, err := erc20.NewErc20(tokenAddr, ec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Store balance of source ERC20 token
|
||||
beforeErc20, err := token.BalanceOf(nil, devnet.GanacheClientDefaultAccountAddress)
|
||||
if err != nil {
|
||||
beforeErc20 = new(big.Int)
|
||||
t.Log(err) // account may not yet exist, defaults to 0
|
||||
}
|
||||
t.Logf("ERC20 balance: %v", beforeErc20)
|
||||
|
||||
// Store balance of destination CW20 token
|
||||
paddedTokenAddress := ethereum.PadAddress(tokenAddr)
|
||||
var terraToken string
|
||||
if isNative {
|
||||
terraToken, err = getAssetAddress(ctx, devnet.TerraBridgeAddress, vaa.ChainIDEthereum, paddedTokenAddress[:])
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
} else {
|
||||
terraToken = devnet.TerraTokenAddress
|
||||
}
|
||||
|
||||
// Get balance if deployed
|
||||
beforeCw20, err := getTerraBalance(ctx, terraToken)
|
||||
if err != nil {
|
||||
beforeCw20 = new(big.Int)
|
||||
t.Log(err) // account may not yet exist, defaults to 0
|
||||
}
|
||||
t.Logf("CW20 balance: %v", beforeCw20)
|
||||
|
||||
// Send lockup
|
||||
dstAddress, err := hex.DecodeString(devnet.TerraMainTestAddressHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var dstAddressBytes [32]byte
|
||||
copy(dstAddressBytes[:], dstAddress)
|
||||
tx, err := ethBridge.LockAssets(kt,
|
||||
// asset address
|
||||
tokenAddr,
|
||||
// token amount
|
||||
new(big.Int).SetInt64(amount),
|
||||
// recipient address on target chain
|
||||
dstAddressBytes,
|
||||
// target chain
|
||||
vaa.ChainIDTerra,
|
||||
// random nonce
|
||||
rand.Uint32(),
|
||||
// refund dust?
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("sent lockup tx: %v", tx.Hash().Hex())
|
||||
|
||||
// Destination account increases by the full amount.
|
||||
if isNative {
|
||||
waitTerraUnknownBalance(t, ctx, devnet.TerraBridgeAddress, vaa.ChainIDEthereum, paddedTokenAddress[:], beforeCw20, int64(float64(amount)/math.Pow10(precisionLoss)))
|
||||
} else {
|
||||
waitTerraBalance(t, ctx, devnet.TerraTokenAddress, beforeCw20, int64(float64(amount)/math.Pow10(precisionLoss)))
|
||||
}
|
||||
|
||||
// Source account decreases by the full amount.
|
||||
waitEthBalance(t, ctx, token, beforeErc20, -int64(amount))
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
)
|
||||
|
||||
const (
|
||||
TiltDefaultNamespace = "wormhole" // hardcoded in Tiltfile
|
||||
)
|
||||
|
||||
func getk8sClient() *kubernetes.Clientset {
|
||||
config, err := getk8sConfig()
|
||||
|
||||
c, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func getk8sConfig() (*rest.Config, error) {
|
||||
// Load local default kubeconfig.
|
||||
rules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
rules.DefaultClientConfig = &clientcmd.DefaultClientConfig
|
||||
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
clientcmd.ClientConfigLoader(rules), nil).ClientConfig()
|
||||
}
|
||||
|
||||
func hasPodReadyCondition(conditions []corev1.PodCondition) bool {
|
||||
for _, condition := range conditions {
|
||||
if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func waitForPods(ctx context.Context, c *kubernetes.Clientset, want []string) {
|
||||
found := make(map[string]bool)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
watchlist := cache.NewListWatchFromClient(
|
||||
c.CoreV1().RESTClient(),
|
||||
"pods",
|
||||
TiltDefaultNamespace,
|
||||
fields.Everything(),
|
||||
)
|
||||
|
||||
handle := func(pod *corev1.Pod) {
|
||||
ready := hasPodReadyCondition(pod.Status.Conditions)
|
||||
log.Printf("pod added/changed: %s is %s, ready: %v", pod.Name, pod.Status.Phase, ready)
|
||||
|
||||
if ready {
|
||||
found[pod.Name] = true
|
||||
}
|
||||
|
||||
missing := 0
|
||||
for _, v := range want {
|
||||
if found[v] == false {
|
||||
missing += 1
|
||||
}
|
||||
}
|
||||
|
||||
if missing == 0 {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
_, controller := cache.NewInformer(
|
||||
watchlist,
|
||||
&corev1.Pod{},
|
||||
0,
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) { handle(obj.(*corev1.Pod)) },
|
||||
UpdateFunc: func(oldObj, newObj interface{}) { handle(newObj.(*corev1.Pod)) },
|
||||
},
|
||||
)
|
||||
|
||||
controller.Run(ctx.Done())
|
||||
}
|
||||
|
||||
func executeCommandInPod(ctx context.Context, c *kubernetes.Clientset, podName string, container string, cmd []string) ([]byte, error) {
|
||||
p, err := c.CoreV1().Pods(TiltDefaultNamespace).Get(ctx, podName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get pod %s: %w", p, err)
|
||||
}
|
||||
|
||||
req := c.CoreV1().RESTClient().Post().
|
||||
Resource("pods").
|
||||
Name(podName).
|
||||
Namespace(TiltDefaultNamespace).
|
||||
SubResource("exec")
|
||||
|
||||
req = req.VersionedParams(&corev1.PodExecOptions{
|
||||
Stdin: false,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
TTY: false,
|
||||
Container: container,
|
||||
Command: cmd,
|
||||
}, scheme.ParameterCodec)
|
||||
|
||||
config, err := getk8sConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get config: %w", err)
|
||||
}
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to init executor: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
execOut bytes.Buffer
|
||||
execErr bytes.Buffer
|
||||
)
|
||||
|
||||
err = exec.Stream(remotecommand.StreamOptions{
|
||||
Stdout: &execOut,
|
||||
Stderr: &execErr,
|
||||
Tty: false,
|
||||
})
|
||||
|
||||
log.Printf("command: %s", strings.Join(cmd, " "))
|
||||
if execErr.Len() > 0 {
|
||||
log.Printf("stderr: %s", execErr.String())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute remote command: %w", err)
|
||||
}
|
||||
|
||||
return execOut.Bytes(), nil
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/tendermint/tendermint/libs/rand"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"github.com/certusone/wormhole/bridge/pkg/devnet"
|
||||
"github.com/certusone/wormhole/bridge/pkg/ethereum/erc20"
|
||||
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||
)
|
||||
|
||||
func getSPLBalance(ctx context.Context, c *kubernetes.Clientset, hexAddr string) (*big.Int, error) {
|
||||
b, err := executeCommandInPod(ctx, c, "solana-devnet-0", "setup",
|
||||
[]string{"cli", "balance", hexAddr})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error running 'cli balance': %w", err)
|
||||
}
|
||||
|
||||
re := regexp.MustCompile("(?m)^amount: (.*)$")
|
||||
m := re.FindStringSubmatch(string(b))
|
||||
if len(m) == 0 {
|
||||
return nil, fmt.Errorf("invalid 'cli balance' output: %s", string(b))
|
||||
}
|
||||
|
||||
n, ok := new(big.Int).SetString(m[1], 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid int: %s", m[1])
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func waitSPLBalance(t *testing.T, ctx context.Context, c *kubernetes.Clientset, hexAddr string, before *big.Int, target int64) {
|
||||
// Wait for target account balance to increase.
|
||||
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
err := wait.PollUntil(1*time.Second, func() (bool, error) {
|
||||
after, err := getSPLBalance(ctx, c, hexAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := new(big.Int).Sub(after, before)
|
||||
t.Logf("SPL balance after: %d -> %d, delta %d", before, after, d)
|
||||
|
||||
if after.Cmp(before) != 0 {
|
||||
if d.Cmp(new(big.Int).SetInt64(target)) != 0 {
|
||||
t.Errorf("expected SPL delta of %v, got: %v", target, d)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}, ctx.Done())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testSolanaLockup(t *testing.T, ctx context.Context, ec *ethclient.Client, c *kubernetes.Clientset,
|
||||
sourceAcct string, tokenAddr string, destination common.Address, amount int, precisionGain int) {
|
||||
token, err := erc20.NewErc20(destination, ec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Store balance of wrapped destination token
|
||||
beforeErc20, err := token.BalanceOf(nil, devnet.GanacheClientDefaultAccountAddress)
|
||||
if err != nil {
|
||||
beforeErc20 = new(big.Int)
|
||||
t.Log(err) // account may not yet exist, defaults to 0
|
||||
}
|
||||
t.Logf("ERC20 balance: %v", beforeErc20)
|
||||
|
||||
// Store balance of source SPL token
|
||||
beforeSPL, err := getSPLBalance(ctx, c, sourceAcct)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("SPL balance: %d", beforeSPL)
|
||||
|
||||
_, err = executeCommandInPod(ctx, c, "solana-devnet-0", "setup",
|
||||
[]string{"cli", "lock",
|
||||
// Address of the Wormhole bridge.
|
||||
devnet.SolanaBridgeContract,
|
||||
// Account which holds the SPL tokens to be sent.
|
||||
sourceAcct,
|
||||
// The SPL token.
|
||||
tokenAddr,
|
||||
// Token amount.
|
||||
strconv.Itoa(amount),
|
||||
// Destination chain ID.
|
||||
strconv.Itoa(vaa.ChainIDEthereum),
|
||||
// Random nonce.
|
||||
strconv.Itoa(int(rand.Uint16())),
|
||||
// Destination account on Ethereum
|
||||
devnet.GanacheClientDefaultAccountAddress.Hex()[2:],
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Destination account increases by the full amount.
|
||||
waitEthBalance(t, ctx, token, beforeErc20, int64(float64(amount)*math.Pow10(precisionGain)))
|
||||
|
||||
// Source account decreases by full amount.
|
||||
waitSPLBalance(t, ctx, c, sourceAcct, beforeSPL, -int64(amount))
|
||||
}
|
||||
|
||||
func testSolanaToTerraLockup(t *testing.T, ctx context.Context, c *kubernetes.Clientset,
|
||||
sourceAcct string, tokenAddr string, isNative bool, amount int, precisionGain int) {
|
||||
|
||||
tokenSlice, err := base58.Decode(tokenAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var terraToken string
|
||||
if isNative {
|
||||
terraToken, err = getAssetAddress(ctx, devnet.TerraBridgeAddress, vaa.ChainIDSolana, tokenSlice)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
} else {
|
||||
terraToken = devnet.TerraTokenAddress
|
||||
}
|
||||
|
||||
// Get balance if deployed
|
||||
beforeCw20, err := getTerraBalance(ctx, terraToken)
|
||||
if err != nil {
|
||||
beforeCw20 = new(big.Int)
|
||||
t.Log(err) // account may not yet exist, defaults to 0
|
||||
}
|
||||
t.Logf("CW20 balance: %v", beforeCw20)
|
||||
|
||||
// Store balance of source SPL token
|
||||
beforeSPL, err := getSPLBalance(ctx, c, sourceAcct)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("SPL balance: %d", beforeSPL)
|
||||
|
||||
_, err = executeCommandInPod(ctx, c, "solana-devnet-0", "setup",
|
||||
[]string{"cli", "lock",
|
||||
// Address of the Wormhole bridge.
|
||||
devnet.SolanaBridgeContract,
|
||||
// Account which holds the SPL tokens to be sent.
|
||||
sourceAcct,
|
||||
// The SPL token.
|
||||
tokenAddr,
|
||||
// Token amount.
|
||||
strconv.Itoa(amount),
|
||||
// Destination chain ID.
|
||||
strconv.Itoa(vaa.ChainIDTerra),
|
||||
// Random nonce.
|
||||
strconv.Itoa(int(rand.Uint16())),
|
||||
// Destination account on Terra
|
||||
devnet.TerraMainTestAddressHex,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Source account decreases by full amount.
|
||||
waitSPLBalance(t, ctx, c, sourceAcct, beforeSPL, -int64(amount))
|
||||
|
||||
// Destination account increases by the full amount.
|
||||
if isNative {
|
||||
waitTerraUnknownBalance(t, ctx, devnet.TerraBridgeAddress, vaa.ChainIDSolana, tokenSlice, beforeCw20, int64(float64(amount)*math.Pow10(precisionGain)))
|
||||
} else {
|
||||
waitTerraBalance(t, ctx, devnet.TerraTokenAddress, beforeCw20, int64(float64(amount)*math.Pow10(precisionGain)))
|
||||
}
|
||||
}
|
|
@ -1,391 +0,0 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/certusone/wormhole/bridge/pkg/devnet"
|
||||
"github.com/certusone/wormhole/bridge/pkg/ethereum"
|
||||
"github.com/certusone/wormhole/bridge/pkg/ethereum/erc20"
|
||||
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/terra-project/terra.go/client"
|
||||
"github.com/terra-project/terra.go/key"
|
||||
"github.com/terra-project/terra.go/msg"
|
||||
"github.com/terra-project/terra.go/tx"
|
||||
"github.com/tidwall/gjson"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
type lockAssetsMsg struct {
|
||||
Params lockAssetsParams `json:"lock_assets"`
|
||||
}
|
||||
|
||||
type increaseAllowanceMsg struct {
|
||||
Params increaseAllowanceParams `json:"increase_allowance"`
|
||||
}
|
||||
|
||||
type lockAssetsParams struct {
|
||||
Asset string `json:"asset"`
|
||||
Amount string `json:"amount"`
|
||||
Recipient []byte `json:"recipient"`
|
||||
TargetChain uint8 `json:"target_chain"`
|
||||
Nonce uint32 `json:"nonce"`
|
||||
}
|
||||
|
||||
type increaseAllowanceParams struct {
|
||||
Spender string `json:"spender"`
|
||||
Amount string `json:"amount"`
|
||||
}
|
||||
|
||||
// TerraClient encapsulates Terra LCD client and fee payer signing address
|
||||
type TerraClient struct {
|
||||
lcdClient client.LCDClient
|
||||
address msg.AccAddress
|
||||
}
|
||||
|
||||
const (
|
||||
feeAmount = 10000
|
||||
feeDenomination = "uluna"
|
||||
)
|
||||
|
||||
func (tc TerraClient) lockAssets(t *testing.T, ctx context.Context, token string, amount *big.Int, recipient [32]byte, targetChain uint8, nonce uint32) (*client.TxResponse, error) {
|
||||
bridgeContract, err := msg.AccAddressFromBech32(devnet.TerraBridgeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenContract, err := msg.AccAddressFromBech32(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create tx
|
||||
increaseAllowanceCall, err := json.Marshal(increaseAllowanceMsg{
|
||||
Params: increaseAllowanceParams{
|
||||
Spender: devnet.TerraBridgeAddress,
|
||||
Amount: amount.String(),
|
||||
}})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lockAssetsCall, err := json.Marshal(lockAssetsMsg{
|
||||
Params: lockAssetsParams{
|
||||
Asset: token,
|
||||
Amount: amount.String(),
|
||||
Recipient: recipient[:],
|
||||
TargetChain: targetChain,
|
||||
Nonce: nonce,
|
||||
}})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.Logf("increaseAllowanceCall\n %s", increaseAllowanceCall)
|
||||
t.Logf("lockAssetsCall\n %s", lockAssetsCall)
|
||||
|
||||
executeIncreaseAllowance := msg.NewExecuteContract(tc.address, tokenContract, increaseAllowanceCall, msg.NewCoins())
|
||||
executeLockAssets := msg.NewExecuteContract(tc.address, bridgeContract, lockAssetsCall, msg.NewCoins(msg.NewInt64Coin(feeDenomination, feeAmount)))
|
||||
|
||||
transaction, err := tc.lcdClient.CreateAndSignTx(ctx, client.CreateTxOptions{
|
||||
Msgs: []msg.Msg{
|
||||
executeIncreaseAllowance,
|
||||
executeLockAssets,
|
||||
},
|
||||
Fee: tx.StdFee{
|
||||
Gas: msg.NewInt(0),
|
||||
Amount: msg.NewCoins(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Broadcast
|
||||
return tc.lcdClient.Broadcast(ctx, transaction)
|
||||
}
|
||||
|
||||
// NewTerraClient creates new TerraClient instance to work
|
||||
func NewTerraClient() (*TerraClient, error) {
|
||||
// Derive Raw Private Key
|
||||
privKey, err := key.DerivePrivKey(devnet.TerraFeePayerKey, key.CreateHDPath(0, 0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate StdPrivKey
|
||||
tmKey, err := key.StdPrivKeyGen(privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate Address from Public Key
|
||||
address := msg.AccAddress(tmKey.PubKey().Address())
|
||||
|
||||
// Terra client
|
||||
lcdClient := client.NewLCDClient(
|
||||
devnet.TerraLCDURL,
|
||||
devnet.TerraChainID,
|
||||
msg.NewDecCoinFromDec("uusd", msg.NewDecFromIntWithPrec(msg.NewInt(15), 2)), // 0.15uusd
|
||||
msg.NewDecFromIntWithPrec(msg.NewInt(15), 1), tmKey, time.Second*15,
|
||||
)
|
||||
|
||||
return &TerraClient{
|
||||
lcdClient: *lcdClient,
|
||||
address: address,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getTerraBalance(ctx context.Context, token string) (*big.Int, error) {
|
||||
json, err := terraQuery(ctx, token, fmt.Sprintf("{\"balance\":{\"address\":\"%s\"}}", devnet.TerraMainTestAddress))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
balance := gjson.Get(json, "result.balance").String()
|
||||
parsed, success := new(big.Int).SetString(balance, 10)
|
||||
|
||||
if !success {
|
||||
return nil, fmt.Errorf("cannot parse balance: %s", balance)
|
||||
}
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func getAssetAddress(ctx context.Context, contract string, chain uint8, asset []byte) (string, error) {
|
||||
json, err := terraQuery(ctx, contract, fmt.Sprintf("{\"wrapped_registry\":{\"chain\":%d,\"address\":\"%s\"}}",
|
||||
chain,
|
||||
base64.StdEncoding.EncodeToString(asset)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return gjson.Get(json, "result.address").String(), nil
|
||||
}
|
||||
|
||||
func terraQuery(ctx context.Context, contract string, query string) (string, error) {
|
||||
|
||||
requestURL := fmt.Sprintf("%s/wasm/contracts/%s/store?query_msg=%s", devnet.TerraLCDURL, contract, url.QueryEscape(query))
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("http request error: %w", err)
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * 15,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("http execution error: %w", err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("http read error: %w", err)
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
// waitTerraAsset waits for asset contract to be deployed on terra
|
||||
func waitTerraAsset(t *testing.T, ctx context.Context, contract string, chain uint8, asset []byte) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 90*time.Second)
|
||||
defer cancel()
|
||||
|
||||
assetAddress := ""
|
||||
|
||||
err := wait.PollUntil(3*time.Second, func() (bool, error) {
|
||||
|
||||
address, err := getAssetAddress(ctx, contract, chain, asset)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check the case if request was successful, but asset address is not yet in the registry
|
||||
if address == "" {
|
||||
return false, nil
|
||||
}
|
||||
t.Logf("Returning asset: %s", address)
|
||||
|
||||
assetAddress = address
|
||||
return true, nil
|
||||
}, ctx.Done())
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return assetAddress, err
|
||||
}
|
||||
|
||||
// waitTerraBalance waits for target account before to increase.
|
||||
func waitTerraBalance(t *testing.T, ctx context.Context, token string, before *big.Int, target int64) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := wait.PollUntil(1*time.Second, func() (bool, error) {
|
||||
|
||||
after, err := getTerraBalance(ctx, token)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
d := new(big.Int).Sub(after, before)
|
||||
t.Logf("CW20 balance after: %d -> %d, delta %d", before, after, d)
|
||||
|
||||
if after.Cmp(before) != 0 {
|
||||
if d.Cmp(new(big.Int).SetInt64(target)) != 0 {
|
||||
t.Errorf("expected CW20 delta of %v, got: %v", target, d)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}, ctx.Done())
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func waitTerraUnknownBalance(t *testing.T, ctx context.Context, contract string, chain uint8, asset []byte, before *big.Int, target int64) {
|
||||
|
||||
token, err := waitTerraAsset(t, ctx, contract, chain, asset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 90*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err = wait.PollUntil(3*time.Second, func() (bool, error) {
|
||||
|
||||
after, err := getTerraBalance(ctx, token)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
d := new(big.Int).Sub(after, before)
|
||||
t.Logf("CW20 balance after: %d -> %d, delta %d", before, after, d)
|
||||
|
||||
if after.Cmp(before) != 0 {
|
||||
if d.Cmp(new(big.Int).SetInt64(target)) != 0 {
|
||||
t.Errorf("expected CW20 delta of %v, got: %v", target, d)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}, ctx.Done())
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testTerraLockup(t *testing.T, ctx context.Context, tc *TerraClient,
|
||||
c *kubernetes.Clientset, token string, destination string, amount int64, precisionLoss int) {
|
||||
|
||||
// Store balance of source CW20 token
|
||||
beforeCw20, err := getTerraBalance(ctx, token)
|
||||
if err != nil {
|
||||
t.Log(err) // account may not yet exist, defaults to 0
|
||||
}
|
||||
t.Logf("CW20 balance: %v", beforeCw20)
|
||||
|
||||
// Store balance of destination SPL token
|
||||
beforeSPL, err := getSPLBalance(ctx, c, destination)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("SPL balance: %d", beforeSPL)
|
||||
|
||||
// Send lockup
|
||||
tx, err := tc.lockAssets(
|
||||
t, ctx,
|
||||
// asset address
|
||||
token,
|
||||
// token amount
|
||||
new(big.Int).SetInt64(amount),
|
||||
// recipient address on target chain
|
||||
devnet.MustBase58ToEthAddress(destination),
|
||||
// target chain
|
||||
vaa.ChainIDSolana,
|
||||
// random nonce
|
||||
rand.Uint32(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Logf("sent lockup tx: %s", tx.TxHash)
|
||||
|
||||
// Destination account increases by full amount.
|
||||
waitSPLBalance(t, ctx, c, destination, beforeSPL, int64(float64(amount)/math.Pow10(precisionLoss)))
|
||||
|
||||
// Source account decreases by the full amount.
|
||||
waitTerraBalance(t, ctx, token, beforeCw20, -int64(amount))
|
||||
}
|
||||
|
||||
func testTerraToEthLockup(t *testing.T, ctx context.Context, tc *TerraClient,
|
||||
ec *ethclient.Client, tokenAddr string, destination common.Address, amount int64, precisionGain int) {
|
||||
|
||||
token, err := erc20.NewErc20(destination, ec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Store balance of source CW20 token
|
||||
beforeCw20, err := getTerraBalance(ctx, tokenAddr)
|
||||
if err != nil {
|
||||
t.Log(err) // account may not yet exist, defaults to 0
|
||||
beforeCw20 = new(big.Int)
|
||||
}
|
||||
t.Logf("CW20 balance: %v", beforeCw20)
|
||||
|
||||
/// Store balance of wrapped destination token
|
||||
beforeErc20, err := token.BalanceOf(nil, devnet.GanacheClientDefaultAccountAddress)
|
||||
if err != nil {
|
||||
t.Log(err) // account may not yet exist, defaults to 0
|
||||
beforeErc20 = new(big.Int)
|
||||
}
|
||||
t.Logf("ERC20 balance: %v", beforeErc20)
|
||||
|
||||
// Send lockup
|
||||
tx, err := tc.lockAssets(
|
||||
t, ctx,
|
||||
// asset address
|
||||
tokenAddr,
|
||||
// token amount
|
||||
new(big.Int).SetInt64(amount),
|
||||
// recipient address on target chain
|
||||
ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||
// target chain
|
||||
vaa.ChainIDEthereum,
|
||||
// random nonce
|
||||
rand.Uint32(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Logf("sent lockup tx: %s", tx.TxHash)
|
||||
|
||||
// Destination account increases by full amount.
|
||||
waitEthBalance(t, ctx, token, beforeErc20, int64(float64(amount)*math.Pow10(precisionGain)))
|
||||
|
||||
// Source account decreases by the full amount.
|
||||
waitTerraBalance(t, ctx, tokenAddr, beforeCw20, -int64(amount))
|
||||
}
|
Loading…
Reference in New Issue