TRC20/WpfApp1TRC20/Services/TronService.cs
2025-07-25 16:05:44 +05:30

256 lines
9.0 KiB
C#

using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using TronNet;
using TronNet.Accounts;
using WpfApp1TRC20.Data;
using TransactionStatus = WpfApp1TRC20.Data.TransactionStatus;
namespace WpfApp1TRC20.Services
{
public class TronService : ITronService
{
private readonly ITronClient _tronClient;
private readonly ILogger<TronService> _logger;
private const string TRON_MAINNET = "https://api.trongrid.io";
private const string TRON_TESTNET = "https://api.shasta.trongrid.io";
public TronService(ILogger<TronService> logger)
{
_logger = logger;
// Use testnet for development, mainnet for production
var httpClient = new HttpClient();
_tronClient = new TronClient(httpClient, TRON_TESTNET);
}
public async Task<string> CreateTokenAsync(TokenCreationParams parameters, string ownerPrivateKey)
{
try
{
var account = new TronAccount(ownerPrivateKey);
// Compile and deploy the smart contract
var contractData = CompileContract(parameters);
var transaction = await _tronClient.CreateTransactionAsync(
account.Address,
contractData,
0,
"Smart Contract Creation"
);
var signedTransaction = transaction.Sign(account);
var result = await _tronClient.BroadcastTransactionAsync(signedTransaction);
_logger.LogInformation($"Token creation transaction broadcast: {result.Txid}");
return result.Txid;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating token");
throw;
}
}
public async Task<string> TransferTokenAsync(string contractAddress, string fromPrivateKey, string toAddress, decimal amount)
{
try
{
var fromAccount = new TronAccount(fromPrivateKey);
// Check if token is expired first
if (await IsTokenExpiredAsync(contractAddress))
{
throw new InvalidOperationException("Token has expired and cannot be transferred");
}
var contract = _tronClient.GetContract(contractAddress);
var transferFunction = contract.GetFunction("transfer");
var transaction = await transferFunction.CreateTransactionAsync(
fromAccount.Address,
toAddress,
(BigInteger)(amount * (decimal)Math.Pow(10, 18)) // Assuming 18 decimals
);
var signedTransaction = transaction.Sign(fromAccount);
var result = await _tronClient.BroadcastTransactionAsync(signedTransaction);
_logger.LogInformation($"Token transfer transaction broadcast: {result.Txid}");
return result.Txid;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error transferring tokens");
throw;
}
}
public async Task<decimal> GetTokenBalanceAsync(string contractAddress, string address)
{
try
{
var contract = _tronClient.GetContract(contractAddress);
var balanceFunction = contract.GetFunction("balanceOf");
var balance = await balanceFunction.CallAsync<BigInteger>(address);
return (decimal)balance / (decimal)Math.Pow(10, 18); // Assuming 18 decimals
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting token balance");
return 0;
}
}
public async Task<decimal> GetTrxBalanceAsync(string address)
{
try
{
var account = await _tronClient.GetAccountAsync(address);
return (decimal)account.Balance / 1_000_000; // TRX has 6 decimals
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting TRX balance");
return 0;
}
}
public async Task<TransactionRecord> GetTransactionAsync(string txHash)
{
try
{
var transaction = await _tronClient.GetTransactionByIdAsync(txHash);
var transactionInfo = await _tronClient.GetTransactionInfoByIdAsync(txHash);
return new TransactionRecord
{
TransactionHash = txHash,
Status = transactionInfo.Receipt.Result == "SUCCESS" ?
TransactionStatus.Confirmed : TransactionStatus.Failed,
Timestamp = DateTimeOffset.FromUnixTimeMilliseconds(transaction.RawData.Timestamp).DateTime,
GasFee = (decimal)transactionInfo.Fee / 1_000_000
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting transaction info");
throw;
}
}
public async Task<bool> IsTokenExpiredAsync(string contractAddress)
{
try
{
var contract = _tronClient.GetContract(contractAddress);
var isExpiredFunction = contract.GetFunction("isExpired");
var isExpired = await isExpiredFunction.CallAsync<bool>();
return isExpired;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking token expiry");
return false;
}
}
public WalletAccount CreateWallet(string name)
{
var account = TronAccount.GenerateAccount();
return new WalletAccount
{
Name = name,
Address = account.Address,
EncryptedPrivateKey = EncryptPrivateKey(account.PrivateKey),
CreatedDate = DateTime.UtcNow,
IsImported = false
};
}
public WalletAccount ImportWallet(string name, string privateKey)
{
try
{
var account = new TronAccount(privateKey);
return new WalletAccount
{
Name = name,
Address = account.Address,
EncryptedPrivateKey = EncryptPrivateKey(privateKey),
CreatedDate = DateTime.UtcNow,
IsImported = true
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error importing wallet");
throw new ArgumentException("Invalid private key");
}
}
public string DecryptPrivateKey(string encryptedPrivateKey)
{
try
{
var encryptedBytes = Convert.FromBase64String(encryptedPrivateKey);
var decryptedBytes = ProtectedData.Unprotect(
encryptedBytes,
null,
DataProtectionScope.CurrentUser
);
return Encoding.UTF8.GetString(decryptedBytes);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error decrypting private key");
throw;
}
}
private string EncryptPrivateKey(string privateKey)
{
var dataBytes = Encoding.UTF8.GetBytes(privateKey);
var encryptedBytes = ProtectedData.Protect(
dataBytes,
null,
DataProtectionScope.CurrentUser
);
return Convert.ToBase64String(encryptedBytes);
}
private string CompileContract(TokenCreationParams parameters)
{
// In a real implementation, you would use a Solidity compiler
// For now, we'll use pre-compiled bytecode with constructor parameters
// This is a simplified approach - in production, use tools like Nethereum.Contracts
var constructorParams = EncodeConstructorParameters(
parameters.Name,
parameters.Symbol,
(byte)parameters.Decimals,
parameters.TotalSupply,
parameters.ExpiryDays
);
return TRC20TokenManager.Contracts.TimedTRC20Contract.Bytecode + constructorParams;
}
private string EncodeConstructorParameters(string name, string symbol, byte decimals, decimal totalSupply, int expiryDays)
{
// Simplified ABI encoding - in production use proper ABI encoding library
// This would need to be implemented properly using ABI encoding standards
return "";
}
}
}