Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/from rainbo #1949

Merged
merged 25 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a8b703f
clean up typo etc
chuck-h Apr 30, 2024
64890db
Allow transfer of unfamiliar tokens (1922)
chuck-h Aug 25, 2022
7ad2c81
TokenModel: get acutal contract precision by reading 'stat' table
chuck-h Aug 31, 2022
ef3efbc
use contract precision from 'stat' table for LW 'receive' function
chuck-h Sep 3, 2022
5fa701b
add 'overdraw' field to TokenModel, supporting Mutual Credit transfers
chuck-h Sep 4, 2022
8279b6b
broaden defn of send xfr validity check; keep neg-balance cards in ca…
chuck-h Sep 4, 2022
b779b11
show blockchain errors to user in 'send' flow
chuck-h Sep 4, 2022
a22c862
enable mutual credit validity check in QR transaction flow
chuck-h Sep 4, 2022
12cf9a6
fix bug in unfamiliar token send path
chuck-h Sep 6, 2022
738f2fc
rename claimUnplantedSeedsEnabled
chuck-h Apr 18, 2024
aeab193
authorize 'localscale' usecase for token display
chuck-h Apr 21, 2024
54b364c
allow TokenModel.fromId to be nullable
chuck-h Sep 6, 2022
462d43e
In transaction list, silently skip non-token transfers (e.g. NFT)
chuck-h May 1, 2024
2f3286d
cherry-pick new import api from master pull/1943
n13 Sep 7, 2023
54c6e5b
fix eosdart deserialization for negative bignums
chuck-h May 1, 2024
fd4e7cf
support accounts with several auth keys
chuck-h Sep 28, 2022
952722d
fix dynamic cast in import_accounts
chuck-h May 1, 2024
76c8b6a
fix dynamic cast in import_key
chuck-h May 1, 2024
2990784
cleaner dynamic type casting
chuck-h May 1, 2024
991f3b7
yet another dynamic cast
chuck-h May 1, 2024
c60c7a8
getAccountsByKey limited to `active` permission
chuck-h May 5, 2024
5e13c5c
Merge branch 'master' into update/from_rainbo
chuck-h Jun 8, 2024
8360ccf
default balance subtitle in currency cards
chuck-h Jun 9, 2024
3e99a29
use parallel queuing to make startup loading faster (ported from Loca…
chuck-h Apr 21, 2024
b0c0df6
revert e6ef68e new import API /chain/get_accounts_by_authorizers
chuck-h Jun 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) {

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
throw GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
Expand Down
32 changes: 15 additions & 17 deletions lib/crypto/eosdart/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,21 @@ class EOSClient {
}

/// Get Key Accounts
n13 marked this conversation as resolved.
Show resolved Hide resolved
Future<AccountNames> getKeyAccounts(String pubKey) async {
return _post('/history/get_key_accounts', {'public_key': pubKey}).then((accountNames) {
return AccountNames.fromJson(accountNames);
});

// Get Key Accounts using new API: new get_accounts_by_authorizers API
Future<AccountNames> getAccountsByKey(String pubKey) async {
try {
return _post('/chain/get_accounts_by_authorizers', {
'accounts': [],
'keys': [pubKey]
}).then((response) {
final List<String> accountNames = List.from(response['accounts'].map((e) => e['account_name']));
return AccountNames()..accountNames = accountNames.toSet().toList();
});
} catch (e) {
print("getAccountsByKey error $e");
return AccountNames();
}
}

// Get Key Accounts using new API: new get_accounts_by_authorizers API
Expand Down Expand Up @@ -314,19 +325,6 @@ class EOSClient {
return ser.arrayToHex(buffer.asUint8List());
}

// Future<List<AbiResp>> _getTransactionAbis(Transaction transaction) async {
// Set<String> accounts = Set();
// List<AbiResp> result = [];
//
// for (Action action in transaction.actions) {
// accounts.add(action.account);
// }
//
// for (String accountName in accounts) {
// result.add(await this.getRawAbi(accountName));
// }
// }

Future<PushTransactionArgs> _pushTransactionArgs(
String? chainId, Type transactionType, Transaction transaction, bool sign) async {
final List<String> signatures = [];
Expand Down
2 changes: 1 addition & 1 deletion lib/crypto/eosdart/src/numeric.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ String binaryToDecimal(Uint8List bignum, {minDigits = 1}) {
/// @param minDigits 0-pad result to this many digits
String signedBinaryToDecimal(Uint8List bignum, {int minDigits = 1}) {
if (isNegative(bignum)) {
var x = bignum.getRange(0, 0) as Uint8List;
var x = Uint8List.fromList(bignum.getRange(0, bignum.length).toList());
negate(x);
return '-' + binaryToDecimal(x, minDigits: minDigits);
}
Expand Down
5 changes: 3 additions & 2 deletions lib/datasource/local/models/token_data_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import 'package:seeds/utils/rate_states_extensions.dart';

class TokenDataModel extends AmountDataModel {
String? id;
TokenDataModel(double amount, {TokenModel token = seedsToken})
final TokenModel token;
TokenDataModel(double amount, {TokenModel this.token = seedsToken})
: super(
amount: amount,
symbol: token.symbol,
Expand Down Expand Up @@ -37,7 +38,7 @@ class TokenDataModel extends AmountDataModel {
}

TokenDataModel copyWith(double amount) {
return TokenDataModel(amount, token: TokenModel.fromId(id!));
return TokenDataModel(amount, token: token);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/datasource/local/settings_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class _SettingsStorage {

String get selectedFiatCurrency => _preferences.getString(_kSelectedFiatCurrency) ?? getPlatformCurrency();

TokenModel get selectedToken => TokenModel.fromId(_preferences.getString(_kSelectedToken) ?? seedsToken.id);
TokenModel get selectedToken => TokenModel.fromId(_preferences.getString(_kSelectedToken) ?? seedsToken.id) ?? seedsToken;

bool get inRecoveryMode => _preferences.getBool(_kInRecoveryMode) ?? false;

Expand Down
2 changes: 1 addition & 1 deletion lib/datasource/remote/api/balance_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:seeds/datasource/remote/model/balance_model.dart';
class BalanceRepository extends HttpRepository {
Future<Result<BalanceModel>> getTokenBalance(String userAccount,
{required String tokenContract, required String symbol}) {
print('[http] get seeds getTokenBalance $userAccount for $symbol');
print('[http] get getTokenBalance $userAccount for $symbol');

final String request = '''
{
Expand Down
5 changes: 5 additions & 0 deletions lib/datasource/remote/api/eos_repo/eos_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ abstract class EosRepository {
}

ErrorResult mapEosError(dynamic error) {
final regex = RegExp(r'^.*Internal Service Error.*assertion failure with message: ([^\"]*)');
n13 marked this conversation as resolved.
Show resolved Hide resolved
print('mapEosError: $error');
final match = regex.firstMatch(error);
if (match != null && match.groupCount == 1) {
return ErrorResult("Transaction error:\n${match.group(1)!}");
}
return ErrorResult(error);
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/datasource/remote/api/invoice_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:async/async.dart';

import 'package:seeds/crypto/dart_esr/dart_esr.dart' as esr;
import 'package:seeds/datasource/local/models/token_data_model.dart';
import 'package:seeds/datasource/remote/model/token_model.dart';
import 'package:seeds/datasource/remote/api/eos_repo/eos_repository.dart';
import 'package:seeds/datasource/remote/api/eos_repo/seeds_eos_actions.dart';
import 'package:seeds/datasource/remote/firebase/firebase_remote_config.dart';
Expand All @@ -17,7 +18,7 @@ class InvoiceRepository extends EosRepository {
final Map<String, String> data = {
'from': esr.ESRConstants.PlaceholderName,
'to': accountName,
'quantity': tokenAmount.asFormattedString(),
'quantity': TokenModel.getAssetString(tokenAmount.id, tokenAmount.amount),
'memo': memo ?? ''
};

Expand Down
16 changes: 10 additions & 6 deletions lib/datasource/remote/api/key_accounts_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ import 'package:http/http.dart' as http;
import 'package:seeds/datasource/remote/api/http_repo/http_repository.dart';

class KeyAccountsRepository extends HttpRepository {
Future<Result<dynamic>> getKeyAccounts(String publicKey) {
n13 marked this conversation as resolved.
Show resolved Hide resolved
print('[http] getKeyAccounts');

final url = Uri.parse('$baseURL/v1/history/get_key_accounts');
final body = '{ "public_key": "$publicKey" }';
Future<Result<dynamic>> getAccountsByKey(String publicKey) {
print('[http] getAccountsByKey');

final url = Uri.parse('$baseURL/v1/chain/get_accounts_by_authorizers');
final body = '{ "accounts": [], "keys": ["$publicKey"] }';

return http
.post(url, headers: headers, body: body)
.then((http.Response response) => mapHttpResponse(response, (dynamic body) {
print('result: $body');

final result = List<String>.from(body['account_names']);
// restriction to `active` permission matches ProfileRepository.getAccountPublicKeys
final result =
List<dynamic>.from(body['accounts'] as List).cast<Map<String, dynamic>>()
.where((e) => e['permission_name'] as String == 'active')
.map((e) => e['account_name'] as String).toList().toSet().toList();

result.sort();

Expand Down
6 changes: 3 additions & 3 deletions lib/datasource/remote/api/profile_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ class ProfileRepository extends HttpRepository with EosRepository {

// TODO(Raul): Unify this code with _getAccountPermissions in guardians repo
// Returns the first active key permission - String
Future<Result> getAccountPublicKey(String accountName) async {
print('[http] getAccountPublicKey');
n13 marked this conversation as resolved.
Show resolved Hide resolved
Future<Result> getAccountPublicKeys(String accountName) async {
print('[http] getAccountPublicKeys');

final url = Uri.parse('$host/v1/chain/get_account');
final body = '{ "account_name": "$accountName" }';
Expand All @@ -50,7 +50,7 @@ class ProfileRepository extends HttpRepository with EosRepository {
final permissions = allAccounts.map((item) => Permission.fromJson(item)).toList();
final Permission activePermission = permissions.firstWhere((element) => element.permName == "active");
final RequiredAuth? activeAuth = activePermission.requiredAuth;
return activeAuth?.keys?.first?.key;
return activeAuth?.keys?.map((e)=>e?.key).toList();
}))
.catchError((error) => mapHttpError(error));
}
Expand Down
31 changes: 31 additions & 0 deletions lib/datasource/remote/api/stat_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:async/async.dart';
import 'package:http/http.dart' as http;
import 'package:seeds/datasource/remote/api/http_repo/http_repository.dart';
import 'package:seeds/datasource/remote/model/balance_model.dart';
import 'package:seeds/datasource/remote/model/token_model.dart';
import 'package:seeds/datasource/remote/model/stat_model.dart';

class StatRepository extends HttpRepository {
Future<Result<StatModel>> getTokenStat(
{required String tokenContract, required String symbol}) {
print('[http] get getTokenStat for $symbol');

final String request = '''
{
"code":"$tokenContract",
"table": "stat",
"scope":"$symbol",
"json": "true"
}
''';

final statURL = Uri.parse('$baseURL/v1/chain/get_table_rows');

return http
.post(statURL, headers: headers, body: request)
.then((http.Response response) => mapHttpResponse<StatModel>(response, (dynamic body) {
return StatModel.fromJson(body);
}))
.catchError((dynamic error) => mapHttpError(error));
}
}
3 changes: 2 additions & 1 deletion lib/datasource/remote/api/transactions_repository.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:async/async.dart';
import 'package:collection/collection.dart';
import 'package:http/http.dart' as http;
import 'package:seeds/datasource/remote/api/http_repo/http_repository.dart';
import 'package:seeds/datasource/remote/model/transaction_model.dart';
Expand All @@ -13,7 +14,7 @@ class TransactionsListRepository extends HttpRepository {
.then((http.Response response) => mapHttpResponse<List<TransactionModel>>(response, (dynamic body) {
final List<dynamic> transfers = body['actions'].toList();

return List<TransactionModel>.of(transfers.map((transfer) => TransactionModel.fromJson(transfer)));
return List<TransactionModel>.of(transfers.map((transfer) => TransactionModel.fromJson(transfer)).whereNotNull());
}))
.catchError((dynamic error) => mapHttpError(error));
}
Expand Down
20 changes: 20 additions & 0 deletions lib/datasource/remote/model/stat_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

/// Token per USD
class StatModel {
final String supplyString;
final String maxSupplyString;
final String issuer;

const StatModel(this.supplyString, this.maxSupplyString, this.issuer);

factory StatModel.fromJson(Map<String, dynamic>? json) {
if (json != null && json['rows'].isNotEmpty) {
final supplyString = json['rows'][0]['supply'] ?? '';
final maxSupplyString = json['rows'][0]['max_supply'] ?? '';
final issuer = json['rows'][0]['issuer'] ?? '';
return StatModel(supplyString, maxSupplyString, issuer);
} else {
return const StatModel('', '', '');
}
}
}
50 changes: 45 additions & 5 deletions lib/datasource/remote/model/token_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ class TokenModel extends Equatable {
static const seedsEcosysUsecase = 'seedsecosys';
static List<TokenModel> allTokens = [seedsToken];
static JsonSchema? tmastrSchema;
static Map<String, int?> contractPrecisions = {"token.seeds#SEEDS": 4};
final String chainName;
final String contract;
final String symbol;
final String name;
final String backgroundImageUrl;
final String logoUrl;
final String balanceSubTitle;
final String overdraw;
final int precision;
final List<String>? usecases;

Expand All @@ -47,6 +49,7 @@ class TokenModel extends Equatable {
required this.backgroundImageUrl,
required this.logoUrl,
required this.balanceSubTitle,
required this.overdraw,
this.precision = 4,
this.usecases,
});
Expand Down Expand Up @@ -95,15 +98,16 @@ class TokenModel extends Equatable {
symbol: parsedJson["symbol"]!,
name: parsedJson["name"]!,
logoUrl: parsedJson["logo"]!,
balanceSubTitle: parsedJson["subtitle"],
balanceSubTitle: parsedJson["subtitle"] ?? CurrencyInfoCard.defaultBalanceSubtitle,
backgroundImageUrl: parsedJson["bg_image"] ?? CurrencyInfoCard.defaultBgImage,
overdraw: parsedJson["overdraw"] ?? "allow",
precision: parsedJson["precision"] ?? 4,
usecases: parsedJson["usecases"],
);
}

factory TokenModel.fromId(String tokenId) {
return allTokens.firstWhere((e) => e.id == tokenId);
static TokenModel? fromId(String tokenId) {
return allTokens.firstWhereOrNull((e) => e.id == tokenId);
}

static TokenModel? fromSymbolOrNull(String symbol) {
Expand All @@ -113,8 +117,37 @@ class TokenModel extends Equatable {
@override
List<Object?> get props => [chainName, contract, symbol];

n13 marked this conversation as resolved.
Show resolved Hide resolved
String getAssetString(double quantity) {
return "${quantity.toStringAsFixed(precision)} $symbol";
static String getAssetString(String? id, double quantity) {
if (id!=null && TokenModel.fromId(id)!=null && contractPrecisions.containsKey(id)) {
final symbol = TokenModel.fromId(id)!.symbol;
return symbol==null ? "" : "${quantity.toStringAsFixed(contractPrecisions[id]!)} $symbol";
} else {
return "";
}
}

void setPrecisionFromString(String s) {
n13 marked this conversation as resolved.
Show resolved Hide resolved
final amount = s.split(' ')[0];
final ss = amount.split('.');
if (ss.isEmpty) {
return;
}
contractPrecisions[this.id] = ss.length==1 ? 0 : ss[1].length;
}

// enabling 'send' transfer validity checks, e.g. Mutual Credit,
// membership limitations
n13 marked this conversation as resolved.
Show resolved Hide resolved
bool blockTransfer(double insufficiency, String? toAccount) {
if (overdraw == "block") {
return insufficiency > 0;
} else if (overdraw == "allow") {
return false;
}
print("unexpected overdraw field: $overdraw");
return false;
}
String? warnTransfer(double insufficiency, String? toAccount) {
return insufficiency > 0 ? "insufficient balance" : null;
}

static Future<void> updateModels(List<String> acceptList, [List<String>? infoList]) async {
Expand Down Expand Up @@ -142,6 +175,7 @@ class TokenModel extends Equatable {
}
}
allTokens = _staticTokenList;
contractPrecisions = Map.fromEntries(allTokens.map((t) => MapEntry(t.id , t.precision)));
}

static void pruneRemoving(List<String> useCaseList) {
Expand All @@ -163,6 +197,7 @@ const seedsToken = TokenModel(
backgroundImageUrl: 'assets/images/wallet/currency_info_cards/seeds/background.jpg',
logoUrl: 'assets/images/wallet/currency_info_cards/seeds/logo.jpg',
balanceSubTitle: 'Wallet Balance',
overdraw: "block",
usecases: ["lightwallet", TokenModel.seedsEcosysUsecase],
);

Expand All @@ -175,6 +210,7 @@ const _husdToken = TokenModel(
backgroundImageUrl: 'assets/images/wallet/currency_info_cards/husd/background.jpg',
logoUrl: 'assets/images/wallet/currency_info_cards/husd/logo.jpg',
balanceSubTitle: 'Wallet Balance',
overdraw: "block",
precision: 2,
usecases: ["lightwallet", TokenModel.seedsEcosysUsecase],
);
Expand All @@ -187,6 +223,7 @@ const _hyphaToken = TokenModel(
backgroundImageUrl: 'assets/images/wallet/currency_info_cards/hypha/background.jpg',
logoUrl: 'assets/images/wallet/currency_info_cards/hypha/logo.jpg',
balanceSubTitle: 'Wallet Balance',
overdraw: "block",
precision: 2,
usecases: ["lightwallet", TokenModel.seedsEcosysUsecase],
);
Expand All @@ -199,6 +236,7 @@ const _localScaleToken = TokenModel(
backgroundImageUrl: 'assets/images/wallet/currency_info_cards/lscl/background.jpg',
logoUrl: 'assets/images/wallet/currency_info_cards/lscl/logo.png',
balanceSubTitle: 'Wallet Balance',
overdraw: "block",
usecases: ["lightwallet"],
);

Expand All @@ -210,6 +248,7 @@ const _starsToken = TokenModel(
backgroundImageUrl: 'assets/images/wallet/currency_info_cards/stars/background.jpg',
logoUrl: 'assets/images/wallet/currency_info_cards/stars/logo.jpg',
balanceSubTitle: 'Wallet Balance',
overdraw: "block",
usecases: ["lightwallet"],
);

Expand All @@ -221,5 +260,6 @@ const _telosToken = TokenModel(
backgroundImageUrl: 'assets/images/wallet/currency_info_cards/tlos/background.png',
logoUrl: 'assets/images/wallet/currency_info_cards/tlos/logo.png',
balanceSubTitle: 'Wallet Balance',
overdraw: "block",
usecases: ["lightwallet"],
);
Loading
Loading