diff --git a/WpfApp1TRC20.sln b/WpfApp1TRC20.sln
new file mode 100644
index 0000000..a3d3a8b
--- /dev/null
+++ b/WpfApp1TRC20.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.6.33815.320
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApp1TRC20", "WpfApp1TRC20\WpfApp1TRC20.csproj", "{B5C8DC98-1B86-4718-B4D2-CE2EF908249E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B5C8DC98-1B86-4718-B4D2-CE2EF908249E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5C8DC98-1B86-4718-B4D2-CE2EF908249E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5C8DC98-1B86-4718-B4D2-CE2EF908249E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5C8DC98-1B86-4718-B4D2-CE2EF908249E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {CE428E59-6FEF-42AE-BF99-6493814BA044}
+ EndGlobalSection
+EndGlobal
diff --git a/WpfApp1TRC20/App.xaml b/WpfApp1TRC20/App.xaml
new file mode 100644
index 0000000..f012783
--- /dev/null
+++ b/WpfApp1TRC20/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WpfApp1TRC20/App.xaml.cs b/WpfApp1TRC20/App.xaml.cs
new file mode 100644
index 0000000..a773f64
--- /dev/null
+++ b/WpfApp1TRC20/App.xaml.cs
@@ -0,0 +1,71 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System.Windows;
+using Serilog;
+using System;
+using WpfApp1TRC20;
+using WpfApp1TRC20.Views;
+using WpfApp1TRC20.Data;
+using WpfApp1TRC20.Services;
+using WpfApp1TRC20.ViewModels;
+
+namespace WpfApp1TRC20
+{
+ public partial class App : Application
+ {
+ private IHost _host;
+
+ protected override async void OnStartup(StartupEventArgs e)
+ {
+ // Configure Serilog
+ Log.Logger = new LoggerConfiguration()
+ .WriteTo.File("logs/app-.txt", rollingInterval: RollingInterval.Day)
+ .CreateLogger();
+
+ _host = Host.CreateDefaultBuilder()
+ .ConfigureServices((context, services) =>
+ {
+ // Database
+ services.AddDbContext();
+
+ // Services
+ services.AddSingleton();
+ services.AddScoped();
+
+ // ViewModels
+ services.AddTransient();
+
+ // Views
+ services.AddTransient();
+
+ // Logging
+ services.AddLogging(a =>
+ a.AddProvider( ));
+ })
+ .Build();
+
+ await _host.StartAsync();
+
+ // Ensure database is created
+ using (var scope = _host.Services.CreateScope())
+ {
+ var context = scope.ServiceProvider.GetRequiredService();
+ await context.Database.EnsureCreatedAsync();
+ }
+
+ var mainWindow = _host.Services.GetRequiredService();
+ mainWindow.Show();
+
+ base.OnStartup(e);
+ }
+
+ protected override async void OnExit(ExitEventArgs e)
+ {
+ await _host?.StopAsync();
+ _host?.Dispose();
+ Log.CloseAndFlush();
+ base.OnExit(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WpfApp1TRC20/AssemblyInfo.cs b/WpfApp1TRC20/AssemblyInfo.cs
new file mode 100644
index 0000000..8b5504e
--- /dev/null
+++ b/WpfApp1TRC20/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/WpfApp1TRC20/Contract/Ganatch.cs b/WpfApp1TRC20/Contract/Ganatch.cs
new file mode 100644
index 0000000..518de05
--- /dev/null
+++ b/WpfApp1TRC20/Contract/Ganatch.cs
@@ -0,0 +1,116 @@
+namespace TRC20TokenManager.Contracts
+{
+ public static class TimedTRC20Contract
+ {
+ public const string Bytecode = @"
+pragma solidity ^0.8.0;
+
+interface IERC20 {
+ function totalSupply() external view returns (uint256);
+ function balanceOf(address account) external view returns (uint256);
+ function transfer(address recipient, uint256 amount) external returns (bool);
+ function allowance(address owner, address spender) external view returns (uint256);
+ function approve(address spender, uint256 amount) external returns (bool);
+ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
+}
+
+contract TimedTRC20 is IERC20 {
+ mapping(address => uint256) private _balances;
+ mapping(address => mapping(address => uint256)) private _allowances;
+
+ uint256 private _totalSupply;
+ string private _name;
+ string private _symbol;
+ uint8 private _decimals;
+ uint256 public immutable expiryTimestamp;
+ address private _owner;
+
+ event Transfer(address indexed from, address indexed to, uint256 value);
+ event Approval(address indexed owner, address indexed spender, uint256 value);
+
+ constructor(
+ string memory name_,
+ string memory symbol_,
+ uint8 decimals_,
+ uint256 totalSupply_,
+ uint256 expiryDays
+ ) {
+ _name = name_;
+ _symbol = symbol_;
+ _decimals = decimals_;
+ _totalSupply = totalSupply_ * 10**decimals_;
+ _owner = msg.sender;
+ _balances[msg.sender] = _totalSupply;
+ expiryTimestamp = block.timestamp + (expiryDays * 1 days);
+ }
+
+ modifier notExpired() {
+ require(block.timestamp < expiryTimestamp, ""Token has expired"");
+ _;
+ }
+
+ function name() public view returns (string memory) { return _name; }
+ function symbol() public view returns (string memory) { return _symbol; }
+ function decimals() public view returns (uint8) { return _decimals; }
+ function totalSupply() public view override returns (uint256) { return _totalSupply; }
+
+ function balanceOf(address account) public view override returns (uint256) {
+ return _balances[account];
+ }
+
+ function transfer(address recipient, uint256 amount) public override notExpired returns (bool) {
+ _transfer(msg.sender, recipient, amount);
+ return true;
+ }
+
+ function allowance(address owner, address spender) public view override returns (uint256) {
+ return _allowances[owner][spender];
+ }
+
+ function approve(address spender, uint256 amount) public override notExpired returns (bool) {
+ _approve(msg.sender, spender, amount);
+ return true;
+ }
+
+ function transferFrom(address sender, address recipient, uint256 amount) public override notExpired returns (bool) {
+ uint256 currentAllowance = _allowances[sender][msg.sender];
+ require(currentAllowance >= amount, ""ERC20: transfer amount exceeds allowance"");
+
+ _transfer(sender, recipient, amount);
+ _approve(sender, msg.sender, currentAllowance - amount);
+
+ return true;
+ }
+
+ function _transfer(address sender, address recipient, uint256 amount) internal {
+ require(sender != address(0), ""ERC20: transfer from the zero address"");
+ require(recipient != address(0), ""ERC20: transfer to the zero address"");
+
+ uint256 senderBalance = _balances[sender];
+ require(senderBalance >= amount, ""ERC20: transfer amount exceeds balance"");
+
+ _balances[sender] = senderBalance - amount;
+ _balances[recipient] += amount;
+
+ emit Transfer(sender, recipient, amount);
+ }
+
+ function _approve(address owner, address spender, uint256 amount) internal {
+ require(owner != address(0), ""ERC20: approve from the zero address"");
+ require(spender != address(0), ""ERC20: approve to the zero address"");
+
+ _allowances[owner][spender] = amount;
+ emit Approval(owner, spender, amount);
+ }
+
+ function isExpired() public view returns (bool) {
+ return block.timestamp >= expiryTimestamp;
+ }
+
+ function timeRemaining() public view returns (uint256) {
+ if (block.timestamp >= expiryTimestamp) return 0;
+ return expiryTimestamp - block.timestamp;
+ }
+}";
+ }
+}
\ No newline at end of file
diff --git a/WpfApp1TRC20/Data/AppDbContext.cs b/WpfApp1TRC20/Data/AppDbContext.cs
new file mode 100644
index 0000000..3fa41aa
--- /dev/null
+++ b/WpfApp1TRC20/Data/AppDbContext.cs
@@ -0,0 +1,40 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WpfApp1TRC20.Data
+{
+ public class AppDbContext : DbContext
+ {
+ public DbSet Tokens { get; set; }
+ public DbSet Wallets { get; set; }
+ public DbSet Transactions { get; set; }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ var dbPath = Path.Combine(appDataPath, "TRC20TokenManager", "app.db");
+ Directory.CreateDirectory(Path.GetDirectoryName(dbPath));
+ optionsBuilder.UseSqlite($"Data Source={dbPath}");
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .Property(e => e.TotalSupply)
+ .HasConversion();
+
+ modelBuilder.Entity()
+ .Property(e => e.Amount)
+ .HasConversion();
+
+ modelBuilder.Entity()
+ .Property(e => e.TrxBalance)
+ .HasConversion();
+ }
+ }
+}
diff --git a/WpfApp1TRC20/Data/TokenBalance.cs b/WpfApp1TRC20/Data/TokenBalance.cs
new file mode 100644
index 0000000..c57f043
--- /dev/null
+++ b/WpfApp1TRC20/Data/TokenBalance.cs
@@ -0,0 +1,11 @@
+namespace WpfApp1TRC20.Data
+{
+ public class TokenBalance
+ {
+ public string TokenSymbol { get; set; }
+ public string ContractAddress { get; set; }
+ public decimal Balance { get; set; }
+ public int Decimals { get; set; }
+ public string WalletAddress { get; set; }
+ }
+}
diff --git a/WpfApp1TRC20/Data/TokenCreationParams.cs b/WpfApp1TRC20/Data/TokenCreationParams.cs
new file mode 100644
index 0000000..3c0bca4
--- /dev/null
+++ b/WpfApp1TRC20/Data/TokenCreationParams.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WpfApp1TRC20.Data
+{
+
+ public class TokenCreationParams
+ {
+ [Required]
+ public string Name { get; set; }
+ [Required]
+ public string Symbol { get; set; }
+ [Range(0, 18)]
+ public int Decimals { get; set; } = 18;
+ [Range(1, double.MaxValue)]
+ public decimal TotalSupply { get; set; }
+ [Range(10, 90)]
+ public int ExpiryDays { get; set; } = 30;
+ public string OwnerAddress { get; set; }
+ }
+}
diff --git a/WpfApp1TRC20/Data/TokenInfo.cs b/WpfApp1TRC20/Data/TokenInfo.cs
new file mode 100644
index 0000000..aa960eb
--- /dev/null
+++ b/WpfApp1TRC20/Data/TokenInfo.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WpfApp1TRC20.Data
+{
+ public class TokenInfo
+ {
+ public int Id { get; set; }
+ [Required]
+ public string Name { get; set; }
+ [Required]
+ public string Symbol { get; set; }
+ public int Decimals { get; set; }
+ public decimal TotalSupply { get; set; }
+ public string ContractAddress { get; set; }
+ public string OwnerAddress { get; set; }
+ public DateTime CreationDate { get; set; }
+ public DateTime ExpiryDate { get; set; }
+ public int ExpiryDays { get; set; }
+ public bool IsExpired => DateTime.UtcNow > ExpiryDate;
+ public string TransactionHash { get; set; }
+ public TokenStatus Status { get; set; }
+ }
+}
diff --git a/WpfApp1TRC20/Data/TokenStatus.cs b/WpfApp1TRC20/Data/TokenStatus.cs
new file mode 100644
index 0000000..297ae41
--- /dev/null
+++ b/WpfApp1TRC20/Data/TokenStatus.cs
@@ -0,0 +1,10 @@
+namespace WpfApp1TRC20.Data
+{
+ public enum TokenStatus
+ {
+ Creating,
+ Active,
+ Expired,
+ Failed
+ }
+}
diff --git a/WpfApp1TRC20/Data/TransactionRecord.cs b/WpfApp1TRC20/Data/TransactionRecord.cs
new file mode 100644
index 0000000..b243faf
--- /dev/null
+++ b/WpfApp1TRC20/Data/TransactionRecord.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace WpfApp1TRC20.Data
+{
+ public class TransactionRecord
+ {
+ public int Id { get; set; }
+ public string TransactionHash { get; set; }
+ public string FromAddress { get; set; }
+ public string ToAddress { get; set; }
+ public decimal Amount { get; set; }
+ public string TokenSymbol { get; set; }
+ public string ContractAddress { get; set; }
+ public DateTime Timestamp { get; set; }
+ public TransactionStatus Status { get; set; }
+ public string ErrorMessage { get; set; }
+ public decimal GasFee { get; set; }
+ }
+}
diff --git a/WpfApp1TRC20/Data/TransactionStatus.cs b/WpfApp1TRC20/Data/TransactionStatus.cs
new file mode 100644
index 0000000..705baa0
--- /dev/null
+++ b/WpfApp1TRC20/Data/TransactionStatus.cs
@@ -0,0 +1,9 @@
+namespace WpfApp1TRC20.Data
+{
+ public enum TransactionStatus
+ {
+ Pending,
+ Confirmed,
+ Failed
+ }
+}
diff --git a/WpfApp1TRC20/Data/WalletAccount.cs b/WpfApp1TRC20/Data/WalletAccount.cs
new file mode 100644
index 0000000..ef05922
--- /dev/null
+++ b/WpfApp1TRC20/Data/WalletAccount.cs
@@ -0,0 +1,18 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace WpfApp1TRC20.Data
+{
+ public class WalletAccount
+ {
+ public int Id { get; set; }
+ [Required]
+ public string Name { get; set; }
+ [Required]
+ public string Address { get; set; }
+ public string EncryptedPrivateKey { get; set; }
+ public DateTime CreatedDate { get; set; }
+ public decimal TrxBalance { get; set; }
+ public bool IsImported { get; set; }
+ }
+}
diff --git a/WpfApp1TRC20/Services/DataService.cs b/WpfApp1TRC20/Services/DataService.cs
new file mode 100644
index 0000000..fb0f825
--- /dev/null
+++ b/WpfApp1TRC20/Services/DataService.cs
@@ -0,0 +1,147 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WpfApp1TRC20.Data;
+
+namespace WpfApp1TRC20.Services
+{
+ public class DataService : IDataService
+ {
+ private readonly AppDbContext _context;
+ private readonly ITronService _tronService;
+
+ public DataService(AppDbContext context, ITronService tronService)
+ {
+ _context = context;
+ _tronService = tronService;
+ }
+
+ public async Task> GetTokensAsync()
+ {
+ return await _context.Tokens.ToListAsync();
+ }
+
+ public async Task> GetWalletsAsync()
+ {
+ var wallets = await _context.Wallets.ToListAsync();
+
+ // Update TRX balances
+ foreach (var wallet in wallets)
+ {
+ try
+ {
+ wallet.TrxBalance = await _tronService.GetTrxBalanceAsync(wallet.Address);
+ }
+ catch
+ {
+ // Continue if balance check fails
+ }
+ }
+
+ return wallets;
+ }
+
+ public async Task> GetTransactionsAsync()
+ {
+ return await _context.Transactions
+ .OrderByDescending(t => t.Timestamp)
+ .ToListAsync();
+ }
+
+ public async Task SaveTokenAsync(TokenInfo token)
+ {
+ if (token.Id == 0)
+ {
+ _context.Tokens.Add(token);
+ }
+ else
+ {
+ _context.Tokens.Update(token);
+ }
+
+ await _context.SaveChangesAsync();
+ return token;
+ }
+
+ public async Task SaveWalletAsync(WalletAccount wallet)
+ {
+ if (wallet.Id == 0)
+ {
+ _context.Wallets.Add(wallet);
+ }
+ else
+ {
+ _context.Wallets.Update(wallet);
+ }
+
+ await _context.SaveChangesAsync();
+ return wallet;
+ }
+
+ public async Task SaveTransactionAsync(TransactionRecord transaction)
+ {
+ if (transaction.Id == 0)
+ {
+ _context.Transactions.Add(transaction);
+ }
+ else
+ {
+ _context.Transactions.Update(transaction);
+ }
+
+ await _context.SaveChangesAsync();
+ return transaction;
+ }
+
+ public async Task DeleteTokenAsync(int tokenId)
+ {
+ var token = await _context.Tokens.FindAsync(tokenId);
+ if (token != null)
+ {
+ _context.Tokens.Remove(token);
+ await _context.SaveChangesAsync();
+ }
+ }
+
+ public async Task DeleteWalletAsync(int walletId)
+ {
+ var wallet = await _context.Wallets.FindAsync(walletId);
+ if (wallet != null)
+ {
+ _context.Wallets.Remove(wallet);
+ await _context.SaveChangesAsync();
+ }
+ }
+
+ public async Task> GetTokenBalancesAsync(string walletAddress)
+ {
+ var tokens = await _context.Tokens.Where(t => t.Status == TokenStatus.Active).ToListAsync();
+ var balances = new List();
+
+ foreach (var token in tokens)
+ {
+ try
+ {
+ var balance = await _tronService.GetTokenBalanceAsync(token.ContractAddress, walletAddress);
+ balances.Add(new TokenBalance
+ {
+ TokenSymbol = token.Symbol,
+ ContractAddress = token.ContractAddress,
+ Balance = balance,
+ Decimals = token.Decimals,
+ WalletAddress = walletAddress
+ });
+ }
+ catch
+ {
+ // Continue if balance check fails
+ }
+ }
+
+ return balances;
+ }
+ }
+}
diff --git a/WpfApp1TRC20/Services/IDataService.cs b/WpfApp1TRC20/Services/IDataService.cs
new file mode 100644
index 0000000..a995768
--- /dev/null
+++ b/WpfApp1TRC20/Services/IDataService.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WpfApp1TRC20.Data;
+
+namespace WpfApp1TRC20.Services
+{
+ public interface IDataService
+ {
+ Task> GetTokensAsync();
+ Task> GetWalletsAsync();
+ Task> GetTransactionsAsync();
+ Task SaveTokenAsync(TokenInfo token);
+ Task SaveWalletAsync(WalletAccount wallet);
+ Task SaveTransactionAsync(TransactionRecord transaction);
+ Task DeleteTokenAsync(int tokenId);
+ Task DeleteWalletAsync(int walletId);
+ Task> GetTokenBalancesAsync(string walletAddress);
+ }
+}
diff --git a/WpfApp1TRC20/Services/ITronService.cs b/WpfApp1TRC20/Services/ITronService.cs
new file mode 100644
index 0000000..4ff297d
--- /dev/null
+++ b/WpfApp1TRC20/Services/ITronService.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WpfApp1TRC20.Data;
+
+namespace WpfApp1TRC20.Services
+{
+ public interface ITronService
+ {
+ Task CreateTokenAsync(TokenCreationParams parameters, string ownerPrivateKey);
+ Task TransferTokenAsync(string contractAddress, string fromPrivateKey, string toAddress, decimal amount);
+ Task GetTokenBalanceAsync(string contractAddress, string address);
+ Task GetTrxBalanceAsync(string address);
+ Task GetTransactionAsync(string txHash);
+ Task IsTokenExpiredAsync(string contractAddress);
+ WalletAccount CreateWallet(string name);
+ WalletAccount ImportWallet(string name, string privateKey);
+ string DecryptPrivateKey(string encryptedPrivateKey);
+ }
+}
diff --git a/WpfApp1TRC20/Services/TronService.cs b/WpfApp1TRC20/Services/TronService.cs
new file mode 100644
index 0000000..5c199ee
--- /dev/null
+++ b/WpfApp1TRC20/Services/TronService.cs
@@ -0,0 +1,255 @@
+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 _logger;
+ private const string TRON_MAINNET = "https://api.trongrid.io";
+ private const string TRON_TESTNET = "https://api.shasta.trongrid.io";
+
+ public TronService(ILogger logger)
+ {
+ _logger = logger;
+ // Use testnet for development, mainnet for production
+ var httpClient = new HttpClient();
+ _tronClient = new TronClient(httpClient, TRON_TESTNET);
+ }
+
+ public async Task 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 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 GetTokenBalanceAsync(string contractAddress, string address)
+ {
+ try
+ {
+ var contract = _tronClient.GetContract(contractAddress);
+ var balanceFunction = contract.GetFunction("balanceOf");
+
+ var balance = await balanceFunction.CallAsync(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 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 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 IsTokenExpiredAsync(string contractAddress)
+ {
+ try
+ {
+ var contract = _tronClient.GetContract(contractAddress);
+ var isExpiredFunction = contract.GetFunction("isExpired");
+
+ var isExpired = await isExpiredFunction.CallAsync();
+ 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 "";
+ }
+ }
+}
diff --git a/WpfApp1TRC20/Utils/InverseBooleanConverter.cs b/WpfApp1TRC20/Utils/InverseBooleanConverter.cs
new file mode 100644
index 0000000..1cb9c87
--- /dev/null
+++ b/WpfApp1TRC20/Utils/InverseBooleanConverter.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace WpfApp1TRC20.Utils
+{
+ public class InverseBooleanConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool boolValue)
+ return !boolValue;
+ return true;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool boolValue)
+ return !boolValue;
+ return false;
+ }
+ }
+}
diff --git a/WpfApp1TRC20/Utils/StatusToColorConverter.cs b/WpfApp1TRC20/Utils/StatusToColorConverter.cs
new file mode 100644
index 0000000..e0d01e4
--- /dev/null
+++ b/WpfApp1TRC20/Utils/StatusToColorConverter.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using WpfApp1TRC20.Data;
+
+namespace WpfApp1TRC20.Utils
+{
+ public class StatusToColorConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is TransactionStatus status)
+ {
+ return status switch
+ {
+ TransactionStatus.Confirmed => "Green",
+ TransactionStatus.Pending => "Orange",
+ TransactionStatus.Failed => "Red",
+ _ => "Gray"
+ };
+ }
+ return "Gray";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/WpfApp1TRC20/ViewModels/MainViewModel.cs b/WpfApp1TRC20/ViewModels/MainViewModel.cs
new file mode 100644
index 0000000..68c48b9
--- /dev/null
+++ b/WpfApp1TRC20/ViewModels/MainViewModel.cs
@@ -0,0 +1,445 @@
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using WpfApp1TRC20.Data;
+using WpfApp1TRC20.Services;
+using TransactionStatus = WpfApp1TRC20.Data.TransactionStatus;
+
+namespace WpfApp1TRC20.ViewModels
+{
+
+ public class MainViewModel : ViewModelBase
+ {
+ private readonly IDataService _dataService;
+ private readonly ITronService _tronService;
+ private readonly ILogger _logger;
+
+ private TokenInfo _selectedToken;
+ private WalletAccount _selectedWallet;
+ private string _selectedTabItem = "Dashboard";
+
+ public MainViewModel(IDataService dataService, ITronService tronService, ILogger logger)
+ {
+ _dataService = dataService;
+ _tronService = tronService;
+ _logger = logger;
+
+ Tokens = new ObservableCollection();
+ Wallets = new ObservableCollection();
+ Transactions = new ObservableCollection();
+ TokenBalances = new ObservableCollection();
+
+ // Commands
+ CreateTokenCommand = new RelayCommand(async () => await CreateTokenAsync());
+ CreateWalletCommand = new RelayCommand(async () => await CreateWalletAsync());
+ ImportWalletCommand = new RelayCommand(async () => await ImportWalletAsync());
+ TransferTokenCommand = new RelayCommand(async () => await TransferTokenAsync(), CanTransferToken);
+ RefreshDataCommand = new RelayCommand(async () => await RefreshDataAsync());
+
+ // Load initial data
+ _ = Task.Run(RefreshDataAsync);
+ }
+
+ // Properties
+ public ObservableCollection Tokens { get; }
+ public ObservableCollection Wallets { get; }
+ public ObservableCollection Transactions { get; }
+ public ObservableCollection TokenBalances { get; }
+
+ public TokenInfo SelectedToken
+ {
+ get => _selectedToken;
+ set => SetProperty(ref _selectedToken, value);
+ }
+
+ public WalletAccount SelectedWallet
+ {
+ get => _selectedWallet;
+ set
+ {
+ SetProperty(ref _selectedWallet, value);
+ if (value != null)
+ _ = Task.Run(() => LoadTokenBalancesAsync(value.Address));
+ }
+ }
+
+ public string SelectedTabItem
+ {
+ get => _selectedTabItem;
+ set => SetProperty(ref _selectedTabItem, value);
+ }
+
+ // Token Creation Properties
+ private string _newTokenName;
+ public string NewTokenName
+ {
+ get => _newTokenName;
+ set => SetProperty(ref _newTokenName, value);
+ }
+
+ private string _newTokenSymbol;
+ public string NewTokenSymbol
+ {
+ get => _newTokenSymbol;
+ set => SetProperty(ref _newTokenSymbol, value);
+ }
+
+ private int _newTokenDecimals = 18;
+ public int NewTokenDecimals
+ {
+ get => _newTokenDecimals;
+ set => SetProperty(ref _newTokenDecimals, value);
+ }
+
+ private decimal _newTokenSupply = 1000000;
+ public decimal NewTokenSupply
+ {
+ get => _newTokenSupply;
+ set => SetProperty(ref _newTokenSupply, value);
+ }
+
+ private int _newTokenExpiryDays = 30;
+ public int NewTokenExpiryDays
+ {
+ get => _newTokenExpiryDays;
+ set => SetProperty(ref _newTokenExpiryDays, value);
+ }
+
+ // Transfer Properties
+ private string _transferToAddress;
+ public string TransferToAddress
+ {
+ get => _transferToAddress;
+ set => SetProperty(ref _transferToAddress, value);
+ }
+
+ private decimal _transferAmount;
+ public decimal TransferAmount
+ {
+ get => _transferAmount;
+ set => SetProperty(ref _transferAmount, value);
+ }
+
+ private TokenBalance _selectedTokenBalance;
+ public TokenBalance SelectedTokenBalance
+ {
+ get => _selectedTokenBalance;
+ set => SetProperty(ref _selectedTokenBalance, value);
+ }
+
+ private string _newWalletName;
+ public string NewWalletName
+ {
+ get => _newWalletName;
+ set => SetProperty(ref _newWalletName, value);
+ }
+
+ private string _importPrivateKey;
+ public string ImportPrivateKey
+ {
+ get => _importPrivateKey;
+ set => SetProperty(ref _importPrivateKey, value);
+ }
+
+ private bool _isLoading;
+ public bool IsLoading
+ {
+ get => _isLoading;
+ set => SetProperty(ref _isLoading, value);
+ }
+
+ private string _statusMessage;
+ public string StatusMessage
+ {
+ get => _statusMessage;
+ set => SetProperty(ref _statusMessage, value);
+ }
+
+ // Commands
+ public ICommand CreateTokenCommand { get; }
+ public ICommand CreateWalletCommand { get; }
+ public ICommand ImportWalletCommand { get; }
+ public ICommand TransferTokenCommand { get; }
+ public ICommand RefreshDataCommand { get; }
+
+ // Methods
+ private async Task CreateTokenAsync()
+ {
+ if (string.IsNullOrWhiteSpace(NewTokenName) || string.IsNullOrWhiteSpace(NewTokenSymbol) || SelectedWallet == null)
+ {
+ StatusMessage = "Please fill all required fields and select a wallet";
+ return;
+ }
+
+ IsLoading = true;
+ StatusMessage = "Creating token...";
+
+ try
+ {
+ var parameters = new TokenCreationParams
+ {
+ Name = NewTokenName,
+ Symbol = NewTokenSymbol,
+ Decimals = NewTokenDecimals,
+ TotalSupply = NewTokenSupply,
+ ExpiryDays = NewTokenExpiryDays,
+ OwnerAddress = SelectedWallet.Address
+ };
+
+ var privateKey = _tronService.DecryptPrivateKey(SelectedWallet.EncryptedPrivateKey);
+ var txHash = await _tronService.CreateTokenAsync(parameters, privateKey);
+
+ var token = new TokenInfo
+ {
+ Name = parameters.Name,
+ Symbol = parameters.Symbol,
+ Decimals = parameters.Decimals,
+ TotalSupply = parameters.TotalSupply,
+ OwnerAddress = parameters.OwnerAddress,
+ CreationDate = DateTime.UtcNow,
+ ExpiryDate = DateTime.UtcNow.AddDays(parameters.ExpiryDays),
+ ExpiryDays = parameters.ExpiryDays,
+ TransactionHash = txHash,
+ Status = TokenStatus.Creating
+ };
+
+ await _dataService.SaveTokenAsync(token);
+ Tokens.Add(token);
+
+ // Clear form
+ NewTokenName = "";
+ NewTokenSymbol = "";
+ NewTokenDecimals = 18;
+ NewTokenSupply = 1000000;
+ NewTokenExpiryDays = 30;
+
+ StatusMessage = $"Token creation initiated. Transaction: {txHash}";
+
+ // Monitor transaction status
+ _ = Task.Run(() => MonitorTokenCreation(token));
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error creating token");
+ StatusMessage = $"Error creating token: {ex.Message}";
+ }
+ finally
+ {
+ IsLoading = false;
+ }
+ }
+
+ private async Task CreateWalletAsync()
+ {
+ if (string.IsNullOrWhiteSpace(NewWalletName))
+ {
+ StatusMessage = "Please enter a wallet name";
+ return;
+ }
+
+ try
+ {
+ var wallet = _tronService.CreateWallet(NewWalletName);
+ await _dataService.SaveWalletAsync(wallet);
+ Wallets.Add(wallet);
+
+ NewWalletName = "";
+ StatusMessage = $"Wallet '{wallet.Name}' created successfully";
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error creating wallet");
+ StatusMessage = $"Error creating wallet: {ex.Message}";
+ }
+ }
+
+ private async Task ImportWalletAsync()
+ {
+ if (string.IsNullOrWhiteSpace(NewWalletName) || string.IsNullOrWhiteSpace(ImportPrivateKey))
+ {
+ StatusMessage = "Please enter wallet name and private key";
+ return;
+ }
+
+ try
+ {
+ var wallet = _tronService.ImportWallet(NewWalletName, ImportPrivateKey);
+ await _dataService.SaveWalletAsync(wallet);
+ Wallets.Add(wallet);
+
+ NewWalletName = "";
+ ImportPrivateKey = "";
+ StatusMessage = $"Wallet '{wallet.Name}' imported successfully";
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error importing wallet");
+ StatusMessage = $"Error importing wallet: {ex.Message}";
+ }
+ }
+
+ private bool CanTransferToken()
+ {
+ return SelectedWallet != null && SelectedTokenBalance != null &&
+ !string.IsNullOrWhiteSpace(TransferToAddress) && TransferAmount > 0;
+ }
+
+ private async Task TransferTokenAsync()
+ {
+ if (!CanTransferToken()) return;
+
+ IsLoading = true;
+ StatusMessage = "Transferring tokens...";
+
+ try
+ {
+ var privateKey = _tronService.DecryptPrivateKey(SelectedWallet.EncryptedPrivateKey);
+ var txHash = await _tronService.TransferTokenAsync(
+ SelectedTokenBalance.ContractAddress,
+ privateKey,
+ TransferToAddress,
+ TransferAmount
+ );
+
+ var transaction = new TransactionRecord
+ {
+ TransactionHash = txHash,
+ FromAddress = SelectedWallet.Address,
+ ToAddress = TransferToAddress,
+ Amount = TransferAmount,
+ TokenSymbol = SelectedTokenBalance.TokenSymbol,
+ ContractAddress = SelectedTokenBalance.ContractAddress,
+ Timestamp = DateTime.UtcNow,
+ Status = TransactionStatus.Pending
+ };
+
+ await _dataService.SaveTransactionAsync(transaction);
+ Transactions.Insert(0, transaction);
+
+ // Clear form
+ TransferToAddress = "";
+ TransferAmount = 0;
+
+ StatusMessage = $"Transfer initiated. Transaction: {txHash}";
+
+ // Refresh balances
+ await LoadTokenBalancesAsync(SelectedWallet.Address);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error transferring tokens");
+ StatusMessage = $"Error transferring tokens: {ex.Message}";
+ }
+ finally
+ {
+ IsLoading = false;
+ }
+ }
+
+ private async Task RefreshDataAsync()
+ {
+ try
+ {
+ var tokens = await _dataService.GetTokensAsync();
+ var wallets = await _dataService.GetWalletsAsync();
+ var transactions = await _dataService.GetTransactionsAsync();
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ Tokens.Clear();
+ foreach (var token in tokens) Tokens.Add(token);
+
+ Wallets.Clear();
+ foreach (var wallet in wallets) Wallets.Add(wallet);
+
+ Transactions.Clear();
+ foreach (var transaction in transactions) Transactions.Add(transaction);
+ });
+
+ if (SelectedWallet != null)
+ {
+ await LoadTokenBalancesAsync(SelectedWallet.Address);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error refreshing data");
+ }
+ }
+
+ private async Task LoadTokenBalancesAsync(string walletAddress)
+ {
+ try
+ {
+ var balances = await _dataService.GetTokenBalancesAsync(walletAddress);
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ TokenBalances.Clear();
+ foreach (var balance in balances) TokenBalances.Add(balance);
+ });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error loading token balances");
+ }
+ }
+
+ private async Task MonitorTokenCreation(TokenInfo token)
+ {
+ // Wait for transaction confirmation
+ for (int i = 0; i < 30; i++) // Wait up to 5 minutes
+ {
+ await Task.Delay(10000); // Wait 10 seconds
+
+ try
+ {
+ var txInfo = await _tronService.GetTransactionAsync(token.TransactionHash);
+
+ if (txInfo.Status == TransactionStatus.Confirmed)
+ {
+ // Extract contract address from transaction (simplified)
+ // In real implementation, parse the transaction receipt
+ token.ContractAddress = GenerateContractAddress(token.TransactionHash);
+ token.Status = TokenStatus.Active;
+
+ await _dataService.SaveTokenAsync(token);
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ StatusMessage = $"Token '{token.Symbol}' deployed successfully!";
+ });
+
+ break;
+ }
+ else if (txInfo.Status == TransactionStatus.Failed)
+ {
+ token.Status = TokenStatus.Failed;
+ await _dataService.SaveTokenAsync(token);
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ StatusMessage = $"Token '{token.Symbol}' deployment failed";
+ });
+
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error monitoring token creation");
+ }
+ }
+ }
+
+ private string GenerateContractAddress(string transactionHash)
+ {
+ // Simplified contract address generation
+ // In real implementation, calculate from transaction details
+ return "T" + transactionHash.Substring(0, 33);
+ }
+ }
+}
diff --git a/WpfApp1TRC20/ViewModels/RelayCommand .cs b/WpfApp1TRC20/ViewModels/RelayCommand .cs
new file mode 100644
index 0000000..65c8b77
--- /dev/null
+++ b/WpfApp1TRC20/ViewModels/RelayCommand .cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace WpfApp1TRC20.ViewModels
+{
+ public class RelayCommand : ICommand
+ {
+ private readonly Func _execute;
+ private readonly Func _canExecute;
+
+ public RelayCommand(Func execute, Func canExecute = null)
+ {
+ _execute = execute ?? throw new ArgumentNullException(nameof(execute));
+ _canExecute = canExecute;
+ }
+
+ public event EventHandler CanExecuteChanged
+ {
+ add { CommandManager.RequerySuggested += value; }
+ remove { CommandManager.RequerySuggested -= value; }
+ }
+
+ public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
+
+ public async void Execute(object parameter) => await _execute();
+ }
+}
diff --git a/WpfApp1TRC20/ViewModels/ViewModelBase.cs b/WpfApp1TRC20/ViewModels/ViewModelBase.cs
new file mode 100644
index 0000000..b3efaf9
--- /dev/null
+++ b/WpfApp1TRC20/ViewModels/ViewModelBase.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WpfApp1TRC20.ViewModels
+{
+ public abstract class ViewModelBase : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ protected virtual bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
+ {
+ if (EqualityComparer.Default.Equals(storage, value))
+ return false;
+
+ storage = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+ }
+}
diff --git a/WpfApp1TRC20/Views/MainWindow.xaml b/WpfApp1TRC20/Views/MainWindow.xaml
new file mode 100644
index 0000000..41a30db
--- /dev/null
+++ b/WpfApp1TRC20/Views/MainWindow.xaml
@@ -0,0 +1,505 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WpfApp1TRC20/Views/MainWindow.xaml.cs b/WpfApp1TRC20/Views/MainWindow.xaml.cs
new file mode 100644
index 0000000..8a12c05
--- /dev/null
+++ b/WpfApp1TRC20/Views/MainWindow.xaml.cs
@@ -0,0 +1,23 @@
+using System.Windows;
+using System.Windows.Controls;
+using WpfApp1TRC20.ViewModels;
+
+namespace WpfApp1TRC20.Views
+{
+ public partial class MainWindow : Window
+ {
+ public MainWindow(MainViewModel viewModel)
+ {
+ InitializeComponent();
+ DataContext = viewModel;
+ }
+
+ private void PrivateKeyBox_PasswordChanged(object sender, RoutedEventArgs e)
+ {
+ if (DataContext is MainViewModel vm && sender is PasswordBox pb)
+ {
+ vm.ImportPrivateKey = pb.Password;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WpfApp1TRC20/WpfApp1TRC20.csproj b/WpfApp1TRC20/WpfApp1TRC20.csproj
new file mode 100644
index 0000000..9d57adc
--- /dev/null
+++ b/WpfApp1TRC20/WpfApp1TRC20.csproj
@@ -0,0 +1,21 @@
+
+
+
+ WinExe
+ net7.0-windows
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WpfApp1TRC20/appsetting.json b/WpfApp1TRC20/appsetting.json
new file mode 100644
index 0000000..90906c9
--- /dev/null
+++ b/WpfApp1TRC20/appsetting.json
@@ -0,0 +1,18 @@
+{
+ "TronNetwork": {
+ "UseMainnet": false,
+ "MainnetUrl": "https://api.trongrid.io",
+ "TestnetUrl": "https://api.shasta.trongrid.io",
+ "ApiKey": "your-trongrid-api-key"
+ },
+ "Database": {
+ "ConnectionString": "Data Source=app.db"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
\ No newline at end of file