From 5f75e2889cdcb659456774bec3b228e5c9cef849 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Tue, 3 Dec 2024 01:38:52 +0200 Subject: [PATCH 01/15] WIP: support creating && cancelling orders --- .../stellar_client/bin/stellar_client.dart | 26 ++- packages/stellar_client/lib/src/client.dart | 182 +++++++++++++++--- 2 files changed, 175 insertions(+), 33 deletions(-) diff --git a/packages/stellar_client/bin/stellar_client.dart b/packages/stellar_client/bin/stellar_client.dart index c66f7d75..f380f188 100644 --- a/packages/stellar_client/bin/stellar_client.dart +++ b/packages/stellar_client/bin/stellar_client.dart @@ -1,14 +1,24 @@ import 'package:stellar_client/stellar_client.dart'; void main() async { - final stellarClient = Client.create(NetworkType.PUBLIC); - await stellarClient.activateThroughThreefoldService(); + // final stellarClient = Client.create(NetworkType.PUBLIC); + // await stellarClient.activateThroughThreefoldService(); - await stellarClient.transfer( - destinationAddress: "destination-public-key", - amount: "20", - currency: "TFT", - memoText: "Memo Text"); + // await stellarClient.transfer( + // destinationAddress: "destination-public-key", + // amount: "20", + // currency: "TFT", + // memoText: "Memo Text"); - await stellarClient.getTransactions(); + // await stellarClient.getTransactions(); + + final stellarClient = Client.create(NetworkType.TESTNET); + await stellarClient.activateThroughFriendBot( + accountId: stellarClient.accountId); + await stellarClient.addTrustLine(); + final balances = await stellarClient.getBalance(); + print(balances); + await stellarClient.listOffers(); + // await stellarClient.getOrderBook( + // sellingAssetCode: 'USDC', buyingAssetCode: 'TFT'); } diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 99b99320..67495f4f 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -6,6 +6,7 @@ class Client { late KeyPair _keyPair; late currency.Currencies _currencies; late Map _serviceUrls; + late Map _horizonServerUrls; late Network _stellarNetwork; String get accountId => _keyPair.accountId; @@ -37,10 +38,16 @@ class Client { void _initialize() { late final currency.Currency tft; + late final currency.Currency usdc; _serviceUrls = { 'PUBLIC': 'https://tokenservices.threefold.io/threefoldfoundation', 'TESTNET': 'https://testnet.threefold.io/threefoldfoundation' }; + _horizonServerUrls = { + 'PUBLIC': 'https://horizon.stellar.org/', + 'TESTNET': 'https://horizon-testnet.stellar.org/' + }; + switch (_network) { case NetworkType.TESTNET: _sdk = StellarSDK.TESTNET; @@ -49,6 +56,9 @@ class Client { assetCode: 'TFT', issuer: "GA47YZA3PKFUZMPLQ3B5F2E3CJIB57TGGU7SPCQT2WAEYKN766PWIMB3", ); + usdc = currency.Currency( + assetCode: 'USDC', + issuer: 'GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5'); break; case NetworkType.PUBLIC: _sdk = StellarSDK.PUBLIC; @@ -57,12 +67,15 @@ class Client { assetCode: 'TFT', issuer: "GBOVQKJYHXRR3DX6NOX2RRYFRCUMSADGDESTDNBDS6CDVLGVESRTAC47", ); + usdc = currency.Currency( + assetCode: 'USDC', + issuer: 'GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN'); break; default: throw Exception('Unsupported network type'); } - _currencies = currency.Currencies({'TFT': tft}); + _currencies = currency.Currencies({'TFT': tft, 'USDC': usdc}); } Future activateThroughThreefoldService() async { @@ -121,38 +134,41 @@ class Client { } Future addTrustLine() async { - for (var entry in _currencies.currencies.entries) { - String currencyCode = entry.key; - currency.Currency currentCurrency = entry.value; + try { + for (var entry in _currencies.currencies.entries) { + String currencyCode = entry.key; + currency.Currency currentCurrency = entry.value; - String issuerAccountId = currentCurrency.issuer; - Asset currencyAsset = - AssetTypeCreditAlphaNum4(currentCurrency.assetCode, issuerAccountId); + String issuerAccountId = currentCurrency.issuer; + Asset currencyAsset = AssetTypeCreditAlphaNum4( + currentCurrency.assetCode, issuerAccountId); - ChangeTrustOperationBuilder changeTrustOperation = - ChangeTrustOperationBuilder(currencyAsset, "300000"); + ChangeTrustOperationBuilder changeTrustOperation = + ChangeTrustOperationBuilder(currencyAsset, "300000"); - final account = await _sdk.accounts.account(accountId); + final account = await _sdk.accounts.account(accountId); - Transaction transaction = TransactionBuilder(account) - .addOperation(changeTrustOperation.build()) - .build(); - transaction.sign(_keyPair, _stellarNetwork); + Transaction transaction = TransactionBuilder(account) + .addOperation(changeTrustOperation.build()) + .build(); + transaction.sign(_keyPair, _stellarNetwork); - SubmitTransactionResponse response = - await _sdk.submitTransaction(transaction); + SubmitTransactionResponse response = + await _sdk.submitTransaction(transaction); - if (!response.success) { - print("Failed to add trustline for $currencyCode"); - return false; - } else { - print("trustline for $currencyCode was added successfully"); - return true; + if (!response.success) { + print("Failed to add trustline for $currencyCode"); + return false; + } else { + print("trustline for $currencyCode was added successfully"); + } } - } - print("No trustlines were processed"); - return false; + return true; + } catch (error) { + print("An error occurred while adding trustlines: $error"); + return false; + } } Future transfer( @@ -479,4 +495,120 @@ class Client { throw Exception("Couldn't get memo text due to ${e}"); } } + + Future createOrder( + {required String fromAssetCode, + required String toAssetCode, + required String amount, + required String price, + String? memo}) async { + if (!_currencies.currencies.containsKey(fromAssetCode)) { + throw Exception('Sell asset $fromAssetCode is not available.'); + } + if (!_currencies.currencies.containsKey(toAssetCode)) { + throw Exception('Buy asset $toAssetCode is not available.'); + } + + final Asset buyAsset = AssetTypeCreditAlphaNum12( + _currencies.currencies[fromAssetCode]!.assetCode, + _currencies.currencies[fromAssetCode]!.issuer); + final Asset sellAsset = AssetTypeCreditAlphaNum12( + _currencies.currencies[toAssetCode]!.assetCode, + _currencies.currencies[toAssetCode]!.issuer); + final ManageBuyOfferOperation buyOfferOperation = + ManageBuyOfferOperationBuilder(buyAsset, sellAsset, amount, price) + .build(); + + final account = await _sdk.accounts.account(accountId); + + final balances = account.balances; + final sellAssetBalance = balances.firstWhere( + (balance) => balance.assetCode == fromAssetCode, + orElse: () => throw Exception('Insufficient balance in $fromAssetCode'), + ); + + final double sellAmount = double.parse(amount); + final double availableBalance = double.parse(sellAssetBalance.balance); + if (sellAmount > availableBalance) { + throw Exception( + 'Insufficient balance in $fromAssetCode. Available: $availableBalance'); + } + final Transaction transaction = TransactionBuilder(account) + .addOperation(buyOfferOperation) + .addMemo(memo != null ? Memo.text(memo) : Memo.none()) + .build(); + transaction.sign(_keyPair, _stellarNetwork); + try { + final SubmitTransactionResponse response = + await _sdk.submitTransaction(transaction); + return response; + } catch (error) { + throw Exception('Transaction failed due to: ${error.toString()}'); + } + } + + // native --> Represents the Stellar native asset (XLM) + // credit_alphanum4: Represents a credit asset with a 4-character code. + // credit_alphanum12: Represents a credit asset with a 12-character code. + Future getOrderBook({ + // required String sellingAssetType, + required String sellingAssetCode, + // required String buyingAssetType, + required String buyingAssetCode, + int limit = 200, + }) async { + if (!_currencies.currencies.containsKey(sellingAssetCode)) { + throw Exception('Sell asset $sellingAssetCode is not available.'); + } + if (!_currencies.currencies.containsKey(buyingAssetCode)) { + throw Exception('Buy asset $buyingAssetCode is not available.'); + } + + final sellingAssetIssuer = _currencies.currencies[sellingAssetCode]!.issuer; + final buyingAssetIssuer = _currencies.currencies[buyingAssetCode]!.issuer; + + final String horizonUrl = _horizonServerUrls[_network.toString()]!; + + final String endpoint = + '/order_book?selling_asset_type=credit_alphanum4&selling_asset_code=$sellingAssetCode&selling_asset_issuer=$sellingAssetIssuer&buying_asset_type=credit_alphanum4&buying_asset_code=$buyingAssetCode&buying_asset_issuer=$buyingAssetIssuer&limit=$limit'; + + final Uri uri = Uri.parse(horizonUrl + endpoint); + + try { + final response = await http.get(uri); + + if (response.statusCode == 200) { + final Map data = json.decode(response.body); + print(data); + + } else { + print('Failed to load order book. Status code: ${response.statusCode}'); + } + } catch (e) { + print('Error fetching order book: $e'); + } + } + + Future listOffers() async { + try { + final offers = await _sdk.offers.forAccount(accountId).execute(); + print(offers); + + if (offers.records.isEmpty) { + print('No offers found for account: $accountId'); + return; + } + + for (var offer in offers.records) { + print('Offer ID: ${offer.id}'); + print('Selling Asset: ${offer.selling}'); + print('Buying Asset: ${offer.buying}'); + print('Amount: ${offer.amount}'); + print('Price: ${offer.price}'); + print('-----------------------------------'); + } + } catch (error) { + print('Error listing offers for account $accountId: $error'); + } + } } From 0f603abd8eb2009dd33437898631cf899372301d Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Thu, 5 Dec 2024 12:41:42 +0200 Subject: [PATCH 02/15] support creating && cancelling orders --- .../stellar_client/bin/stellar_client.dart | 26 +-- packages/stellar_client/lib/src/client.dart | 153 ++++++++++++------ 2 files changed, 112 insertions(+), 67 deletions(-) diff --git a/packages/stellar_client/bin/stellar_client.dart b/packages/stellar_client/bin/stellar_client.dart index f380f188..c66f7d75 100644 --- a/packages/stellar_client/bin/stellar_client.dart +++ b/packages/stellar_client/bin/stellar_client.dart @@ -1,24 +1,14 @@ import 'package:stellar_client/stellar_client.dart'; void main() async { - // final stellarClient = Client.create(NetworkType.PUBLIC); - // await stellarClient.activateThroughThreefoldService(); + final stellarClient = Client.create(NetworkType.PUBLIC); + await stellarClient.activateThroughThreefoldService(); - // await stellarClient.transfer( - // destinationAddress: "destination-public-key", - // amount: "20", - // currency: "TFT", - // memoText: "Memo Text"); + await stellarClient.transfer( + destinationAddress: "destination-public-key", + amount: "20", + currency: "TFT", + memoText: "Memo Text"); - // await stellarClient.getTransactions(); - - final stellarClient = Client.create(NetworkType.TESTNET); - await stellarClient.activateThroughFriendBot( - accountId: stellarClient.accountId); - await stellarClient.addTrustLine(); - final balances = await stellarClient.getBalance(); - print(balances); - await stellarClient.listOffers(); - // await stellarClient.getOrderBook( - // sellingAssetCode: 'USDC', buyingAssetCode: 'TFT'); + await stellarClient.getTransactions(); } diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index f2716b77..3d6b70b1 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -487,42 +487,87 @@ class Client { } Future createOrder( - {required String fromAssetCode, - required String toAssetCode, + {required String sellingAsset, + required String buyingAsset, required String amount, required String price, String? memo}) async { - if (!_currencies.currencies.containsKey(fromAssetCode)) { - throw Exception('Sell asset $fromAssetCode is not available.'); + if (!_currencies.currencies.containsKey(sellingAsset)) { + throw Exception('Sell asset $sellingAsset is not available.'); } - if (!_currencies.currencies.containsKey(toAssetCode)) { - throw Exception('Buy asset $toAssetCode is not available.'); + if (!_currencies.currencies.containsKey(buyingAsset)) { + throw Exception('Buy asset $buyingAsset is not available.'); } - final Asset buyAsset = AssetTypeCreditAlphaNum12( - _currencies.currencies[fromAssetCode]!.assetCode, - _currencies.currencies[fromAssetCode]!.issuer); - final Asset sellAsset = AssetTypeCreditAlphaNum12( - _currencies.currencies[toAssetCode]!.assetCode, - _currencies.currencies[toAssetCode]!.issuer); + final Asset sellAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[sellingAsset]!.assetCode, + _currencies.currencies[sellingAsset]!.issuer); + final Asset buyAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[buyingAsset]!.assetCode, + _currencies.currencies[buyingAsset]!.issuer); + final ManageBuyOfferOperation buyOfferOperation = - ManageBuyOfferOperationBuilder(buyAsset, sellAsset, amount, price) + ManageBuyOfferOperationBuilder(sellAsset, buyAsset, amount, price) .build(); final account = await _sdk.accounts.account(accountId); final balances = account.balances; final sellAssetBalance = balances.firstWhere( - (balance) => balance.assetCode == fromAssetCode, - orElse: () => throw Exception('Insufficient balance in $fromAssetCode'), + (balance) => balance.assetCode == sellingAsset, + orElse: () => throw Exception('Insufficient balance in $sellingAsset'), ); final double sellAmount = double.parse(amount); final double availableBalance = double.parse(sellAssetBalance.balance); if (sellAmount > availableBalance) { throw Exception( - 'Insufficient balance in $fromAssetCode. Available: $availableBalance'); + 'Insufficient balance in $sellingAsset. Available: $availableBalance'); + } + final Transaction transaction = TransactionBuilder(account) + .addOperation(buyOfferOperation) + .addMemo(memo != null ? Memo.text(memo) : Memo.none()) + .build(); + print('Transaction XDR: ${transaction.toEnvelopeXdrBase64()}'); + + transaction.sign(_keyPair, _stellarNetwork); + try { + final SubmitTransactionResponse response = + await _sdk.submitTransaction(transaction); + if (!response.success) { + print('Transaction failed with result: ${response.resultXdr}'); + } + return response; + } catch (error) { + throw Exception('Transaction failed due to: ${error.toString()}'); + } + } + + Future cancelOrder( + {required String sellingAsset, + required String buyingAsset, + required String offerId, + String? memo}) async { + if (!_currencies.currencies.containsKey(sellingAsset)) { + throw Exception('Sell asset $sellingAsset is not available.'); + } + if (!_currencies.currencies.containsKey(buyingAsset)) { + throw Exception('Buy asset $buyingAsset is not available.'); } + + final Asset sellAsset = AssetTypeCreditAlphaNum12( + _currencies.currencies[sellingAsset]!.assetCode, + _currencies.currencies[sellingAsset]!.issuer); + final Asset buyAsset = AssetTypeCreditAlphaNum12( + _currencies.currencies[buyingAsset]!.assetCode, + _currencies.currencies[buyingAsset]!.issuer); + + final ManageBuyOfferOperation buyOfferOperation = + ManageBuyOfferOperationBuilder(sellAsset, buyAsset, '0', '0') + .setOfferId(offerId) + .build(); + + final account = await _sdk.accounts.account(accountId); final Transaction transaction = TransactionBuilder(account) .addOperation(buyOfferOperation) .addMemo(memo != null ? Memo.text(memo) : Memo.none()) @@ -539,54 +584,63 @@ class Client { // native --> Represents the Stellar native asset (XLM) // credit_alphanum4: Represents a credit asset with a 4-character code. - // credit_alphanum12: Represents a credit asset with a 12-character code. - Future getOrderBook({ - // required String sellingAssetType, - required String sellingAssetCode, - // required String buyingAssetType, - required String buyingAssetCode, - int limit = 200, - }) async { + // credit_alphanum12: Represents a credit asset with a 12-character code + + Future getOrderBook( + {required String sellingAssetCode, + required String buyingAssetCode}) async { if (!_currencies.currencies.containsKey(sellingAssetCode)) { throw Exception('Sell asset $sellingAssetCode is not available.'); } if (!_currencies.currencies.containsKey(buyingAssetCode)) { throw Exception('Buy asset $buyingAssetCode is not available.'); } + http.Client httpClient = http.Client(); + Uri serverURI = Uri.parse(_horizonServerUrls[_network.toString()]!); + + Asset sellingAsset = Asset.createNonNativeAsset( + sellingAssetCode, _currencies.currencies[sellingAssetCode]!.issuer); + Asset buyingAsset = Asset.createNonNativeAsset( + buyingAssetCode, _currencies.currencies[buyingAssetCode]!.issuer); + + OrderBookRequestBuilder orderBookRequest = + OrderBookRequestBuilder(httpClient, serverURI) + ..sellingAsset(sellingAsset) + ..buyingAsset(buyingAsset); + + Stream orderBookStream = orderBookRequest.stream(); + orderBookStream.listen((orderBookResponse) { + print("Received OrderBookResponse:"); + print("Base: ${orderBookResponse.base}"); + print("Counter: ${orderBookResponse.counter}"); + + print("\nBids:"); + for (var offer in orderBookResponse.bids) { + // priceR numerator/denominator + print( + 'Bid - Amount: ${offer.amount}, Price: ${offer.price}, PriceR: ${offer.priceR}'); + } - final sellingAssetIssuer = _currencies.currencies[sellingAssetCode]!.issuer; - final buyingAssetIssuer = _currencies.currencies[buyingAssetCode]!.issuer; - - final String horizonUrl = _horizonServerUrls[_network.toString()]!; - - final String endpoint = - '/order_book?selling_asset_type=credit_alphanum4&selling_asset_code=$sellingAssetCode&selling_asset_issuer=$sellingAssetIssuer&buying_asset_type=credit_alphanum4&buying_asset_code=$buyingAssetCode&buying_asset_issuer=$buyingAssetIssuer&limit=$limit'; - - final Uri uri = Uri.parse(horizonUrl + endpoint); - - try { - final response = await http.get(uri); - - if (response.statusCode == 200) { - final Map data = json.decode(response.body); - print(data); - - } else { - print('Failed to load order book. Status code: ${response.statusCode}'); + print("\nAsks:"); + for (var offer in orderBookResponse.asks) { + print( + 'Ask - Amount: ${offer.amount}, Price: ${offer.price}, PriceR: ${offer.priceR}'); } - } catch (e) { - print('Error fetching order book: $e'); - } + }, onError: (error) { + print("Error while listening to order book stream: $error"); + }, onDone: () { + print("Order book stream is closed."); + }); } - Future listOffers() async { + Future> listMyOffers() async { try { final offers = await _sdk.offers.forAccount(accountId).execute(); print(offers); if (offers.records.isEmpty) { print('No offers found for account: $accountId'); - return; + return []; } for (var offer in offers.records) { @@ -597,8 +651,9 @@ class Client { print('Price: ${offer.price}'); print('-----------------------------------'); } + return offers.records; } catch (error) { - print('Error listing offers for account $accountId: $error'); + throw Exception('Error listing offers for account $accountId: $error'); } } } From 3d6c32086a4f4c697e714328885392580710ec23 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 8 Dec 2024 10:15:44 +0200 Subject: [PATCH 03/15] WIP: trying to create orders with TF service --- .../stellar_client/bin/stellar_client.dart | 55 +++++++-- packages/stellar_client/lib/src/client.dart | 111 +++++++++++++++++- 2 files changed, 152 insertions(+), 14 deletions(-) diff --git a/packages/stellar_client/bin/stellar_client.dart b/packages/stellar_client/bin/stellar_client.dart index c66f7d75..de0de7a6 100644 --- a/packages/stellar_client/bin/stellar_client.dart +++ b/packages/stellar_client/bin/stellar_client.dart @@ -1,14 +1,53 @@ import 'package:stellar_client/stellar_client.dart'; void main() async { - final stellarClient = Client.create(NetworkType.PUBLIC); - await stellarClient.activateThroughThreefoldService(); + // final stellarClient = Client.create(NetworkType.PUBLIC); + // await stellarClient.activateThroughThreefoldService(); - await stellarClient.transfer( - destinationAddress: "destination-public-key", - amount: "20", - currency: "TFT", - memoText: "Memo Text"); + // await stellarClient.transfer( + // destinationAddress: "destination-public-key", + // amount: "20", + // currency: "TFT", + // memoText: "Memo Text"); - await stellarClient.getTransactions(); + // await stellarClient.getTransactions(); + + final stellarClient = Client(NetworkType.PUBLIC, + "SDSVD7L2OZXHW2PLARRJ34ZRJVBPMJWKVG4HNIEJKFHI35E3D3WWAUL4"); + print(stellarClient.accountId); + print(stellarClient.secretSeed); + + // final response = await stellarClient.createOrderThroughThreefoldService( + // sellingAsset: 'TFT', buyingAsset: 'USDC', amount: '5', price: '0.9'); + + // // await stellarClient.addTrustLineThroughThreefoldService('USDC'); + await stellarClient.createOrderThroughThreefoldService( + sellingAsset: 'TFT', buyingAsset: 'USDC', amount: '3', price: '1'); + final balances = await stellarClient.getBalance(); + print(balances); + // // final orderbook = await stellarClient.getOrderBook( + // // sellingAssetCode: 'TFT', buyingAssetCode: 'USDC'); + // await stellarClient.listMyOffers(); + // await stellarClient.getOrderBook( + // sellingAssetCode: 'USDC', buyingAssetCode: 'TFT'); + + // final stellarClient = Client(NetworkType.TESTNET, + // "SA7C4BONCZUVAMPOAGKJMKIHN4FQYLRF6KVFEFUDQS6EBIOCLLNKQQCY"); + // print(stellarClient.accountId); + // print(stellarClient.secretSeed); +//SCG2NAYKV2AS4N3INA3DMFXKLMBAOH2MN2TA3FSGF6FUUJXJQVDYPXRN + // await stellarClient.activateThroughFriendBot( + // accountId: stellarClient.accountId); + + // await stellarClient.addTrustLine(); + // final balances = await stellarClient.getBalance(); + // print(balances); + + // await stellarClient.createOrder( + // sellingAsset: 'TFT', buyingAsset: 'USDC', amount: '3', price: '1'); + // await stellarClient.listMyOffers(); + // final res = await stellarClient.cancelOrder( + // sellingAsset: 'TFT', buyingAsset: 'USDC', offerId: '12910'); + // print(res.success); + // await stellarClient.listMyOffers(); } diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 3d6b70b1..6226c483 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -234,7 +234,6 @@ class Client { ); final data = jsonDecode(response.body); - String trustlineTransaction = data['addtrustline_transaction']; XdrTransactionEnvelope xdrTxEnvelope = XdrTransactionEnvelope.fromEnvelopeXdrString(trustlineTransaction); @@ -555,27 +554,37 @@ class Client { throw Exception('Buy asset $buyingAsset is not available.'); } - final Asset sellAsset = AssetTypeCreditAlphaNum12( + final offers = (await _sdk.offers.forAccount(accountId).execute()).records; + final OfferResponse? targetOffer = offers.firstWhere( + (offer) => offer.id == offerId, + orElse: () => throw Exception( + 'Offer with ID $offerId not found in user\'s account.'), + ); + + final Asset sellAsset = AssetTypeCreditAlphaNum4( _currencies.currencies[sellingAsset]!.assetCode, _currencies.currencies[sellingAsset]!.issuer); - final Asset buyAsset = AssetTypeCreditAlphaNum12( + final Asset buyAsset = AssetTypeCreditAlphaNum4( _currencies.currencies[buyingAsset]!.assetCode, _currencies.currencies[buyingAsset]!.issuer); - final ManageBuyOfferOperation buyOfferOperation = - ManageBuyOfferOperationBuilder(sellAsset, buyAsset, '0', '0') + final ManageBuyOfferOperation cancelOfferOperation = + ManageBuyOfferOperationBuilder(sellAsset, buyAsset, '0', '1') .setOfferId(offerId) .build(); final account = await _sdk.accounts.account(accountId); final Transaction transaction = TransactionBuilder(account) - .addOperation(buyOfferOperation) + .addOperation(cancelOfferOperation) .addMemo(memo != null ? Memo.text(memo) : Memo.none()) .build(); transaction.sign(_keyPair, _stellarNetwork); try { final SubmitTransactionResponse response = await _sdk.submitTransaction(transaction); + if (!response.success) { + print('Transaction failed with result: ${response.resultXdr}'); + } return response; } catch (error) { throw Exception('Transaction failed due to: ${error.toString()}'); @@ -656,4 +665,94 @@ class Client { throw Exception('Error listing offers for account $accountId: $error'); } } + + Future _buildOrderTransaction( + {required String sellingAsset, + required String buyingAsset, + required String amount, + required String price, + String? memo, + required bool funded}) async { + if (!_currencies.currencies.containsKey(sellingAsset)) { + throw Exception('Sell asset $sellingAsset is not available.'); + } + if (!_currencies.currencies.containsKey(buyingAsset)) { + throw Exception('Buy asset $buyingAsset is not available.'); + } + + final Asset sellAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[sellingAsset]!.assetCode, + _currencies.currencies[sellingAsset]!.issuer); + final Asset buyAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[buyingAsset]!.assetCode, + _currencies.currencies[buyingAsset]!.issuer); + + final ManageBuyOfferOperation buyOfferOperation = + ManageBuyOfferOperationBuilder(sellAsset, buyAsset, amount, price) + .build(); + + final account = await _sdk.accounts.account(accountId); + + final balances = account.balances; + final sellAssetBalance = balances.firstWhere( + (balance) => balance.assetCode == sellingAsset, + orElse: () => throw Exception('Insufficient balance in $sellingAsset'), + ); + + final double sellAmount = double.parse(amount); + final double availableBalance = double.parse(sellAssetBalance.balance); + if (sellAmount > availableBalance) { + throw Exception( + 'Insufficient balance in $sellingAsset. Available: $availableBalance'); + } + Transaction? transaction; + if (funded) { + Operation? operation = await _makeFundPaymentOperation( + assetCode: sellingAsset, + issuer: _currencies.currencies[sellingAsset]!.issuer); + transaction = TransactionBuilder(account) + .addOperation(operation!) + .addOperation(buyOfferOperation) + .addMemo(memo != null ? Memo.text(memo) : Memo.none()) + .build(); + } else { + transaction = TransactionBuilder(account) + .addOperation(buyOfferOperation) + .addMemo(memo != null ? Memo.text(memo) : Memo.none()) + .build(); + } + return transaction; + } + + Future createOrderThroughThreefoldService( + {required String sellingAsset, + required String buyingAsset, + required String amount, + required String price, + String? memo}) async { + Transaction? fundedOrder = await _buildOrderTransaction( + sellingAsset: sellingAsset, + buyingAsset: buyingAsset, + amount: amount, + price: price, + funded: true); + + fundedOrder!.sign(_keyPair, _stellarNetwork); + + print('Sending to'); + print( + '${_serviceUrls[_network.toString()]}/transactionfunding_service/fund_transaction'); + try { + final response = await http.post( + Uri.parse( + '${_serviceUrls[_network.toString()]}/transactionfunding_service/fund_transaction'), + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({'transaction': fundedOrder.toEnvelopeXdrBase64()}), + ); + + print(response.body); + } catch (error) { + throw Exception('Something went wrong! $error'); + } + } } From 3b94cab0f9cbf128091165b95dd616f794a9f1bf Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 8 Dec 2024 12:58:45 +0200 Subject: [PATCH 04/15] support native asset && comment orders with TF service --- .../stellar_client/bin/stellar_client.dart | 55 +--- packages/stellar_client/lib/src/client.dart | 266 ++++++++++-------- 2 files changed, 159 insertions(+), 162 deletions(-) diff --git a/packages/stellar_client/bin/stellar_client.dart b/packages/stellar_client/bin/stellar_client.dart index de0de7a6..c66f7d75 100644 --- a/packages/stellar_client/bin/stellar_client.dart +++ b/packages/stellar_client/bin/stellar_client.dart @@ -1,53 +1,14 @@ import 'package:stellar_client/stellar_client.dart'; void main() async { - // final stellarClient = Client.create(NetworkType.PUBLIC); - // await stellarClient.activateThroughThreefoldService(); + final stellarClient = Client.create(NetworkType.PUBLIC); + await stellarClient.activateThroughThreefoldService(); - // await stellarClient.transfer( - // destinationAddress: "destination-public-key", - // amount: "20", - // currency: "TFT", - // memoText: "Memo Text"); + await stellarClient.transfer( + destinationAddress: "destination-public-key", + amount: "20", + currency: "TFT", + memoText: "Memo Text"); - // await stellarClient.getTransactions(); - - final stellarClient = Client(NetworkType.PUBLIC, - "SDSVD7L2OZXHW2PLARRJ34ZRJVBPMJWKVG4HNIEJKFHI35E3D3WWAUL4"); - print(stellarClient.accountId); - print(stellarClient.secretSeed); - - // final response = await stellarClient.createOrderThroughThreefoldService( - // sellingAsset: 'TFT', buyingAsset: 'USDC', amount: '5', price: '0.9'); - - // // await stellarClient.addTrustLineThroughThreefoldService('USDC'); - await stellarClient.createOrderThroughThreefoldService( - sellingAsset: 'TFT', buyingAsset: 'USDC', amount: '3', price: '1'); - final balances = await stellarClient.getBalance(); - print(balances); - // // final orderbook = await stellarClient.getOrderBook( - // // sellingAssetCode: 'TFT', buyingAssetCode: 'USDC'); - // await stellarClient.listMyOffers(); - // await stellarClient.getOrderBook( - // sellingAssetCode: 'USDC', buyingAssetCode: 'TFT'); - - // final stellarClient = Client(NetworkType.TESTNET, - // "SA7C4BONCZUVAMPOAGKJMKIHN4FQYLRF6KVFEFUDQS6EBIOCLLNKQQCY"); - // print(stellarClient.accountId); - // print(stellarClient.secretSeed); -//SCG2NAYKV2AS4N3INA3DMFXKLMBAOH2MN2TA3FSGF6FUUJXJQVDYPXRN - // await stellarClient.activateThroughFriendBot( - // accountId: stellarClient.accountId); - - // await stellarClient.addTrustLine(); - // final balances = await stellarClient.getBalance(); - // print(balances); - - // await stellarClient.createOrder( - // sellingAsset: 'TFT', buyingAsset: 'USDC', amount: '3', price: '1'); - // await stellarClient.listMyOffers(); - // final res = await stellarClient.cancelOrder( - // sellingAsset: 'TFT', buyingAsset: 'USDC', offerId: '12910'); - // print(res.success); - // await stellarClient.listMyOffers(); + await stellarClient.getTransactions(); } diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 6226c483..33d08e69 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -491,19 +491,32 @@ class Client { required String amount, required String price, String? memo}) async { - if (!_currencies.currencies.containsKey(sellingAsset)) { + if (!_currencies.currencies.containsKey(sellingAsset) || + sellingAsset == 'XLM') { throw Exception('Sell asset $sellingAsset is not available.'); } - if (!_currencies.currencies.containsKey(buyingAsset)) { + if (!_currencies.currencies.containsKey(buyingAsset) || + buyingAsset == 'XLM') { throw Exception('Buy asset $buyingAsset is not available.'); } - final Asset sellAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAsset]!.assetCode, - _currencies.currencies[sellingAsset]!.issuer); - final Asset buyAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAsset]!.assetCode, - _currencies.currencies[buyingAsset]!.issuer); + late final Asset sellAsset; + late final Asset buyAsset; + + if (sellingAsset == 'XLM') { + sellAsset = AssetTypeNative(); + } else { + sellAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[sellingAsset]!.assetCode, + _currencies.currencies[sellingAsset]!.issuer); + } + if (sellingAsset == 'XLM') { + buyAsset = AssetTypeNative(); + } else { + buyAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[buyingAsset]!.assetCode, + _currencies.currencies[buyingAsset]!.issuer); + } final ManageBuyOfferOperation buyOfferOperation = ManageBuyOfferOperationBuilder(sellAsset, buyAsset, amount, price) @@ -547,10 +560,12 @@ class Client { required String buyingAsset, required String offerId, String? memo}) async { - if (!_currencies.currencies.containsKey(sellingAsset)) { + if (!_currencies.currencies.containsKey(sellingAsset) || + sellingAsset == 'XLM') { throw Exception('Sell asset $sellingAsset is not available.'); } - if (!_currencies.currencies.containsKey(buyingAsset)) { + if (!_currencies.currencies.containsKey(buyingAsset) || + buyingAsset == 'XLM') { throw Exception('Buy asset $buyingAsset is not available.'); } @@ -561,12 +576,23 @@ class Client { 'Offer with ID $offerId not found in user\'s account.'), ); - final Asset sellAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAsset]!.assetCode, - _currencies.currencies[sellingAsset]!.issuer); - final Asset buyAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAsset]!.assetCode, - _currencies.currencies[buyingAsset]!.issuer); + late final Asset sellAsset; + late final Asset buyAsset; + + if (sellingAsset == 'XLM') { + sellAsset = AssetTypeNative(); + } else { + sellAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[sellingAsset]!.assetCode, + _currencies.currencies[sellingAsset]!.issuer); + } + if (sellingAsset == 'XLM') { + buyAsset = AssetTypeNative(); + } else { + buyAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[buyingAsset]!.assetCode, + _currencies.currencies[buyingAsset]!.issuer); + } final ManageBuyOfferOperation cancelOfferOperation = ManageBuyOfferOperationBuilder(sellAsset, buyAsset, '0', '1') @@ -591,26 +617,36 @@ class Client { } } - // native --> Represents the Stellar native asset (XLM) - // credit_alphanum4: Represents a credit asset with a 4-character code. - // credit_alphanum12: Represents a credit asset with a 12-character code - Future getOrderBook( {required String sellingAssetCode, required String buyingAssetCode}) async { - if (!_currencies.currencies.containsKey(sellingAssetCode)) { + if (!_currencies.currencies.containsKey(sellingAssetCode) || + sellingAssetCode == 'XLM') { throw Exception('Sell asset $sellingAssetCode is not available.'); } - if (!_currencies.currencies.containsKey(buyingAssetCode)) { + if (!_currencies.currencies.containsKey(buyingAssetCode) || + buyingAssetCode == 'XLM') { throw Exception('Buy asset $buyingAssetCode is not available.'); } http.Client httpClient = http.Client(); Uri serverURI = Uri.parse(_horizonServerUrls[_network.toString()]!); + late final Asset sellingAsset; + late final Asset buyingAsset; - Asset sellingAsset = Asset.createNonNativeAsset( - sellingAssetCode, _currencies.currencies[sellingAssetCode]!.issuer); - Asset buyingAsset = Asset.createNonNativeAsset( - buyingAssetCode, _currencies.currencies[buyingAssetCode]!.issuer); + if (sellingAssetCode == 'XLM') { + sellingAsset = AssetTypeNative(); + } else { + sellingAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[sellingAssetCode]!.assetCode, + _currencies.currencies[sellingAssetCode]!.issuer); + } + if (buyingAssetCode == 'XLM') { + buyingAsset = AssetTypeNative(); + } else { + buyingAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[buyingAssetCode]!.assetCode, + _currencies.currencies[buyingAssetCode]!.issuer); + } OrderBookRequestBuilder orderBookRequest = OrderBookRequestBuilder(httpClient, serverURI) @@ -666,93 +702,93 @@ class Client { } } - Future _buildOrderTransaction( - {required String sellingAsset, - required String buyingAsset, - required String amount, - required String price, - String? memo, - required bool funded}) async { - if (!_currencies.currencies.containsKey(sellingAsset)) { - throw Exception('Sell asset $sellingAsset is not available.'); - } - if (!_currencies.currencies.containsKey(buyingAsset)) { - throw Exception('Buy asset $buyingAsset is not available.'); - } - - final Asset sellAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAsset]!.assetCode, - _currencies.currencies[sellingAsset]!.issuer); - final Asset buyAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAsset]!.assetCode, - _currencies.currencies[buyingAsset]!.issuer); - - final ManageBuyOfferOperation buyOfferOperation = - ManageBuyOfferOperationBuilder(sellAsset, buyAsset, amount, price) - .build(); - - final account = await _sdk.accounts.account(accountId); - - final balances = account.balances; - final sellAssetBalance = balances.firstWhere( - (balance) => balance.assetCode == sellingAsset, - orElse: () => throw Exception('Insufficient balance in $sellingAsset'), - ); - - final double sellAmount = double.parse(amount); - final double availableBalance = double.parse(sellAssetBalance.balance); - if (sellAmount > availableBalance) { - throw Exception( - 'Insufficient balance in $sellingAsset. Available: $availableBalance'); - } - Transaction? transaction; - if (funded) { - Operation? operation = await _makeFundPaymentOperation( - assetCode: sellingAsset, - issuer: _currencies.currencies[sellingAsset]!.issuer); - transaction = TransactionBuilder(account) - .addOperation(operation!) - .addOperation(buyOfferOperation) - .addMemo(memo != null ? Memo.text(memo) : Memo.none()) - .build(); - } else { - transaction = TransactionBuilder(account) - .addOperation(buyOfferOperation) - .addMemo(memo != null ? Memo.text(memo) : Memo.none()) - .build(); - } - return transaction; - } - - Future createOrderThroughThreefoldService( - {required String sellingAsset, - required String buyingAsset, - required String amount, - required String price, - String? memo}) async { - Transaction? fundedOrder = await _buildOrderTransaction( - sellingAsset: sellingAsset, - buyingAsset: buyingAsset, - amount: amount, - price: price, - funded: true); - - fundedOrder!.sign(_keyPair, _stellarNetwork); - - print('Sending to'); - print( - '${_serviceUrls[_network.toString()]}/transactionfunding_service/fund_transaction'); - try { - final response = await http.post( - Uri.parse( - '${_serviceUrls[_network.toString()]}/transactionfunding_service/fund_transaction'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({'transaction': fundedOrder.toEnvelopeXdrBase64()}), - ); - - print(response.body); - } catch (error) { - throw Exception('Something went wrong! $error'); - } - } + // Future _buildOrderTransaction( + // {required String sellingAsset, + // required String buyingAsset, + // required String amount, + // required String price, + // String? memo, + // required bool funded}) async { + // if (!_currencies.currencies.containsKey(sellingAsset)) { + // throw Exception('Sell asset $sellingAsset is not available.'); + // } + // if (!_currencies.currencies.containsKey(buyingAsset)) { + // throw Exception('Buy asset $buyingAsset is not available.'); + // } + + // final Asset sellAsset = AssetTypeCreditAlphaNum4( + // _currencies.currencies[sellingAsset]!.assetCode, + // _currencies.currencies[sellingAsset]!.issuer); + // final Asset buyAsset = AssetTypeCreditAlphaNum4( + // _currencies.currencies[buyingAsset]!.assetCode, + // _currencies.currencies[buyingAsset]!.issuer); + + // final ManageBuyOfferOperation buyOfferOperation = + // ManageBuyOfferOperationBuilder(sellAsset, buyAsset, amount, price) + // .build(); + + // final account = await _sdk.accounts.account(accountId); + + // final balances = account.balances; + // final sellAssetBalance = balances.firstWhere( + // (balance) => balance.assetCode == sellingAsset, + // orElse: () => throw Exception('Insufficient balance in $sellingAsset'), + // ); + + // final double sellAmount = double.parse(amount); + // final double availableBalance = double.parse(sellAssetBalance.balance); + // if (sellAmount > availableBalance) { + // throw Exception( + // 'Insufficient balance in $sellingAsset. Available: $availableBalance'); + // } + // Transaction? transaction; + // if (funded) { + // // Operation? operation = await _makeFundPaymentOperation( + // // assetCode: sellingAsset, + // // issuer: _currencies.currencies[sellingAsset]!.issuer); + // transaction = TransactionBuilder(account) + // .addOperation(buyOfferOperation) + // .addOperation(buyOfferOperation) + // .addMemo(memo != null ? Memo.text(memo) : Memo.none()) + // .build(); + // } else { + // transaction = TransactionBuilder(account) + // .addOperation(buyOfferOperation) + // .addMemo(memo != null ? Memo.text(memo) : Memo.none()) + // .build(); + // } + // return transaction; + // } + + // Future createOrderThroughThreefoldService( + // {required String sellingAsset, + // required String buyingAsset, + // required String amount, + // required String price, + // String? memo}) async { + // Transaction? fundedOrder = await _buildOrderTransaction( + // sellingAsset: sellingAsset, + // buyingAsset: buyingAsset, + // amount: amount, + // price: price, + // funded: true); + + // fundedOrder!.sign(_keyPair, _stellarNetwork); + + // print('Sending to'); + // print( + // '${_serviceUrls[_network.toString()]}/transactionfunding_service/fund_transaction'); + // try { + // final response = await http.post( + // Uri.parse( + // '${_serviceUrls[_network.toString()]}/transactionfunding_service/fund_transaction'), + // headers: {'Content-Type': 'application/json'}, + // body: jsonEncode({'transaction': fundedOrder.toEnvelopeXdrBase64()}), + // ); + + // print(response.body); + // } catch (error) { + // throw Exception('Something went wrong! $error'); + // } + // } } From 8bdabab0c908407e595e0bc9f5f3e8f5a0b28162 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 22 Dec 2024 17:29:05 +0200 Subject: [PATCH 05/15] remova commented code --- packages/stellar_client/lib/src/client.dart | 90 --------------------- 1 file changed, 90 deletions(-) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 33d08e69..2a12d1f7 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -701,94 +701,4 @@ class Client { throw Exception('Error listing offers for account $accountId: $error'); } } - - // Future _buildOrderTransaction( - // {required String sellingAsset, - // required String buyingAsset, - // required String amount, - // required String price, - // String? memo, - // required bool funded}) async { - // if (!_currencies.currencies.containsKey(sellingAsset)) { - // throw Exception('Sell asset $sellingAsset is not available.'); - // } - // if (!_currencies.currencies.containsKey(buyingAsset)) { - // throw Exception('Buy asset $buyingAsset is not available.'); - // } - - // final Asset sellAsset = AssetTypeCreditAlphaNum4( - // _currencies.currencies[sellingAsset]!.assetCode, - // _currencies.currencies[sellingAsset]!.issuer); - // final Asset buyAsset = AssetTypeCreditAlphaNum4( - // _currencies.currencies[buyingAsset]!.assetCode, - // _currencies.currencies[buyingAsset]!.issuer); - - // final ManageBuyOfferOperation buyOfferOperation = - // ManageBuyOfferOperationBuilder(sellAsset, buyAsset, amount, price) - // .build(); - - // final account = await _sdk.accounts.account(accountId); - - // final balances = account.balances; - // final sellAssetBalance = balances.firstWhere( - // (balance) => balance.assetCode == sellingAsset, - // orElse: () => throw Exception('Insufficient balance in $sellingAsset'), - // ); - - // final double sellAmount = double.parse(amount); - // final double availableBalance = double.parse(sellAssetBalance.balance); - // if (sellAmount > availableBalance) { - // throw Exception( - // 'Insufficient balance in $sellingAsset. Available: $availableBalance'); - // } - // Transaction? transaction; - // if (funded) { - // // Operation? operation = await _makeFundPaymentOperation( - // // assetCode: sellingAsset, - // // issuer: _currencies.currencies[sellingAsset]!.issuer); - // transaction = TransactionBuilder(account) - // .addOperation(buyOfferOperation) - // .addOperation(buyOfferOperation) - // .addMemo(memo != null ? Memo.text(memo) : Memo.none()) - // .build(); - // } else { - // transaction = TransactionBuilder(account) - // .addOperation(buyOfferOperation) - // .addMemo(memo != null ? Memo.text(memo) : Memo.none()) - // .build(); - // } - // return transaction; - // } - - // Future createOrderThroughThreefoldService( - // {required String sellingAsset, - // required String buyingAsset, - // required String amount, - // required String price, - // String? memo}) async { - // Transaction? fundedOrder = await _buildOrderTransaction( - // sellingAsset: sellingAsset, - // buyingAsset: buyingAsset, - // amount: amount, - // price: price, - // funded: true); - - // fundedOrder!.sign(_keyPair, _stellarNetwork); - - // print('Sending to'); - // print( - // '${_serviceUrls[_network.toString()]}/transactionfunding_service/fund_transaction'); - // try { - // final response = await http.post( - // Uri.parse( - // '${_serviceUrls[_network.toString()]}/transactionfunding_service/fund_transaction'), - // headers: {'Content-Type': 'application/json'}, - // body: jsonEncode({'transaction': fundedOrder.toEnvelopeXdrBase64()}), - // ); - - // print(response.body); - // } catch (error) { - // throw Exception('Something went wrong! $error'); - // } - // } } From 3f4b8d1e18bb2776984b6e60a5dd77e3e2ad5417 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 25 Dec 2024 10:12:48 +0200 Subject: [PATCH 06/15] fix workflow --- packages/stellar_client/lib/src/client.dart | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 7681b5ec..f043a253 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -138,27 +138,26 @@ class Client { } Future addTrustLine() async { - try { - for (var entry in _currencies.currencies.entries) { - String currencyCode = entry.key; - currency.Currency currentCurrency = entry.value; + for (var entry in _currencies.currencies.entries) { + String currencyCode = entry.key; + currency.Currency currentCurrency = entry.value; - String issuerAccountId = currentCurrency.issuer; - Asset currencyAsset = AssetTypeCreditAlphaNum4( - currentCurrency.assetCode, issuerAccountId); + String issuerAccountId = currentCurrency.issuer; + Asset currencyAsset = + AssetTypeCreditAlphaNum4(currentCurrency.assetCode, issuerAccountId); - ChangeTrustOperationBuilder changeTrustOperation = - ChangeTrustOperationBuilder(currencyAsset, "300000"); + ChangeTrustOperationBuilder changeTrustOperation = + ChangeTrustOperationBuilder(currencyAsset, "300000"); - final account = await _sdk.accounts.account(accountId); + final account = await _sdk.accounts.account(accountId); - Transaction transaction = TransactionBuilder(account) - .addOperation(changeTrustOperation.build()) - .build(); - transaction.sign(_keyPair, _stellarNetwork); + Transaction transaction = TransactionBuilder(account) + .addOperation(changeTrustOperation.build()) + .build(); + transaction.sign(_keyPair, _stellarNetwork); - SubmitTransactionResponse response = - await _sdk.submitTransaction(transaction); + SubmitTransactionResponse response = + await _sdk.submitTransaction(transaction); if (!response.success) { logger.e("Failed to add trustline for $currencyCode"); @@ -167,6 +166,7 @@ class Client { logger.i("trustline for $currencyCode was added successfully"); return true; } + } logger.i("No trustlines were processed"); return false; From 5c44c9c7468474979687cf801b3d997159abe589 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 25 Dec 2024 10:41:39 +0200 Subject: [PATCH 07/15] added native asset in currencies list && remove all prints --- packages/stellar_client/lib/src/client.dart | 66 +++++---------------- 1 file changed, 16 insertions(+), 50 deletions(-) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index f043a253..7c4b5452 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -79,7 +79,11 @@ class Client { throw Exception('Unsupported network type'); } - _currencies = currency.Currencies({'TFT': tft, 'USDC': usdc}); + _currencies = currency.Currencies({ + 'TFT': tft, + 'USDC': usdc, + 'XLM': currency.Currency(assetCode: 'XLM', issuer: "") + }); } Future activateThroughThreefoldService() async { @@ -492,12 +496,10 @@ class Client { required String amount, required String price, String? memo}) async { - if (!_currencies.currencies.containsKey(sellingAsset) || - sellingAsset == 'XLM') { + if (!_currencies.currencies.containsKey(sellingAsset)) { throw Exception('Sell asset $sellingAsset is not available.'); } - if (!_currencies.currencies.containsKey(buyingAsset) || - buyingAsset == 'XLM') { + if (!_currencies.currencies.containsKey(buyingAsset)) { throw Exception('Buy asset $buyingAsset is not available.'); } @@ -541,14 +543,13 @@ class Client { .addOperation(buyOfferOperation) .addMemo(memo != null ? Memo.text(memo) : Memo.none()) .build(); - print('Transaction XDR: ${transaction.toEnvelopeXdrBase64()}'); transaction.sign(_keyPair, _stellarNetwork); try { final SubmitTransactionResponse response = await _sdk.submitTransaction(transaction); if (!response.success) { - print('Transaction failed with result: ${response.resultXdr}'); + logger.e('Transaction failed with result: ${response.resultXdr}'); } return response; } catch (error) { @@ -561,12 +562,10 @@ class Client { required String buyingAsset, required String offerId, String? memo}) async { - if (!_currencies.currencies.containsKey(sellingAsset) || - sellingAsset == 'XLM') { + if (!_currencies.currencies.containsKey(sellingAsset)) { throw Exception('Sell asset $sellingAsset is not available.'); } - if (!_currencies.currencies.containsKey(buyingAsset) || - buyingAsset == 'XLM') { + if (!_currencies.currencies.containsKey(buyingAsset)) { throw Exception('Buy asset $buyingAsset is not available.'); } @@ -610,7 +609,7 @@ class Client { final SubmitTransactionResponse response = await _sdk.submitTransaction(transaction); if (!response.success) { - print('Transaction failed with result: ${response.resultXdr}'); + logger.e('Transaction failed with result: ${response.resultXdr}'); } return response; } catch (error) { @@ -618,15 +617,13 @@ class Client { } } - Future getOrderBook( + Future> getOrderBook( {required String sellingAssetCode, required String buyingAssetCode}) async { - if (!_currencies.currencies.containsKey(sellingAssetCode) || - sellingAssetCode == 'XLM') { + if (!_currencies.currencies.containsKey(sellingAssetCode)) { throw Exception('Sell asset $sellingAssetCode is not available.'); } - if (!_currencies.currencies.containsKey(buyingAssetCode) || - buyingAssetCode == 'XLM') { + if (!_currencies.currencies.containsKey(buyingAssetCode)) { throw Exception('Buy asset $buyingAssetCode is not available.'); } http.Client httpClient = http.Client(); @@ -654,49 +651,18 @@ class Client { ..sellingAsset(sellingAsset) ..buyingAsset(buyingAsset); - Stream orderBookStream = orderBookRequest.stream(); - orderBookStream.listen((orderBookResponse) { - print("Received OrderBookResponse:"); - print("Base: ${orderBookResponse.base}"); - print("Counter: ${orderBookResponse.counter}"); - - print("\nBids:"); - for (var offer in orderBookResponse.bids) { - // priceR numerator/denominator - print( - 'Bid - Amount: ${offer.amount}, Price: ${offer.price}, PriceR: ${offer.priceR}'); - } - - print("\nAsks:"); - for (var offer in orderBookResponse.asks) { - print( - 'Ask - Amount: ${offer.amount}, Price: ${offer.price}, PriceR: ${offer.priceR}'); - } - }, onError: (error) { - print("Error while listening to order book stream: $error"); - }, onDone: () { - print("Order book stream is closed."); - }); + return await orderBookRequest.stream(); } Future> listMyOffers() async { try { final offers = await _sdk.offers.forAccount(accountId).execute(); - print(offers); if (offers.records.isEmpty) { - print('No offers found for account: $accountId'); + logger.i('No offers found for account: $accountId'); return []; } - for (var offer in offers.records) { - print('Offer ID: ${offer.id}'); - print('Selling Asset: ${offer.selling}'); - print('Buying Asset: ${offer.buying}'); - print('Amount: ${offer.amount}'); - print('Price: ${offer.price}'); - print('-----------------------------------'); - } return offers.records; } catch (error) { throw Exception('Error listing offers for account $accountId: $error'); From 1b7492bc7a6b58337be6654479063a13af32478c Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 12 Jan 2025 11:13:10 +0200 Subject: [PATCH 08/15] WIP: fix logger && trustlines issue --- packages/stellar_client/lib/src/client.dart | 32 +++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 7c4b5452..01e79580 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -14,7 +14,15 @@ class Client { Uint8List? get privateKey => _keyPair.privateKey; var logger = Logger( - printer: PrettyPrinter(), + printer: PrettyPrinter( + methodCount: 2, + errorMethodCount: 8, + lineLength: 120, + colors: true, + printEmojis: true, + printTime: true), + level: Level.debug, + filter: ProductionFilter(), ); Client(this._network, String secretSeed) { @@ -142,9 +150,17 @@ class Client { } Future addTrustLine() async { + bool allTrustlinesAdded = true; + for (var entry in _currencies.currencies.entries) { String currencyCode = entry.key; currency.Currency currentCurrency = entry.value; + if (currencyCode == 'XLM') { + logger.i("Skipping trustline for native asset $currencyCode"); + continue; + } + logger.i( + "Processing trustline for ${entry.key} with issuer ${entry.value.issuer}"); String issuerAccountId = currentCurrency.issuer; Asset currencyAsset = @@ -165,15 +181,19 @@ class Client { if (!response.success) { logger.e("Failed to add trustline for $currencyCode"); - return false; + allTrustlinesAdded = false; } else { - logger.i("trustline for $currencyCode was added successfully"); - return true; + logger.i("Trustline for $currencyCode was added successfully"); } } - logger.i("No trustlines were processed"); - return false; + if (allTrustlinesAdded) { + logger.i("All trustlines were added successfully"); + return true; + } else { + logger.e("One or more trustlines failed to be added"); + return false; + } } Future transfer( From ccae64c83d5a9ccf3c197f46ca3f37ead6381970 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 12 Jan 2025 14:09:01 +0200 Subject: [PATCH 09/15] fix balance checking issue && update renaming --- packages/stellar_client/lib/src/client.dart | 99 ++++++++++++--------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 01e79580..cfa403b1 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -511,34 +511,34 @@ class Client { } Future createOrder( - {required String sellingAsset, - required String buyingAsset, + {required String sellingAssetCode, + required String buyingAssetCode, required String amount, required String price, String? memo}) async { - if (!_currencies.currencies.containsKey(sellingAsset)) { - throw Exception('Sell asset $sellingAsset is not available.'); + if (!_currencies.currencies.containsKey(sellingAssetCode)) { + throw Exception('Sell asset $sellingAssetCode is not available.'); } - if (!_currencies.currencies.containsKey(buyingAsset)) { - throw Exception('Buy asset $buyingAsset is not available.'); + if (!_currencies.currencies.containsKey(buyingAssetCode)) { + throw Exception('Buy asset $buyingAssetCode is not available.'); } late final Asset sellAsset; late final Asset buyAsset; - if (sellingAsset == 'XLM') { + if (sellingAssetCode == 'XLM') { sellAsset = AssetTypeNative(); } else { sellAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAsset]!.assetCode, - _currencies.currencies[sellingAsset]!.issuer); + _currencies.currencies[sellingAssetCode]!.assetCode, + _currencies.currencies[sellingAssetCode]!.issuer); } - if (sellingAsset == 'XLM') { + if (buyingAssetCode == 'XLM') { buyAsset = AssetTypeNative(); } else { buyAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAsset]!.assetCode, - _currencies.currencies[buyingAsset]!.issuer); + _currencies.currencies[buyingAssetCode]!.assetCode, + _currencies.currencies[buyingAssetCode]!.issuer); } final ManageBuyOfferOperation buyOfferOperation = @@ -546,19 +546,36 @@ class Client { .build(); final account = await _sdk.accounts.account(accountId); - final balances = account.balances; - final sellAssetBalance = balances.firstWhere( - (balance) => balance.assetCode == sellingAsset, - orElse: () => throw Exception('Insufficient balance in $sellingAsset'), - ); - final double sellAmount = double.parse(amount); - final double availableBalance = double.parse(sellAssetBalance.balance); - if (sellAmount > availableBalance) { - throw Exception( - 'Insufficient balance in $sellingAsset. Available: $availableBalance'); + try { + final sellAssetBalance = balances.firstWhere( + (balance) { + if (sellingAssetCode == 'XLM' && balance.assetCode == null) { + // Special case for XLM + return true; + } else { + return balance.assetCode == sellingAssetCode; + } + }, + orElse: () { + logger.e("Sell asset $sellingAssetCode not found in balances."); + throw Exception('Insufficient balance in $sellingAssetCode'); + }, + ); + + final double sellAmount = double.parse(amount); + final double availableBalance = double.parse(sellAssetBalance.balance); + + if (sellAmount > availableBalance) { + throw Exception( + 'Insufficient balance in $sellingAssetCode. Available: $availableBalance'); + } + } catch (e) { + logger.e("Error: ${e.toString()}"); + rethrow; } + final Transaction transaction = TransactionBuilder(account) .addOperation(buyOfferOperation) .addMemo(memo != null ? Memo.text(memo) : Memo.none()) @@ -578,15 +595,15 @@ class Client { } Future cancelOrder( - {required String sellingAsset, - required String buyingAsset, + {required String sellingAssetCode, + required String buyingAssetCode, required String offerId, String? memo}) async { - if (!_currencies.currencies.containsKey(sellingAsset)) { - throw Exception('Sell asset $sellingAsset is not available.'); + if (!_currencies.currencies.containsKey(sellingAssetCode)) { + throw Exception('Sell asset $sellingAssetCode is not available.'); } - if (!_currencies.currencies.containsKey(buyingAsset)) { - throw Exception('Buy asset $buyingAsset is not available.'); + if (!_currencies.currencies.containsKey(buyingAssetCode)) { + throw Exception('Buy asset $buyingAssetCode is not available.'); } final offers = (await _sdk.offers.forAccount(accountId).execute()).records; @@ -596,26 +613,26 @@ class Client { 'Offer with ID $offerId not found in user\'s account.'), ); - late final Asset sellAsset; - late final Asset buyAsset; + late final Asset sellingAsset; + late final Asset buyingAsset; - if (sellingAsset == 'XLM') { - sellAsset = AssetTypeNative(); + if (sellingAssetCode == 'XLM') { + sellingAsset = AssetTypeNative(); } else { - sellAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAsset]!.assetCode, - _currencies.currencies[sellingAsset]!.issuer); + sellingAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[sellingAssetCode]!.assetCode, + _currencies.currencies[sellingAssetCode]!.issuer); } - if (sellingAsset == 'XLM') { - buyAsset = AssetTypeNative(); + if (buyingAssetCode == 'XLM') { + buyingAsset = AssetTypeNative(); } else { - buyAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAsset]!.assetCode, - _currencies.currencies[buyingAsset]!.issuer); + buyingAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[buyingAssetCode]!.assetCode, + _currencies.currencies[buyingAssetCode]!.issuer); } final ManageBuyOfferOperation cancelOfferOperation = - ManageBuyOfferOperationBuilder(sellAsset, buyAsset, '0', '1') + ManageBuyOfferOperationBuilder(sellingAsset, buyingAsset, '0', '1') .setOfferId(offerId) .build(); From 3e8d0b59ccbbadc8bcb892f6249de1c8db3f34bd Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 12 Jan 2025 15:46:22 +0200 Subject: [PATCH 10/15] support updating an offer --- packages/stellar_client/lib/src/client.dart | 63 +++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index cfa403b1..2ced22a8 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -654,6 +654,69 @@ class Client { } } + Future updateOrder( + {required String sellingAssetCode, + required String buyingAssetCode, + required String amount, + required String price, + required String offerId, + String? memo}) async { + if (!_currencies.currencies.containsKey(sellingAssetCode)) { + throw Exception('Sell asset $sellingAssetCode is not available.'); + } + if (!_currencies.currencies.containsKey(buyingAssetCode)) { + throw Exception('Buy asset $buyingAssetCode is not available.'); + } + + final offers = (await _sdk.offers.forAccount(accountId).execute()).records; + final OfferResponse? targetOffer = offers.firstWhere( + (offer) => offer.id == offerId, + orElse: () => throw Exception( + 'Offer with ID $offerId not found in user\'s account.'), + ); + + late final Asset sellingAsset; + late final Asset buyingAsset; + + if (sellingAssetCode == 'XLM') { + sellingAsset = AssetTypeNative(); + } else { + sellingAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[sellingAssetCode]!.assetCode, + _currencies.currencies[sellingAssetCode]!.issuer); + } + if (buyingAssetCode == 'XLM') { + buyingAsset = AssetTypeNative(); + } else { + buyingAsset = AssetTypeCreditAlphaNum4( + _currencies.currencies[buyingAssetCode]!.assetCode, + _currencies.currencies[buyingAssetCode]!.issuer); + } + + ManageBuyOfferOperation updateOfferOperation = ManageBuyOfferOperationBuilder( + sellingAsset, + buyingAsset, + amount, + price, + ).setOfferId(offerId).build(); + + final account = await _sdk.accounts.account(accountId); + final Transaction transaction = TransactionBuilder(account) + .addOperation(updateOfferOperation) + .build(); + transaction.sign(_keyPair, _stellarNetwork); + try { + final SubmitTransactionResponse response = + await _sdk.submitTransaction(transaction); + if (!response.success) { + logger.e('Transaction failed with result: ${response.resultXdr}'); + } + return response; + } catch (error) { + throw Exception('Transaction failed due to: ${error.toString()}'); + } + } + Future> getOrderBook( {required String sellingAssetCode, required String buyingAssetCode}) async { From 12c2650ec05cf5c8b38ee382d0d8ccf70b98649a Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 2 Feb 2025 08:46:40 +0200 Subject: [PATCH 11/15] upgrade http version --- packages/stellar_client/pubspec.lock | 18 +++++++++--------- packages/stellar_client/pubspec.yaml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/stellar_client/pubspec.lock b/packages/stellar_client/pubspec.lock index e5b0e43b..2c24ecc2 100644 --- a/packages/stellar_client/pubspec.lock +++ b/packages/stellar_client/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" convert: dependency: transitive description: @@ -154,10 +154,10 @@ packages: dependency: "direct main" description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" http_multi_server: dependency: transitive description: @@ -234,18 +234,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: transitive description: @@ -362,7 +362,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_map_stack_trace: dependency: transitive description: diff --git a/packages/stellar_client/pubspec.yaml b/packages/stellar_client/pubspec.yaml index a13017f1..29581727 100644 --- a/packages/stellar_client/pubspec.yaml +++ b/packages/stellar_client/pubspec.yaml @@ -7,7 +7,7 @@ environment: # Add regular dependencies here. dependencies: - http: 1.2.1 + http: 1.3.0 stellar_flutter_sdk: ^1.8.9 dev_dependencies: From b5beea7d548fcc7afb735d2f101088708c099f4a Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Mon, 10 Feb 2025 12:15:00 +0200 Subject: [PATCH 12/15] added documentation && removed redundant code for getting asset --- packages/stellar_client/lib/src/client.dart | 199 +++++++++++++------- 1 file changed, 127 insertions(+), 72 deletions(-) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index fc1d538a..9f9e36f1 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -147,6 +147,17 @@ class Client { } } + /// Adds trustline for all non-native assets in the `_currencies.currencies` map. + /// + /// Trustlines are required to hold non-native assets on a Stellar account. + /// This function iterates over all available currencies and attempts to + /// establish trustlines for each, except for the native asset (`XLM`). + /// + /// **Note:** Adding trustline requires having XLM in the account + /// + /// ### Returns: + /// - `true` if all trustlines were successfully added. + /// - `false` if one or more trustlines failed. Future addTrustLine() async { bool allTrustlinesAdded = true; @@ -194,6 +205,11 @@ class Client { } } + /// Transfers a specified amount of currency to a destination address. + /// + /// This function builds a Stellar transaction to send funds from the current account + /// to a given recipient. It supports optional memo fields for additional transaction details. + /// **Note:** Transfer requires having XLM in the account Future transfer( {required String destinationAddress, required String amount, @@ -547,6 +563,32 @@ class Client { } } + Asset _getAsset(String assetCode) { + if (assetCode == 'XLM') { + return AssetTypeNative(); + } + + final asset = _currencies.currencies[assetCode]; + if (asset == null) { + throw Exception('Asset $assetCode is not available'); + } + + return AssetTypeCreditAlphaNum4(asset.assetCode, asset.issuer); + } + + /// Creates a DEX order by submitting a `ManageBuyOfferOperation` transaction. + /// + /// This function allows user to create an order to buy a specified asset + /// using another asset on Stellar network. + /// + /// **Note:** Creating an order requires having XLM in the account + /// to cover transaction fees and reserve requirements. + /// + /// **Price Format:** + /// - The `price` should always include a leading zero for decimal values. + /// - For example, instead of writing `.1`, the price should be written as `0.1`. + /// - **Correct format**: `0.1` + /// - **Incorrect format**: `.1` Future createOrder( {required String sellingAssetCode, required String buyingAssetCode, @@ -560,26 +602,11 @@ class Client { throw Exception('Buy asset $buyingAssetCode is not available.'); } - late final Asset sellAsset; - late final Asset buyAsset; - - if (sellingAssetCode == 'XLM') { - sellAsset = AssetTypeNative(); - } else { - sellAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAssetCode]!.assetCode, - _currencies.currencies[sellingAssetCode]!.issuer); - } - if (buyingAssetCode == 'XLM') { - buyAsset = AssetTypeNative(); - } else { - buyAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAssetCode]!.assetCode, - _currencies.currencies[buyingAssetCode]!.issuer); - } + final Asset sellingAsset = _getAsset(sellingAssetCode); + final Asset buyingAsset = _getAsset(buyingAssetCode); final ManageBuyOfferOperation buyOfferOperation = - ManageBuyOfferOperationBuilder(sellAsset, buyAsset, amount, price) + ManageBuyOfferOperationBuilder(sellingAsset, buyingAsset, amount, price) .build(); final account = await _sdk.accounts.account(accountId); @@ -631,6 +658,12 @@ class Client { } } + /// Cancels a DEX order by submitting a `ManageBuyOfferOperation` transaction with zero amount. + /// + /// This function allows user to cancel previously created order with its offerId. + /// + /// **Note:** Cancelling an order requires having XLM in the account + /// to cover transaction fees and reserve requirements. Future cancelOrder( {required String sellingAssetCode, required String buyingAssetCode, @@ -650,23 +683,8 @@ class Client { 'Offer with ID $offerId not found in user\'s account.'), ); - late final Asset sellingAsset; - late final Asset buyingAsset; - - if (sellingAssetCode == 'XLM') { - sellingAsset = AssetTypeNative(); - } else { - sellingAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAssetCode]!.assetCode, - _currencies.currencies[sellingAssetCode]!.issuer); - } - if (buyingAssetCode == 'XLM') { - buyingAsset = AssetTypeNative(); - } else { - buyingAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAssetCode]!.assetCode, - _currencies.currencies[buyingAssetCode]!.issuer); - } + final Asset sellingAsset = _getAsset(sellingAssetCode); + final Asset buyingAsset = _getAsset(buyingAssetCode); final ManageBuyOfferOperation cancelOfferOperation = ManageBuyOfferOperationBuilder(sellingAsset, buyingAsset, '0', '1') @@ -691,6 +709,18 @@ class Client { } } + /// Updating a DEX order by submitting a `ManageBuyOfferOperation` transaction. + /// + /// This function allows user to update previously created order by its offerId. + /// + /// **Note:** Updating an order requires having XLM in the account + /// to cover transaction fees and reserve requirements. + /// + /// **Price Format:** + /// - The `price` should always include a leading zero for decimal values. + /// - For example, instead of writing `.1`, the price should be written as `0.1`. + /// - **Correct format**: `0.1` + /// - **Incorrect format**: `.1` Future updateOrder( {required String sellingAssetCode, required String buyingAssetCode, @@ -712,25 +742,11 @@ class Client { 'Offer with ID $offerId not found in user\'s account.'), ); - late final Asset sellingAsset; - late final Asset buyingAsset; - - if (sellingAssetCode == 'XLM') { - sellingAsset = AssetTypeNative(); - } else { - sellingAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAssetCode]!.assetCode, - _currencies.currencies[sellingAssetCode]!.issuer); - } - if (buyingAssetCode == 'XLM') { - buyingAsset = AssetTypeNative(); - } else { - buyingAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAssetCode]!.assetCode, - _currencies.currencies[buyingAssetCode]!.issuer); - } + final Asset sellingAsset = _getAsset(sellingAssetCode); + final Asset buyingAsset = _getAsset(buyingAssetCode); - ManageBuyOfferOperation updateOfferOperation = ManageBuyOfferOperationBuilder( + ManageBuyOfferOperation updateOfferOperation = + ManageBuyOfferOperationBuilder( sellingAsset, buyingAsset, amount, @@ -738,9 +754,8 @@ class Client { ).setOfferId(offerId).build(); final account = await _sdk.accounts.account(accountId); - final Transaction transaction = TransactionBuilder(account) - .addOperation(updateOfferOperation) - .build(); + final Transaction transaction = + TransactionBuilder(account).addOperation(updateOfferOperation).build(); transaction.sign(_keyPair, _stellarNetwork); try { final SubmitTransactionResponse response = @@ -754,6 +769,29 @@ class Client { } } + /// Retrieves the order book for a given asset pair on the Stellar network. + /// + /// This function returns a stream of `OrderBookResponse`, which provides + /// real-time updates on buy and sell orders for the specified asset pair. + /// + /// ### Example: + /// **Creating an Order** + /// ```dart + /// await stellarClient.createOrder( + /// sellingAssetCode: 'XLM', + /// buyingAssetCode: 'TFT', + /// amount: '2', + /// price: '0.1'); // Meaning 1 XLM = 0.1 TFT + /// ``` + /// + /// **Retrieved Offer:** + /// ```dart + /// OfferResponse { + /// amount: "0.2", // Because 2 * 0.1 = 0.2 TFT + /// price: "10.0" // Because 1 / 0.1 = 10 XLM per TFT + /// } + /// ``` + /// Future> getOrderBook( {required String sellingAssetCode, required String buyingAssetCode}) async { @@ -765,23 +803,9 @@ class Client { } http.Client httpClient = http.Client(); Uri serverURI = Uri.parse(_horizonServerUrls[_network.toString()]!); - late final Asset sellingAsset; - late final Asset buyingAsset; - if (sellingAssetCode == 'XLM') { - sellingAsset = AssetTypeNative(); - } else { - sellingAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[sellingAssetCode]!.assetCode, - _currencies.currencies[sellingAssetCode]!.issuer); - } - if (buyingAssetCode == 'XLM') { - buyingAsset = AssetTypeNative(); - } else { - buyingAsset = AssetTypeCreditAlphaNum4( - _currencies.currencies[buyingAssetCode]!.assetCode, - _currencies.currencies[buyingAssetCode]!.issuer); - } + final Asset sellingAsset = _getAsset(sellingAssetCode); + final Asset buyingAsset = _getAsset(buyingAssetCode); OrderBookRequestBuilder orderBookRequest = OrderBookRequestBuilder(httpClient, serverURI) @@ -791,6 +815,37 @@ class Client { return await orderBookRequest.stream(); } + /// Lists all active offers created by the current account. + /// + /// This function fetches a list of `OfferResponse` objects representing + /// open orders created by the account. + /// + /// ### Understanding Stellar Order Representation: + /// - **Price (`OfferResponse.price`)**: Stellar stores price as `buying / selling`, + /// which means the displayed value is the **inverse** of the price provided + /// when creating an order. + /// - **Amount (`OfferResponse.amount`)**: The amount reflects how much of the + /// **buying asset** is available for trade, rather than the original selling + /// amount provided when placing the order. + /// + /// ### Example: + /// **Creating an Order** + /// ```dart + /// await stellarClient.createOrder( + /// sellingAssetCode: 'XLM', + /// buyingAssetCode: 'TFT', + /// amount: '2', + /// price: '0.1'); // Meaning 1 XLM = 0.1 TFT + /// ``` + /// + /// **Retrieved Offer:** + /// ```dart + /// OfferResponse { + /// amount: "0.2", // Because 2 * 0.1 = 0.2 TFT + /// price: "10.0" // Because 1 / 0.1 = 10 XLM per TFT + /// } + /// ``` + /// Future> listMyOffers() async { try { final offers = await _sdk.offers.forAccount(accountId).execute(); From 6b2baa034f286eb839d60dd71df1761c1b14f2ca Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Tue, 18 Feb 2025 22:31:12 +0200 Subject: [PATCH 13/15] update docs --- packages/stellar_client/lib/src/client.dart | 70 +++++++++++++++------ 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 9f9e36f1..686cf2cb 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -774,24 +774,44 @@ class Client { /// This function returns a stream of `OrderBookResponse`, which provides /// real-time updates on buy and sell orders for the specified asset pair. /// + /// ### Understanding Stellar Order Representation: + /// - **Price (`OrderBookResponse.asks[].price` & `OrderBookResponse.bids[].price`)**: + /// Stellar stores price as `buying / selling`, meaning the displayed price + /// is the **inverse** of the price provided when creating an order. + /// - **Amount (`OrderBookResponse.asks[].amount`)**: + /// This represents the total amount of the **selling asset** available in the order book. + /// + /// ### Conversion Formula: + /// ``` + /// Total selling amount = Buying amount * Price + /// Stored price = 1 / Provided price + /// ``` + /// /// ### Example: - /// **Creating an Order** + /// #### **Creating an Order** /// ```dart /// await stellarClient.createOrder( /// sellingAssetCode: 'XLM', /// buyingAssetCode: 'TFT', - /// amount: '2', - /// price: '0.1'); // Meaning 1 XLM = 0.1 TFT + /// amount: '2', // Buying 2 TFT + /// price: '0.1'); // 1 XLM = 0.1 TFT /// ``` /// - /// **Retrieved Offer:** + /// #### **Retrieved Order Book Entry** /// ```dart - /// OfferResponse { - /// amount: "0.2", // Because 2 * 0.1 = 0.2 TFT - /// price: "10.0" // Because 1 / 0.1 = 10 XLM per TFT + /// OrderBookResponse { + /// asks: [ + /// { + /// amount: "0.2", // Total selling amount = 2 * 0.1 = 0.2 XLM + /// price: "10.0" // Inverted: 1 / 0.1 = 10 XLM per TFT + /// } + /// ] /// } /// ``` /// + /// **Key Takeaways:** + /// - `OrderBookResponse.asks[].amount` = **Total amount of the selling asset**. + /// - `OrderBookResponse.asks[].price` = **Inverse of the provided price**. Future> getOrderBook( {required String sellingAssetCode, required String buyingAssetCode}) async { @@ -822,30 +842,44 @@ class Client { /// /// ### Understanding Stellar Order Representation: /// - **Price (`OfferResponse.price`)**: Stellar stores price as `buying / selling`, - /// which means the displayed value is the **inverse** of the price provided + /// meaning the displayed price is the **inverse** of the price provided /// when creating an order. - /// - **Amount (`OfferResponse.amount`)**: The amount reflects how much of the - /// **buying asset** is available for trade, rather than the original selling - /// amount provided when placing the order. + /// - **Amount (`OfferResponse.amount`)**: This represents the amount of the + /// **buying asset** still available for trade, not the original amount + /// of the selling asset. + /// + /// ### Conversion Formula: + /// When placing an order: + /// ``` + /// Total selling amount = Buying amount * Price + /// ``` + /// + /// Stellar inverts the price when storing the offer: + /// ``` + /// Stored price = 1 / Provided price + /// ``` /// /// ### Example: - /// **Creating an Order** + /// #### **Creating an Order** /// ```dart /// await stellarClient.createOrder( - /// sellingAssetCode: 'XLM', + /// sellingAssetCode: 'USDC', /// buyingAssetCode: 'TFT', - /// amount: '2', - /// price: '0.1'); // Meaning 1 XLM = 0.1 TFT + /// amount: '5', // Buying 5 TFT + /// price: '0.02'); // 1 USDC = 0.02 TFT /// ``` /// - /// **Retrieved Offer:** + /// #### **Retrieved Offer (from Stellar Order Book)** /// ```dart /// OfferResponse { - /// amount: "0.2", // Because 2 * 0.1 = 0.2 TFT - /// price: "10.0" // Because 1 / 0.1 = 10 XLM per TFT + /// amount: "0.2", // Total selling amount = 5 * 0.02 = 0.2 USDC + /// price: "50.0" // Inverted: 1 / 0.02 = 50 USDC per TFT /// } /// ``` /// + /// **Key Takeaways:** + /// - `OfferResponse.amount` = **Total amount of the selling asset left**. + /// - `OfferResponse.price` = **Inverse of the provided price**. Future> listMyOffers() async { try { final offers = await _sdk.offers.forAccount(accountId).execute(); From 20bbbef56f8556680d602edf49d29d9a070e4bc6 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Tue, 11 Mar 2025 00:22:07 +0200 Subject: [PATCH 14/15] added function for getting trading history --- packages/stellar_client/lib/src/client.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 686cf2cb..234d4ba3 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -894,4 +894,15 @@ class Client { throw Exception('Error listing offers for account $accountId: $error'); } } + + Future> getTradingHistory(String accountId) async { + try { + Page tradesPage = + await _sdk.trades.forAccount(accountId).execute(); + + return tradesPage.records; + } catch (e) { + throw Exception('Failed to fetch trading history: ${e.toString()}'); + } + } } From c86f2f91f67c5e5aff7097f9e67e3d41233f9b25 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Thu, 13 Mar 2025 00:53:44 +0200 Subject: [PATCH 15/15] update return of create, cancel and update orders to be bool --- packages/stellar_client/lib/src/client.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/stellar_client/lib/src/client.dart b/packages/stellar_client/lib/src/client.dart index 234d4ba3..f00759d4 100644 --- a/packages/stellar_client/lib/src/client.dart +++ b/packages/stellar_client/lib/src/client.dart @@ -589,7 +589,7 @@ class Client { /// - For example, instead of writing `.1`, the price should be written as `0.1`. /// - **Correct format**: `0.1` /// - **Incorrect format**: `.1` - Future createOrder( + Future createOrder( {required String sellingAssetCode, required String buyingAssetCode, required String amount, @@ -651,8 +651,9 @@ class Client { await _sdk.submitTransaction(transaction); if (!response.success) { logger.e('Transaction failed with result: ${response.resultXdr}'); + return false; } - return response; + return true; } catch (error) { throw Exception('Transaction failed due to: ${error.toString()}'); } @@ -664,7 +665,7 @@ class Client { /// /// **Note:** Cancelling an order requires having XLM in the account /// to cover transaction fees and reserve requirements. - Future cancelOrder( + Future cancelOrder( {required String sellingAssetCode, required String buyingAssetCode, required String offerId, @@ -702,8 +703,9 @@ class Client { await _sdk.submitTransaction(transaction); if (!response.success) { logger.e('Transaction failed with result: ${response.resultXdr}'); + return false; } - return response; + return true; } catch (error) { throw Exception('Transaction failed due to: ${error.toString()}'); } @@ -721,7 +723,7 @@ class Client { /// - For example, instead of writing `.1`, the price should be written as `0.1`. /// - **Correct format**: `0.1` /// - **Incorrect format**: `.1` - Future updateOrder( + Future updateOrder( {required String sellingAssetCode, required String buyingAssetCode, required String amount, @@ -762,8 +764,9 @@ class Client { await _sdk.submitTransaction(transaction); if (!response.success) { logger.e('Transaction failed with result: ${response.resultXdr}'); + return false; } - return response; + return true; } catch (error) { throw Exception('Transaction failed due to: ${error.toString()}'); }