256 lines
9.0 KiB
C#
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 "";
|
|
}
|
|
}
|
|
}
|