diff --git a/assets/logo/logo_erc20.png b/assets/logo/logo_erc20.png deleted file mode 100644 index b49d2a9..0000000 Binary files a/assets/logo/logo_erc20.png and /dev/null differ diff --git a/lib/apps/wallet/models.dart b/lib/apps/wallet/models.dart index c40878c..b4c22d8 100644 --- a/lib/apps/wallet/models.dart +++ b/lib/apps/wallet/models.dart @@ -158,3 +158,54 @@ class Address { this.isGen = params[5]; } } + +class Token { + int id = 0; + ChainToken chain = ChainToken.ETH; + Network network = Network.EthMain; + String name = 'ETH'; + String contract = ''; + int decimal = 18; + + String balanceString = ''; + double amount = 0.0; + double fiat = 0.0; + String logo = 'assets/logo/logo_eth.png'; + + Token() {} + + Token.fromList(List params) { + this.id = params[0]; + this.chain = ChainTokenExtension.fromInt(params[1]); + this.network = NetworkExtension.fromInt(params[2]); + this.name = params[3]; + this.contract = params[4]; + this.decimal = params[5]; + } + + Token.eth(Network network) { + this.network = network; + } + + Token.btc(Network network) { + this.network = network; + this.name = 'BTC'; + this.decimal = 8; + } + + balance(String number) { + this.balanceString = number; + + final pad = number.length - (this.decimal + 1); // 0.00..00 + if (pad < 0) { + number = ('0' * (-pad)) + number; + } + String right = number.substring(number.length - this.decimal, number.length); + final left = number.substring(0, number.length - this.decimal); + if (right.length > 8) { + right = right.substring(0, 8); + } + final amount_s = left + '.' + right; + this.amount = double.parse(amount_s); + } +} diff --git a/lib/apps/wallet/page.dart b/lib/apps/wallet/page.dart index 82dc924..8466675 100644 --- a/lib/apps/wallet/page.dart +++ b/lib/apps/wallet/page.dart @@ -31,11 +31,13 @@ class _WalletDetailState extends State with SingleTickerProviderSt List _networks = []; Network? _selectedNetwork; + Token _mainToken = Token(); + List _tokens = []; + List tokens = [ - ['ETH', '100', '1000', 'assets/logo/logo_eth.png'], ['USDT', '2000', '2000', 'assets/logo/logo_tether.png'], - ['XXX', '100', '1000', 'assets/logo/logo_erc20.png'], - ['FFF', '100', '1000', 'assets/logo/logo_erc20.png'], + ['XXX', '100', '1000', 'assets/logo/logo_eth.png'], + ['FFF', '100', '1000', 'assets/logo/logo_eth.png'], ]; @override @@ -62,8 +64,17 @@ class _WalletDetailState extends State with SingleTickerProviderSt } _walletBalance(List params) { - // TODO print(params); + final address = params[0]; + final network = NetworkExtension.fromInt(params[1]); + final contract = params[2]; + final balance = params[3]; + + // TODO check token. + + this._mainToken = Token.eth(network); + this._mainToken.balance(balance); + setState(() {}); } _load() async { @@ -249,7 +260,7 @@ class _WalletDetailState extends State with SingleTickerProviderSt height: 36.0, decoration: BoxDecoration( image: DecorationImage( - image: AssetImage(tokens[0][3]), + image: AssetImage(this._mainToken.logo), fit: BoxFit.cover, ), ), @@ -258,7 +269,7 @@ class _WalletDetailState extends State with SingleTickerProviderSt height: 60.0, alignment: Alignment.center, child: Text( - '100 ETH', + "${this._mainToken.amount} ${this._mainToken.name}", style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold)), ), Text('\$1000', style: TextStyle(color: Color(0xFFADB0BB))), diff --git a/pubspec.yaml b/pubspec.yaml index 3202aa1..746db7f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -86,7 +86,6 @@ flutter: - assets/logo/logo_wallet.png - assets/logo/logo_eth.png - assets/logo/logo_tether.png - - assets/logo/logo_erc20.png - assets/images/background_light.jpg - assets/images/background_dark.jpg - assets/images/image_missing.png diff --git a/src/apps/wallet/mod.rs b/src/apps/wallet/mod.rs index 6f2598b..3a9db84 100644 --- a/src/apps/wallet/mod.rs +++ b/src/apps/wallet/mod.rs @@ -1,6 +1,4 @@ mod models; mod rpc; -pub const ETH_NODE: &'static str = "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"; - pub(crate) use rpc::new_rpc_handler; diff --git a/src/apps/wallet/models.rs b/src/apps/wallet/models.rs index a9f1379..97c193a 100644 --- a/src/apps/wallet/models.rs +++ b/src/apps/wallet/models.rs @@ -5,6 +5,22 @@ use tdn::types::{ use tdn_storage::local::{DStorage, DsValue}; +#[rustfmt::skip] +pub const ETH_NODE: &'static str = + "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"; +#[rustfmt::skip] +pub const ETH_ROPSTEN: &'static str = + "https://ropsten.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"; +#[rustfmt::skip] +pub const ETH_RINKEBY: &'static str = + "https://rinkeby.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"; +#[rustfmt::skip] +pub const ETH_KOVAN: &'static str = + "https://kovan.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"; +#[rustfmt::skip] +pub const ETH_LOCAL: &'static str = + "http://localhost:8545"; + pub(crate) enum ChainToken { ETH, ERC20, @@ -44,6 +60,30 @@ pub(crate) enum Network { } impl Network { + pub fn node<'a>(&self) -> &'a str { + // TODO more. + match self { + Network::EthMain => ETH_NODE, + Network::EthTestRopsten => ETH_ROPSTEN, + Network::EthTestRinkeby => ETH_RINKEBY, + Network::EthTestKovan => ETH_KOVAN, + Network::EthLocal => ETH_LOCAL, + Network::BtcMain => ETH_NODE, + Network::BtcLocal => ETH_NODE, + } + } + + pub fn chain(&self) -> ChainToken { + match self { + Network::EthMain + | Network::EthTestRopsten + | Network::EthTestRinkeby + | Network::EthTestKovan + | Network::EthLocal => ChainToken::ETH, + Network::BtcMain | Network::BtcLocal => ChainToken::BTC, + } + } + pub fn to_i64(&self) -> i64 { match self { Network::EthMain => 1, @@ -70,13 +110,6 @@ impl Network { } } -pub(crate) struct Token { - pub id: i64, - pub chain: ChainToken, - pub contract: String, - pub decimal: i64, -} - pub(crate) struct Address { pub id: i64, pub chain: ChainToken, @@ -172,3 +205,85 @@ impl Address { Ok(()) } } + +pub(crate) struct Token { + pub id: i64, + pub chain: ChainToken, + pub network: Network, + pub name: String, + pub contract: String, + pub decimal: i64, +} + +impl Token { + pub fn new( + chain: ChainToken, + network: Network, + name: String, + contract: String, + decimal: i64, + ) -> Self { + Self { + chain, + network, + name, + contract, + decimal, + id: 0, + } + } + + pub fn to_rpc(&self) -> RpcParam { + json!([ + self.id, + self.chain.to_i64(), + self.network.to_i64(), + self.name, + self.contract, + self.decimal, + ]) + } + + fn from_values(mut v: Vec) -> Self { + Self { + decimal: v.pop().unwrap().as_i64(), + contract: v.pop().unwrap().as_string(), + name: v.pop().unwrap().as_string(), + network: Network::from_i64(v.pop().unwrap().as_i64()), + chain: ChainToken::from_i64(v.pop().unwrap().as_i64()), + id: v.pop().unwrap().as_i64(), + } + } + + pub fn insert(&mut self, db: &DStorage) -> Result<()> { + let sql = format!( + "INSERT INTO tokens (chain, network, name, contract, decimal) VALUES ({}, {}, '{}', '{}', {})", + self.chain.to_i64(), + self.network.to_i64(), + self.name, + self.contract, + self.decimal, + ); + let id = db.insert(&sql)?; + self.id = id; + Ok(()) + } + + pub fn list(db: &DStorage, network: &Network) -> Result> { + let matrix = db.query(&format!( + "SELECT id, chain, network, name, contract, decimal FROM tokens where network = {}", + network.to_i64() + ))?; + let mut tokens = vec![]; + for values in matrix { + tokens.push(Self::from_values(values)); + } + Ok(tokens) + } + + pub fn _delete(db: &DStorage, id: &i64) -> Result<()> { + let sql = format!("DELETE FROM tokens WHERE id = {}", id); + db.delete(&sql)?; + Ok(()) + } +} diff --git a/src/apps/wallet/rpc.rs b/src/apps/wallet/rpc.rs index 653c98a..23ee8a3 100644 --- a/src/apps/wallet/rpc.rs +++ b/src/apps/wallet/rpc.rs @@ -2,20 +2,17 @@ use std::sync::Arc; use tdn::types::{ group::GroupId, message::SendMessage, - primitive::HandleResult, + primitive::{HandleResult, Result}, rpc::{json, rpc_response, RpcError, RpcHandler, RpcParam}, }; use tdn_did::{generate_btc_account, generate_eth_account}; use tdn_storage::local::DStorage; -use tokio::sync::mpsc::{error::SendError, Sender}; +use tokio::sync::mpsc::Sender; use web3::signing::Key; use crate::{rpc::RpcState, storage::wallet_db}; -use super::{ - models::{Address, ChainToken, Network}, - ETH_NODE, -}; +use super::models::{Address, ChainToken, Network, Token}; #[inline] fn wallet_list(devices: Vec
) -> RpcParam { @@ -26,45 +23,67 @@ fn wallet_list(devices: Vec
) -> RpcParam { json!(results) } +#[inline] +fn res_balance( + gid: GroupId, + address: &str, + network: &Network, + contract: &str, + balance: &str, +) -> RpcParam { + rpc_response( + 0, + "wallet-balance", + json!([address, network.to_i64(), contract, balance]), + gid, + ) +} + async fn loop_token( sender: Sender, - _db: DStorage, + db: DStorage, gid: GroupId, network: Network, address: String, -) -> std::result::Result<(), SendError> { +) -> Result<()> { // loop get balance of all tokens. - - match network { - Network::EthMain => { - // + let node = network.node(); + let chain = network.chain(); + let tokens = Token::list(&db, &network)?; + + match chain { + ChainToken::ETH => { + let transport = web3::transports::Http::new(node).unwrap(); + let web3 = web3::Web3::new(transport); + let balance = web3 + .eth() + .balance(address.parse().unwrap(), None) + .await + .unwrap(); + let balance = balance.to_string(); + let res = res_balance(gid, &address, &network, "", &balance); + sender.send(SendMessage::Rpc(0, res, true)).await?; + + for token in tokens { + match token.chain { + ChainToken::ERC20 => { + // + } + ChainToken::ERC721 => { + // + } + _ => { + // + } + } + } + } + ChainToken::BTC => { + // TODO } - Network::EthTestRopsten => {} - Network::EthTestRinkeby => {} - Network::EthTestKovan => {} - Network::EthLocal => {} - Network::BtcMain => {} - Network::BtcLocal => {} + _ => panic!("nerver here!"), } - let transport = web3::transports::Http::new(ETH_NODE).unwrap(); - let web3 = web3::Web3::new(transport); - - let balance = web3 - .eth() - .balance(address.parse().unwrap(), None) - .await - .unwrap(); - println!("Balance of {:?}: {}", address, balance); - - let res = rpc_response( - 0, - "wallet-balance", - json!([address, network.to_i64(), balance]), - gid, - ); - sender.send(SendMessage::Rpc(0, res, true)).await?; - Ok(()) } diff --git a/src/migrate/wallet.rs b/src/migrate/wallet.rs index bc01a98..870616d 100644 --- a/src/migrate/wallet.rs +++ b/src/migrate/wallet.rs @@ -1,5 +1,5 @@ #[rustfmt::skip] -pub(super) const WALLET_VERSIONS: [&str; 1] = [ +pub(super) const WALLET_VERSIONS: [&str; 2] = [ "CREATE TABLE IF NOT EXISTS addresses( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, chain INTEGER NOT NULL, @@ -7,4 +7,11 @@ pub(super) const WALLET_VERSIONS: [&str; 1] = [ name TEXT NOT NULL, address TEXT NOT NULL, secret TEXT NOT NULL);", + "CREATE TABLE IF NOT EXISTS tokens( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + chain INTEGER NOT NULL, + network INTEGER NOT NULL, + name TEXT NOT NULL, + contract TEXT NOT NULL, + decimal INTEGER NOT NULL);", ];