diff --git a/DEVELOP.md b/DEVELOP.md index b47f7d9f1..e65703004 100644 --- a/DEVELOP.md +++ b/DEVELOP.md @@ -142,6 +142,7 @@ using `kubectl exec solana-devnet-0 -c setup -- cli airdrop solana-devnet:9900` | `0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab` | Wrapped asset contract | | `0xCfEB869F69431e42cdB54A4F4f105C19C080A601` | Example ERC20 token | | `0xf5b1d8fab1054b9cf7db274126972f97f9d42a11` | Wrapped asset address for the 6qRhs8oA... SPL token | +| `0x62b47a23cd900da982bdbe75aeb891d3ed18cc36` | Wrapped asset address for the terra18v... Terra token | **Terra** diff --git a/bridge/cmd/vaa-test-terra/main.go b/bridge/cmd/vaa-test-terra/main.go index 53aad5507..6f87a9719 100644 --- a/bridge/cmd/vaa-test-terra/main.go +++ b/bridge/cmd/vaa-test-terra/main.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/certusone/wormhole/bridge/pkg/ethereum" "github.com/certusone/wormhole/bridge/pkg/vaa" ) @@ -322,14 +323,5 @@ func generateKeys(n int) (keys []*ecdsa.PrivateKey) { func hexToAddress(hex string) vaa.Address { hexAddr := common.HexToAddress(hex) - return padAddress(hexAddr) -} - -func padAddress(address common.Address) vaa.Address { - paddedAddress := common.LeftPadBytes(address[:], 32) - - addr := vaa.Address{} - copy(addr[:], paddedAddress) - - return addr + return ethereum.PadAddress(hexAddr) } diff --git a/bridge/cmd/vaa-test/main.go b/bridge/cmd/vaa-test/main.go index c4e3eb9e8..64f6329c4 100644 --- a/bridge/cmd/vaa-test/main.go +++ b/bridge/cmd/vaa-test/main.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/certusone/wormhole/bridge/pkg/devnet" + "github.com/certusone/wormhole/bridge/pkg/ethereum" "github.com/certusone/wormhole/bridge/pkg/vaa" ) @@ -39,7 +40,7 @@ func main() { SourceChain: 1, TargetChain: 2, SourceAddress: vaa.Address{2, 1, 4}, - TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress), + TargetAddress: ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress), Asset: &vaa.AssetMeta{ Chain: vaa.ChainIDSolana, Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"), @@ -58,7 +59,7 @@ func main() { SourceChain: 1, TargetChain: 2, SourceAddress: vaa.Address{2, 1, 4}, - TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress), + TargetAddress: ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress), Asset: &vaa.AssetMeta{ Chain: vaa.ChainIDEthereum, Address: hexToAddress("0xd833215cbcc3f914bd1c9ece3ee7bf8b14f841bb"), @@ -101,7 +102,7 @@ func main() { SourceChain: 1, TargetChain: 2, SourceAddress: vaa.Address{2, 1, 4}, - TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress), + TargetAddress: ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress), Asset: &vaa.AssetMeta{ Chain: vaa.ChainIDSolana, Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"), @@ -120,7 +121,7 @@ func main() { SourceChain: 1, TargetChain: 2, SourceAddress: vaa.Address{2, 1, 5}, - TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress), + TargetAddress: ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress), Asset: &vaa.AssetMeta{ Chain: vaa.ChainIDSolana, Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"), @@ -139,7 +140,7 @@ func main() { SourceChain: 1, TargetChain: 2, SourceAddress: vaa.Address{2, 1, 5}, - TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress), + TargetAddress: ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress), Asset: &vaa.AssetMeta{ Chain: vaa.ChainIDSolana, Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"), @@ -158,7 +159,7 @@ func main() { SourceChain: 1, TargetChain: 2, SourceAddress: vaa.Address{2, 1, 5}, - TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress), + TargetAddress: ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress), Asset: &vaa.AssetMeta{ Chain: vaa.ChainIDSolana, Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"), @@ -194,7 +195,7 @@ func main() { SourceChain: 1, TargetChain: 2, SourceAddress: vaa.Address{2, 1, 5}, - TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress), + TargetAddress: ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress), Asset: &vaa.AssetMeta{ Chain: vaa.ChainIDSolana, Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"), @@ -213,7 +214,7 @@ func main() { SourceChain: 1, TargetChain: 2, SourceAddress: vaa.Address{2, 1, 5}, - TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress), + TargetAddress: ethereum.PadAddress(devnet.GanacheClientDefaultAccountAddress), Asset: &vaa.AssetMeta{ Chain: vaa.ChainIDSolana, Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"), @@ -253,14 +254,5 @@ func generateKeys(n int) (keys []*ecdsa.PrivateKey) { func hexToAddress(hex string) vaa.Address { hexAddr := common.HexToAddress(hex) - return padAddress(hexAddr) -} - -func padAddress(address common.Address) vaa.Address { - paddedAddress := common.LeftPadBytes(address[:], 32) - - addr := vaa.Address{} - copy(addr[:], paddedAddress) - - return addr + return ethereum.PadAddress(hexAddr) } diff --git a/bridge/e2e/e2e_test.go b/bridge/e2e/e2e_test.go index ab1a0fa03..30c1b375c 100644 --- a/bridge/e2e/e2e_test.go +++ b/bridge/e2e/e2e_test.go @@ -2,16 +2,20 @@ package e2e import ( "context" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "k8s.io/client-go/kubernetes" "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) { +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. @@ -48,7 +52,13 @@ func setup(t *testing.T) (*kubernetes.Clientset, *ethclient.Client, *bind.Transa } kt := devnet.GetKeyedTransactor(context.Background()) - return c, ec, kt + // 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 @@ -56,7 +66,7 @@ func setup(t *testing.T) (*kubernetes.Clientset, *ethclient.Client, *bind.Transa // 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) + c, ec, kt, _ := setup(t) t.Run("[SOL] Native -> [ETH] Wrapped", func(t *testing.T) { testSolanaLockup(t, context.Background(), ec, c, @@ -117,12 +127,7 @@ func TestEndToEnd_SOL_ETH(t *testing.T) { } func TestEndToEnd_SOL_Terra(t *testing.T) { - c, _, _ := setup(t) - - tc, err := NewTerraClient() - if err != nil { - t.Fatalf("creating devnet terra client failed: %v", err) - } + c, _, _, tc := setup(t) t.Run("[Terra] Native -> [SOL] Wrapped", func(t *testing.T) { testTerraLockup(t, context.Background(), tc, c, @@ -137,14 +142,29 @@ func TestEndToEnd_SOL_Terra(t *testing.T) { ) }) - // TODO(https://github.com/certusone/wormhole/issues/164): [SOL] Wrapped -> [Terra] Native + 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(), tc, c, + 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. @@ -152,7 +172,91 @@ func TestEndToEnd_SOL_Terra(t *testing.T) { ) }) - // TODO(https://github.com/certusone/wormhole/issues/164): [Terra] Wrapped -> [SOL] Native + 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, + ) + }) } -// TODO(https://github.com/certusone/wormhole/issues/164): TestEndToEnd_ETH_Terra +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, + ) + }) +} diff --git a/bridge/e2e/eth.go b/bridge/e2e/eth.go index db797b2e3..d0bd4c8ae 100644 --- a/bridge/e2e/eth.go +++ b/bridge/e2e/eth.go @@ -2,6 +2,7 @@ package e2e import ( "context" + "encoding/hex" "math" "math/big" "testing" @@ -15,6 +16,7 @@ import ( "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" @@ -105,3 +107,84 @@ func testEthereumLockup(t *testing.T, ctx context.Context, ec *ethclient.Client, // 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)) +} diff --git a/bridge/e2e/solana.go b/bridge/e2e/solana.go index 7be458ea8..766891148 100644 --- a/bridge/e2e/solana.go +++ b/bridge/e2e/solana.go @@ -119,14 +119,22 @@ func testSolanaLockup(t *testing.T, ctx context.Context, ec *ethclient.Client, c waitSPLBalance(t, ctx, c, sourceAcct, beforeSPL, -int64(amount)) } -func testSolanaToTerraLockup(t *testing.T, ctx context.Context, tc *TerraClient, c *kubernetes.Clientset, - sourceAcct string, tokenAddr string, amount int, precisionGain int) { +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) } - terraToken, err := getAssetAddress(ctx, devnet.TerraBridgeAddress, vaa.ChainIDSolana, tokenSlice) + 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) @@ -168,5 +176,9 @@ func testSolanaToTerraLockup(t *testing.T, ctx context.Context, tc *TerraClient, waitSPLBalance(t, ctx, c, sourceAcct, beforeSPL, -int64(amount)) // Destination account increases by the full amount. - waitTerraUnknownBalance(t, ctx, devnet.TerraBridgeAddress, vaa.ChainIDSolana, tokenSlice, beforeCw20, int64(float64(amount)*math.Pow10(precisionGain))) + 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))) + } } diff --git a/bridge/e2e/terra.go b/bridge/e2e/terra.go index 14e0681d4..e11caac90 100644 --- a/bridge/e2e/terra.go +++ b/bridge/e2e/terra.go @@ -9,11 +9,16 @@ import ( "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" @@ -168,7 +173,7 @@ func getAssetAddress(ctx context.Context, contract string, chain uint8, asset [] 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, query) + 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 { @@ -193,21 +198,27 @@ func terraQuery(ctx context.Context, contract string, query string) (string, err // 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, 60*time.Second) + ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() assetAddress := "" - err := wait.PollUntil(1*time.Second, func() (bool, error) { + err := wait.PollUntil(3*time.Second, func() (bool, error) { address, err := getAssetAddress(ctx, contract, chain, asset) if err != nil { t.Log(err) - return true, nil + 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 false, nil + return true, nil }, ctx.Done()) if err != nil { @@ -252,10 +263,10 @@ func waitTerraUnknownBalance(t *testing.T, ctx context.Context, contract string, return } - ctx, cancel := context.WithTimeout(ctx, 60*time.Second) + ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - err = wait.PollUntil(1*time.Second, func() (bool, error) { + err = wait.PollUntil(3*time.Second, func() (bool, error) { after, err := getTerraBalance(ctx, token) if err != nil { @@ -322,3 +333,54 @@ func testTerraLockup(t *testing.T, ctx context.Context, tc *TerraClient, // 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)) +} diff --git a/bridge/pkg/devnet/constants.go b/bridge/pkg/devnet/constants.go index eeff5f0bb..dccd79222 100644 --- a/bridge/pkg/devnet/constants.go +++ b/bridge/pkg/devnet/constants.go @@ -26,8 +26,9 @@ var ( GanacheBridgeContractAddress = common.HexToAddress("0x5b1869D9A4C187F2EAa108f3062412ecf0526b24") // ERC20 example tokens. - GanacheExampleERC20Token = common.HexToAddress("0xCfEB869F69431e42cdB54A4F4f105C19C080A601") - GanacheExampleERC20WrappedSOL = common.HexToAddress("0xf5b1d8fab1054b9cf7db274126972f97f9d42a11") + GanacheExampleERC20Token = common.HexToAddress("0xCfEB869F69431e42cdB54A4F4f105C19C080A601") + GanacheExampleERC20WrappedSOL = common.HexToAddress("0xf5b1d8fab1054b9cf7db274126972f97f9d42a11") + GanacheExampleERC20WrappedTerra = common.HexToAddress("0x62b47a23cd900da982bdbe75aeb891d3ed18cc36") ) const ( diff --git a/bridge/pkg/ethereum/utils.go b/bridge/pkg/ethereum/utils.go new file mode 100644 index 000000000..8160943ff --- /dev/null +++ b/bridge/pkg/ethereum/utils.go @@ -0,0 +1,16 @@ +package ethereum + +import ( + "github.com/certusone/wormhole/bridge/pkg/vaa" + "github.com/ethereum/go-ethereum/common" +) + +// PadAddress creates 32-byte VAA.Address from 20-byte Ethereum addresses by adding 12 0-bytes at the left +func PadAddress(address common.Address) vaa.Address { + paddedAddress := common.LeftPadBytes(address[:], 32) + + addr := vaa.Address{} + copy(addr[:], paddedAddress) + + return addr +} diff --git a/bridge/pkg/ethereum/watcher.go b/bridge/pkg/ethereum/watcher.go index 69b63af91..044d0583b 100644 --- a/bridge/pkg/ethereum/watcher.go +++ b/bridge/pkg/ethereum/watcher.go @@ -3,11 +3,12 @@ package ethereum import ( "context" "fmt" - "github.com/prometheus/client_golang/prometheus" "math/big" "sync" "time" + "github.com/prometheus/client_golang/prometheus" + "github.com/ethereum/go-ethereum/accounts/abi/bind" eth_common "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" diff --git a/terra/contracts/wormhole/src/contract.rs b/terra/contracts/wormhole/src/contract.rs index 28ae4866f..c515dc0ec 100644 --- a/terra/contracts/wormhole/src/contract.rs +++ b/terra/contracts/wormhole/src/contract.rs @@ -353,6 +353,7 @@ fn vaa_transfer( const TARGET_ADDRESS_POS: usize = 38; const TOKEN_CHAIN_POS: usize = 70; const TOKEN_ADDRESS_POS: usize = 71; + const DECIMALS_POS: usize = 103; const AMOUNT_POS: usize = 104; const PAYLOAD_LEN: usize = 136; @@ -413,7 +414,7 @@ fn vaa_transfer( msg: to_binary(&WrappedInit { asset_chain: token_chain, asset_address: asset_address.to_vec().into(), - decimals: data.get_u8(103), + decimals: data.get_u8(DECIMALS_POS), mint: Some(InitMint { recipient: deps .api