diff --git a/api/gateway/chain/config.yml b/api/gateway/chain/config.yml index 16df91c..241259e 100644 --- a/api/gateway/chain/config.yml +++ b/api/gateway/chain/config.yml @@ -34,8 +34,8 @@ messaging: reconnect_wait: 5 chains: - - name: tron - chain_id: 728126428 + - name: tron_mainnet + chain_id: 728126428 # 0x2b6653dc native_token: TRX rpc_url_env: CHAIN_GATEWAY_RPC_URL tokens: @@ -45,7 +45,7 @@ chains: contract: "0x3487b63d30b5b2c87fb7ffa8bcfade38eaac1abe" service_wallet: - chain: tron + chain: tron_mainnet address_env: CHAIN_GATEWAY_SERVICE_WALLET_ADDRESS private_key_env: CHAIN_GATEWAY_SERVICE_WALLET_KEY diff --git a/api/gateway/chain/internal/server/internal/serverimp.go b/api/gateway/chain/internal/server/internal/serverimp.go index 5e99215..b6c6483 100644 --- a/api/gateway/chain/internal/server/internal/serverimp.go +++ b/api/gateway/chain/internal/server/internal/serverimp.go @@ -2,6 +2,7 @@ package serverimp import ( "context" + "fmt" "os" "strings" "time" @@ -98,7 +99,11 @@ func (i *Imp) Start() error { } cl := i.logger.Named("config") - networkConfigs := resolveNetworkConfigs(cl.Named("network"), cfg.Chains) + networkConfigs, err := resolveNetworkConfigs(cl.Named("network"), cfg.Chains) + if err != nil { + i.logger.Error("invalid chain network configuration", zap.Error(err)) + return err + } walletConfig := resolveServiceWallet(cl.Named("wallet"), cfg.ServiceWallet) keyManager, err := resolveKeyManager(i.logger.Named("key_manager"), cfg.KeyManagement) if err != nil { @@ -157,7 +162,7 @@ func (i *Imp) loadConfig() (*config, error) { return cfg, nil } -func resolveNetworkConfigs(logger mlogger.Logger, chains []chainConfig) []gatewayshared.Network { +func resolveNetworkConfigs(logger mlogger.Logger, chains []chainConfig) ([]gatewayshared.Network, error) { result := make([]gatewayshared.Network, 0, len(chains)) for _, chain := range chains { if strings.TrimSpace(chain.Name) == "" { @@ -166,7 +171,8 @@ func resolveNetworkConfigs(logger mlogger.Logger, chains []chainConfig) []gatewa } rpcURL := strings.TrimSpace(os.Getenv(chain.RPCURLEnv)) if rpcURL == "" { - logger.Warn("chain RPC endpoint not configured", zap.String("chain", chain.Name), zap.String("env", chain.RPCURLEnv)) + logger.Error("RPC url not configured", zap.String("chain", chain.Name), zap.String("env", chain.RPCURLEnv)) + return nil, merrors.InvalidArgument(fmt.Sprintf("chain RPC endpoint not configured (chain=%s env=%s)", chain.Name, chain.RPCURLEnv)) } contracts := make([]gatewayshared.TokenContract, 0, len(chain.Tokens)) for _, token := range chain.Tokens { @@ -202,7 +208,7 @@ func resolveNetworkConfigs(logger mlogger.Logger, chains []chainConfig) []gatewa TokenConfigs: contracts, }) } - return result + return result, nil } func resolveServiceWallet(logger mlogger.Logger, cfg serviceWalletConfig) gatewayshared.ServiceWallet { diff --git a/api/gateway/chain/internal/service/gateway/commands/wallet/deps.go b/api/gateway/chain/internal/service/gateway/commands/wallet/deps.go index 1747875..2f14ed5 100644 --- a/api/gateway/chain/internal/service/gateway/commands/wallet/deps.go +++ b/api/gateway/chain/internal/service/gateway/commands/wallet/deps.go @@ -22,8 +22,9 @@ type Deps struct { } func (d Deps) WithLogger(name string) Deps { - if d.Logger != nil { - d.Logger = d.Logger.Named(name) + if d.Logger == nil { + panic("wallet deps: logger is required") } + d.Logger = d.Logger.Named(name) return d } diff --git a/api/gateway/chain/internal/service/gateway/commands/wallet/onchain_balance.go b/api/gateway/chain/internal/service/gateway/commands/wallet/onchain_balance.go index 3233280..cb0dd48 100644 --- a/api/gateway/chain/internal/service/gateway/commands/wallet/onchain_balance.go +++ b/api/gateway/chain/internal/service/gateway/commands/wallet/onchain_balance.go @@ -34,34 +34,24 @@ func onChainWalletBalance(ctx context.Context, deps Deps, wallet *model.ManagedW } if rpcURL == "" { - if logger != nil { - logger.Warn("network rpc url is not configured", logFields...) - } + logger.Warn("network rpc url is not configured", logFields...) return nil, merrors.Internal("network rpc url is not configured") } contract := strings.TrimSpace(wallet.ContractAddress) if contract == "" || !common.IsHexAddress(contract) { - if logger != nil { - logger.Warn("invalid contract address for balance fetch", logFields...) - } + logger.Warn("invalid contract address for balance fetch", logFields...) return nil, merrors.InvalidArgument("invalid contract address") } if wallet.DepositAddress == "" || !common.IsHexAddress(wallet.DepositAddress) { - if logger != nil { - logger.Warn("invalid wallet address for balance fetch", logFields...) - } + logger.Warn("invalid wallet address for balance fetch", logFields...) return nil, merrors.InvalidArgument("invalid wallet address") } - if logger != nil { - logger.Info("fetching on-chain wallet balance", logFields...) - } + logger.Info("fetching on-chain wallet balance", logFields...) client, err := ethclient.DialContext(ctx, rpcURL) if err != nil { - if logger != nil { - logger.Warn("failed to connect rpc", append(logFields, zap.Error(err))...) - } + logger.Warn("failed to connect rpc", append(logFields, zap.Error(err))...) return nil, merrors.Internal("failed to connect rpc: " + err.Error()) } defer client.Close() @@ -71,46 +61,34 @@ func onChainWalletBalance(ctx context.Context, deps Deps, wallet *model.ManagedW tokenABI, err := abi.JSON(strings.NewReader(erc20ABIJSON)) if err != nil { - if logger != nil { - logger.Warn("failed to parse erc20 abi", append(logFields, zap.Error(err))...) - } + logger.Warn("failed to parse erc20 abi", append(logFields, zap.Error(err))...) return nil, merrors.Internal("failed to parse erc20 abi: " + err.Error()) } tokenAddr := common.HexToAddress(contract) walletAddr := common.HexToAddress(wallet.DepositAddress) - if logger != nil { - logger.Debug("calling token decimals", logFields...) - } + logger.Debug("calling token decimals", logFields...) decimals, err := readDecimals(timeoutCtx, client, tokenABI, tokenAddr) if err != nil { - if logger != nil { - logger.Warn("token decimals call failed", append(logFields, zap.Error(err))...) - } + logger.Warn("token decimals call failed", append(logFields, zap.Error(err))...) return nil, err } - if logger != nil { - logger.Debug("calling token balanceOf", append(logFields, zap.Uint8("decimals", decimals))...) - } + logger.Debug("calling token balanceOf", append(logFields, zap.Uint8("decimals", decimals))...) bal, err := readBalanceOf(timeoutCtx, client, tokenABI, tokenAddr, walletAddr) if err != nil { - if logger != nil { - logger.Warn("token balanceOf call failed", append(logFields, zap.Uint8("decimals", decimals), zap.Error(err))...) - } + logger.Warn("token balanceOf call failed", append(logFields, zap.Uint8("decimals", decimals), zap.Error(err))...) return nil, err } dec := decimal.NewFromBigInt(bal, 0).Shift(-int32(decimals)) - if logger != nil { - logger.Info("on-chain wallet balance fetched", - append(logFields, - zap.Uint8("decimals", decimals), - zap.String("balance_raw", bal.String()), - zap.String("balance", dec.String()), - )..., - ) - } + logger.Info("on-chain wallet balance fetched", + append(logFields, + zap.Uint8("decimals", decimals), + zap.String("balance_raw", bal.String()), + zap.String("balance", dec.String()), + )..., + ) return &moneyv1.Money{Currency: wallet.TokenSymbol, Amount: dec.String()}, nil } diff --git a/api/proto/gateway/chain/v1/chain.proto b/api/proto/gateway/chain/v1/chain.proto index 83fbd46..3fcfd85 100644 --- a/api/proto/gateway/chain/v1/chain.proto +++ b/api/proto/gateway/chain/v1/chain.proto @@ -15,6 +15,8 @@ enum ChainNetwork { CHAIN_NETWORK_ETHEREUM_MAINNET = 1; CHAIN_NETWORK_ARBITRUM_ONE = 2; CHAIN_NETWORK_OTHER_EVM = 3; + CHAIN_NETWORK_TRON_MAINNET = 4; + CHAIN_NETWORK_TRON_NILE = 5; } enum ManagedWalletStatus { diff --git a/api/server/config.yml b/api/server/config.yml index 5e6e66b..a3bff14 100755 --- a/api/server/config.yml +++ b/api/server/config.yml @@ -80,7 +80,7 @@ api: call_timeout_seconds: 5 insecure: true default_asset: - chain: ARBITRUM_ONE + chain: TRON_MAINNET token_symbol: USDT contract_address: "" ledger: diff --git a/api/server/interface/api/srequest/payment_enums.go b/api/server/interface/api/srequest/payment_enums.go index bc7aaf6..c61b556 100644 --- a/api/server/interface/api/srequest/payment_enums.go +++ b/api/server/interface/api/srequest/payment_enums.go @@ -37,6 +37,8 @@ const ( ChainNetworkEthereumMainnet ChainNetwork = "ethereum_mainnet" ChainNetworkArbitrumOne ChainNetwork = "arbitrum_one" ChainNetworkOtherEVM ChainNetwork = "other_evm" + ChainNetworkTronMainnet ChainNetwork = "tron_mainnet" + ChainNetworkTronNile ChainNetwork = "tron_nile" ) // InsufficientNetPolicy mirrors the fee engine policy override. diff --git a/api/server/internal/server/accountapiimp/service.go b/api/server/internal/server/accountapiimp/service.go index c48a4eb..d6a864e 100644 --- a/api/server/internal/server/accountapiimp/service.go +++ b/api/server/internal/server/accountapiimp/service.go @@ -216,6 +216,10 @@ func parseChainNetwork(value string) (chainv1.ChainNetwork, error) { return chainv1.ChainNetwork_CHAIN_NETWORK_ARBITRUM_ONE, nil case "OTHER_EVM", "CHAIN_NETWORK_OTHER_EVM": return chainv1.ChainNetwork_CHAIN_NETWORK_OTHER_EVM, nil + case "TRON_MAINNET", "CHAIN_NETWORK_TRON_MAINNET": + return chainv1.ChainNetwork_CHAIN_NETWORK_TRON_MAINNET, nil + case "TRON_NILE", "CHAIN_NETWORK_TRON_NILE": + return chainv1.ChainNetwork_CHAIN_NETWORK_TRON_NILE, nil case "", "CHAIN_NETWORK_UNSPECIFIED": return chainv1.ChainNetwork_CHAIN_NETWORK_UNSPECIFIED, merrors.InvalidArgument("chain network must be specified") default: diff --git a/api/server/internal/server/paymentapiimp/mapper.go b/api/server/internal/server/paymentapiimp/mapper.go index 010447a..8019ee4 100644 --- a/api/server/internal/server/paymentapiimp/mapper.go +++ b/api/server/internal/server/paymentapiimp/mapper.go @@ -288,6 +288,10 @@ func mapChainNetwork(chain srequest.ChainNetwork) (chainv1.ChainNetwork, error) return chainv1.ChainNetwork_CHAIN_NETWORK_ARBITRUM_ONE, nil case string(srequest.ChainNetworkOtherEVM): return chainv1.ChainNetwork_CHAIN_NETWORK_OTHER_EVM, nil + case string(srequest.ChainNetworkTronMainnet): + return chainv1.ChainNetwork_CHAIN_NETWORK_TRON_MAINNET, nil + case string(srequest.ChainNetworkTronNile): + return chainv1.ChainNetwork_CHAIN_NETWORK_TRON_NILE, nil default: return chainv1.ChainNetwork_CHAIN_NETWORK_UNSPECIFIED, merrors.InvalidArgument("unsupported chain network: " + string(chain)) } diff --git a/frontend/pshared/lib/api/responses/confirmation.dart b/frontend/pshared/lib/api/responses/confirmation.dart index ff5640e..01b3b96 100644 --- a/frontend/pshared/lib/api/responses/confirmation.dart +++ b/frontend/pshared/lib/api/responses/confirmation.dart @@ -2,6 +2,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'confirmation.g.dart'; + @JsonSerializable() class ConfirmationResponse { @JsonKey(name: 'ttl_seconds', defaultValue: 0) diff --git a/frontend/pshared/lib/data/mapper/payment/enums.dart b/frontend/pshared/lib/data/mapper/payment/enums.dart index 7502c22..a87728c 100644 --- a/frontend/pshared/lib/data/mapper/payment/enums.dart +++ b/frontend/pshared/lib/data/mapper/payment/enums.dart @@ -90,6 +90,10 @@ ChainNetwork chainNetworkFromValue(String? value) { return ChainNetwork.arbitrumOne; case 'other_evm': return ChainNetwork.otherEvm; + case 'tron_mainnet': + return ChainNetwork.tronMainnet; + case 'tron_nile': + return ChainNetwork.tronNile; case 'unspecified': return ChainNetwork.unspecified; default: @@ -105,6 +109,10 @@ String chainNetworkToValue(ChainNetwork chain) { return 'arbitrum_one'; case ChainNetwork.otherEvm: return 'other_evm'; + case ChainNetwork.tronMainnet: + return 'tron_mainnet'; + case ChainNetwork.tronNile: + return 'tron_nile'; case ChainNetwork.unspecified: return 'unspecified'; } diff --git a/frontend/pshared/lib/l10n/en.arb b/frontend/pshared/lib/l10n/en.arb index 2e39006..818d523 100644 --- a/frontend/pshared/lib/l10n/en.arb +++ b/frontend/pshared/lib/l10n/en.arb @@ -49,5 +49,15 @@ "chainNetworkOtherEvm": "Other EVM chain", "@chainNetworkOtherEvm": { "description": "Label for any other EVM-compatible network" + }, + + "chainNetworkTronMainnet": "Tron Mainnet", + "@chainNetworkTronMainnet": { + "description": "Label for the Tron mainnet network" + }, + + "chainNetworkTronNile": "Tron Nile (testnet)", + "@chainNetworkTronNile": { + "description": "Label for the Tron Nile testnet network" } } diff --git a/frontend/pshared/lib/l10n/ru.arb b/frontend/pshared/lib/l10n/ru.arb index 2a30a8b..99240d9 100644 --- a/frontend/pshared/lib/l10n/ru.arb +++ b/frontend/pshared/lib/l10n/ru.arb @@ -49,5 +49,15 @@ "chainNetworkOtherEvm": "Другая EVM сеть", "@chainNetworkOtherEvm": { "description": "Label for any other EVM-compatible network" + }, + + "chainNetworkTronMainnet": "Tron Mainnet", + "@chainNetworkTronMainnet": { + "description": "Label for the Tron mainnet network" + }, + + "chainNetworkTronNile": "Tron Nile (testnet)", + "@chainNetworkTronNile": { + "description": "Label for the Tron Nile testnet network" } } diff --git a/frontend/pshared/lib/models/payment/chain_network.dart b/frontend/pshared/lib/models/payment/chain_network.dart index e055d80..bdaa4a9 100644 --- a/frontend/pshared/lib/models/payment/chain_network.dart +++ b/frontend/pshared/lib/models/payment/chain_network.dart @@ -1 +1,8 @@ -enum ChainNetwork { unspecified, ethereumMainnet, arbitrumOne, otherEvm } +enum ChainNetwork { + unspecified, + ethereumMainnet, + arbitrumOne, + otherEvm, + tronMainnet, + tronNile +} diff --git a/frontend/pshared/lib/utils/l10n/chain.dart b/frontend/pshared/lib/utils/l10n/chain.dart index ff10fce..5ac174b 100644 --- a/frontend/pshared/lib/utils/l10n/chain.dart +++ b/frontend/pshared/lib/utils/l10n/chain.dart @@ -15,6 +15,10 @@ extension ChainNetworkL10n on ChainNetwork { return l10n.chainNetworkArbitrumOne; case ChainNetwork.otherEvm: return l10n.chainNetworkOtherEvm; + case ChainNetwork.tronMainnet: + return l10n.chainNetworkTronMainnet; + case ChainNetwork.tronNile: + return l10n.chainNetworkTronNile; case ChainNetwork.unspecified: return l10n.chainNetworkUnspecified; }