|
|
@@ -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))
|
|
|
-}
|