package gateway import ( "context" "errors" "strings" "time" "github.com/ethereum/go-ethereum/core/types" "github.com/tech/sendico/gateway/tron/internal/service/gateway/driver" "github.com/tech/sendico/gateway/tron/shared" "github.com/tech/sendico/gateway/tron/storage/model" "github.com/tech/sendico/pkg/merrors" "go.uber.org/zap" ) func (s *Service) launchTransferExecution(transferRef, sourceWalletRef string, network shared.Network) { if s.drivers == nil { return } go func(ref, walletRef string, net shared.Network) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) defer cancel() if err := s.executeTransfer(ctx, ref, walletRef, net); err != nil { s.logger.Warn("Failed to execute transfer", zap.String("transfer_ref", ref), zap.Error(err)) } }(transferRef, sourceWalletRef, network) } func (s *Service) executeTransfer(ctx context.Context, transferRef, sourceWalletRef string, network shared.Network) error { transfer, err := s.storage.Transfers().Get(ctx, transferRef) if err != nil { return err } sourceWallet, err := s.storage.Wallets().Get(ctx, sourceWalletRef) if err != nil { return err } if _, err := s.updateTransferStatus(ctx, transferRef, model.TransferStatusProcessing, "", ""); err != nil { s.logger.Warn("Failed to update transfer status to signing", zap.String("transfer_ref", transferRef), zap.Error(err)) } driverDeps := s.driverDeps() chainDriver, err := s.driverForNetwork(network.Name.String()) if err != nil { _, _ = s.updateTransferStatus(ctx, transferRef, model.TransferStatusFailed, err.Error(), "") return err } destinationAddress, err := s.destinationAddress(ctx, chainDriver, transfer.Destination) if err != nil { _, _ = s.updateTransferStatus(ctx, transferRef, model.TransferStatusFailed, err.Error(), "") return err } sourceAddress, err := chainDriver.NormalizeAddress(sourceWallet.DepositAddress) if err != nil { _, _ = s.updateTransferStatus(ctx, transferRef, model.TransferStatusFailed, err.Error(), "") return err } if chainDriver.Name() == "tron" && sourceAddress == destinationAddress { s.logger.Info("Self transfer detected; skipping submission", zap.String("transfer_ref", transferRef), zap.String("wallet_ref", sourceWalletRef), zap.String("network", network.Name.String()), ) if _, err := s.updateTransferStatus(ctx, transferRef, model.TransferStatusSuccess, "", ""); err != nil { s.logger.Warn("Failed to update transfer status to confirmed", zap.String("transfer_ref", transferRef), zap.Error(err)) } return nil } txHash, err := chainDriver.SubmitTransfer(ctx, driverDeps, network, transfer, sourceWallet, destinationAddress) if err != nil { s.logger.Warn("Failed to submit transfer", zap.String("transfer_ref", transferRef), zap.Error(err)) if _, e := s.updateTransferStatus(ctx, transferRef, model.TransferStatusFailed, err.Error(), ""); e != nil { s.logger.Warn("Failed to update transfer status to failed", zap.String("transfer_ref", transferRef), zap.Error(e)) } return err } if _, err := s.updateTransferStatus(ctx, transferRef, model.TransferStatusWaiting, "", txHash); err != nil { s.logger.Warn("Failed to update transfer status to submitted", zap.String("transfer_ref", transferRef), zap.Error(err)) } receiptCtx, cancel := context.WithTimeout(ctx, 10*time.Minute) defer cancel() receipt, err := chainDriver.AwaitConfirmation(receiptCtx, driverDeps, network, txHash) if err != nil { if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) { s.logger.Warn("Failed to await transfer confirmation", zap.String("transfer_ref", transferRef), zap.Error(err)) } return err } failureReason := "" pStatus := model.TransferStatusSuccess if receipt != nil && receipt.Status != types.ReceiptStatusSuccessful { failureReason = "transaction reverted" pStatus = model.TransferStatusFailed } if _, err := s.updateTransferStatus(ctx, transferRef, pStatus, failureReason, txHash); err != nil { s.logger.Warn("Failed to update transfer status", zap.Error(err), zap.String("transfer_ref", transferRef), zap.String("status", string(pStatus))) } return nil } func (s *Service) destinationAddress(ctx context.Context, chainDriver driver.Driver, dest model.TransferDestination) (string, error) { if ref := strings.TrimSpace(dest.ManagedWalletRef); ref != "" { wallet, err := s.storage.Wallets().Get(ctx, ref) if err != nil { return "", err } if strings.TrimSpace(wallet.DepositAddress) == "" { return "", merrors.Internal("destination wallet missing deposit address") } return chainDriver.NormalizeAddress(wallet.DepositAddress) } if addr := strings.TrimSpace(dest.ExternalAddress); addr != "" { return chainDriver.NormalizeAddress(addr) } return "", merrors.InvalidArgument("transfer destination address not resolved") } func (s *Service) driverDeps() driver.Deps { return driver.Deps{ Logger: s.logger.Named("driver"), Registry: s.networkRegistry, TronRegistry: s.tronClients, KeyManager: s.keyManager, RPCTimeout: s.settings.rpcTimeout(), } } func (s *Service) driverForNetwork(network string) (driver.Driver, error) { if s.drivers == nil { return nil, merrors.Internal("chain drivers not configured") } return s.drivers.Driver(network) }