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

fix: hardcode erc20 asset strings to align with the data in foreign coin store #3273

Merged
merged 7 commits into from
Dec 10, 2024
23 changes: 22 additions & 1 deletion pkg/contracts/solana/inbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,28 @@ func Test_ParseInboundAsDepositAndCall(t *testing.T) {
// solana e2e deployer account
sender := "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ"
// example contract deployed during e2e test, read from tx result
expectedReceiver := []byte{117, 160, 106, 140, 37, 135, 57, 218, 223, 226, 53, 45, 87, 151, 61, 239, 158, 231, 162, 186}
expectedReceiver := []byte{
117,
160,
106,
140,
37,
135,
57,
218,
223,
226,
53,
45,
87,
151,
61,
239,
158,
231,
162,
186,
}
expectedMsg := []byte("hello lamports")
expectedDeposit := &Deposit{
Sender: sender,
Expand Down
74 changes: 71 additions & 3 deletions zetaclient/chains/evm/observer/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol"
"github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol"

"github.com/zeta-chain/node/pkg/chains"
"github.com/zeta-chain/node/pkg/coin"
"github.com/zeta-chain/node/pkg/constant"
"github.com/zeta-chain/node/pkg/memo"
Expand All @@ -34,6 +35,67 @@
"github.com/zeta-chain/node/zetaclient/zetacore"
)

var (
// erc20AddressToForeignCoinAssetMap maps the chain id and foreign ERC20 address to the coin asset string
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved
// this is currently necessary because of the following issue: https://github.com/zeta-chain/node/issues/3274
erc20AddressToForeignCoinAssetMap = map[int64]map[ethcommon.Address]string{
// Ethereum mainnet
chains.Ethereum.ChainId: {
// USDC.ETH
ethcommon.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"): "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
// PEPE.ETH
ethcommon.HexToAddress("0x6982508145454ce325ddbe47a25d4ec3d2311933"): "0x6982508145454ce325ddbe47a25d4ec3d2311933",
// SHIB.ETH
ethcommon.HexToAddress("0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce"): "0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce",
// USDT.ETH
ethcommon.HexToAddress("0xdac17f958d2ee523a2206206994597c13d831ec7"): "0xdac17f958d2ee523a2206206994597c13d831ec7",
// DAI.ETH
ethcommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f"): "0x6b175474e89094c44da98b954eedeac495271d0f",
},

// BSC mainnet
chains.BscMainnet.ChainId: {
// USDC.BSC
ethcommon.HexToAddress("0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d"): "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
// USDT.BSC
ethcommon.HexToAddress("0x55d398326f99059ff775485246999027b3197955"): "0x55d398326f99059ff775485246999027b3197955",
},

// Polygon mainnet
chains.Polygon.ChainId: {
// USDT.POL
ethcommon.HexToAddress("0xc2132d05d31c914a87c6611c10748aeb04b58e8f"): "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
// USDC.POL
ethcommon.HexToAddress("0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"): "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
},

// Polygon Amoy
chains.Amoy.ChainId: {
// USDC.AMOY
ethcommon.HexToAddress("0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582"): "0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582",
},
}
)

// PatchZRC20Asset returns a patched asset string for the given chainID and erc20Address
// so that it matches the asset string in the foreign coin store.
func PatchZRC20Asset(chainID int64, erc20Address ethcommon.Address) string {
addressToAsset, found := erc20AddressToForeignCoinAssetMap[chainID]
if found {
// if found, convert the address to asset in foreigh coin store
asset, found := addressToAsset[erc20Address]
if found {
return asset
}

// use the checksum address as asset string by default
return erc20Address.Hex()

Check warning on line 92 in zetaclient/chains/evm/observer/inbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/evm/observer/inbound.go#L92

Added line #L92 was not covered by tests
}

// use the checksum address as asset string by default
return erc20Address.Hex()

Check warning on line 96 in zetaclient/chains/evm/observer/inbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/evm/observer/inbound.go#L96

Added line #L96 was not covered by tests
}

// WatchInbound watches evm chain for incoming txs and post votes to zetacore
// TODO(revamp): move ticker function to a separate file
func (ob *Observer) WatchInbound(ctx context.Context) error {
Expand Down Expand Up @@ -672,11 +734,17 @@
Msgf("thank you rich folk for your donation! tx %s chain %d", event.Raw.TxHash.Hex(), ob.Chain().ChainId)
return nil
}

// get patched asset string so that it matches the asset in the foreign coin store
// TODO: remove once the checksum conversion is fixed in the protocol
// https://github.com/zeta-chain/node/issues/3274
asset := PatchZRC20Asset(ob.Chain().ChainId, event.Asset)

message := hex.EncodeToString(event.Message)
ob.Logger().Inbound.Info().
Msgf("ERC20CustodyDeposited inbound detected on chain %d tx %s block %d from %s value %s message %s",
Msgf("ERC20CustodyDeposited inbound detected on chain %d tx %s block %d from %s value %s asset %s message %s",
ob.Chain().
ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, sender.Hex(), event.Amount.String(), message)
ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, sender.Hex(), event.Amount.String(), asset, message)

return zetacore.GetInboundVoteMessage(
sender.Hex(),
Expand All @@ -690,7 +758,7 @@
event.Raw.BlockNumber,
1_500_000,
coin.CoinType_ERC20,
event.Asset.String(),
asset,
ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(),
event.Raw.Index,
)
Expand Down
89 changes: 84 additions & 5 deletions zetaclient/chains/evm/observer/inbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,95 @@ import (
"github.com/zeta-chain/node/pkg/coin"
"github.com/zeta-chain/node/pkg/constant"
"github.com/zeta-chain/node/zetaclient/chains/evm"
"github.com/zeta-chain/node/zetaclient/chains/evm/observer"
"github.com/zeta-chain/node/zetaclient/chains/interfaces"
"github.com/zeta-chain/node/zetaclient/config"
"github.com/zeta-chain/node/zetaclient/testutils"
"github.com/zeta-chain/node/zetaclient/testutils/mocks"
clienttypes "github.com/zeta-chain/node/zetaclient/types"
)

func Test_PatchZRC20Asset(t *testing.T) {
tests := []struct {
name string
chainID int64
erc20Address ethcommon.Address
assetString string
}{
// Ethereum Mainnet
{
name: "USDC.ETH",
chainID: chains.Ethereum.ChainId,
erc20Address: ethcommon.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"),
assetString: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
},
{
name: "PEPE.ETH",
chainID: chains.Ethereum.ChainId,
erc20Address: ethcommon.HexToAddress("0x6982508145454ce325ddbe47a25d4ec3d2311933"),
assetString: "0x6982508145454ce325ddbe47a25d4ec3d2311933",
},
{
name: "SHIB.ETH",
chainID: chains.Ethereum.ChainId,
erc20Address: ethcommon.HexToAddress("0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce"),
assetString: "0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce",
},
{
name: "USDT.ETH",
chainID: chains.Ethereum.ChainId,
erc20Address: ethcommon.HexToAddress("0xdac17f958d2ee523a2206206994597c13d831ec7"),
assetString: "0xdac17f958d2ee523a2206206994597c13d831ec7",
},
{
name: "DAI.ETH",
chainID: chains.Ethereum.ChainId,
erc20Address: ethcommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f"),
assetString: "0x6b175474e89094c44da98b954eedeac495271d0f",
},
// BSC Mainnet
{
name: "USDC.BSC",
chainID: chains.BscMainnet.ChainId,
erc20Address: ethcommon.HexToAddress("0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d"),
assetString: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
},
{
name: "USDT.BSC",
chainID: chains.BscMainnet.ChainId,
erc20Address: ethcommon.HexToAddress("0x55d398326f99059ff775485246999027b3197955"),
assetString: "0x55d398326f99059ff775485246999027b3197955",
},
// Polygon Mainnet
{
name: "USDT.POL",
chainID: chains.Polygon.ChainId,
erc20Address: ethcommon.HexToAddress("0xc2132d05d31c914a87c6611c10748aeb04b58e8f"),
assetString: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
},
{
name: "USDC.POL",
chainID: chains.Polygon.ChainId,
erc20Address: ethcommon.HexToAddress("0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"),
assetString: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
},
// Polygon Amoy
{
name: "USDC.AMOY",
chainID: chains.Amoy.ChainId,
erc20Address: ethcommon.HexToAddress("0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582"),
assetString: "0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
asset := observer.PatchZRC20Asset(tt.chainID, tt.erc20Address)
require.Equal(t, tt.assetString, asset)
})
}
}

func Test_CheckAndVoteInboundTokenZeta(t *testing.T) {
// load archived ZetaSent inbound, receipt and cctx
// https://etherscan.io/tx/0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76
Expand Down Expand Up @@ -133,7 +215,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) {
ctx := context.Background()

t.Run("should pass for archived inbound, receipt and cctx", func(t *testing.T) {
tx, receipt, cctx := testutils.LoadEVMInboundNReceiptNCctx(
tx, receipt, _ := testutils.LoadEVMInboundNReceiptNCctx(
t,
TestDataDir,
chainID,
Expand All @@ -144,9 +226,8 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) {
lastBlock := receipt.BlockNumber.Uint64() + confirmation

ob, _ := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam)
ballot, err := ob.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, false)
_, err := ob.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, false)
require.NoError(t, err)
require.Equal(t, cctx.InboundParams.BallotIndex, ballot)
})
t.Run("should fail on unconfirmed inbound", func(t *testing.T) {
tx, receipt, _ := testutils.LoadEVMInboundNReceiptNCctx(
Expand Down Expand Up @@ -341,7 +422,6 @@ func Test_BuildInboundVoteMsgForDepositedEvent(t *testing.T) {
chainID := chain.ChainId
inboundHash := "0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da"
tx, receipt := testutils.LoadEVMInboundNReceipt(t, TestDataDir, chainID, inboundHash, coin.CoinType_ERC20)
cctx := testutils.LoadCctxByInbound(t, chainID, coin.CoinType_ERC20, inboundHash)

// parse Deposited event
ob, _ := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, mocks.MockChainParams(1, 1))
Expand All @@ -357,7 +437,6 @@ func Test_BuildInboundVoteMsgForDepositedEvent(t *testing.T) {
t.Run("should return vote msg for archived Deposited event", func(t *testing.T) {
msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender)
require.NotNil(t, msg)
require.Equal(t, cctx.InboundParams.BallotIndex, msg.Digest())
})
t.Run("should return nil msg if sender is restricted", func(t *testing.T) {
cfg.ComplianceConfig.RestrictedAddresses = []string{sender.Hex()}
Expand Down
24 changes: 22 additions & 2 deletions zetaclient/chains/evm/observer/v2_inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@
isCrossChainCall = true
}

// get patched asset string so that it matches the one in the foreign coin store
// TODO: remove once the checksum conversion is fixed in the protocol
// https://github.com/zeta-chain/node/issues/3274
asset := PatchZRC20Asset(ob.Chain().ChainId, event.Asset)
if asset != event.Asset.Hex() {
ob.Logger().
Inbound.Info().
Msgf("newDepositInboundVote converted asset %s to %s for chain %d", event.Asset.Hex(), asset, ob.Chain().ChainId)
}

Check warning on line 192 in zetaclient/chains/evm/observer/v2_inbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/evm/observer/v2_inbound.go#L187-L192

Added lines #L187 - L192 were not covered by tests

return *types.NewMsgVoteInbound(
ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(),
event.Sender.Hex(),
Expand All @@ -194,7 +204,7 @@
event.Raw.BlockNumber,
zetacore.PostVoteInboundCallOptionsGasLimit,
coinType,
event.Asset.Hex(),
asset,

Check warning on line 207 in zetaclient/chains/evm/observer/v2_inbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/evm/observer/v2_inbound.go#L207

Added line #L207 was not covered by tests
event.Raw.Index,
types.ProtocolContractVersion_V2,
false, // currently not relevant since calls are not arbitrary
Expand Down Expand Up @@ -454,6 +464,16 @@
coinType = coin.CoinType_Gas
}

// get patched asset string so that it matches the one in the foreign coin store
// TODO: remove once the checksum conversion is fixed in the protocol
// https://github.com/zeta-chain/node/issues/3274
asset := PatchZRC20Asset(ob.Chain().ChainId, event.Asset)
if asset != event.Asset.Hex() {
ob.Logger().
Inbound.Info().
Msgf("newDepositAndCallInboundVote converted asset %s to %s for chain %d", event.Asset.Hex(), asset, ob.Chain().ChainId)
}

Check warning on line 475 in zetaclient/chains/evm/observer/v2_inbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/evm/observer/v2_inbound.go#L470-L475

Added lines #L470 - L475 were not covered by tests

return *types.NewMsgVoteInbound(
ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(),
event.Sender.Hex(),
Expand All @@ -467,7 +487,7 @@
event.Raw.BlockNumber,
1_500_000,
coinType,
event.Asset.Hex(),
asset,

Check warning on line 490 in zetaclient/chains/evm/observer/v2_inbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/evm/observer/v2_inbound.go#L490

Added line #L490 was not covered by tests
event.Raw.Index,
types.ProtocolContractVersion_V2,
false, // currently not relevant since calls are not arbitrary
Expand Down
Loading