mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-05-03 03:12:18 +00:00
allow sending to unfunded account
This commit is contained in:
parent
bbd070ba7f
commit
c68b2e0d02
1 changed files with 170 additions and 104 deletions
|
@ -153,38 +153,65 @@ class StellarWallet extends CoinServiceAPI
|
||||||
Coin get coin => _coin;
|
Coin get coin => _coin;
|
||||||
late Coin _coin;
|
late Coin _coin;
|
||||||
|
|
||||||
|
Future<bool> accoutExists(String accountId) async {
|
||||||
|
bool exists = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
AccountResponse 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;
|
||||||
|
}
|
||||||
@override
|
@override
|
||||||
Future<String> confirmSend({required Map<String, dynamic> txData}) async {
|
Future<String> confirmSend({required Map<String, dynamic> txData}) async {
|
||||||
print("TX DATA IS $txData");
|
|
||||||
final secretSeed = await _secureStore.read(
|
final secretSeed = await _secureStore.read(
|
||||||
key: '${_walletId}_secretSeed'
|
key: '${_walletId}_secretSeed'
|
||||||
);
|
);
|
||||||
|
|
||||||
// final amt = Amount(
|
|
||||||
// rawValue: BigInt.from(txData['recipientAmt']),
|
|
||||||
// fractionDigits: coin.decimals,
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// print("THIS AMOUNT IS $amount");
|
|
||||||
//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.
|
|
||||||
AccountResponse receiverAccount = await stellarSdk.accounts
|
|
||||||
.account(txData['address'] as String).onError(
|
|
||||||
(error, stackTrace) => throw("Error getting account :: Cannot send transaction"));
|
|
||||||
|
|
||||||
KeyPair senderKeyPair = KeyPair.fromSecretSeed(secretSeed!);
|
KeyPair senderKeyPair = KeyPair.fromSecretSeed(secretSeed!);
|
||||||
AccountResponse sender = await stellarSdk.accounts.account(senderKeyPair.accountId);
|
AccountResponse sender = await stellarSdk.accounts.account(senderKeyPair.accountId);
|
||||||
|
|
||||||
Transaction transaction = TransactionBuilder(sender)
|
final amountToSend = txData['recipientAmt'] as Amount;
|
||||||
.addOperation(PaymentOperationBuilder(receiverAccount.accountId, Asset.NATIVE, "100").build())
|
|
||||||
.build();
|
|
||||||
transaction.sign(senderKeyPair, Network.TESTNET);
|
|
||||||
SubmitTransactionResponse response = await stellarSdk.submitTransaction(transaction);
|
|
||||||
|
|
||||||
if (!response.success) {
|
//First check if account exists, can be skipped, but if the account does not exist,
|
||||||
throw("Unable to send transaction");
|
// the transaction fee will be charged when the transaction fails.
|
||||||
|
bool validAccount = await accoutExists(txData['address'] as String);
|
||||||
|
Transaction transaction;
|
||||||
|
|
||||||
|
if (!validAccount) {
|
||||||
|
//Fund the account, user must ensure account is correct
|
||||||
|
CreateAccountOperationBuilder createAccBuilder =
|
||||||
|
CreateAccountOperationBuilder(
|
||||||
|
txData['address'] as String, amountToSend.decimal.toString()
|
||||||
|
);
|
||||||
|
transaction = TransactionBuilder(sender)
|
||||||
|
.addOperation(createAccBuilder.build())
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
transaction = TransactionBuilder(sender)
|
||||||
|
.addOperation(PaymentOperationBuilder(
|
||||||
|
txData['address'] as String, Asset.NATIVE,
|
||||||
|
amountToSend.decimal.toString())
|
||||||
|
.build()
|
||||||
|
).build();
|
||||||
}
|
}
|
||||||
return response.hash!;
|
transaction.sign(senderKeyPair, Network.TESTNET);
|
||||||
|
try {
|
||||||
|
SubmitTransactionResponse response = await stellarSdk.submitTransaction(transaction);
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
throw("Unable to send transaction");
|
||||||
|
}
|
||||||
|
return response.hash!;
|
||||||
|
} catch(e, s) {
|
||||||
|
Logging.instance.log("Error sending TX $e - $s", level: LogLevel.Error);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SWAddress.Address?> get _currentReceivingAddress =>
|
Future<SWAddress.Address?> get _currentReceivingAddress =>
|
||||||
|
@ -460,68 +487,72 @@ class StellarWallet extends CoinServiceAPI
|
||||||
);
|
);
|
||||||
|
|
||||||
for (OperationResponse response in payments.records!) {
|
for (OperationResponse response in payments.records!) {
|
||||||
|
PaymentOperationResponse por;
|
||||||
if (response is PaymentOperationResponse) {
|
if (response is PaymentOperationResponse) {
|
||||||
var por = response;
|
por = response;
|
||||||
|
|
||||||
|
Logging.instance.log(
|
||||||
|
"ALL TRANSACTIONS IS $por",
|
||||||
|
level: LogLevel.Info);
|
||||||
SWTransaction.TransactionType type;
|
SWTransaction.TransactionType type;
|
||||||
if (por.sourceAccount == await getAddressSW()) {
|
if (por.sourceAccount == await getAddressSW()) {
|
||||||
type = SWTransaction.TransactionType.outgoing;
|
type = SWTransaction.TransactionType.outgoing;
|
||||||
} else {
|
} else {
|
||||||
type = SWTransaction.TransactionType.incoming;
|
type = SWTransaction.TransactionType.incoming;
|
||||||
}
|
}
|
||||||
final amount = Amount(
|
|
||||||
rawValue: BigInt.parse(float.parse(por.amount!).toStringAsFixed(7).replaceAll(".", "")),
|
|
||||||
fractionDigits: 7,
|
|
||||||
<<<<<<< HEAD
|
|
||||||
);
|
|
||||||
int fee = 0;
|
|
||||||
int height = 0;
|
|
||||||
var transaction = por.transaction;
|
|
||||||
if (transaction != null) {
|
|
||||||
fee = transaction.feeCharged!;
|
|
||||||
height = transaction.ledger;
|
|
||||||
}
|
|
||||||
var theTransaction = SWTransaction.Transaction(
|
|
||||||
walletId: walletId,
|
|
||||||
txid: por.transactionHash!,
|
|
||||||
timestamp: DateTime.parse(por.createdAt!).millisecondsSinceEpoch ~/ 1000,
|
|
||||||
type: type,
|
|
||||||
subType: SWTransaction.TransactionSubType.none,
|
|
||||||
amount: 0,
|
|
||||||
amountString: amount.toJsonString(),
|
|
||||||
fee: fee,
|
|
||||||
height: height,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: false,
|
|
||||||
slateId: "",
|
|
||||||
otherData: "",
|
|
||||||
inputs: [],
|
|
||||||
outputs: [],
|
|
||||||
nonce: 0,
|
|
||||||
numberOfMessages: null,
|
|
||||||
);
|
|
||||||
SWAddress.Address? receivingAddress = await _currentReceivingAddress;
|
|
||||||
SWAddress.Address address = type == SWTransaction.TransactionType.incoming
|
|
||||||
? receivingAddress!
|
|
||||||
: SWAddress.Address(
|
|
||||||
walletId: walletId,
|
|
||||||
value: por.sourceAccount!,
|
|
||||||
publicKey: KeyPair.fromAccountId(por.sourceAccount!).publicKey,
|
|
||||||
derivationIndex: 0,
|
|
||||||
derivationPath: null,
|
|
||||||
type: SWAddress.AddressType.unknown, // TODO: set type
|
|
||||||
subType: SWAddress.AddressSubType.unknown
|
|
||||||
);
|
|
||||||
Tuple2<SWTransaction.Transaction, SWAddress.Address> tuple = Tuple2(theTransaction, address);
|
|
||||||
transactionList.add(tuple);
|
|
||||||
} else if (response is CreateAccountOperationResponse) {
|
|
||||||
var caor = response;
|
|
||||||
SWTransaction.TransactionType type;
|
|
||||||
if (caor.sourceAccount == await getAddressSW()) {
|
|
||||||
type = SWTransaction.TransactionType.outgoing;
|
|
||||||
} else {
|
|
||||||
type = SWTransaction.TransactionType.incoming;
|
|
||||||
}
|
|
||||||
final amount = Amount(
|
final amount = Amount(
|
||||||
|
rawValue: BigInt.parse(float.parse(por.amount!).toStringAsFixed(coin.decimals).replaceAll(".", "")),
|
||||||
|
fractionDigits: coin.decimals,
|
||||||
|
);
|
||||||
|
int fee = 0;
|
||||||
|
int height = 0;
|
||||||
|
var transaction = por.transaction;
|
||||||
|
if (transaction != null) {
|
||||||
|
fee = transaction.feeCharged!;
|
||||||
|
height = transaction.ledger;
|
||||||
|
}
|
||||||
|
var theTransaction = SWTransaction.Transaction(
|
||||||
|
walletId: walletId,
|
||||||
|
txid: por.transactionHash!,
|
||||||
|
timestamp: DateTime.parse(por.createdAt!).millisecondsSinceEpoch ~/ 1000,
|
||||||
|
type: type,
|
||||||
|
subType: SWTransaction.TransactionSubType.none,
|
||||||
|
amount: 0,
|
||||||
|
amountString: amount.toJsonString(),
|
||||||
|
fee: fee,
|
||||||
|
height: height,
|
||||||
|
isCancelled: false,
|
||||||
|
isLelantus: false,
|
||||||
|
slateId: "",
|
||||||
|
otherData: "",
|
||||||
|
inputs: [],
|
||||||
|
outputs: [],
|
||||||
|
nonce: 0,
|
||||||
|
numberOfMessages: null,
|
||||||
|
);
|
||||||
|
SWAddress.Address? receivingAddress = await _currentReceivingAddress;
|
||||||
|
SWAddress.Address address = type == SWTransaction.TransactionType.incoming
|
||||||
|
? receivingAddress!
|
||||||
|
: SWAddress.Address(
|
||||||
|
walletId: walletId,
|
||||||
|
value: por.sourceAccount!,
|
||||||
|
publicKey: KeyPair.fromAccountId(por.sourceAccount!).publicKey,
|
||||||
|
derivationIndex: 0,
|
||||||
|
derivationPath: null,
|
||||||
|
type: SWAddress.AddressType.unknown, // TODO: set type
|
||||||
|
subType: SWAddress.AddressSubType.unknown,
|
||||||
|
);
|
||||||
|
Tuple2<SWTransaction.Transaction, SWAddress.Address> tuple = Tuple2(theTransaction, address);
|
||||||
|
transactionList.add(tuple);
|
||||||
|
} else if (response is CreateAccountOperationResponse) {
|
||||||
|
var caor = response;
|
||||||
|
SWTransaction.TransactionType type;
|
||||||
|
if (caor.sourceAccount == await getAddressSW()) {
|
||||||
|
type = SWTransaction.TransactionType.outgoing;
|
||||||
|
} else {
|
||||||
|
type = SWTransaction.TransactionType.incoming;
|
||||||
|
}
|
||||||
|
final amount = Amount(
|
||||||
rawValue: BigInt.parse(float.parse(caor.startingBalance!).toStringAsFixed(coin.decimals).replaceAll(".", "")),
|
rawValue: BigInt.parse(float.parse(caor.startingBalance!).toStringAsFixed(coin.decimals).replaceAll(".", "")),
|
||||||
fractionDigits: coin.decimals,
|
fractionDigits: coin.decimals,
|
||||||
);
|
);
|
||||||
|
@ -572,37 +603,43 @@ class StellarWallet extends CoinServiceAPI
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Exception rethrown from updateTransactions(): $e\n$s",
|
"Exception rethrown from updateTransactions(): $e\n$s",
|
||||||
level: LogLevel.Error);
|
level: LogLevel.Error);
|
||||||
rethrow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateBalance() async {
|
Future<void> updateBalance() async {
|
||||||
AccountResponse accountResponse = await stellarSdk.accounts.account(await getAddressSW());
|
try {
|
||||||
for (Balance balance in accountResponse.balances) {
|
AccountResponse accountResponse = await stellarSdk.accounts.account(await getAddressSW());
|
||||||
switch (balance.assetType) {
|
for (Balance balance in accountResponse.balances) {
|
||||||
case Asset.TYPE_NATIVE:
|
switch (balance.assetType) {
|
||||||
_balance = SWBalance.Balance(
|
case Asset.TYPE_NATIVE:
|
||||||
total: Amount(
|
_balance = SWBalance.Balance(
|
||||||
rawValue: BigInt.from(float.parse(balance.balance) * 10000000 - 10000000), // Minus 1 XLM for account activation fee
|
total: Amount(
|
||||||
fractionDigits: 7,
|
rawValue: BigInt.from(float.parse(balance.balance) * 10000000 - 10000000), // Minus 1 XLM for account activation fee
|
||||||
),
|
fractionDigits: coin.decimals,
|
||||||
spendable: Amount(
|
),
|
||||||
rawValue: BigInt.from(float.parse(balance.balance) * 10000000 - 10000000), // Minus 1 XLM for account activation fee
|
spendable: Amount(
|
||||||
fractionDigits: 7,
|
rawValue: BigInt.from(float.parse(balance.balance) * 10000000 - 10000000), // Minus 1 XLM for account activation fee
|
||||||
),
|
fractionDigits: coin.decimals,
|
||||||
blockedTotal: Amount(
|
),
|
||||||
rawValue: BigInt.from(0),
|
blockedTotal: Amount(
|
||||||
fractionDigits: 7,
|
rawValue: BigInt.from(0),
|
||||||
),
|
fractionDigits: coin.decimals,
|
||||||
pendingSpendable: Amount(
|
),
|
||||||
rawValue: BigInt.from(0),
|
pendingSpendable: Amount(
|
||||||
fractionDigits: 7,
|
rawValue: BigInt.from(0),
|
||||||
),
|
fractionDigits: coin.decimals,
|
||||||
);
|
),
|
||||||
Logging.instance.log(_balance, level: LogLevel.Info);
|
);
|
||||||
await updateCachedBalance(_balance!);
|
Logging.instance.log(_balance, level: LogLevel.Info);
|
||||||
|
await updateCachedBalance(_balance!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch(e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"ERROR GETTING BALANCE $e\$s",
|
||||||
|
level: LogLevel.Info,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,9 +734,38 @@ class StellarWallet extends CoinServiceAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) {
|
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async {
|
||||||
// TODO: implement updateSentCachedTxData
|
final transaction = SWTransaction.Transaction(
|
||||||
throw UnimplementedError();
|
walletId: walletId,
|
||||||
|
txid: txData["txid"] as String,
|
||||||
|
timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
type: SWTransaction.TransactionType.outgoing,
|
||||||
|
subType: SWTransaction.TransactionSubType.none,
|
||||||
|
// precision may be lost here hence the following amountString
|
||||||
|
amount: (txData["recipientAmt"] as Amount).raw.toInt(),
|
||||||
|
amountString: (txData["recipientAmt"] as Amount).toJsonString(),
|
||||||
|
fee: txData["fee"] as int,
|
||||||
|
height: null,
|
||||||
|
isCancelled: false,
|
||||||
|
isLelantus: false,
|
||||||
|
otherData: null,
|
||||||
|
slateId: null,
|
||||||
|
nonce: null,
|
||||||
|
inputs: [],
|
||||||
|
outputs: [],
|
||||||
|
numberOfMessages: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
final address = txData["address"] is String
|
||||||
|
? await db.getAddress(walletId, txData["address"] as String)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
await db.addNewTransactionData(
|
||||||
|
[
|
||||||
|
Tuple2(transaction, address),
|
||||||
|
],
|
||||||
|
walletId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
Loading…
Reference in a new issue