mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-03 17:29:23 +00:00
untested stellar
This commit is contained in:
parent
3f282edd33
commit
90deb600b4
10 changed files with 852 additions and 952 deletions
|
@ -86,6 +86,11 @@ Future<void> migrateWalletsToIsar({
|
|||
}
|
||||
}
|
||||
|
||||
// reset stellar address type
|
||||
if (old.coin == Coin.stellar || old.coin == Coin.stellarTestnet) {
|
||||
await MainDB.instance.deleteWalletBlockchainData(old.walletId);
|
||||
}
|
||||
|
||||
//
|
||||
// Set other data values
|
||||
//
|
||||
|
|
|
@ -161,6 +161,7 @@ enum AddressType {
|
|||
nano,
|
||||
banano,
|
||||
spark,
|
||||
stellar,
|
||||
;
|
||||
|
||||
String get readableName {
|
||||
|
@ -187,6 +188,8 @@ enum AddressType {
|
|||
return "Banano";
|
||||
case AddressType.spark:
|
||||
return "Spark";
|
||||
case AddressType.stellar:
|
||||
return "Stellar";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -264,6 +264,7 @@ const _AddresstypeEnumValueMap = {
|
|||
'nano': 8,
|
||||
'banano': 9,
|
||||
'spark': 10,
|
||||
'stellar': 11,
|
||||
};
|
||||
const _AddresstypeValueEnumMap = {
|
||||
0: AddressType.p2pkh,
|
||||
|
@ -277,6 +278,7 @@ const _AddresstypeValueEnumMap = {
|
|||
8: AddressType.nano,
|
||||
9: AddressType.banano,
|
||||
10: AddressType.spark,
|
||||
11: AddressType.stellar,
|
||||
};
|
||||
|
||||
Id _addressGetId(Address object) {
|
||||
|
|
|
@ -14,7 +14,6 @@ import 'package:stackwallet/models/balance.dart';
|
|||
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
||||
import 'package:stackwallet/models/node_model.dart';
|
||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||
import 'package:stackwallet/services/coins/stellar/stellar_wallet.dart';
|
||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
@ -109,22 +108,10 @@ abstract class CoinServiceAPI {
|
|||
throw UnimplementedError("moved");
|
||||
|
||||
case Coin.stellar:
|
||||
return StellarWallet(
|
||||
walletId: walletId,
|
||||
walletName: walletName,
|
||||
coin: coin,
|
||||
secureStore: secureStorageInterface,
|
||||
tracker: tracker,
|
||||
);
|
||||
throw UnimplementedError("moved");
|
||||
|
||||
case Coin.stellarTestnet:
|
||||
return StellarWallet(
|
||||
walletId: walletId,
|
||||
walletName: walletName,
|
||||
coin: coin,
|
||||
secureStore: secureStorageInterface,
|
||||
tracker: tracker,
|
||||
);
|
||||
throw UnimplementedError("moved");
|
||||
|
||||
case Coin.tezos:
|
||||
throw UnimplementedError("moved");
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -399,9 +399,7 @@ extension CoinExt on Coin {
|
|||
|
||||
case Coin.stellar:
|
||||
case Coin.stellarTestnet:
|
||||
// should not be unknown but since already used in prod changing
|
||||
// this requires a migrate
|
||||
return AddressType.unknown;
|
||||
return AddressType.stellar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
42
lib/wallets/crypto_currency/coins/stellar.dart
Normal file
42
lib/wallets/crypto_currency/coins/stellar.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
import 'package:stackwallet/models/node_model.dart';
|
||||
import 'package:stackwallet/utilities/default_nodes.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_currency.dart';
|
||||
|
||||
class Stellar extends Bip39Currency {
|
||||
Stellar(super.network) {
|
||||
switch (network) {
|
||||
case CryptoCurrencyNetwork.main:
|
||||
coin = Coin.stellar;
|
||||
case CryptoCurrencyNetwork.test:
|
||||
coin = Coin.stellarTestnet;
|
||||
default:
|
||||
throw Exception("Unsupported network: $network");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get minConfirms => 1;
|
||||
|
||||
@override
|
||||
String get genesisHash => throw UnimplementedError(
|
||||
"Not used for stellar",
|
||||
);
|
||||
|
||||
@override
|
||||
NodeModel get defaultNode {
|
||||
switch (network) {
|
||||
case CryptoCurrencyNetwork.main:
|
||||
return DefaultNodes.stellar;
|
||||
case CryptoCurrencyNetwork.test:
|
||||
return DefaultNodes.stellarTestnet;
|
||||
default:
|
||||
throw Exception("Unsupported network");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool validateAddress(String address) =>
|
||||
RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address);
|
||||
}
|
|
@ -263,6 +263,7 @@ const _WalletInfomainAddressTypeEnumValueMap = {
|
|||
'nano': 8,
|
||||
'banano': 9,
|
||||
'spark': 10,
|
||||
'stellar': 11,
|
||||
};
|
||||
const _WalletInfomainAddressTypeValueEnumMap = {
|
||||
0: AddressType.p2pkh,
|
||||
|
@ -276,6 +277,7 @@ const _WalletInfomainAddressTypeValueEnumMap = {
|
|||
8: AddressType.nano,
|
||||
9: AddressType.banano,
|
||||
10: AddressType.spark,
|
||||
11: AddressType.stellar,
|
||||
};
|
||||
|
||||
Id _walletInfoGetId(WalletInfo object) {
|
||||
|
|
561
lib/wallets/wallet/impl/stellar_wallet.dart
Normal file
561
lib/wallets/wallet/impl/stellar_wallet.dart
Normal file
|
@ -0,0 +1,561 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/balance.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/test_stellar_node_connection.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/coins/stellar.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_wallet.dart';
|
||||
import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar;
|
||||
|
||||
class StellarWallet extends Bip39Wallet<Stellar> {
|
||||
StellarWallet(CryptoCurrencyNetwork network) : super(Stellar(network));
|
||||
|
||||
stellar.StellarSDK get stellarSdk {
|
||||
if (_stellarSdk == null) {
|
||||
_updateSdk();
|
||||
}
|
||||
return _stellarSdk!;
|
||||
}
|
||||
|
||||
stellar.Network get stellarNetwork {
|
||||
switch (cryptoCurrency.network) {
|
||||
case CryptoCurrencyNetwork.main:
|
||||
return stellar.Network.PUBLIC;
|
||||
case CryptoCurrencyNetwork.test:
|
||||
return stellar.Network.TESTNET;
|
||||
default:
|
||||
throw Exception("Unsupported network");
|
||||
}
|
||||
}
|
||||
|
||||
// ============== Private ====================================================
|
||||
|
||||
stellar.StellarSDK? _stellarSdk;
|
||||
|
||||
Future<int> _getBaseFee() async {
|
||||
final fees = await stellarSdk.feeStats.execute();
|
||||
return int.parse(fees.lastLedgerBaseFee);
|
||||
}
|
||||
|
||||
void _updateSdk() {
|
||||
final currentNode = getCurrentNode();
|
||||
_stellarSdk = stellar.StellarSDK("${currentNode.host}:${currentNode.port}");
|
||||
}
|
||||
|
||||
Future<bool> _accountExists(String accountId) async {
|
||||
bool exists = false;
|
||||
|
||||
try {
|
||||
final receiverAccount = await stellarSdk.accounts.account(accountId);
|
||||
if (receiverAccount.accountId != "") {
|
||||
exists = true;
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Error getting account ${e.toString()} - ${s.toString()}",
|
||||
level: LogLevel.Error);
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
|
||||
Future<stellar.Wallet> _getStellarWallet() async {
|
||||
return await stellar.Wallet.from(
|
||||
await getMnemonic(),
|
||||
passphrase: await getMnemonicPassphrase(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<stellar.KeyPair> _getSenderKeyPair({required int index}) async {
|
||||
final wallet = await _getStellarWallet();
|
||||
return await wallet.getKeyPair(index: index);
|
||||
}
|
||||
|
||||
Future<Address> _fetchStellarAddress({required int index}) async {
|
||||
final stellar.KeyPair keyPair = await _getSenderKeyPair(index: index);
|
||||
final String address = keyPair.accountId;
|
||||
|
||||
return Address(
|
||||
walletId: walletId,
|
||||
value: address,
|
||||
publicKey: keyPair.publicKey,
|
||||
derivationIndex: index,
|
||||
derivationPath: null,
|
||||
type: AddressType.stellar,
|
||||
subType: AddressSubType.receiving,
|
||||
);
|
||||
}
|
||||
|
||||
// ============== Overrides ==================================================
|
||||
|
||||
@override
|
||||
int get isarTransactionVersion => 2;
|
||||
|
||||
@override
|
||||
FilterOperation? get changeAddressFilterOperation =>
|
||||
FilterGroup.and(standardChangeAddressFilters);
|
||||
|
||||
@override
|
||||
FilterOperation? get receivingAddressFilterOperation =>
|
||||
FilterGroup.and(standardReceivingAddressFilters);
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
try {
|
||||
final address = await getCurrentReceivingAddress();
|
||||
if (address == null) {
|
||||
await mainDB
|
||||
.updateOrPutAddresses([await _fetchStellarAddress(index: 0)]);
|
||||
}
|
||||
} catch (_) {
|
||||
// do nothing, still allow user into wallet
|
||||
}
|
||||
return super.init();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<TxData> prepareSend({required TxData txData}) async {
|
||||
try {
|
||||
if (txData.recipients?.length != 1) {
|
||||
throw Exception("Missing recipient");
|
||||
}
|
||||
|
||||
final feeRate = txData.feeRateType;
|
||||
var fee = 1000;
|
||||
if (feeRate is FeeRateType) {
|
||||
final theFees = await fees;
|
||||
switch (feeRate) {
|
||||
case FeeRateType.fast:
|
||||
fee = theFees.fast;
|
||||
case FeeRateType.slow:
|
||||
fee = theFees.slow;
|
||||
case FeeRateType.average:
|
||||
default:
|
||||
fee = theFees.medium;
|
||||
}
|
||||
}
|
||||
|
||||
return txData.copyWith(
|
||||
fee: Amount(
|
||||
rawValue: BigInt.from(fee),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$runtimeType prepareSend() failed: $e\n$s",
|
||||
level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<TxData> confirmSend({required TxData txData}) async {
|
||||
final senderKeyPair = await _getSenderKeyPair(index: 0);
|
||||
final sender = await stellarSdk.accounts.account(senderKeyPair.accountId);
|
||||
|
||||
final address = txData.recipients!.first.address;
|
||||
final amountToSend = txData.recipients!.first.amount;
|
||||
final memo = txData.memo;
|
||||
|
||||
//First check if account exists, can be skipped, but if the account does not exist,
|
||||
// the transaction fee will be charged when the transaction fails.
|
||||
final validAccount = await _accountExists(address);
|
||||
final stellar.TransactionBuilder transactionBuilder;
|
||||
|
||||
if (!validAccount) {
|
||||
//Fund the account, user must ensure account is correct
|
||||
final createAccBuilder = stellar.CreateAccountOperationBuilder(
|
||||
address,
|
||||
amountToSend.decimal.toString(),
|
||||
);
|
||||
transactionBuilder = stellar.TransactionBuilder(sender).addOperation(
|
||||
createAccBuilder.build(),
|
||||
);
|
||||
} else {
|
||||
transactionBuilder = stellar.TransactionBuilder(sender).addOperation(
|
||||
stellar.PaymentOperationBuilder(
|
||||
address,
|
||||
stellar.Asset.NATIVE,
|
||||
amountToSend.decimal.toString(),
|
||||
).build(),
|
||||
);
|
||||
}
|
||||
|
||||
if (memo != null) {
|
||||
transactionBuilder.addMemo(stellar.MemoText(memo));
|
||||
}
|
||||
|
||||
final transaction = transactionBuilder.build();
|
||||
|
||||
transaction.sign(senderKeyPair, stellarNetwork);
|
||||
try {
|
||||
final response = await stellarSdk.submitTransaction(transaction);
|
||||
if (!response.success) {
|
||||
throw Exception("${response.extras?.resultCodes?.transactionResultCode}"
|
||||
" ::: ${response.extras?.resultCodes?.operationsResultCodes}");
|
||||
}
|
||||
|
||||
return txData.copyWith(
|
||||
txHash: response.hash!,
|
||||
txid: response.hash!,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("Error sending TX $e - $s", level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
||||
final baseFee = await _getBaseFee();
|
||||
return Amount(
|
||||
rawValue: BigInt.from(baseFee),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<FeeObject> get fees async {
|
||||
int fee = await _getBaseFee();
|
||||
return FeeObject(
|
||||
numberOfBlocksFast: 1,
|
||||
numberOfBlocksAverage: 1,
|
||||
numberOfBlocksSlow: 1,
|
||||
fast: fee,
|
||||
medium: fee,
|
||||
slow: fee,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> pingCheck() async {
|
||||
final currentNode = getCurrentNode();
|
||||
return await testStellarNodeConnection(currentNode.host, currentNode.port);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> recover({required bool isRescan}) async {
|
||||
await refreshMutex.protect(() async {
|
||||
if (isRescan) {
|
||||
await mainDB.deleteWalletBlockchainData(walletId);
|
||||
}
|
||||
|
||||
await mainDB.updateOrPutAddresses([await _fetchStellarAddress(index: 0)]);
|
||||
});
|
||||
|
||||
if (isRescan) {
|
||||
unawaited(refresh());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateBalance() async {
|
||||
try {
|
||||
stellar.AccountResponse accountResponse;
|
||||
|
||||
try {
|
||||
accountResponse = await stellarSdk.accounts
|
||||
.account((await getCurrentReceivingAddress())!.value)
|
||||
.onError((error, stackTrace) => throw error!);
|
||||
} catch (e) {
|
||||
if (e is stellar.ErrorResponse &&
|
||||
e.body.contains("The resource at the url requested was not found. "
|
||||
"This usually occurs for one of two reasons: "
|
||||
"The url requested is not valid, or no data in our database "
|
||||
"could be found with the parameters provided.")) {
|
||||
// probably just doesn't have any history yet or whatever stellar needs
|
||||
return;
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"$runtimeType ${info.name} $walletId "
|
||||
"failed to fetch account to updateBalance",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
for (stellar.Balance balance in accountResponse.balances) {
|
||||
switch (balance.assetType) {
|
||||
case stellar.Asset.TYPE_NATIVE:
|
||||
final swBalance = Balance(
|
||||
total: Amount(
|
||||
rawValue: BigInt.from(float.parse(balance.balance) * 10000000),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
spendable: Amount(
|
||||
rawValue: BigInt.from(float.parse(balance.balance) * 10000000),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
blockedTotal: Amount(
|
||||
rawValue: BigInt.from(0),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
pendingSpendable: Amount(
|
||||
rawValue: BigInt.from(0),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
);
|
||||
await info.updateBalance(newBalance: swBalance, isar: mainDB.isar);
|
||||
}
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"$runtimeType ${info.name} $walletId "
|
||||
"updateBalance() failed: $e\n$s",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateChainHeight() async {
|
||||
try {
|
||||
final height = await stellarSdk.ledgers
|
||||
.order(stellar.RequestBuilderOrder.DESC)
|
||||
.limit(1)
|
||||
.execute()
|
||||
.then((value) => value.records!.first.sequence);
|
||||
await info.updateCachedChainHeight(newHeight: height, isar: mainDB.isar);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"$runtimeType updateChainHeight() failed: $e\n$s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateNode() async {
|
||||
_updateSdk();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateTransactions() async {
|
||||
try {
|
||||
final myAddress = (await getCurrentReceivingAddress())!;
|
||||
|
||||
List<TransactionV2> transactionList = [];
|
||||
stellar.Page<stellar.OperationResponse> payments;
|
||||
try {
|
||||
payments = await stellarSdk.payments
|
||||
.forAccount(myAddress.value)
|
||||
.order(stellar.RequestBuilderOrder.DESC)
|
||||
.execute();
|
||||
} catch (e) {
|
||||
if (e is stellar.ErrorResponse &&
|
||||
e.body.contains("The resource at the url requested was not found. "
|
||||
"This usually occurs for one of two reasons: "
|
||||
"The url requested is not valid, or no data in our database "
|
||||
"could be found with the parameters provided.")) {
|
||||
// probably just doesn't have any history yet or whatever stellar needs
|
||||
return;
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"Stellar ${info.name} $walletId failed to fetch transactions",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
for (stellar.OperationResponse response in payments.records!) {
|
||||
// PaymentOperationResponse por;
|
||||
if (response is stellar.PaymentOperationResponse) {
|
||||
final por = response;
|
||||
|
||||
final addressTo = por.to!.accountId;
|
||||
final addressFrom = por.from!.accountId;
|
||||
|
||||
final TransactionType type;
|
||||
if (addressFrom == myAddress.value) {
|
||||
if (addressTo == myAddress.value) {
|
||||
type = TransactionType.sentToSelf;
|
||||
} else {
|
||||
type = TransactionType.outgoing;
|
||||
}
|
||||
} else {
|
||||
type = TransactionType.incoming;
|
||||
}
|
||||
final amount = Amount(
|
||||
rawValue: BigInt.parse(float
|
||||
.parse(por.amount!)
|
||||
.toStringAsFixed(cryptoCurrency.fractionDigits)
|
||||
.replaceAll(".", "")),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
|
||||
// hack eth tx data into inputs and outputs
|
||||
final List<OutputV2> outputs = [];
|
||||
final List<InputV2> inputs = [];
|
||||
|
||||
OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor(
|
||||
scriptPubKeyHex: "00",
|
||||
valueStringSats: amount.raw.toString(),
|
||||
addresses: [
|
||||
addressTo,
|
||||
],
|
||||
walletOwns: addressTo == myAddress.value,
|
||||
);
|
||||
InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor(
|
||||
scriptSigHex: null,
|
||||
sequence: null,
|
||||
outpoint: null,
|
||||
addresses: [addressFrom],
|
||||
valueStringSats: amount.raw.toString(),
|
||||
witness: null,
|
||||
innerRedeemScriptAsm: null,
|
||||
coinbase: null,
|
||||
walletOwns: addressFrom == myAddress.value,
|
||||
);
|
||||
|
||||
outputs.add(output);
|
||||
inputs.add(input);
|
||||
|
||||
int fee = 0;
|
||||
int height = 0;
|
||||
//Query the transaction linked to the payment,
|
||||
// por.transaction returns a null sometimes
|
||||
stellar.TransactionResponse tx =
|
||||
await stellarSdk.transactions.transaction(por.transactionHash!);
|
||||
|
||||
if (tx.hash.isNotEmpty) {
|
||||
fee = tx.feeCharged!;
|
||||
height = tx.ledger;
|
||||
}
|
||||
|
||||
final otherData = {
|
||||
"overrideFee": Amount(
|
||||
rawValue: BigInt.from(fee),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
).toJsonString(),
|
||||
};
|
||||
|
||||
final theTransaction = TransactionV2(
|
||||
walletId: walletId,
|
||||
blockHash: "",
|
||||
hash: por.transactionHash!,
|
||||
txid: por.transactionHash!,
|
||||
timestamp:
|
||||
DateTime.parse(por.createdAt!).millisecondsSinceEpoch ~/ 1000,
|
||||
height: height,
|
||||
inputs: inputs,
|
||||
outputs: outputs,
|
||||
version: -1,
|
||||
type: type,
|
||||
subType: TransactionSubType.none,
|
||||
otherData: jsonEncode(otherData),
|
||||
);
|
||||
|
||||
transactionList.add(theTransaction);
|
||||
} else if (response is stellar.CreateAccountOperationResponse) {
|
||||
final caor = response;
|
||||
final TransactionType type;
|
||||
if (caor.sourceAccount == myAddress.value) {
|
||||
type = TransactionType.outgoing;
|
||||
} else {
|
||||
type = TransactionType.incoming;
|
||||
}
|
||||
final amount = Amount(
|
||||
rawValue: BigInt.parse(float
|
||||
.parse(caor.startingBalance!)
|
||||
.toStringAsFixed(cryptoCurrency.fractionDigits)
|
||||
.replaceAll(".", "")),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
|
||||
// hack eth tx data into inputs and outputs
|
||||
final List<OutputV2> outputs = [];
|
||||
final List<InputV2> inputs = [];
|
||||
|
||||
OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor(
|
||||
scriptPubKeyHex: "00",
|
||||
valueStringSats: amount.raw.toString(),
|
||||
addresses: [
|
||||
// this is what the previous code was doing and I don't think its correct
|
||||
caor.sourceAccount!,
|
||||
],
|
||||
walletOwns: caor.sourceAccount! == myAddress.value,
|
||||
);
|
||||
InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor(
|
||||
scriptSigHex: null,
|
||||
sequence: null,
|
||||
outpoint: null,
|
||||
addresses: [
|
||||
// this is what the previous code was doing and I don't think its correct
|
||||
caor.sourceAccount!,
|
||||
],
|
||||
valueStringSats: amount.raw.toString(),
|
||||
witness: null,
|
||||
innerRedeemScriptAsm: null,
|
||||
coinbase: null,
|
||||
walletOwns: caor.sourceAccount! == myAddress.value,
|
||||
);
|
||||
|
||||
outputs.add(output);
|
||||
inputs.add(input);
|
||||
|
||||
int fee = 0;
|
||||
int height = 0;
|
||||
final tx =
|
||||
await stellarSdk.transactions.transaction(caor.transactionHash!);
|
||||
if (tx.hash.isNotEmpty) {
|
||||
fee = tx.feeCharged!;
|
||||
height = tx.ledger;
|
||||
}
|
||||
|
||||
final otherData = {
|
||||
"overrideFee": Amount(
|
||||
rawValue: BigInt.from(fee),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
).toJsonString(),
|
||||
};
|
||||
|
||||
final theTransaction = TransactionV2(
|
||||
walletId: walletId,
|
||||
blockHash: "",
|
||||
hash: caor.transactionHash!,
|
||||
txid: caor.transactionHash!,
|
||||
timestamp:
|
||||
DateTime.parse(caor.createdAt!).millisecondsSinceEpoch ~/ 1000,
|
||||
height: height,
|
||||
inputs: inputs,
|
||||
outputs: outputs,
|
||||
version: -1,
|
||||
type: type,
|
||||
subType: TransactionSubType.none,
|
||||
otherData: jsonEncode(otherData),
|
||||
);
|
||||
|
||||
transactionList.add(theTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
await mainDB.updateOrPutTransactionV2s(transactionList);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Exception rethrown from updateTransactions(): $e\n$s",
|
||||
level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> updateUTXOs() async {
|
||||
// do nothing for stellar
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ import 'package:stackwallet/wallets/wallet/impl/monero_wallet.dart';
|
|||
import 'package:stackwallet/wallets/wallet/impl/namecoin_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/nano_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/particl_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/stellar_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/tezos_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/wownero_wallet.dart';
|
||||
|
@ -209,7 +210,7 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
static Wallet loadTokenWallet({
|
||||
required EthereumWallet ethWallet,
|
||||
required EthContract contract,
|
||||
}) {
|
||||
}) {
|
||||
final Wallet wallet = EthTokenWallet(
|
||||
ethWallet,
|
||||
contract,
|
||||
|
@ -329,6 +330,11 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
case Coin.particl:
|
||||
return ParticlWallet(CryptoCurrencyNetwork.main);
|
||||
|
||||
case Coin.stellar:
|
||||
return StellarWallet(CryptoCurrencyNetwork.main);
|
||||
case Coin.stellarTestnet:
|
||||
return StellarWallet(CryptoCurrencyNetwork.test);
|
||||
|
||||
case Coin.tezos:
|
||||
return TezosWallet(CryptoCurrencyNetwork.main);
|
||||
|
||||
|
|
Loading…
Reference in a new issue