From 4fe701aeb71db0bc9880b5132e5a78d38ceea6d5 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Mon, 13 Jan 2025 05:12:58 +0100 Subject: [PATCH 1/5] add venus protocol --- pkg/constants.go | 22 +- pkg/registry.go | 2 +- pkg/validate.go | 4 + pkg/venus.go | 665 ++++++++++++++++++++++++++++++++++++++++++++++ pkg/venus_test.go | 239 +++++++++++++++++ 5 files changed, 921 insertions(+), 11 deletions(-) create mode 100644 pkg/venus.go create mode 100644 pkg/venus_test.go diff --git a/pkg/constants.go b/pkg/constants.go index e5fd765..88568eb 100644 --- a/pkg/constants.go +++ b/pkg/constants.go @@ -59,16 +59,18 @@ type Protocol interface { } const ( - AaveV3 ProtocolName = "aave_v3" - SparkLend ProtocolName = "spark_lend" - Lido ProtocolName = "lido" - RocketPool ProtocolName = "rocket_pool" - Ankr ProtocolName = "ankr" - Renzo ProtocolName = "renzo" - Compound ProtocolName = "compound" - ListaDao ProtocolName = "lista_dao" - AvalonFinance ProtocolName = "avalon_finance" - BinanceStakedETH ProtocolName = "binance_staked_eth" + AaveV3 ProtocolName = "aave_v3" + SparkLend ProtocolName = "spark_lend" + Lido ProtocolName = "lido" + RocketPool ProtocolName = "rocket_pool" + Ankr ProtocolName = "ankr" + Renzo ProtocolName = "renzo" + Compound ProtocolName = "compound" + ListaDao ProtocolName = "lista_dao" + AvalonFinance ProtocolName = "avalon_finance" + BinanceStakedETH ProtocolName = "binance_staked_eth" + VenusProtocolIsolatedPool ProtocolName = "venus_isolated_pool" + VenusProtocolCorePool ProtocolName = "venus_core_pool" ) var ( diff --git a/pkg/registry.go b/pkg/registry.go index 35dcff7..94b504d 100644 --- a/pkg/registry.go +++ b/pkg/registry.go @@ -336,5 +336,5 @@ func (r *ProtocolRegistryImpl) setupBnbProtocols(client *ethclient.Client) error return err } - return nil + return registerVenusPools(r, client, BscChainID.Int64()) } diff --git a/pkg/validate.go b/pkg/validate.go index be423f8..85bfbfb 100644 --- a/pkg/validate.go +++ b/pkg/validate.go @@ -8,6 +8,10 @@ import ( const zeroAddress = "0x0000000000000000000000000000000000000000" +func IsZeroAddress(addr common.Address) bool { + return common.HexToAddress(zeroAddress).Hex() == addr.Hex() +} + // nativeDenomAddress native denom token address. const nativeDenomAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" diff --git a/pkg/venus.go b/pkg/venus.go new file mode 100644 index 0000000..3bd5622 --- /dev/null +++ b/pkg/venus.go @@ -0,0 +1,665 @@ +package pkg + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "log" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" +) + +// venusABIs contains all abis used by pool registry, vtokens and others +// this allows us only compile and init once +const venusABIs = ` +[ +{ + "inputs": [], + "name": "getAllPools", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" +}, + { + "inputs": [], + "name": "getAllMarkets", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mintBehalf", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "wrapAndSupply", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "underlying", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "badDebt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemBehalf", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemAndUnwrap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + +{ + "name": "actionPaused", + "type": "function", + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "enum Action", + "name": "action", + "type": "uint8" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ] +}, +{ + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "redeemUnderlyingBehalf", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, +{ + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "redeemUnderlying", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +} +] +` + +var venusBNBContractAddress ContractAddress = common.HexToAddress("0x9F7b01A536aFA00EF10310A162877fd792cD0666") + +// VenusOperation implements the Protocol interface for Lido +type VenusOperation struct { + parsedABI abi.ABI + contract common.Address + chainID *big.Int + version string + + poolMarkets map[string]struct { + address string + // VWBNB can supply BNB or native assets. In this instance, we want to use the NativeGateway instead + isNative bool + } + + assetToVTokenMap map[string]string + + client *ethclient.Client +} + +// dynamically registers all supported pools +func registerVenusPools(registry ProtocolRegistry, client *ethclient.Client, chainID int64) error { + parsedABI, err := abi.JSON(strings.NewReader(venusABIs)) + if err != nil { + return err + } + + data, err := parsedABI.Pack("getAllPools") + if err != nil { + log.Fatalf("Failed to pack contract call: %v", err) + } + + callMsg := ethereum.CallMsg{ + To: &venusBNBContractAddress, + Data: data, + } + + result, err := client.CallContract(context.Background(), callMsg, nil) + if err != nil { + return err + } + + var pools []struct { + Name string + Creator common.Address + Comptroller common.Address + BlockPosted *big.Int + TimestampPosted *big.Int + } + err = parsedABI.UnpackIntoInterface(&pools, "getAllPools", result) + if err != nil { + return err + } + + for _, poolAddr := range pools { + c, err := NewVenusOperation(client, big.NewInt(chainID), poolAddr.Comptroller) + if err != nil { + return err + } + + if err := registry.RegisterProtocol(big.NewInt(chainID), poolAddr.Comptroller, c); err != nil { + return err + } + } + + return nil +} + +func NewVenusOperation(client *ethclient.Client, + chainID *big.Int, + marketPool common.Address) (*VenusOperation, error) { + if !IsBnb(chainID) { + return nil, fmt.Errorf("unsupported chain ID (%d)", chainID.Int64()) + } + + if client == nil { + return nil, errors.New("ethclient cannot be nil") + } + + parsedABI, err := abi.JSON(strings.NewReader(venusABIs)) + if err != nil { + return nil, err + } + + v := &VenusOperation{ + parsedABI: parsedABI, + contract: marketPool, + chainID: chainID, + version: "4", + client: client, + poolMarkets: map[string]struct { + address string + isNative bool + }{}, + assetToVTokenMap: make(map[string]string), + } + + if err := v.getSupportedAssets(); err != nil { + return nil, err + } + + return v, nil +} + +func (v *VenusOperation) getSupportedAssets() error { + + getAllMarketsCalldata, err := v.parsedABI.Pack("getAllMarkets") + if err != nil { + return err + } + + msg := ethereum.CallMsg{ + To: &v.contract, + Data: getAllMarketsCalldata, + } + + result, err := v.client.CallContract(context.Background(), msg, nil) + if err != nil { + return err + } + + var marketInfo []common.Address + + err = v.parsedABI.UnpackIntoInterface(&marketInfo, "getAllMarkets", result) + if err != nil { + return fmt.Errorf("failed to unpack output: %v", err) + } + + // get underlying token to add to map + for _, addr := range marketInfo { + + getSymbol, err := v.parsedABI.Pack("symbol") + if err != nil { + return err + } + + msg := ethereum.CallMsg{ + To: &addr, + Data: getSymbol, + } + + result, err := v.client.CallContract(context.Background(), msg, nil) + if err != nil { + return err + } + + var symbol string + + err = v.parsedABI.UnpackIntoInterface(&symbol, "symbol", result) + if err != nil { + return fmt.Errorf("failed to unpack symbol output: %v", err) + } + + getUnderlying, err := v.parsedABI.Pack("underlying") + if err != nil { + return err + } + + msg = ethereum.CallMsg{ + To: &addr, + Data: getUnderlying, + } + + result, err = v.client.CallContract(context.Background(), msg, nil) + if err != nil { + return err + } + + var underlyingAddr common.Address + + err = v.parsedABI.UnpackIntoInterface(&underlyingAddr, "underlying", result) + if err != nil { + return fmt.Errorf("failed to unpack underlying output: %v", err) + } + + v.poolMarkets[addr.Hex()] = struct { + address string + isNative bool + }{ + address: underlyingAddr.Hex(), + isNative: symbol == "vWBNB_LiquidStakedBNB", + } + + v.assetToVTokenMap[underlyingAddr.Hex()] = addr.Hex() + if v.poolMarkets[addr.Hex()].isNative { + v.assetToVTokenMap[nativeDenomAddress] = addr.Hex() + } + } + + return nil +} + +// GenerateCalldata creates the necessary blockchain transaction data to +// supply or withdraw your asset +func (l *VenusOperation) GenerateCalldata(ctx context.Context, chainID *big.Int, + action ContractAction, params TransactionParams) (string, error) { + if !IsBnb(chainID) { + return "", ErrChainUnsupported + } + + var calldata []byte + var err error + + switch action { + case LoanSupply: + + calldata, err = l.parsedABI.Pack("mint", params.Amount) + if err != nil { + return "", err + } + + if !IsZeroAddress(params.Recipient) { + calldata, err = l.parsedABI.Pack("mintBehalf", params.Recipient, params.Amount) + if err != nil { + return "", err + } + } + + if IsNativeToken(params.Asset) { + calldata, err = l.parsedABI.Pack("wrapAndSupply", params.Sender) + if err != nil { + return "", err + } + } + + case LoanWithdraw: + + calldata, err = l.parsedABI.Pack("redeemUnderlying", params.Amount) + if err != nil { + return "", err + } + + if !IsZeroAddress(params.Recipient) { + calldata, err = l.parsedABI.Pack("redeemUnderlyingBehalf", params.Recipient, params.Amount) + if err != nil { + return "", err + } + } + + if IsNativeToken(params.Asset) { + calldata, err = l.parsedABI.Pack("redeemAndUnwrap", params.Amount) + if err != nil { + return "", err + } + } + + default: + return "", errors.New("action not supported") + } + + return HexPrefix + hex.EncodeToString(calldata), nil +} + +// Validate checks if the provided parameters are valid for the specified action +func (l *VenusOperation) Validate(ctx context.Context, + chainID *big.Int, action ContractAction, params TransactionParams) error { + + if !IsBnb(chainID) { + return ErrChainUnsupported + } + + if !l.IsSupportedAsset(ctx, l.chainID, params.Asset) { + return fmt.Errorf("asset not supported %s", params.Asset) + } + + if action != LoanSupply && action != LoanWithdraw { + return errors.New("unsupported action") + } + + if params.Amount.Cmp(big.NewInt(0)) <= 0 { + return errors.New("amount must be greater than zero") + } + + if action == LoanSupply { + return nil + } + + _, balance, err := l.GetBalance(ctx, l.chainID, params.Sender, params.Asset) + if err != nil { + return err + } + + if balance.Cmp(params.Amount) == -1 { + return errors.New("balance not enough") + } + + return nil +} + +// GetBalance retrieves the balance for a specified account and asset +func (l *VenusOperation) GetBalance(ctx context.Context, + chainID *big.Int, account, asset common.Address) (common.Address, *big.Int, error) { + + var address common.Address + if !IsBnb(chainID) { + return address, nil, ErrChainUnsupported + } + + contract, ok := l.assetToVTokenMap[asset.Hex()] + if !ok { + return common.Address{}, big.NewInt(0), errors.New("asset not supported") + } + + callData, err := l.parsedABI.Pack("balanceOf", account) + if err != nil { + return address, nil, err + } + + c := common.HexToAddress(contract) + + result, err := l.client.CallContract(ctx, ethereum.CallMsg{ + To: &c, + Data: callData, + }, nil) + if err != nil { + return address, nil, err + } + + balance := new(big.Int) + err = l.parsedABI.UnpackIntoInterface(&balance, "balanceOf", result) + return c, balance, err +} + +// GetSupportedAssets returns a list of assets supported by the protocol on the specified chain +func (l *VenusOperation) GetSupportedAssets(ctx context.Context, chainID *big.Int) ([]common.Address, error) { + supportedAssets := make([]common.Address, 0) + + // only add native token once. Ideally this might not be needed as vBNB pool is the only one + // right now and most realistic one ever but doesn't hurt to be defensive here + var nativeTokenAdded bool + + for _, v := range l.poolMarkets { + supportedAssets = append(supportedAssets, common.HexToAddress(v.address)) + + if v.isNative && !nativeTokenAdded { + supportedAssets = append(supportedAssets, common.HexToAddress(nativeDenomAddress)) + nativeTokenAdded = true + } + } + + return supportedAssets, nil +} + +// IsSupportedAsset checks if the specified asset is supported on the given chain +func (l *VenusOperation) IsSupportedAsset(ctx context.Context, chainID *big.Int, asset common.Address) bool { + if !IsBnb(chainID) { + return false + } + + assets, err := l.GetSupportedAssets(ctx, chainID) + if err != nil { + return false + } + + for _, a := range assets { + if asset.Hex() == a.Hex() { + return true + } + } + + return false +} + +// GetProtocolConfig returns the protocol config for a specific chain +func (l *VenusOperation) GetProtocolConfig(chainID *big.Int) ProtocolConfig { + return ProtocolConfig{ + ChainID: l.chainID, + Contract: l.contract, + ABI: l.parsedABI, + Type: TypeLoan, + } +} + +// GetABI returns the ABI of the protocol's contract +func (l *VenusOperation) GetABI(chainID *big.Int) abi.ABI { return l.parsedABI } + +// GetType returns the protocol type +func (l *VenusOperation) GetType() ProtocolType { return TypeLoan } + +// GetContractAddress returns the contract address for a specific chain +func (l *VenusOperation) GetContractAddress(chainID *big.Int) common.Address { return l.contract } + +// Name returns the human readable name for the protocol +func (l *VenusOperation) GetName() string { return VenusProtocolIsolatedPool } + +// GetVersion returns the version of the protocol +func (l *VenusOperation) GetVersion() string { return l.version } diff --git a/pkg/venus_test.go b/pkg/venus_test.go new file mode 100644 index 0000000..9bfa360 --- /dev/null +++ b/pkg/venus_test.go @@ -0,0 +1,239 @@ +//go:build integration +// +build integration + +package pkg + +import ( + "context" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestVenusOperation_New(t *testing.T) { + t.Run("unsupported chain", func(t *testing.T) { + venus, err := NewVenusOperation(getTestClient(t, ChainETH), + big.NewInt(1), + common.HexToAddress("0xd933909A4a2b7A4638903028f44D1d38ce27c352")) + require.Error(t, err) + require.Nil(t, venus) + }) + + t.Run("venus correctly setup", func(t *testing.T) { + venus, err := NewVenusOperation(getTestClient(t, ChainBSC), + big.NewInt(56), + common.HexToAddress("0xd933909A4a2b7A4638903028f44D1d38ce27c352")) + require.NoError(t, err) + require.NotNil(t, venus) + }) +} + +func TestVenusOperation_Validate(t *testing.T) { + t.Run("unsupported chain", func(t *testing.T) { + venus, err := NewVenusOperation(getTestClient(t, ChainBSC), + big.NewInt(56), + common.HexToAddress("0xd933909A4a2b7A4638903028f44D1d38ce27c352")) + require.NoError(t, err) + + err = venus.Validate(context.Background(), big.NewInt(1), LoanSupply, TransactionParams{ + Amount: big.NewInt(0), + Asset: common.HexToAddress(nativeDenomAddress), + }) + require.Error(t, err) + }) + + t.Run("unsupported action", func(t *testing.T) { + venus, err := NewVenusOperation(getTestClient(t, ChainBSC), + big.NewInt(56), + common.HexToAddress("0xd933909A4a2b7A4638903028f44D1d38ce27c352")) + require.NoError(t, err) + + err = venus.Validate(context.Background(), big.NewInt(56), NativeStake, TransactionParams{ + Amount: big.NewInt(1), + Asset: common.HexToAddress(nativeDenomAddress), + }) + require.Error(t, err) + }) + + t.Run("unsupported asset", func(t *testing.T) { + venus, err := NewVenusOperation(getTestClient(t, ChainBSC), + big.NewInt(56), + common.HexToAddress("0xd933909A4a2b7A4638903028f44D1d38ce27c352")) + require.NoError(t, err) + + err = venus.Validate(context.Background(), big.NewInt(56), LoanSupply, TransactionParams{ + Amount: big.NewInt(1), + Asset: common.HexToAddress("0x1234567890123456789012345678901234567890"), + }) + require.Error(t, err) + }) +} + +func TestVenusOperation_GetBalance(t *testing.T) { + client := getTestClient(t, ChainBSC) + + venus, err := NewVenusOperation(client, + big.NewInt(56), + common.HexToAddress("0xd933909A4a2b7A4638903028f44D1d38ce27c352")) + require.NoError(t, err) + + t.Run("unsupported chain", func(t *testing.T) { + _, _, err := venus.GetBalance(context.Background(), big.NewInt(1), emptyTestWallet, common.HexToAddress("")) + require.Error(t, err) + }) + + t.Run("get balance for address", func(t *testing.T) { + token, balance, err := venus.GetBalance(context.Background(), big.NewInt(56), emptyTestWallet, common.HexToAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c")) + require.NoError(t, err) + require.NotNil(t, balance) + require.NotEmpty(t, token) + }) +} + +func TestVenusOperation_GenerateCalldata(t *testing.T) { + venus, err := NewVenusOperation(getTestClient(t, ChainBSC), + big.NewInt(56), + common.HexToAddress("0xd933909A4a2b7A4638903028f44D1d38ce27c352")) + require.NoError(t, err) + + t.Run("unsupported chain", func(t *testing.T) { + _, err := venus.GenerateCalldata(context.Background(), big.NewInt(1), LoanSupply, TransactionParams{}) + require.Error(t, err) + }) + + t.Run("unsupported action", func(t *testing.T) { + _, err := venus.GenerateCalldata(context.Background(), big.NewInt(56), NativeStake, TransactionParams{}) + require.Error(t, err) + }) + + t.Run("generate supply calldata with WBNB", func(t *testing.T) { + params := TransactionParams{ + Amount: big.NewInt(100000000000000000), // 0.1 BNB + Asset: common.HexToAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"), + } + calldata, err := venus.GenerateCalldata(context.Background(), big.NewInt(56), LoanSupply, params) + require.NoError(t, err) + require.NotEmpty(t, calldata) + }) + + t.Run("generate supply calldata with native BNB", func(t *testing.T) { + params := TransactionParams{ + Amount: big.NewInt(100000000000000000), // 0.1 BNB + Asset: common.HexToAddress(nativeDenomAddress), + } + calldata, err := venus.GenerateCalldata(context.Background(), big.NewInt(56), LoanSupply, params) + require.NoError(t, err) + require.NotEmpty(t, calldata) + }) + + t.Run("generate supply calldata with recipient", func(t *testing.T) { + recipient := common.HexToAddress("0x1234567890123456789012345678901234567890") + params := TransactionParams{ + Amount: big.NewInt(100000000000000000), // 0.1 BNB + Asset: common.HexToAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"), + Recipient: recipient, + } + calldata, err := venus.GenerateCalldata(context.Background(), big.NewInt(56), LoanSupply, params) + require.NoError(t, err) + require.NotEmpty(t, calldata) + }) + + t.Run("generate withdraw calldata with WBNB", func(t *testing.T) { + params := TransactionParams{ + Amount: big.NewInt(100000000000000000), // 0.1 BNB + Asset: common.HexToAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"), + } + calldata, err := venus.GenerateCalldata(context.Background(), big.NewInt(56), LoanWithdraw, params) + require.NoError(t, err) + require.NotEmpty(t, calldata) + }) + + t.Run("generate withdraw calldata with native BNB", func(t *testing.T) { + params := TransactionParams{ + Amount: big.NewInt(100000000000000000), // 0.1 BNB + Asset: common.HexToAddress(nativeDenomAddress), + } + calldata, err := venus.GenerateCalldata(context.Background(), big.NewInt(56), LoanWithdraw, params) + require.NoError(t, err) + require.NotEmpty(t, calldata) + }) + + t.Run("generate withdraw calldata with recipient", func(t *testing.T) { + recipient := common.HexToAddress("0x1234567890123456789012345678901234567890") + params := TransactionParams{ + Amount: big.NewInt(100000000000000000), // 0.1 BNB + Asset: common.HexToAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"), + Recipient: recipient, + } + calldata, err := venus.GenerateCalldata(context.Background(), big.NewInt(56), LoanWithdraw, params) + require.NoError(t, err) + require.NotEmpty(t, calldata) + }) +} + +func TestVenus(t *testing.T) { + + t.Run("Liquid staking pool", func(t *testing.T) { + + venus, err := NewVenusOperation(getTestClient(t, ChainBSC), + big.NewInt(56), + common.HexToAddress("0xd933909A4a2b7A4638903028f44D1d38ce27c352")) + require.NoError(t, err) + + tt := []struct { + asset string + name string + unsupported bool + }{ + {asset: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", name: "WBNB is supported"}, + {asset: nativeDenomAddress, name: "BNB is supported"}, + {asset: "0x55d398326f99059fF775485246999027B3197955", name: "USDT is not supported", unsupported: true}, + } + + _, err = venus.GetSupportedAssets(context.Background(), big.NewInt(56)) + require.NoError(t, err) + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + if tc.unsupported { + require.False(t, venus.IsSupportedAsset(context.Background(), big.NewInt(56), common.HexToAddress(tc.asset))) + return + } + + require.True(t, venus.IsSupportedAsset(context.Background(), big.NewInt(56), common.HexToAddress(tc.asset))) + }) + } + }) + + t.Run("Stablecoins pool", func(t *testing.T) { + + venus, err := NewVenusOperation(getTestClient(t, ChainBSC), big.NewInt(56), common.HexToAddress("0x94c1495cD4c557f1560Cbd68EAB0d197e6291571")) + require.NoError(t, err) + + tt := []struct { + asset string + name string + unsupported bool + }{ + {asset: "0x55d398326f99059fF775485246999027B3197955", name: "USDT is supported"}, + {asset: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", name: "WBNB is not supported", unsupported: true}, + {asset: nativeDenomAddress, name: "BNB is not supported", unsupported: true}, + } + + _, err = venus.GetSupportedAssets(context.Background(), big.NewInt(56)) + require.NoError(t, err) + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + if tc.unsupported { + require.False(t, venus.IsSupportedAsset(context.Background(), big.NewInt(56), common.HexToAddress(tc.asset))) + return + } + + require.True(t, venus.IsSupportedAsset(context.Background(), big.NewInt(56), common.HexToAddress(tc.asset))) + }) + } + }) +} From 7ea72a15412616f20b1e77913ed688c89ae5d5aa Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Mon, 13 Jan 2025 23:00:41 +0100 Subject: [PATCH 2/5] add new interface and auto generate *.json files --- generate.go | 3 ++ pkg/aave.go | 3 ++ pkg/ankr.go | 3 ++ pkg/compound.go | 3 ++ pkg/constants.go | 2 + pkg/lido.go | 3 ++ pkg/rocket_pool.go | 3 ++ tools/tokens.go | 118 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 138 insertions(+) create mode 100644 generate.go create mode 100644 tools/tokens.go diff --git a/generate.go b/generate.go new file mode 100644 index 0000000..a972106 --- /dev/null +++ b/generate.go @@ -0,0 +1,3 @@ +package malak + +//go:generate go run tools/tokens.go diff --git a/pkg/aave.go b/pkg/aave.go index 0454da8..668dab0 100644 --- a/pkg/aave.go +++ b/pkg/aave.go @@ -475,3 +475,6 @@ func (l *AaveOperation) GetName() string { // GetVersion returns the version of the protocol func (l *AaveOperation) GetVersion() string { return l.version } + +func (l *AaveOperation) IsSource() bool { return true } +func (l *AaveOperation) IsDestination() bool { return true } diff --git a/pkg/ankr.go b/pkg/ankr.go index caafa9f..b27d8e3 100644 --- a/pkg/ankr.go +++ b/pkg/ankr.go @@ -202,3 +202,6 @@ func (l *AnkrOperation) GetName() string { return Ankr } // GetVersion returns the version of the protocol func (l *AnkrOperation) GetVersion() string { return l.version } + +func (l *AnkrOperation) IsSource() bool { return true } +func (l *AnkrOperation) IsDestination() bool { return true } diff --git a/pkg/compound.go b/pkg/compound.go index c3899c7..b86b1ee 100644 --- a/pkg/compound.go +++ b/pkg/compound.go @@ -420,3 +420,6 @@ func (l *CompoundOperation) GetName() string { return Compound } // GetVersion returns the version of the protocol func (l *CompoundOperation) GetVersion() string { return l.version } + +func (l *CompoundOperation) IsSource() bool { return true } +func (l *CompoundOperation) IsDestination() bool { return true } diff --git a/pkg/constants.go b/pkg/constants.go index 88568eb..3f8fcb7 100644 --- a/pkg/constants.go +++ b/pkg/constants.go @@ -56,6 +56,8 @@ type Protocol interface { GetName() string GetVersion() string GetContractAddress(chainID *big.Int) common.Address + IsSource() bool + IsDestination() bool } const ( diff --git a/pkg/lido.go b/pkg/lido.go index 0fd1187..5c0da01 100644 --- a/pkg/lido.go +++ b/pkg/lido.go @@ -192,3 +192,6 @@ func (l *LidoOperation) GetName() string { return Lido } // GetVersion returns the version of the protocol func (l *LidoOperation) GetVersion() string { return l.version } + +func (l *LidoOperation) IsSource() bool { return false } +func (l *LidoOperation) IsDestination() bool { return true } diff --git a/pkg/rocket_pool.go b/pkg/rocket_pool.go index ad07412..0e6be0d 100644 --- a/pkg/rocket_pool.go +++ b/pkg/rocket_pool.go @@ -290,3 +290,6 @@ func (l *RocketpoolOperation) GetName() string { return RocketPool } // GetVersion returns the version of the protocol func (l *RocketpoolOperation) GetVersion() string { return l.version } + +func (l *RocketpoolOperation) IsSource() bool { return true } +func (l *RocketpoolOperation) IsDestination() bool { return true } diff --git a/tools/tokens.go b/tools/tokens.go new file mode 100644 index 0000000..f6edf5f --- /dev/null +++ b/tools/tokens.go @@ -0,0 +1,118 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "os" + + "github.com/blndgs/protocol_registry/pkg" + "github.com/blndgs/protocol_registry/tokens" +) + +func main() { + chains := []struct { + Name string + ChainID *big.Int + }{ + { + Name: "Ethereum", + ChainID: big.NewInt(1), + }, + { + Name: "BNB Chain", + ChainID: big.NewInt(56), + }, + { + Name: "Polygon", + ChainID: big.NewInt(137), + }, + } + + registry, err := pkg.NewProtocolRegistry([]pkg.ChainConfig{ + { + ChainID: big.NewInt(1), + RPCURL: "https://rpc.ankr.com/eth", + }, + { + ChainID: big.NewInt(56), + RPCURL: "https://rpc.ankr.com/bsc", + }, + { + ChainID: big.NewInt(137), + RPCURL: "https://rpc.ankr.com/polygon", + }, + }) + + if err != nil { + panic(err.Error()) + } + + ctx := context.Background() + + for _, chain := range chains { + fmt.Println("updating file for", chain.Name) + + // Read existing token data + fileName := fmt.Sprintf("tokens/%d.json", chain.ChainID) + existingData, err := os.ReadFile(fileName) + if err != nil { + fmt.Printf("error reading existing data for %s: %v\n", chain.Name, err) + continue + } + + var protocolData tokens.Data + err = json.Unmarshal(existingData, &protocolData) + if err != nil { + fmt.Printf("error unmarshaling existing data for %s: %v\n", chain.Name, err) + continue + } + + // Clear existing protocols but keep tokens + existingTokens := protocolData.Tokens + protocolData.Protocols = nil + + protocols := registry.ListProtocols(chain.ChainID) + + // Convert protocols to the format expected by tokens.Data + for _, p := range protocols { + supportedAssets, err := p.GetSupportedAssets(ctx, chain.ChainID) + if err != nil { + fmt.Printf("error getting supported assets for protocol %s on chain %s: %v\n", p.GetName(), chain.Name, err) + continue + } + + // Convert addresses to strings + tokenAddrs := make([]string, len(supportedAssets)) + for i, addr := range supportedAssets { + tokenAddrs[i] = addr.Hex() + } + + protocolData.Protocols = append(protocolData.Protocols, tokens.Protocol{ + Address: p.GetContractAddress(chain.ChainID).Hex(), + Name: p.GetName(), + Type: string(p.GetType()), + Source: true, + Destination: true, + Tokens: tokenAddrs, + }) + } + + // Restore existing tokens + protocolData.Tokens = existingTokens + + // Write the data to the corresponding chain file + data, err := json.MarshalIndent(protocolData, "", " ") + if err != nil { + fmt.Printf("error marshaling data for %s: %v\n", chain.Name, err) + continue + } + + err = os.WriteFile(fileName, data, 0644) + if err != nil { + fmt.Printf("error writing file for %s: %v\n", chain.Name, err) + continue + } + } +} From cb4a12627a1c9efae96c673a11d7e4f31b2aba60 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 14 Jan 2025 10:32:04 +0100 Subject: [PATCH 3/5] implement all interfaces --- pkg/binance.go | 3 +++ pkg/listadao_stake.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/pkg/binance.go b/pkg/binance.go index 0a50a6b..cb74433 100644 --- a/pkg/binance.go +++ b/pkg/binance.go @@ -206,3 +206,6 @@ func (l *BinanceWrappedEthOperation) GetName() string { return BinanceStakedETH // GetVersion returns the version of the protocol func (l *BinanceWrappedEthOperation) GetVersion() string { return l.version } + +func (l *BinanceWrappedEthOperation) IsSource() bool { return true } +func (l *BinanceWrappedEthOperation) IsDestination() bool { return true } diff --git a/pkg/listadao_stake.go b/pkg/listadao_stake.go index 121c396..6f52559 100644 --- a/pkg/listadao_stake.go +++ b/pkg/listadao_stake.go @@ -203,3 +203,6 @@ func (l *ListaStakingOperation) GetName() string { return ListaDao } // GetVersion returns the version of the protocol func (l *ListaStakingOperation) GetVersion() string { return "1" } + +func (l *ListaStakingOperation) IsSource() bool { return false } +func (l *ListaStakingOperation) IsDestination() bool { return true } From a63111a572c43bacd6425957e412f5771b7dd18f Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 14 Jan 2025 11:05:15 +0100 Subject: [PATCH 4/5] fixes. update tooling --- docs/00_protocol.md | 5 + pkg/venus.go | 3 + tokens/1.json | 269 ++++++++++++++++++++--------------- tokens/137.json | 130 +++++++++++------ tokens/56.json | 332 ++++++++++++++++++++++++++++++++++++++------ tools/tokens.go | 195 +++++++++++++++++++++----- 6 files changed, 702 insertions(+), 232 deletions(-) diff --git a/docs/00_protocol.md b/docs/00_protocol.md index 2c13ef4..e18bfbb 100644 --- a/docs/00_protocol.md +++ b/docs/00_protocol.md @@ -41,6 +41,11 @@ type Protocol interface { // GetContractAddress returns the contract address for a specific chain. GetContractAddress(chainID *big.Int) common.Address + // Can this protocol be withdrawn from or be the source state of an intent? + IsSource() bool + + // Can you supply to this protocol? + IsDestination() bool } // ProtocolConfig contains configuration data for initializing a protocol. diff --git a/pkg/venus.go b/pkg/venus.go index 3bd5622..8957894 100644 --- a/pkg/venus.go +++ b/pkg/venus.go @@ -663,3 +663,6 @@ func (l *VenusOperation) GetName() string { return VenusProtocolIsolatedPool } // GetVersion returns the version of the protocol func (l *VenusOperation) GetVersion() string { return l.version } + +func (l *VenusOperation) IsSource() bool { return false } +func (l *VenusOperation) IsDestination() bool { return true } diff --git a/tokens/1.json b/tokens/1.json index 50cae71..14da434 100644 --- a/tokens/1.json +++ b/tokens/1.json @@ -1,103 +1,139 @@ { "tokens": [ { - "token_address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "name": "Ethereum", - "symbol": "ETH", + "token_address": "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7", + "name": "rsETH", + "symbol": "rsETH", "decimals": 18 }, { - "token_address": "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2", - "name": "Maker", - "symbol": "MKR", + "token_address": "0x18084fbA666a33d37592fA2633fD49a74DD93a88", + "name": "tBTC v2", + "symbol": "tBTC", "decimals": 18 }, { - "token_address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "name": "Wrapped liquid staked Ether 2.0", - "symbol": "wstETH", + "token_address": "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b", + "name": "ETHx", + "symbol": "ETHx", "decimals": 18 }, { - "token_address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", + "token_address": "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "name": "ChainLink Token", + "symbol": "LINK", + "decimals": 18 + }, + { + "token_address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", "name": "Wrapped BTC", "symbol": "WBTC", "decimals": 8 }, { - "token_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "token_address": "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110", + "name": "Renzo Restaked ETH", + "symbol": "ezETH", + "decimals": 18 + }, + { + "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "name": "USD Coin", "symbol": "USDC", "decimals": 6 }, { - "token_address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "token_address": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "name": "Dai Stablecoin", + "symbol": "DAI", + "decimals": 18 + }, + { + "token_address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "name": "Ethereum", + "symbol": "ETH", + "decimals": 18 + }, + { + "token_address": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "name": "Tether USD", "symbol": "USDT", "decimals": 6 }, { - "token_address": "0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee", - "name": "Wrapped eETH", - "symbol": "weETH", + "token_address": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", + "name": "Maker", + "symbol": "MKR", "decimals": 18 }, { - "token_address": "0x514910771af9ca656af840dff83e8264ecf986ca", - "name": "ChainLink Token", - "symbol": "LINK", + "token_address": "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", + "name": "Coinbase Wrapped Staked ETH", + "symbol": "cbETH", "decimals": 18 }, { - "token_address": "0xae78736cd615f374d3085123a210448e74fc6393", + "token_address": "0xae78736Cd615f374D3085123A210448E74Fc6393", "name": "Rocket Pool ETH", "symbol": "rETH", "decimals": 18 }, { - "token_address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "name": "Dai Stablecoin", - "symbol": "DAI", + "token_address": "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", + "name": "Coinbase Wrapped BTC", + "symbol": "cbBTC", + "decimals": 8 + }, + { + "token_address": "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0", + "name": "rswETH", + "symbol": "rswETH", "decimals": 18 }, { - "token_address": "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9", + "token_address": "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "name": "Compound", + "symbol": "COMP", + "decimals": 18 + }, + { + "token_address": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", "name": "Aave Token", "symbol": "AAVE", "decimals": 18 }, { - "token_address": "0x83f20f44975d03b1b09e64809b757c47f942beea", + "token_address": "0x83F20F44975D03b1b09e64809B757c47f942BEeA", "name": "Savings Dai", "symbol": "sDAI", "decimals": 18 }, { - "token_address": "0xbe9895146f7af43049ca1c1ae358b0541ea49704", - "name": "Coinbase Wrapped Staked ETH", - "symbol": "cbETH", + "token_address": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "name": "Wrapped liquid staked Ether 2.0", + "symbol": "wstETH", + "decimals": 18 + }, + { + "token_address": "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", + "name": "Wrapped eETH", + "symbol": "weETH", "decimals": 18 }, { - "token_address": "0xf1c9acdc66974dfb6decb12aa385b9cd01190e38", + "token_address": "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38", "name": "Staked ETH", "symbol": "osETH", "decimals": 18 }, { - "token_address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "token_address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "name": "Wrapped Ether", "symbol": "WETH", "decimals": 18 }, { - "token_address": "0xc00e94cb662c3520282e6f5717214004a7f26888", - "name": "Compound", - "symbol": "COMP", - "decimals": 18 - }, - { - "token_address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", + "token_address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "name": "Uniswap", "symbol": "UNI", "decimals": 18 @@ -105,103 +141,112 @@ ], "protocols": [ { - "address": "0x84db6ee82b7cf3b47e8f19270abde5718b936670", - "type": "staking", - "name": "Ankr", + "address": "0xA17581A9E3356d9A858b789D68B4d866e593aE94", + "name": "compound", "source": true, "destination": true, "tokens": [ - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - ] - }, - { - "address": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84", - "name": "Lido", - "type": "staking", - "source": false, + "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0xae78736Cd615f374D3085123A210448E74Fc6393", + "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7", + "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", + "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38", + "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110", + "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", + "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0", + "0x18084fbA666a33d37592fA2633fD49a74DD93a88", + "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b" + ], + "type": "Loan" + }, + { + "address": "0xc3d688B66703497DAA19211EEdff47f25384cdc3", + "name": "compound", + "source": true, "destination": true, "tokens": [ - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - ] - }, - { - "address": "0x1d8f8f00cfa6758d7bE78336684788Fb0ee0Fa46", - "name": "Rocketpool", - "type": "staking", + "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", + "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", + "0x18084fbA666a33d37592fA2633fD49a74DD93a88" + ], + "type": "Loan" + }, + { + "address": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", + "name": "lido", "source": true, "destination": true, "tokens": [ - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "0xae78736cd615f374d3085123a210448e74fc6393" - ] + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + ], + "type": "Stake" }, { - "address": "0xC13e21B648A5Ee794902342038FF3aDAB66BE987", - "name": "SparkLend", - "type": "lending", + "address": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", + "name": "aave_v3", "source": true, "destination": true, "tokens": [ - "0x83f20f44975d03b1b09e64809b757c47f942beea", - "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "0xae78736cd615f374d3085123a210448e74fc6393", - "0xdac17f958d2ee523a2206206994597c13d831ec7" - ] - }, - { - "address": "0xa17581a9e3356d9a858b789d68b4d866e593ae94", - "name": "Compound ETH Pool", - "type": "lending", + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", + "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "0xae78736Cd615f374D3085123A210448E74Fc6393", + "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "0x83F20F44975D03b1b09e64809B757c47f942BEeA", + "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", + "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", + "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0xae78736Cd615f374D3085123A210448E74Fc6393" + ], + "type": "Loan" + }, + { + "address": "0xC13e21B648A5Ee794902342038FF3aDAB66BE987", + "name": "spark_lend", "source": true, "destination": true, "tokens": [ - "0xbe9895146f7af43049ca1c1ae358b0541ea49704", - "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "0xae78736cd615f374d3085123a210448e74fc6393", - "0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee", - "0xf1c9acdc66974dfb6decb12aa385b9cd01190e38", - "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599" - ] - }, - { - "address": "0xc3d688b66703497daa19211eedff47f25384cdc3", - "name": "Compound USDC Pool", - "type": "lending", + "0x83F20F44975D03b1b09e64809B757c47f942BEeA", + "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0xae78736Cd615f374D3085123A210448E74Fc6393", + "0xdAC17F958D2ee523a2206206994597C13D831ec7" + ], + "type": "Loan" + }, + { + "address": "0x84db6eE82b7Cf3b47E8F19270abdE5718B936670", + "name": "ankr", "source": true, "destination": true, "tokens": [ - "0xc00e94cb662c3520282e6f5717214004a7f26888", - "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", - "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", - "0x514910771af9ca656af840dff83e8264ecf986ca", - "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0" - ] - }, - { - "address": "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", - "name": "AaveV3", - "type": "lending", + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + ], + "type": "Stake" + }, + { + "address": "0xDD3f50F8A6CafbE9b31a427582963f465E745AF8", + "name": "rocket_pool", "source": true, "destination": true, "tokens": [ - "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "0xdac17f958d2ee523a2206206994597c13d831ec7", - "0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee", - "0x514910771af9ca656af840dff83e8264ecf986ca", - "0xae78736cd615f374d3085123a210448e74fc6393", - "0x6b175474e89094c44da98b954eedeac495271d0f", - "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9", - "0x83f20f44975d03b1b09e64809b757c47f942beea", - "0xbe9895146f7af43049ca1c1ae358b0541ea49704", - "0xf1c9acdc66974dfb6decb12aa385b9cd01190e38", - "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" - ] + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + ], + "type": "Stake" } ] -} +} \ No newline at end of file diff --git a/tokens/137.json b/tokens/137.json index 7e07f2d..1246918 100644 --- a/tokens/137.json +++ b/tokens/137.json @@ -1,83 +1,114 @@ { "tokens": [ { - "token_address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "name": "Polygon ( MATIC )", - "symbol": "POL", + "token_address": "0x172370d5Cd63279eFa6d502DAB29171933a610AF", + "name": "CRV (PoS)", + "symbol": "CRV", "decimals": 18 }, { - "token_address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", - "name": "Wrapped Matic", - "symbol": "WMATIC", + "token_address": "0x03b54A6e9a984069379fae1a4fC4dBAE93B3bCCD", + "name": "Wrapped liquid staked Ether 2.0 (PoS)", + "symbol": "wstETH", "decimals": 18 }, { - "token_address": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", - "name": "Dai Token", - "symbol": "DAI", - "decimals": 18 + "token_address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", + "name": "USD Coin", + "symbol": "USDC", + "decimals": 6 }, { - "token_address": "0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39", - "name": "Chainlink token", - "symbol": "LINK", + "token_address": "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", + "name": "Liquid Staking Matic (PoS)", + "symbol": "MaticX", "decimals": 18 }, { - "token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", - "name": "USDC.E", - "symbol": "USDC.e", - "decimals": 6 + "token_address": "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", + "name": "(PoS) Wrapped BTC", + "symbol": "WBTC", + "decimals": 8 }, { - "token_address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", - "name": "USDC", - "symbol": "USDC", - "decimals": 6 + "token_address": "0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39", + "name": "ChainLink Token", + "symbol": "LINK", + "decimals": 18 }, { - "token_address": "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", - "name": "Wrapped Bitcoin", - "symbol": "WBTC", - "decimals": 8 + "token_address": "0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a", + "name": "SushiToken (PoS)", + "symbol": "SUSHI", + "decimals": 18 }, { - "token_address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - "name": "Wrapped Ethereum", - "symbol": "WETH", + "token_address": "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4", + "name": "Staked MATIC (PoS)", + "symbol": "stMATIC", "decimals": 18 }, + { + "token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "name": "USD Coin (PoS)", + "symbol": "USDC", + "decimals": 6 + }, { "token_address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", - "name": "Tether USD", + "name": "(PoS) Tether USD", "symbol": "USDT", "decimals": 6 }, { "token_address": "0xD6DF932A45C0f255f85145f286eA0b292B21C90B", - "name": "Aave", + "name": "Aave (PoS)", "symbol": "AAVE", "decimals": 18 }, { - "token_address": "0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a", - "name": "Sushi token", - "symbol": "SUSHI", + "token_address": "0x385Eeac5cB85A38A9a07A70c73e0a3271CfB54A7", + "name": "Aavegotchi GHST Token (PoS)", + "symbol": "GHST", "decimals": 18 }, { - "token_address": "0x03b54A6e9a984069379fae1a4fC4dBAE93B3bCCD", - "name": "Wrapped liquid staked Ether 2.0", - "symbol": "WsETH", + "token_address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "name": "Wrapped Polygon Ecosystem Token", + "symbol": "WPOL", + "decimals": 18 + }, + { + "token_address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + "name": "Wrapped Ether", + "symbol": "WETH", + "decimals": 18 + }, + { + "token_address": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + "name": "(PoS) Dai Stablecoin", + "symbol": "DAI", "decimals": 18 } ], "protocols": [ { - "address": "0x794a61358D6845594F94dc1DB02A252b5b4814aD", - "name": "AaveV3", - "type": "lending", + "address": "0xaeB318360f27748Acb200CE616E389A6C9409a07", + "name": "compound", + "source": true, + "destination": true, + "tokens": [ + "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", + "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4", + "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6" + ], + "type": "Loan" + }, + { + "address": "0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654", + "name": "aave_v3", "source": true, "destination": true, "tokens": [ @@ -89,10 +120,27 @@ "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", "0xD6DF932A45C0f255f85145f286eA0b292B21C90B", "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "0x172370d5Cd63279eFa6d502DAB29171933a610AF", "0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a", + "0x385Eeac5cB85A38A9a07A70c73e0a3271CfB54A7", "0x03b54A6e9a984069379fae1a4fC4dBAE93B3bCCD", "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359" - ] + ], + "type": "Loan" + }, + { + "address": "0xF25212E676D1F7F89Cd72fFEe66158f541246445", + "name": "compound", + "source": true, + "destination": true, + "tokens": [ + "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", + "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", + "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4" + ], + "type": "Loan" } ] -} +} \ No newline at end of file diff --git a/tokens/56.json b/tokens/56.json index 0fd9903..c9afced 100644 --- a/tokens/56.json +++ b/tokens/56.json @@ -1,21 +1,39 @@ { "tokens": [ { - "token_address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "token_address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + "name": "Ethereum Token", + "symbol": "ETH", + "decimals": 18 + }, + { + "token_address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "name": "Binance Coin", "symbol": "BNB", "decimals": 18 }, { - "token_address": "0x2170ed0880ac9a755fd29b2688956bd959f933f8", - "name": "Ethereum Token", - "symbol": "ETH", + "token_address": "0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275", + "name": "Liquid Staking BNB", + "symbol": "BNBx", "decimals": 18 }, { - "token_address": "0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c", - "name": "BTCB Token", - "symbol": "BTCB", + "token_address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", + "name": "USD Coin", + "symbol": "USDC", + "decimals": 18 + }, + { + "token_address": "0x12f31B73D812C6Bb0d735a218c086d44D5fe5f89", + "name": "EURA (previously agEUR)", + "symbol": "EURA", + "decimals": 18 + }, + { + "token_address": "0x4aae823a6a0b376De6A78e74eCC5b079d38cBCf7", + "name": "Solv BTC", + "symbol": "SolvBTC", "decimals": 18 }, { @@ -25,9 +43,93 @@ "decimals": 18 }, { - "token_address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", - "name": "USD Coin", - "symbol": "USDC", + "token_address": "0x4B0F1812e5Df2A09796481Ff14017e6005508003", + "name": "Trust Wallet", + "symbol": "TWT", + "decimals": 18 + }, + { + "token_address": "0x12BB890508c125661E03b09EC06E404bc9289040", + "name": "Radio Caca V2", + "symbol": "RACA", + "decimals": 18 + }, + { + "token_address": "0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16", + "name": "Staked BNB", + "symbol": "stkBNB", + "decimals": 18 + }, + { + "token_address": "0x352Cb5E19b12FC216548a2677bD0fce83BaE434B", + "name": "BitTorrent", + "symbol": "BTT", + "decimals": 18 + }, + { + "token_address": "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", + "name": "BTCB Token", + "symbol": "BTCB", + "decimals": 18 + }, + { + "token_address": "0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5", + "name": "Lista USD", + "symbol": "lisUSD", + "decimals": 18 + }, + { + "token_address": "0xfb5B838b6cfEEdC2873aB27866079AC55363D37E", + "name": "FLOKI", + "symbol": "FLOKI", + "decimals": 9 + }, + { + "token_address": "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409", + "name": "First Digital USD", + "symbol": "FDUSD", + "decimals": 18 + }, + { + "token_address": "0x541B5eEAC7D4434C8f87e2d32019d67611179606", + "name": "PT SolvBTC Babylon 27MAR2025", + "symbol": "PT-SolvBTC.BBN-27MAR2025", + "decimals": 18 + }, + { + "token_address": "0x53E63a31fD1077f949204b94F431bCaB98F72BCE", + "name": "SolvBTC Ethena", + "symbol": "SolvBTC.ENA", + "decimals": 18 + }, + { + "token_address": "0x1346b618dC92810EC74163e4c27004c921D446a5", + "name": "SolvBTC Babylon", + "symbol": "SolvBTC.BBN", + "decimals": 18 + }, + { + "token_address": "0xaeF0d72a118ce24feE3cD1d43d383897D05B4e99", + "name": "WINk", + "symbol": "WIN", + "decimals": 18 + }, + { + "token_address": "0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3", + "name": "TRON", + "symbol": "TRX", + "decimals": 6 + }, + { + "token_address": "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C", + "name": "Wrapped liquid staked Ether 2.0", + "symbol": "wstETH", + "decimals": 18 + }, + { + "token_address": "0xCa6d678e74f553f0E59cccC03ae644a3c2c5EE7d", + "name": "PLANET", + "symbol": "PLANET", "decimals": 18 }, { @@ -37,70 +139,212 @@ "decimals": 18 }, { - "token_address": "0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3", - "name": "Dai Token", - "symbol": "DAI", + "token_address": "0x965F527D9159dCe6288a2219DB51fc6Eef120dD1", + "name": "Biswap", + "symbol": "BSW", "decimals": 18 }, { - "token_address": "0x5f0da599bb2cccfcf6fdfd7d81743b6020864350", - "name": "Maker", - "symbol": "MKR", + "token_address": "0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F", + "name": "AlpacaToken", + "symbol": "ALPACA", "decimals": 18 }, { - "token_address": "0x52ce071bd9b1c4b00a0b92d298c512478cad67e8", - "name": "Compound Coin", - "symbol": "COMP", + "token_address": "0xf307910A4c7bbc79691fD374889b36d8531B08e3", + "name": "Ankr", + "symbol": "ANKR", + "decimals": 18 + }, + { + "token_address": "0xd17479997F34dd9156Deef8F95A52D81D265be9c", + "name": "Decentralized USD", + "symbol": "USDD", + "decimals": 18 + }, + { + "token_address": "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", + "name": "Staked Lista BNB", + "symbol": "slisBNB", + "decimals": 18 + }, + { + "token_address": "0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827", + "name": "Ankr Staked BNB", + "symbol": "ankrBNB", + "decimals": 18 + }, + { + "token_address": "0xc748673057861a797275CD8A068AbB95A902e8de", + "name": "Baby Doge Coin", + "symbol": "BabyDoge", + "decimals": 9 + }, + { + "token_address": "0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A", + "name": "Wrapped eETH", + "symbol": "weETH", "decimals": 18 } ], "protocols": [ { - "address": "0x6807dc923806fE8Fd134338EABCA509979a7e0cB", - "name": "AaveV3", - "type": "lending", + "address": "0x1b43ea8622e76627B81665B1eCeBB4867566B963", + "name": "venus_isolated_pool", "source": true, "destination": true, "tokens": [ - "0x2170ed0880ac9a755fd29b2688956bd959f933f8", - "0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c", + "0x12BB890508c125661E03b09EC06E404bc9289040", + "0xfb5B838b6cfEEdC2873aB27866079AC55363D37E", "0x55d398326f99059fF775485246999027B3197955", - "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", - "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c" - ] + "0xd17479997F34dd9156Deef8F95A52D81D265be9c" + ], + "type": "Loan" }, { - "address": "0xf9278C7c4AEfAC4dDfd0D496f7a1C39cA6BCA6d4", - "name": "AvalonFinance", - "type": "lending", + "address": "0xd933909A4a2b7A4638903028f44D1d38ce27c352", + "name": "venus_isolated_pool", + "source": true, + "destination": true, + "tokens": [ + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", + "0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827", + "0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275", + "0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16" + ], + "type": "Loan" + }, + { + "address": "0x23b4404E4E5eC5FF5a6FFb70B7d14E3FabF237B0", + "name": "venus_isolated_pool", + "source": true, + "destination": true, + "tokens": [ + "0x352Cb5E19b12FC216548a2677bD0fce83BaE434B", + "0xaeF0d72a118ce24feE3cD1d43d383897D05B4e99", + "0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3", + "0x55d398326f99059fF775485246999027B3197955", + "0xd17479997F34dd9156Deef8F95A52D81D265be9c" + ], + "type": "Loan" + }, + { + "address": "0x33B6fa34cd23e5aeeD1B112d5988B026b8A5567d", + "name": "venus_isolated_pool", + "source": true, + "destination": true, + "tokens": [ + "0xc748673057861a797275CD8A068AbB95A902e8de", + "0x55d398326f99059fF775485246999027B3197955" + ], + "type": "Loan" + }, + { + "address": "0xBE609449Eb4D76AD8545f957bBE04b596E8fC529", + "name": "venus_isolated_pool", + "source": true, + "destination": true, + "tokens": [ + "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C", + "0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A", + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8" + ], + "type": "Loan" + }, + { + "address": "0x6807dc923806fE8Fd134338EABCA509979a7e0cB", + "name": "aave_v3", "source": true, "destination": true, "tokens": [ + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", + "0x55d398326f99059fF775485246999027B3197955", + "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409", "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", - "0x2170ed0880ac9a755fd29b2688956bd959f933f8", "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c" - ] + ], + "type": "Loan" }, { - "address": "0x1adB950d8bB3dA4bE104211D5AB038628e477fE6", - "name": "ListaDAO", - "type": "staking", - "source": false, + "address": "0x94c1495cD4c557f1560Cbd68EAB0d197e6291571", + "name": "venus_isolated_pool", + "source": true, "destination": true, "tokens": [ - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - ] + "0x12f31B73D812C6Bb0d735a218c086d44D5fe5f89", + "0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5", + "0x55d398326f99059fF775485246999027B3197955", + "0xd17479997F34dd9156Deef8F95A52D81D265be9c" + ], + "type": "Loan" }, { "address": "0xa2E3356610840701BDf5611a53974510Ae27E2e1", - "name": "Binance Ethereum Staking", - "type": "staking", - "source": false, + "name": "binance_staked_eth", + "source": true, + "destination": true, + "tokens": [ + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8" + ], + "type": "Stake" + }, + { + "address": "0x3344417c9360b963ca93A4e8305361AEde340Ab9", + "name": "venus_isolated_pool", + "source": true, + "destination": true, + "tokens": [ + "0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827", + "0x965F527D9159dCe6288a2219DB51fc6Eef120dD1", + "0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F", + "0x55d398326f99059fF775485246999027B3197955", + "0xd17479997F34dd9156Deef8F95A52D81D265be9c", + "0xf307910A4c7bbc79691fD374889b36d8531B08e3", + "0x4B0F1812e5Df2A09796481Ff14017e6005508003", + "0xCa6d678e74f553f0E59cccC03ae644a3c2c5EE7d" + ], + "type": "Loan" + }, + { + "address": "0x9DF11376Cf28867E2B0741348044780FbB7cb1d6", + "name": "venus_isolated_pool", + "source": true, + "destination": true, + "tokens": [ + "0x541B5eEAC7D4434C8f87e2d32019d67611179606", + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c" + ], + "type": "Loan" + }, + { + "address": "0xf9278C7c4AEfAC4dDfd0D496f7a1C39cA6BCA6d4", + "name": "avalon_finance", + "source": true, + "destination": true, + "tokens": [ + "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", + "0x55d398326f99059fF775485246999027B3197955", + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", + "0x53E63a31fD1077f949204b94F431bCaB98F72BCE", + "0x4aae823a6a0b376De6A78e74eCC5b079d38cBCf7", + "0x1346b618dC92810EC74163e4c27004c921D446a5" + ], + "type": "Loan" + }, + { + "address": "0x1adB950d8bB3dA4bE104211D5AB038628e477fE6", + "name": "lista_dao", + "source": true, "destination": true, "tokens": [ - "0x2170ed0880ac9a755fd29b2688956bd959f933f8" - ] + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + ], + "type": "Stake" } ] -} +} \ No newline at end of file diff --git a/tools/tokens.go b/tools/tokens.go index f6edf5f..2d709e2 100644 --- a/tools/tokens.go +++ b/tools/tokens.go @@ -6,42 +6,149 @@ import ( "fmt" "math/big" "os" + "strings" "github.com/blndgs/protocol_registry/pkg" "github.com/blndgs/protocol_registry/tokens" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" ) +const ( + erc20ABI = `[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [{"name": "", "type": "string"}], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [{"name": "", "type": "string"}], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [{"name": "", "type": "uint8"}], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + nativeTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + mkrTokenAddress = "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2" +) + +func getTokenMetadata(ctx context.Context, client *ethclient.Client, tokenAddr common.Address, chainID *big.Int) (string, string, int, error) { + // Special handling for MKR token on Ethereum + if chainID.Cmp(big.NewInt(1)) == 0 && strings.EqualFold(tokenAddr.Hex(), mkrTokenAddress) { + return "Maker", "MKR", 18, nil + } + + parsedABI, err := abi.JSON(strings.NewReader(erc20ABI)) + if err != nil { + return "", "", 0, fmt.Errorf("failed to parse ABI: %v", err) + } + + // Get name + nameData, err := client.CallContract(ctx, ethereum.CallMsg{ + To: &tokenAddr, + Data: parsedABI.Methods["name"].ID, + }, nil) + if err != nil { + return "", "", 0, fmt.Errorf("failed to get name: %v", err) + } + var name string + err = parsedABI.UnpackIntoInterface(&name, "name", nameData) + if err != nil { + return "", "", 0, fmt.Errorf("failed to unpack name: %v", err) + } + + // Get symbol + symbolData, err := client.CallContract(ctx, ethereum.CallMsg{ + To: &tokenAddr, + Data: parsedABI.Methods["symbol"].ID, + }, nil) + if err != nil { + return "", "", 0, fmt.Errorf("failed to get symbol: %v", err) + } + var symbol string + err = parsedABI.UnpackIntoInterface(&symbol, "symbol", symbolData) + if err != nil { + return "", "", 0, fmt.Errorf("failed to unpack symbol: %v", err) + } + + // Get decimals + decimalsData, err := client.CallContract(ctx, ethereum.CallMsg{ + To: &tokenAddr, + Data: parsedABI.Methods["decimals"].ID, + }, nil) + if err != nil { + return "", "", 0, fmt.Errorf("failed to get decimals: %v", err) + } + var decimals uint8 + err = parsedABI.UnpackIntoInterface(&decimals, "decimals", decimalsData) + if err != nil { + return "", "", 0, fmt.Errorf("failed to unpack decimals: %v", err) + } + + return name, symbol, int(decimals), nil +} + func main() { chains := []struct { - Name string - ChainID *big.Int + Name string + ChainID *big.Int + RPCURL string + NativeSymbol string + NativeName string }{ { - Name: "Ethereum", - ChainID: big.NewInt(1), + Name: "Ethereum", + ChainID: big.NewInt(1), + RPCURL: "https://rpc.ankr.com/eth", + NativeSymbol: "ETH", + NativeName: "Ethereum", }, { - Name: "BNB Chain", - ChainID: big.NewInt(56), + Name: "BNB Chain", + ChainID: big.NewInt(56), + RPCURL: "https://rpc.ankr.com/bsc", + NativeSymbol: "BNB", + NativeName: "Binance Coin", }, { - Name: "Polygon", - ChainID: big.NewInt(137), + Name: "Polygon", + ChainID: big.NewInt(137), + RPCURL: "https://rpc.ankr.com/polygon", + NativeSymbol: "MATIC", + NativeName: "Polygon", }, } registry, err := pkg.NewProtocolRegistry([]pkg.ChainConfig{ { ChainID: big.NewInt(1), - RPCURL: "https://rpc.ankr.com/eth", + RPCURL: chains[0].RPCURL, }, { ChainID: big.NewInt(56), - RPCURL: "https://rpc.ankr.com/bsc", + RPCURL: chains[1].RPCURL, }, { ChainID: big.NewInt(137), - RPCURL: "https://rpc.ankr.com/polygon", + RPCURL: chains[2].RPCURL, }, }) @@ -54,24 +161,14 @@ func main() { for _, chain := range chains { fmt.Println("updating file for", chain.Name) - // Read existing token data - fileName := fmt.Sprintf("tokens/%d.json", chain.ChainID) - existingData, err := os.ReadFile(fileName) + client, err := ethclient.Dial(chain.RPCURL) if err != nil { - fmt.Printf("error reading existing data for %s: %v\n", chain.Name, err) - continue + panic(fmt.Sprintf("error connecting to %s: %v", chain.Name, err)) } + defer client.Close() var protocolData tokens.Data - err = json.Unmarshal(existingData, &protocolData) - if err != nil { - fmt.Printf("error unmarshaling existing data for %s: %v\n", chain.Name, err) - continue - } - - // Clear existing protocols but keep tokens - existingTokens := protocolData.Tokens - protocolData.Protocols = nil + uniqueTokens := make(map[string]bool) protocols := registry.ListProtocols(chain.ChainID) @@ -79,14 +176,15 @@ func main() { for _, p := range protocols { supportedAssets, err := p.GetSupportedAssets(ctx, chain.ChainID) if err != nil { - fmt.Printf("error getting supported assets for protocol %s on chain %s: %v\n", p.GetName(), chain.Name, err) - continue + panic(fmt.Sprintf("error getting supported assets for protocol %s on chain %s: %v", p.GetName(), chain.Name, err)) } - // Convert addresses to strings + // Convert addresses to strings and collect unique tokens tokenAddrs := make([]string, len(supportedAssets)) for i, addr := range supportedAssets { - tokenAddrs[i] = addr.Hex() + addrStr := addr.Hex() + tokenAddrs[i] = addrStr + uniqueTokens[addrStr] = true } protocolData.Protocols = append(protocolData.Protocols, tokens.Protocol{ @@ -99,20 +197,47 @@ func main() { }) } - // Restore existing tokens - protocolData.Tokens = existingTokens + // Convert unique tokens to array and fetch metadata + for addr := range uniqueTokens { + var token tokens.Token + + if strings.EqualFold(addr, nativeTokenAddress) { + // Handle native token + token = tokens.Token{ + TokenAddress: addr, + Name: chain.NativeName, + Symbol: chain.NativeSymbol, + Decimals: 18, // Native tokens always have 18 decimals + } + } else { + // Handle ERC20 token + tokenAddr := common.HexToAddress(addr) + name, symbol, decimals, err := getTokenMetadata(ctx, client, tokenAddr, chain.ChainID) + if err != nil { + panic(fmt.Sprintf("error getting metadata for token %s on chain %s: %v", addr, chain.Name, err)) + } + + token = tokens.Token{ + TokenAddress: addr, + Name: name, + Symbol: symbol, + Decimals: decimals, + } + } + + protocolData.Tokens = append(protocolData.Tokens, token) + } // Write the data to the corresponding chain file + fileName := fmt.Sprintf("tokens/%d.json", chain.ChainID) data, err := json.MarshalIndent(protocolData, "", " ") if err != nil { - fmt.Printf("error marshaling data for %s: %v\n", chain.Name, err) - continue + panic(fmt.Sprintf("error marshaling data for %s: %v", chain.Name, err)) } err = os.WriteFile(fileName, data, 0644) if err != nil { - fmt.Printf("error writing file for %s: %v\n", chain.Name, err) - continue + panic(fmt.Sprintf("error writing file for %s: %v", chain.Name, err)) } } } From 91711084b3298eb3570c77051d816a73b1bc0018 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 14 Jan 2025 12:08:37 +0100 Subject: [PATCH 5/5] go generate ./... --- .env.example | 1 + generate.go | 2 +- go.mod | 2 +- go.sum | 2 + pkg/aave.go | 2 +- pkg/compound.go | 16 +- pkg/venus.go | 3 +- tokens/1.json | 228 ++++++++++++++--------------- tokens/137.json | 121 ++++++++------- tokens/56.json | 299 ++++++++++++++++---------------------- tokens/token_impl.go | 8 +- tokens/token_impl_test.go | 16 +- tools/tokens.go | 266 ++++++++++++++++++++------------- 13 files changed, 494 insertions(+), 472 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..57147c6 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +MORALIS_API_KEY= diff --git a/generate.go b/generate.go index a972106..5823b6c 100644 --- a/generate.go +++ b/generate.go @@ -1,3 +1,3 @@ -package malak +package protocolregistry //go:generate go run tools/tokens.go diff --git a/go.mod b/go.mod index 71146f9..82744fa 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.22 require ( github.com/ethereum/go-ethereum v1.11.5 + github.com/joho/godotenv v1.5.1 github.com/rocket-pool/rocketpool-go v1.8.2 github.com/stretchr/testify v1.9.0 ) @@ -35,4 +36,3 @@ require ( gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - diff --git a/go.sum b/go.sum index 75d0832..3182e84 100644 --- a/go.sum +++ b/go.sum @@ -75,6 +75,8 @@ github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= diff --git a/pkg/aave.go b/pkg/aave.go index 668dab0..2657c56 100644 --- a/pkg/aave.go +++ b/pkg/aave.go @@ -184,7 +184,7 @@ func NewAaveOperation( case AaveProtocolDeploymentSpark: contract = SparkLendContractAddress case AaveProtocolDeploymentPolygon: - contract = polygonAaveDataProviderContract + contract = AavePolygonV3ContractAddress } var version string = "3" diff --git a/pkg/compound.go b/pkg/compound.go index b86b1ee..8f884a5 100644 --- a/pkg/compound.go +++ b/pkg/compound.go @@ -416,7 +416,21 @@ func (l *CompoundOperation) GetType() ProtocolType { return TypeLoan } func (l *CompoundOperation) GetContractAddress(chainID *big.Int) common.Address { return l.contract } // Name returns the human readable name for the protocol -func (l *CompoundOperation) GetName() string { return Compound } +func (l *CompoundOperation) GetName() string { + + switch strings.ToLower(l.contract.Hex()) { + case strings.ToLower(CompoundV3ETHPool): + return "Compound ETH pool" + case strings.ToLower(CompoundV3PolygonUSDCPool), + strings.ToLower(CompoundV3USDCPool): + return "Compound USDC Pool" + case strings.ToLower(CompoundV3PolygonUSDTPool): + return "Compound USDT pool" + + default: + return Compound + } +} // GetVersion returns the version of the protocol func (l *CompoundOperation) GetVersion() string { return l.version } diff --git a/pkg/venus.go b/pkg/venus.go index 8957894..f7c323f 100644 --- a/pkg/venus.go +++ b/pkg/venus.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "errors" "fmt" - "log" "math/big" "strings" @@ -305,7 +304,7 @@ func registerVenusPools(registry ProtocolRegistry, client *ethclient.Client, cha data, err := parsedABI.Pack("getAllPools") if err != nil { - log.Fatalf("Failed to pack contract call: %v", err) + return fmt.Errorf("failed to pack contract call: %w", err) } callMsg := ethereum.CallMsg{ diff --git a/tokens/1.json b/tokens/1.json index 14da434..366fc64 100644 --- a/tokens/1.json +++ b/tokens/1.json @@ -1,46 +1,28 @@ { "tokens": [ { - "token_address": "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7", - "name": "rsETH", - "symbol": "rsETH", - "decimals": 18 - }, - { - "token_address": "0x18084fbA666a33d37592fA2633fD49a74DD93a88", - "name": "tBTC v2", - "symbol": "tBTC", - "decimals": 18 - }, - { - "token_address": "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b", - "name": "ETHx", - "symbol": "ETHx", - "decimals": 18 - }, - { - "token_address": "0x514910771AF9Ca656af840dff83E8264EcF986CA", - "name": "ChainLink Token", - "symbol": "LINK", + "token_address": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "name": "Aave Token", + "symbol": "AAVE", "decimals": 18 }, { - "token_address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "name": "Wrapped BTC", - "symbol": "WBTC", + "token_address": "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", + "name": "Coinbase Wrapped BTC", + "symbol": "cbBTC", "decimals": 8 }, { - "token_address": "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110", - "name": "Renzo Restaked ETH", - "symbol": "ezETH", + "token_address": "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", + "name": "Coinbase Wrapped Staked ETH", + "symbol": "cbETH", "decimals": 18 }, { - "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "name": "USD Coin", - "symbol": "USDC", - "decimals": 6 + "token_address": "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "name": "Compound", + "symbol": "COMP", + "decimals": 18 }, { "token_address": "0x6B175474E89094C44Da98b954EedeAC495271d0F", @@ -55,10 +37,10 @@ "decimals": 18 }, { - "token_address": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "name": "Tether USD", - "symbol": "USDT", - "decimals": 6 + "token_address": "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "name": "ChainLink Token", + "symbol": "LINK", + "decimals": 18 }, { "token_address": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", @@ -67,9 +49,15 @@ "decimals": 18 }, { - "token_address": "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", - "name": "Coinbase Wrapped Staked ETH", - "symbol": "cbETH", + "token_address": "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38", + "name": "Staked ETH", + "symbol": "osETH", + "decimals": 18 + }, + { + "token_address": "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110", + "name": "ezETH", + "symbol": "Renzo Restaked ETH", "decimals": 18 }, { @@ -79,10 +67,10 @@ "decimals": 18 }, { - "token_address": "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", - "name": "Coinbase Wrapped BTC", - "symbol": "cbBTC", - "decimals": 8 + "token_address": "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7", + "name": "rsETH", + "symbol": "rsETH", + "decimals": 18 }, { "token_address": "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0", @@ -91,39 +79,45 @@ "decimals": 18 }, { - "token_address": "0xc00e94Cb662C3520282E6f5717214004A7f26888", - "name": "Compound", - "symbol": "COMP", + "token_address": "0x83F20F44975D03b1b09e64809B757c47f942BEeA", + "name": "Savings Dai", + "symbol": "sDAI", "decimals": 18 }, { - "token_address": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", - "name": "Aave Token", - "symbol": "AAVE", + "token_address": "0x18084fbA666a33d37592fA2633fD49a74DD93a88", + "name": "tBTC v2", + "symbol": "tBTC", "decimals": 18 }, { - "token_address": "0x83F20F44975D03b1b09e64809B757c47f942BEeA", - "name": "Savings Dai", - "symbol": "sDAI", + "token_address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", + "name": "Uniswap", + "symbol": "UNI", "decimals": 18 }, { - "token_address": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", - "name": "Wrapped liquid staked Ether 2.0", - "symbol": "wstETH", - "decimals": 18 + "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "name": "USD Coin", + "symbol": "USDC", + "decimals": 6 }, { - "token_address": "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", - "name": "Wrapped eETH", - "symbol": "weETH", - "decimals": 18 + "token_address": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "name": "Tether USD", + "symbol": "USDT", + "decimals": 6 }, { - "token_address": "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38", - "name": "Staked ETH", - "symbol": "osETH", + "token_address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "name": "Wrapped BTC", + "symbol": "WBTC", + "decimals": 8 + }, + { + "token_address": "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", + "name": "EtherFi wrapped ETH", + "symbol": "weETH", "decimals": 18 }, { @@ -133,54 +127,39 @@ "decimals": 18 }, { - "token_address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", - "name": "Uniswap", - "symbol": "UNI", + "token_address": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "name": "Wrapped liquid staked Ether 2.0", + "symbol": "wstETH", "decimals": 18 } ], "protocols": [ { - "address": "0xA17581A9E3356d9A858b789D68B4d866e593aE94", - "name": "compound", - "source": true, - "destination": true, - "tokens": [ - "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", - "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", - "0xae78736Cd615f374D3085123A210448E74Fc6393", - "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7", - "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", - "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38", - "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110", - "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", - "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0", - "0x18084fbA666a33d37592fA2633fD49a74DD93a88", - "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b" - ], - "type": "Loan" - }, - { - "address": "0xc3d688B66703497DAA19211EEdff47f25384cdc3", - "name": "compound", + "address": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", + "name": "aave_v3", "source": true, "destination": true, "tokens": [ - "0xc00e94Cb662C3520282E6f5717214004A7f26888", "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", - "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", - "0x18084fbA666a33d37592fA2633fD49a74DD93a88" + "0x83F20F44975D03b1b09e64809B757c47f942BEeA", + "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", + "0xae78736Cd615f374D3085123A210448E74Fc6393", + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38" ], "type": "Loan" }, { - "address": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", - "name": "lido", + "address": "0x84db6eE82b7Cf3b47E8F19270abdE5718B936670", + "name": "ankr", "source": true, "destination": true, "tokens": [ @@ -189,48 +168,45 @@ "type": "Stake" }, { - "address": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", - "name": "aave_v3", + "address": "0xA17581A9E3356d9A858b789D68B4d866e593aE94", + "name": "Compound ETH pool", "source": true, "destination": true, "tokens": [ - "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0x18084fbA666a33d37592fA2633fD49a74DD93a88", "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7", + "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", - "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0", "0xae78736Cd615f374D3085123A210448E74Fc6393", - "0x6B175474E89094C44Da98b954EedeAC495271d0F", - "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", - "0x83F20F44975D03b1b09e64809B757c47f942BEeA", - "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", - "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", - "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38", - "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "0xae78736Cd615f374D3085123A210448E74Fc6393" + "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110", + "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", + "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38" ], "type": "Loan" }, { - "address": "0xC13e21B648A5Ee794902342038FF3aDAB66BE987", - "name": "spark_lend", + "address": "0xc3d688B66703497DAA19211EEdff47f25384cdc3", + "name": "Compound USDC Pool", "source": true, "destination": true, "tokens": [ - "0x83F20F44975D03b1b09e64809B757c47f942BEeA", + "0x18084fbA666a33d37592fA2633fD49a74DD93a88", + "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0x514910771AF9Ca656af840dff83E8264EcF986CA", "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", - "0xae78736Cd615f374D3085123A210448E74Fc6393", - "0xdAC17F958D2ee523a2206206994597C13D831ec7" + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf" ], "type": "Loan" }, { - "address": "0x84db6eE82b7Cf3b47E8F19270abdE5718B936670", - "name": "ankr", + "address": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", + "name": "lido", "source": true, "destination": true, "tokens": [ @@ -247,6 +223,22 @@ "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" ], "type": "Stake" + }, + { + "address": "0xC13e21B648A5Ee794902342038FF3aDAB66BE987", + "name": "spark_lend", + "source": true, + "destination": true, + "tokens": [ + "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0x83F20F44975D03b1b09e64809B757c47f942BEeA", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0xae78736Cd615f374D3085123A210448E74Fc6393", + "0xdAC17F958D2ee523a2206206994597C13D831ec7" + ], + "type": "Loan" } ] } \ No newline at end of file diff --git a/tokens/137.json b/tokens/137.json index 1246918..5d86bce 100644 --- a/tokens/137.json +++ b/tokens/137.json @@ -1,5 +1,11 @@ { "tokens": [ + { + "token_address": "0xD6DF932A45C0f255f85145f286eA0b292B21C90B", + "name": "Aave (PoS)", + "symbol": "AAVE", + "decimals": 18 + }, { "token_address": "0x172370d5Cd63279eFa6d502DAB29171933a610AF", "name": "CRV (PoS)", @@ -7,29 +13,17 @@ "decimals": 18 }, { - "token_address": "0x03b54A6e9a984069379fae1a4fC4dBAE93B3bCCD", - "name": "Wrapped liquid staked Ether 2.0 (PoS)", - "symbol": "wstETH", + "token_address": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + "name": "(PoS) Dai Stablecoin", + "symbol": "DAI", "decimals": 18 }, { - "token_address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", - "name": "USD Coin", - "symbol": "USDC", - "decimals": 6 - }, - { - "token_address": "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", - "name": "Liquid Staking Matic (PoS)", - "symbol": "MaticX", + "token_address": "0x385Eeac5cB85A38A9a07A70c73e0a3271CfB54A7", + "name": "Aavegotchi GHST Token (PoS)", + "symbol": "GHST", "decimals": 18 }, - { - "token_address": "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", - "name": "(PoS) Wrapped BTC", - "symbol": "WBTC", - "decimals": 8 - }, { "token_address": "0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39", "name": "ChainLink Token", @@ -37,9 +31,9 @@ "decimals": 18 }, { - "token_address": "0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a", - "name": "SushiToken (PoS)", - "symbol": "SUSHI", + "token_address": "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", + "name": "Liquid Staking Matic (PoS)", + "symbol": "MaticX", "decimals": 18 }, { @@ -48,12 +42,24 @@ "symbol": "stMATIC", "decimals": 18 }, + { + "token_address": "0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a", + "name": "SushiToken (PoS)", + "symbol": "SUSHI", + "decimals": 18 + }, { "token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", "name": "USD Coin (PoS)", "symbol": "USDC", "decimals": 6 }, + { + "token_address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", + "name": "USD Coin", + "symbol": "USDC", + "decimals": 6 + }, { "token_address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", "name": "(PoS) Tether USD", @@ -61,22 +67,10 @@ "decimals": 6 }, { - "token_address": "0xD6DF932A45C0f255f85145f286eA0b292B21C90B", - "name": "Aave (PoS)", - "symbol": "AAVE", - "decimals": 18 - }, - { - "token_address": "0x385Eeac5cB85A38A9a07A70c73e0a3271CfB54A7", - "name": "Aavegotchi GHST Token (PoS)", - "symbol": "GHST", - "decimals": 18 - }, - { - "token_address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", - "name": "Wrapped Polygon Ecosystem Token", - "symbol": "WPOL", - "decimals": 18 + "token_address": "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", + "name": "(PoS) Wrapped BTC", + "symbol": "WBTC", + "decimals": 8 }, { "token_address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", @@ -85,60 +79,59 @@ "decimals": 18 }, { - "token_address": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", - "name": "(PoS) Dai Stablecoin", - "symbol": "DAI", + "token_address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "name": "Wrapped Matic", + "symbol": "WMATIC", "decimals": 18 } ], "protocols": [ { - "address": "0xaeB318360f27748Acb200CE616E389A6C9409a07", - "name": "compound", + "address": "0x794a61358D6845594F94dc1DB02A252b5b4814aD", + "name": "aave_v3", "source": true, "destination": true, "tokens": [ + "0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a", "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "0x172370d5Cd63279eFa6d502DAB29171933a610AF", + "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", + "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "0x385Eeac5cB85A38A9a07A70c73e0a3271CfB54A7", + "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", + "0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39", "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", - "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4", - "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6" + "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + "0xD6DF932A45C0f255f85145f286eA0b292B21C90B", + "0xc2132D05D31c914a87C6611C10748AEb04B58e8F" ], "type": "Loan" }, { - "address": "0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654", - "name": "aave_v3", + "address": "0xF25212E676D1F7F89Cd72fFEe66158f541246445", + "name": "Compound USDC Pool", "source": true, "destination": true, "tokens": [ - "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", - "0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39", - "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", + "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4", "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", - "0xD6DF932A45C0f255f85145f286eA0b292B21C90B", - "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", - "0x172370d5Cd63279eFa6d502DAB29171933a610AF", - "0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a", - "0x385Eeac5cB85A38A9a07A70c73e0a3271CfB54A7", - "0x03b54A6e9a984069379fae1a4fC4dBAE93B3bCCD", - "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359" + "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6" ], "type": "Loan" }, { - "address": "0xF25212E676D1F7F89Cd72fFEe66158f541246445", - "name": "compound", + "address": "0xaeB318360f27748Acb200CE616E389A6C9409a07", + "name": "Compound USDT pool", "source": true, "destination": true, "tokens": [ - "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", - "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", - "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4" + "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", + "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4", + "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6" ], "type": "Loan" } diff --git a/tokens/56.json b/tokens/56.json index c9afced..56e2a69 100644 --- a/tokens/56.json +++ b/tokens/56.json @@ -1,63 +1,57 @@ { "tokens": [ { - "token_address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", - "name": "Ethereum Token", - "symbol": "ETH", - "decimals": 18 - }, - { - "token_address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - "name": "Binance Coin", - "symbol": "BNB", + "token_address": "0x12f31B73D812C6Bb0d735a218c086d44D5fe5f89", + "name": "agEUR", + "symbol": "agEUR", "decimals": 18 }, { - "token_address": "0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275", - "name": "Liquid Staking BNB", - "symbol": "BNBx", + "token_address": "0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F", + "name": "AlpacaToken", + "symbol": "ALPACA", "decimals": 18 }, { - "token_address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", - "name": "USD Coin", - "symbol": "USDC", + "token_address": "0xf307910A4c7bbc79691fD374889b36d8531B08e3", + "name": "Ankr", + "symbol": "ANKR", "decimals": 18 }, { - "token_address": "0x12f31B73D812C6Bb0d735a218c086d44D5fe5f89", - "name": "EURA (previously agEUR)", - "symbol": "EURA", + "token_address": "0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827", + "name": "Ankr Staked BNB", + "symbol": "ankrBNB", "decimals": 18 }, { - "token_address": "0x4aae823a6a0b376De6A78e74eCC5b079d38cBCf7", - "name": "Solv BTC", - "symbol": "SolvBTC", - "decimals": 18 + "token_address": "0xc748673057861a797275CD8A068AbB95A902e8de", + "name": "Baby Doge Coin", + "symbol": "BabyDoge", + "decimals": 9 }, { - "token_address": "0x55d398326f99059fF775485246999027B3197955", - "name": "Tether USD", - "symbol": "USDT", + "token_address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "name": "Binance Coin", + "symbol": "BNB", "decimals": 18 }, { - "token_address": "0x4B0F1812e5Df2A09796481Ff14017e6005508003", - "name": "Trust Wallet", - "symbol": "TWT", + "token_address": "0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275", + "name": "Liquid Staking BNB", + "symbol": "BNBx", "decimals": 18 }, { - "token_address": "0x12BB890508c125661E03b09EC06E404bc9289040", - "name": "Radio Caca V2", - "symbol": "RACA", + "token_address": "0x965F527D9159dCe6288a2219DB51fc6Eef120dD1", + "name": "Biswap", + "symbol": "BSW", "decimals": 18 }, { - "token_address": "0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16", - "name": "Staked BNB", - "symbol": "stkBNB", + "token_address": "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", + "name": "BTCB Token", + "symbol": "BTCB", "decimals": 18 }, { @@ -67,15 +61,15 @@ "decimals": 18 }, { - "token_address": "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", - "name": "BTCB Token", - "symbol": "BTCB", + "token_address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + "name": "Ethereum Token", + "symbol": "ETH", "decimals": 18 }, { - "token_address": "0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5", - "name": "Lista USD", - "symbol": "lisUSD", + "token_address": "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409", + "name": "First Digital USD", + "symbol": "FDUSD", "decimals": 18 }, { @@ -85,33 +79,33 @@ "decimals": 9 }, { - "token_address": "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409", - "name": "First Digital USD", - "symbol": "FDUSD", + "token_address": "0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5", + "name": "Hay Destablecoin", + "symbol": "HAY", "decimals": 18 }, { - "token_address": "0x541B5eEAC7D4434C8f87e2d32019d67611179606", - "name": "PT SolvBTC Babylon 27MAR2025", - "symbol": "PT-SolvBTC.BBN-27MAR2025", + "token_address": "0xCa6d678e74f553f0E59cccC03ae644a3c2c5EE7d", + "name": "PLANET", + "symbol": "PLANET", "decimals": 18 }, { - "token_address": "0x53E63a31fD1077f949204b94F431bCaB98F72BCE", - "name": "SolvBTC Ethena", - "symbol": "SolvBTC.ENA", + "token_address": "0x12BB890508c125661E03b09EC06E404bc9289040", + "name": "Radio Caca V2", + "symbol": "RACA", "decimals": 18 }, { - "token_address": "0x1346b618dC92810EC74163e4c27004c921D446a5", - "name": "SolvBTC Babylon", - "symbol": "SolvBTC.BBN", + "token_address": "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", + "name": "Synclub Staked BNB", + "symbol": "SnBNB", "decimals": 18 }, { - "token_address": "0xaeF0d72a118ce24feE3cD1d43d383897D05B4e99", - "name": "WINk", - "symbol": "WIN", + "token_address": "0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16", + "name": "Staked BNB", + "symbol": "stkBNB", "decimals": 18 }, { @@ -121,150 +115,115 @@ "decimals": 6 }, { - "token_address": "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C", - "name": "Wrapped liquid staked Ether 2.0", - "symbol": "wstETH", - "decimals": 18 - }, - { - "token_address": "0xCa6d678e74f553f0E59cccC03ae644a3c2c5EE7d", - "name": "PLANET", - "symbol": "PLANET", - "decimals": 18 - }, - { - "token_address": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", - "name": "Wrapped BNB", - "symbol": "WBNB", - "decimals": 18 - }, - { - "token_address": "0x965F527D9159dCe6288a2219DB51fc6Eef120dD1", - "name": "Biswap", - "symbol": "BSW", - "decimals": 18 - }, - { - "token_address": "0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F", - "name": "AlpacaToken", - "symbol": "ALPACA", + "token_address": "0x4B0F1812e5Df2A09796481Ff14017e6005508003", + "name": "Trust Wallet", + "symbol": "TWT", "decimals": 18 }, { - "token_address": "0xf307910A4c7bbc79691fD374889b36d8531B08e3", - "name": "Ankr", - "symbol": "ANKR", + "token_address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", + "name": "USD Coin", + "symbol": "USDC", "decimals": 18 }, { - "token_address": "0xd17479997F34dd9156Deef8F95A52D81D265be9c", - "name": "Decentralized USD", - "symbol": "USDD", + "token_address": "0x55d398326f99059fF775485246999027B3197955", + "name": "Tether USD", + "symbol": "USDT", "decimals": 18 }, { - "token_address": "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", - "name": "Staked Lista BNB", - "symbol": "slisBNB", + "token_address": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "name": "Wrapped BNB", + "symbol": "WBNB", "decimals": 18 }, { - "token_address": "0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827", - "name": "Ankr Staked BNB", - "symbol": "ankrBNB", + "token_address": "0xaeF0d72a118ce24feE3cD1d43d383897D05B4e99", + "name": "WINk", + "symbol": "WIN", "decimals": 18 }, { - "token_address": "0xc748673057861a797275CD8A068AbB95A902e8de", - "name": "Baby Doge Coin", - "symbol": "BabyDoge", - "decimals": 9 - }, - { - "token_address": "0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A", - "name": "Wrapped eETH", - "symbol": "weETH", + "token_address": "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C", + "name": "Wrapped liquid staked Ether 2.0", + "symbol": "wstETH", "decimals": 18 } ], "protocols": [ { - "address": "0x1b43ea8622e76627B81665B1eCeBB4867566B963", - "name": "venus_isolated_pool", + "address": "0x6807dc923806fE8Fd134338EABCA509979a7e0cB", + "name": "aave_v3", "source": true, "destination": true, "tokens": [ - "0x12BB890508c125661E03b09EC06E404bc9289040", - "0xfb5B838b6cfEEdC2873aB27866079AC55363D37E", + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", "0x55d398326f99059fF775485246999027B3197955", - "0xd17479997F34dd9156Deef8F95A52D81D265be9c" + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", + "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409" ], "type": "Loan" }, { - "address": "0xd933909A4a2b7A4638903028f44D1d38ce27c352", - "name": "venus_isolated_pool", + "address": "0xf9278C7c4AEfAC4dDfd0D496f7a1C39cA6BCA6d4", + "name": "avalon_finance", "source": true, "destination": true, "tokens": [ - "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", - "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", - "0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827", - "0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275", - "0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16" + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + "0x55d398326f99059fF775485246999027B3197955", + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", + "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c" ], "type": "Loan" }, { - "address": "0x23b4404E4E5eC5FF5a6FFb70B7d14E3FabF237B0", - "name": "venus_isolated_pool", + "address": "0xa2E3356610840701BDf5611a53974510Ae27E2e1", + "name": "binance_staked_eth", "source": true, "destination": true, "tokens": [ - "0x352Cb5E19b12FC216548a2677bD0fce83BaE434B", - "0xaeF0d72a118ce24feE3cD1d43d383897D05B4e99", - "0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3", - "0x55d398326f99059fF775485246999027B3197955", - "0xd17479997F34dd9156Deef8F95A52D81D265be9c" + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8" ], - "type": "Loan" + "type": "Stake" }, { - "address": "0x33B6fa34cd23e5aeeD1B112d5988B026b8A5567d", - "name": "venus_isolated_pool", + "address": "0x1adB950d8bB3dA4bE104211D5AB038628e477fE6", + "name": "lista_dao", "source": true, "destination": true, "tokens": [ - "0xc748673057861a797275CD8A068AbB95A902e8de", - "0x55d398326f99059fF775485246999027B3197955" + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" ], - "type": "Loan" + "type": "Stake" }, { - "address": "0xBE609449Eb4D76AD8545f957bBE04b596E8fC529", + "address": "0x3344417c9360b963ca93A4e8305361AEde340Ab9", "name": "venus_isolated_pool", "source": true, "destination": true, "tokens": [ - "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C", - "0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A", - "0x2170Ed0880ac9A755fd29B2688956BD959F933F8" + "0x4B0F1812e5Df2A09796481Ff14017e6005508003", + "0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827", + "0x55d398326f99059fF775485246999027B3197955", + "0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F", + "0x965F527D9159dCe6288a2219DB51fc6Eef120dD1", + "0xCa6d678e74f553f0E59cccC03ae644a3c2c5EE7d", + "0xf307910A4c7bbc79691fD374889b36d8531B08e3" ], "type": "Loan" }, { - "address": "0x6807dc923806fE8Fd134338EABCA509979a7e0cB", - "name": "aave_v3", + "address": "0x9DF11376Cf28867E2B0741348044780FbB7cb1d6", + "name": "venus_isolated_pool", "source": true, "destination": true, "tokens": [ - "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", - "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", - "0x55d398326f99059fF775485246999027B3197955", - "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409", - "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", - "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c" + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c" ], "type": "Loan" }, @@ -274,77 +233,73 @@ "source": true, "destination": true, "tokens": [ - "0x12f31B73D812C6Bb0d735a218c086d44D5fe5f89", "0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5", - "0x55d398326f99059fF775485246999027B3197955", - "0xd17479997F34dd9156Deef8F95A52D81D265be9c" + "0x12f31B73D812C6Bb0d735a218c086d44D5fe5f89", + "0x55d398326f99059fF775485246999027B3197955" ], "type": "Loan" }, { - "address": "0xa2E3356610840701BDf5611a53974510Ae27E2e1", - "name": "binance_staked_eth", + "address": "0x1b43ea8622e76627B81665B1eCeBB4867566B963", + "name": "venus_isolated_pool", "source": true, "destination": true, "tokens": [ - "0x2170Ed0880ac9A755fd29B2688956BD959F933F8" + "0x12BB890508c125661E03b09EC06E404bc9289040", + "0x55d398326f99059fF775485246999027B3197955", + "0xfb5B838b6cfEEdC2873aB27866079AC55363D37E" ], - "type": "Stake" + "type": "Loan" }, { - "address": "0x3344417c9360b963ca93A4e8305361AEde340Ab9", + "address": "0xd933909A4a2b7A4638903028f44D1d38ce27c352", "name": "venus_isolated_pool", "source": true, "destination": true, "tokens": [ + "0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275", "0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827", - "0x965F527D9159dCe6288a2219DB51fc6Eef120dD1", - "0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F", - "0x55d398326f99059fF775485246999027B3197955", - "0xd17479997F34dd9156Deef8F95A52D81D265be9c", - "0xf307910A4c7bbc79691fD374889b36d8531B08e3", - "0x4B0F1812e5Df2A09796481Ff14017e6005508003", - "0xCa6d678e74f553f0E59cccC03ae644a3c2c5EE7d" + "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16" ], "type": "Loan" }, { - "address": "0x9DF11376Cf28867E2B0741348044780FbB7cb1d6", + "address": "0x23b4404E4E5eC5FF5a6FFb70B7d14E3FabF237B0", "name": "venus_isolated_pool", "source": true, "destination": true, "tokens": [ - "0x541B5eEAC7D4434C8f87e2d32019d67611179606", - "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c" + "0x352Cb5E19b12FC216548a2677bD0fce83BaE434B", + "0x55d398326f99059fF775485246999027B3197955", + "0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3", + "0xaeF0d72a118ce24feE3cD1d43d383897D05B4e99" ], "type": "Loan" }, { - "address": "0xf9278C7c4AEfAC4dDfd0D496f7a1C39cA6BCA6d4", - "name": "avalon_finance", + "address": "0x33B6fa34cd23e5aeeD1B112d5988B026b8A5567d", + "name": "venus_isolated_pool", "source": true, "destination": true, "tokens": [ - "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", "0x55d398326f99059fF775485246999027B3197955", - "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", - "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", - "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", - "0x53E63a31fD1077f949204b94F431bCaB98F72BCE", - "0x4aae823a6a0b376De6A78e74eCC5b079d38cBCf7", - "0x1346b618dC92810EC74163e4c27004c921D446a5" + "0xc748673057861a797275CD8A068AbB95A902e8de" ], "type": "Loan" }, { - "address": "0x1adB950d8bB3dA4bE104211D5AB038628e477fE6", - "name": "lista_dao", + "address": "0xBE609449Eb4D76AD8545f957bBE04b596E8fC529", + "name": "venus_isolated_pool", "source": true, "destination": true, "tokens": [ - "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C" ], - "type": "Stake" + "type": "Loan" } ] } \ No newline at end of file diff --git a/tokens/token_impl.go b/tokens/token_impl.go index 635c42b..f5d660e 100644 --- a/tokens/token_impl.go +++ b/tokens/token_impl.go @@ -7,6 +7,7 @@ import ( "math/big" "github.com/blndgs/protocol_registry/pkg" + "github.com/ethereum/go-ethereum/common" ) //go:embed *.json @@ -80,8 +81,9 @@ func (r *JSONTokenRegistry) GetTokenByAddress(chainID *big.Int, address string) return nil, fmt.Errorf("no data available for chain ID %d", chainID) } + addr := common.HexToAddress(address).Hex() for _, token := range data.Tokens { - if token.TokenAddress == address { + if token.TokenAddress == addr { return &token, nil } } @@ -98,8 +100,10 @@ func (r *JSONTokenRegistry) GetProtocolByAddress(chainID *big.Int, address strin return nil, fmt.Errorf("no data available for chain ID %d", chainID) } + addr := common.HexToAddress(address).Hex() + for _, protocol := range data.Protocols { - if protocol.Address == address { + if protocol.Address == addr { return &protocol, nil } } diff --git a/tokens/token_impl_test.go b/tokens/token_impl_test.go index a641e6f..0e6f8ad 100644 --- a/tokens/token_impl_test.go +++ b/tokens/token_impl_test.go @@ -71,9 +71,9 @@ func TestGetTokens(t *testing.T) { want int wantErr bool }{ - {"Ethereum chain", pkg.EthChainID, 17, false}, - {"BSC chain", pkg.BscChainID, 9, false}, - {"Polyhon chain", pkg.PolygonChainID, 12, false}, + {"Ethereum chain", pkg.EthChainID, 22, false}, + {"BSC chain", pkg.BscChainID, 25, false}, + {"Polygon chain", pkg.PolygonChainID, 14, false}, {"Unknown chain", big.NewInt(999), 0, true}, } @@ -101,8 +101,8 @@ func TestGetProtocols(t *testing.T) { wantErr bool }{ {"Ethereum chain", pkg.EthChainID, 7, false}, - {"BSC chain", pkg.BscChainID, 4, false}, - {"Polygon chain", pkg.PolygonChainID, 1, false}, + {"BSC chain", pkg.BscChainID, 12, false}, + {"Polygon chain", pkg.PolygonChainID, 3, false}, {"Unknown chain", big.NewInt(999), 0, true}, } @@ -161,9 +161,9 @@ func TestGetProtocolByAddress(t *testing.T) { want string wantErr bool }{ - {"Ethereum AaveV3", pkg.EthChainID, "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", "AaveV3", false}, - {"BSC AaveV3", pkg.BscChainID, "0x6807dc923806fE8Fd134338EABCA509979a7e0cB", "AaveV3", false}, - {"Polygon AaveV3", pkg.PolygonChainID, "0x794a61358D6845594F94dc1DB02A252b5b4814aD", "AaveV3", false}, + {"Ethereum AaveV3", pkg.EthChainID, "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", "aave_v3", false}, + {"BSC AaveV3", pkg.BscChainID, "0x6807dc923806fE8Fd134338EABCA509979a7e0cB", "aave_v3", false}, + {"Polygon AaveV3", pkg.PolygonChainID, "0x794a61358D6845594F94dc1DB02A252b5b4814aD", "aave_v3", false}, {"Unknown protocol", pkg.EthChainID, "0x1234567890123456789012345678901234567890", "", true}, {"Unknown chain", big.NewInt(999), "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", "", true}, } diff --git a/tools/tokens.go b/tools/tokens.go index 2d709e2..e0d4c04 100644 --- a/tools/tokens.go +++ b/tools/tokens.go @@ -4,134 +4,169 @@ import ( "context" "encoding/json" "fmt" + "io" "math/big" + "net/http" "os" + "sort" "strings" "github.com/blndgs/protocol_registry/pkg" "github.com/blndgs/protocol_registry/tokens" - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethclient" + "github.com/joho/godotenv" ) const ( - erc20ABI = `[ - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [{"name": "", "type": "string"}], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [{"name": "", "type": "string"}], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [{"name": "", "type": "uint8"}], - "payable": false, - "stateMutability": "view", - "type": "function" - } -]` nativeTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - mkrTokenAddress = "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2" ) -func getTokenMetadata(ctx context.Context, client *ethclient.Client, tokenAddr common.Address, chainID *big.Int) (string, string, int, error) { - // Special handling for MKR token on Ethereum - if chainID.Cmp(big.NewInt(1)) == 0 && strings.EqualFold(tokenAddr.Hex(), mkrTokenAddress) { - return "Maker", "MKR", 18, nil +type MoralisResponse struct { + TokenName string `json:"tokenName"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` +} + +func getTokenMetadata(ctx context.Context, tokenAddr string, chainID *big.Int) (string, string, int, error) { + + var moralisAPIKey = os.Getenv("MORALIS_API_KEY") + if moralisAPIKey == "" { + panic("provide moralis api key in .env") } - parsedABI, err := abi.JSON(strings.NewReader(erc20ABI)) - if err != nil { - return "", "", 0, fmt.Errorf("failed to parse ABI: %v", err) + var chainStr string + switch chainID.Int64() { + case 1: + chainStr = "eth" + case 56: + chainStr = "bsc" + case 137: + chainStr = "polygon" + default: + return "", "", 0, fmt.Errorf("unsupported chain ID: %d", chainID) } - // Get name - nameData, err := client.CallContract(ctx, ethereum.CallMsg{ - To: &tokenAddr, - Data: parsedABI.Methods["name"].ID, - }, nil) + url := fmt.Sprintf("https://deep-index.moralis.io/api/v2.2/erc20/%s/price?chain=%s&include=percent_change", tokenAddr, chainStr) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { - return "", "", 0, fmt.Errorf("failed to get name: %v", err) + return "", "", 0, fmt.Errorf("failed to create request: %v", err) } - var name string - err = parsedABI.UnpackIntoInterface(&name, "name", nameData) + + req.Header.Add("Accept", "application/json") + req.Header.Add("X-API-Key", moralisAPIKey) + + client := &http.Client{} + resp, err := client.Do(req) if err != nil { - return "", "", 0, fmt.Errorf("failed to unpack name: %v", err) + return "", "", 0, fmt.Errorf("failed to make request: %v", err) } + defer resp.Body.Close() - // Get symbol - symbolData, err := client.CallContract(ctx, ethereum.CallMsg{ - To: &tokenAddr, - Data: parsedABI.Methods["symbol"].ID, - }, nil) - if err != nil { - return "", "", 0, fmt.Errorf("failed to get symbol: %v", err) + // Check if response status is greater than 201 + if resp.StatusCode > 201 { + return "", "", 0, fmt.Errorf("invalid token or API error: status %d", resp.StatusCode) } - var symbol string - err = parsedABI.UnpackIntoInterface(&symbol, "symbol", symbolData) + + body, err := io.ReadAll(resp.Body) if err != nil { - return "", "", 0, fmt.Errorf("failed to unpack symbol: %v", err) + return "", "", 0, fmt.Errorf("failed to read response body: %v", err) } - // Get decimals - decimalsData, err := client.CallContract(ctx, ethereum.CallMsg{ - To: &tokenAddr, - Data: parsedABI.Methods["decimals"].ID, - }, nil) + var result map[string]interface{} + err = json.Unmarshal(body, &result) if err != nil { - return "", "", 0, fmt.Errorf("failed to get decimals: %v", err) + return "", "", 0, fmt.Errorf("failed to parse response: %v", err) } - var decimals uint8 - err = parsedABI.UnpackIntoInterface(&decimals, "decimals", decimalsData) + + name, ok := result["tokenName"].(string) + if !ok { + return "", "", 0, fmt.Errorf("token name not found") + } + + symbol, ok := result["tokenSymbol"].(string) + if !ok { + return "", "", 0, fmt.Errorf("token symbol not found") + } + + decimalsStr, ok := result["tokenDecimals"].(string) + if !ok { + return "", "", 0, fmt.Errorf("token decimals not found") + } + + var decimals int + _, err = fmt.Sscanf(decimalsStr, "%d", &decimals) if err != nil { - return "", "", 0, fmt.Errorf("failed to unpack decimals: %v", err) + return "", "", 0, fmt.Errorf("failed to parse decimals: %v", err) } - return name, symbol, int(decimals), nil + return name, symbol, decimals, nil +} + +// sortProtocolData sorts both tokens and protocols arrays in the protocol data +func sortProtocolData(data *tokens.Data) { + // Sort tokens by symbol + sort.Slice(data.Tokens, func(i, j int) bool { + return strings.ToLower(data.Tokens[i].Symbol) < strings.ToLower(data.Tokens[j].Symbol) + }) + + // Sort protocols by name + sort.Slice(data.Protocols, func(i, j int) bool { + return strings.ToLower(data.Protocols[i].Name) < strings.ToLower(data.Protocols[j].Name) + }) + + // Deduplicate and sort token arrays within each protocol + for i := range data.Protocols { + // Use a map to deduplicate tokens + tokenMap := make(map[string]bool) + var uniqueTokens []string + + for _, token := range data.Protocols[i].Tokens { + if !tokenMap[token] { + tokenMap[token] = true + uniqueTokens = append(uniqueTokens, token) + } + } + + // Sort the unique tokens + sort.Strings(uniqueTokens) + + // Replace the original tokens array with the sorted, unique tokens + data.Protocols[i].Tokens = uniqueTokens + } } func main() { + + if err := godotenv.Load(); err != nil { + fmt.Println("add an .env file with MORALIS_API_KEY=xyz") + panic(err) + } + chains := []struct { Name string ChainID *big.Int - RPCURL string + MoralisChain string NativeSymbol string NativeName string }{ { Name: "Ethereum", ChainID: big.NewInt(1), - RPCURL: "https://rpc.ankr.com/eth", + MoralisChain: "eth", NativeSymbol: "ETH", NativeName: "Ethereum", }, { Name: "BNB Chain", ChainID: big.NewInt(56), - RPCURL: "https://rpc.ankr.com/bsc", + MoralisChain: "bsc", NativeSymbol: "BNB", NativeName: "Binance Coin", }, { Name: "Polygon", ChainID: big.NewInt(137), - RPCURL: "https://rpc.ankr.com/polygon", + MoralisChain: "polygon", NativeSymbol: "MATIC", NativeName: "Polygon", }, @@ -140,15 +175,15 @@ func main() { registry, err := pkg.NewProtocolRegistry([]pkg.ChainConfig{ { ChainID: big.NewInt(1), - RPCURL: chains[0].RPCURL, + RPCURL: "https://rpc.ankr.com/eth", }, { ChainID: big.NewInt(56), - RPCURL: chains[1].RPCURL, + RPCURL: "https://rpc.ankr.com/bsc", }, { ChainID: big.NewInt(137), - RPCURL: chains[2].RPCURL, + RPCURL: "https://rpc.ankr.com/polygon", }, }) @@ -161,44 +196,68 @@ func main() { for _, chain := range chains { fmt.Println("updating file for", chain.Name) - client, err := ethclient.Dial(chain.RPCURL) - if err != nil { - panic(fmt.Sprintf("error connecting to %s: %v", chain.Name, err)) - } - defer client.Close() - var protocolData tokens.Data uniqueTokens := make(map[string]bool) + validTokens := make(map[string]bool) protocols := registry.ListProtocols(chain.ChainID) - // Convert protocols to the format expected by tokens.Data + // First, collect all unique tokens and validate them for _, p := range protocols { supportedAssets, err := p.GetSupportedAssets(ctx, chain.ChainID) if err != nil { panic(fmt.Sprintf("error getting supported assets for protocol %s on chain %s: %v", p.GetName(), chain.Name, err)) } - // Convert addresses to strings and collect unique tokens - tokenAddrs := make([]string, len(supportedAssets)) - for i, addr := range supportedAssets { + for _, addr := range supportedAssets { addrStr := addr.Hex() - tokenAddrs[i] = addrStr - uniqueTokens[addrStr] = true + if !uniqueTokens[addrStr] { + uniqueTokens[addrStr] = true + + // Skip validation for native token + if strings.EqualFold(addrStr, nativeTokenAddress) { + validTokens[addrStr] = true + continue + } + + // Validate token using Moralis + _, _, _, err := getTokenMetadata(ctx, addrStr, chain.ChainID) + if err == nil { + validTokens[addrStr] = true + } else { + fmt.Printf("Skipping invalid token %s on chain %s: %v\n", addrStr, chain.Name, err) + } + } } + } - protocolData.Protocols = append(protocolData.Protocols, tokens.Protocol{ - Address: p.GetContractAddress(chain.ChainID).Hex(), - Name: p.GetName(), - Type: string(p.GetType()), - Source: true, - Destination: true, - Tokens: tokenAddrs, - }) + // Now add protocols, but only include valid tokens + for _, p := range protocols { + supportedAssets, _ := p.GetSupportedAssets(ctx, chain.ChainID) + var validTokenAddrs []string + + for _, addr := range supportedAssets { + addrStr := addr.Hex() + if validTokens[addrStr] { + validTokenAddrs = append(validTokenAddrs, addrStr) + } + } + + // Only add protocol if it has valid tokens + if len(validTokenAddrs) > 0 { + protocolData.Protocols = append(protocolData.Protocols, tokens.Protocol{ + Address: p.GetContractAddress(chain.ChainID).Hex(), + Name: p.GetName(), + Type: string(p.GetType()), + Source: true, + Destination: true, + Tokens: validTokenAddrs, + }) + } } - // Convert unique tokens to array and fetch metadata - for addr := range uniqueTokens { + // Add valid tokens to the tokens array + for addr := range validTokens { var token tokens.Token if strings.EqualFold(addr, nativeTokenAddress) { @@ -211,10 +270,10 @@ func main() { } } else { // Handle ERC20 token - tokenAddr := common.HexToAddress(addr) - name, symbol, decimals, err := getTokenMetadata(ctx, client, tokenAddr, chain.ChainID) + name, symbol, decimals, err := getTokenMetadata(ctx, addr, chain.ChainID) if err != nil { - panic(fmt.Sprintf("error getting metadata for token %s on chain %s: %v", addr, chain.Name, err)) + // This shouldn't happen as we already validated the token + continue } token = tokens.Token{ @@ -228,6 +287,9 @@ func main() { protocolData.Tokens = append(protocolData.Tokens, token) } + // Sort the protocol data + sortProtocolData(&protocolData) + // Write the data to the corresponding chain file fileName := fmt.Sprintf("tokens/%d.json", chain.ChainID) data, err := json.MarshalIndent(protocolData, "", " ")