From 79ac5eff517624583a2bb823fced925b68b880f5 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Tue, 10 Dec 2024 14:49:53 -0600 Subject: [PATCH] hardcode erc20 asset string to align with foreign coin store data --- zetaclient/chains/evm/observer/inbound.go | 81 +++++++++++++- .../chains/evm/observer/inbound_test.go | 101 ++++++++++++++++++ zetaclient/chains/evm/observer/v2_inbound.go | 20 +++- 3 files changed, 197 insertions(+), 5 deletions(-) diff --git a/zetaclient/chains/evm/observer/inbound.go b/zetaclient/chains/evm/observer/inbound.go index 493a4e3c18..806f87f6d9 100644 --- a/zetaclient/chains/evm/observer/inbound.go +++ b/zetaclient/chains/evm/observer/inbound.go @@ -20,6 +20,7 @@ import ( "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" @@ -34,6 +35,76 @@ import ( "github.com/zeta-chain/node/zetaclient/zetacore" ) +var ( + // erc20AddressToForeignCoinAssetMap maps the chain id and foreign ERC20 address to the coin asset string + 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", + // ULTI.ETH + ethcommon.HexToAddress("0x0E7779e698052f8fe56C415C3818FCf89de9aC6D"): "0x0E7779e698052f8fe56C415C3818FCf89de9aC6D", + }, + + // BSC mainnet + chains.BscMainnet.ChainId: { + // USDC.BSC + ethcommon.HexToAddress("0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d"): "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", + // USDT.BSC + ethcommon.HexToAddress("0x55d398326f99059ff775485246999027b3197955"): "0x55d398326f99059ff775485246999027b3197955", + // ULTI.BSC + ethcommon.HexToAddress("0x0E7779e698052f8fe56C415C3818FCf89de9aC6D"): "0x0E7779e698052f8fe56C415C3818FCf89de9aC6D", + }, + + // 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", + }, + + // Base mainnet + chains.BaseMainnet.ChainId: { + // USDC.BASE + ethcommon.HexToAddress("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"): "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + }, + } +) + +// ERC20AddressToForeignCoinAsset +func ERC20AddressToForeignCoinAsset(chainID int64, erc20Address ethcommon.Address) string { + addressToAsset, found := erc20AddressToForeignCoinAssetMap[chainID] + switch { + case 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() + default: + // use the checksum address as asset string by default + return erc20Address.Hex() + } +} + // 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 { @@ -672,11 +743,15 @@ func (ob *Observer) BuildInboundVoteMsgForDepositedEvent( Msgf("thank you rich folk for your donation! tx %s chain %d", event.Raw.TxHash.Hex(), ob.Chain().ChainId) return nil } + + // convert erc20Address to asset in foreign coin store to avoid checksum mismatch + asset := ERC20AddressToForeignCoinAsset(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(), @@ -690,7 +765,7 @@ func (ob *Observer) BuildInboundVoteMsgForDepositedEvent( event.Raw.BlockNumber, 1_500_000, coin.CoinType_ERC20, - event.Asset.String(), + asset, ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), event.Raw.Index, ) diff --git a/zetaclient/chains/evm/observer/inbound_test.go b/zetaclient/chains/evm/observer/inbound_test.go index e3612678da..0d7bfe4a56 100644 --- a/zetaclient/chains/evm/observer/inbound_test.go +++ b/zetaclient/chains/evm/observer/inbound_test.go @@ -19,6 +19,7 @@ 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" @@ -26,6 +27,106 @@ import ( clienttypes "github.com/zeta-chain/node/zetaclient/types" ) +func Test_ERC20AddressToForeignCoinAsset(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", + }, + { + name: "ULTI.ETH", + chainID: chains.Ethereum.ChainId, + erc20Address: ethcommon.HexToAddress("0x0E7779e698052f8fe56C415C3818FCf89de9aC6D"), + assetString: "0x0E7779e698052f8fe56C415C3818FCf89de9aC6D", + }, + // 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", + }, + { + name: "ULTI.BSC", + chainID: chains.BscMainnet.ChainId, + erc20Address: ethcommon.HexToAddress("0x0E7779e698052f8fe56C415C3818FCf89de9aC6D"), + assetString: "0x0E7779e698052f8fe56C415C3818FCf89de9aC6D", + }, + // 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", + }, + // Base Mainnet + { + name: "USDC.BASE", + chainID: chains.BaseMainnet.ChainId, + erc20Address: ethcommon.HexToAddress("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"), + assetString: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + asset := observer.ERC20AddressToForeignCoinAsset(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 diff --git a/zetaclient/chains/evm/observer/v2_inbound.go b/zetaclient/chains/evm/observer/v2_inbound.go index 9688851af6..5e779d0d05 100644 --- a/zetaclient/chains/evm/observer/v2_inbound.go +++ b/zetaclient/chains/evm/observer/v2_inbound.go @@ -181,6 +181,14 @@ func (ob *Observer) newDepositInboundVote(event *gatewayevm.GatewayEVMDeposited) isCrossChainCall = true } + // convert erc20Address to asset in foreign coin store to avoid checksum mismatch + asset := ERC20AddressToForeignCoinAsset(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) + } + return *types.NewMsgVoteInbound( ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), event.Sender.Hex(), @@ -194,7 +202,7 @@ func (ob *Observer) newDepositInboundVote(event *gatewayevm.GatewayEVMDeposited) event.Raw.BlockNumber, zetacore.PostVoteInboundCallOptionsGasLimit, coinType, - event.Asset.Hex(), + asset, event.Raw.Index, types.ProtocolContractVersion_V2, false, // currently not relevant since calls are not arbitrary @@ -454,6 +462,14 @@ func (ob *Observer) newDepositAndCallInboundVote(event *gatewayevm.GatewayEVMDep coinType = coin.CoinType_Gas } + // convert erc20Address to asset in foreign coin store to avoid checksum mismatch + asset := ERC20AddressToForeignCoinAsset(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) + } + return *types.NewMsgVoteInbound( ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), event.Sender.Hex(), @@ -467,7 +483,7 @@ func (ob *Observer) newDepositAndCallInboundVote(event *gatewayevm.GatewayEVMDep event.Raw.BlockNumber, 1_500_000, coinType, - event.Asset.Hex(), + asset, event.Raw.Index, types.ProtocolContractVersion_V2, false, // currently not relevant since calls are not arbitrary