diff --git a/CHANGELOG.md b/CHANGELOG.md index f72b0b7..c502eca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [1.0.5] - 31.Jul.2020. +- add SEP-0011 implementation (txrep) +- add SEP-0011 examples and test + ## [1.0.4] - 28.Jul.2020. - refactor transaction, move network passphrase to signing - improve examples diff --git a/README.md b/README.md index 1136ca1..9cf538e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The Soneso open source Stellar SDK for Flutter is build with Dart and provides A 1. Add the dependency to your pubspec.yaml file: ``` dependencies: - stellar_flutter_sdk: ^1.0.4 + stellar_flutter_sdk: ^1.0.5 ``` 2. Install it (command line or IDE): ``` @@ -297,6 +297,7 @@ print(response.memo); | [SEP-0001: stellar.toml](documentation/sdk_examples/sep-0001-toml.md) | In this example you can find out how to obtain data about an organization’s Stellar integration.| [SEP-0001](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md)| | [SEP-0002: Federation](documentation/sdk_examples/sep-0002-federation.md) | This example shows how to resolve a stellar address, a stellar account id, a transaction id and a forward by using the federation protocol. | [SEP-0002](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0002.md)| | [SEP-0005: Key derivation](documentation/sdk_examples/sep-0005-key-derivation.md) | In this examples you can see how to generate 12 or 24 words mnemonics for different languages using the Flutter SDK, how to generate key pairs from a mnemonic (with and without BIP 39 passphrase) and how to generate key pairs from a BIP 39 seed. | [SEP-0005](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md)| +| [SEP-0011: Txrep](documentation/sdk_examples/sep-0011-txrep.md) | This example shows how to to generate Txrep (human-readable low-level representation of Stellar transactions) from a transaction and how to create a transaction object from a Txrep string. | [SEP-0011](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0011.md)| Additional examples can be found in the [tests](https://github.com/Soneso/stellar_flutter_sdk/blob/master/test/). @@ -309,6 +310,7 @@ You can fild additional documentation including the API documentation in the [do - [SEP-0001 (stellar.toml)](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md) - [SEP-0002 (Federation)](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0002.md) - [SEP-0005 (Key derivation)](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md) +- [SEP-0011 (Txrep)](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0011.md) ## How to contribute diff --git a/documentation/README.md b/documentation/README.md index 2692a31..b77bb2d 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -35,7 +35,8 @@ The Soneso open source Stellar SDK for Flutter is build with Dart and provides A | [SEP-0001: stellar.toml](sdk_examples/sep-0001-toml.md) | In this example you can find out how to obtain data about an organization’s Stellar integration.| [SEP-0001](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md)| | [SEP-0002: Federation](sdk_examples/sep-0002-federation.md) | This examples shows how to resolve a stellar address, a stellar account id, a transaction id and a forward by using the federation protocol. | [SEP-0002](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0002.md)| | [SEP-0005: Key derivation](sdk_examples/sep-0005-key-derivation.md) | In this examples you can see how to generate 12 or 24 words mnemonics for different languages using the Flutter SDK, how to generate key pairs from a mnemonic (with and without BIP 39 passphrase) and how to generate key pairs from a BIP 39 seed. | [SEP-0005](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md)| +| [SEP-0011: Txrep](sdk_examples/sep-0011-txrep.md) | This example shows how to to generate Txrep (human-readable low-level representation of Stellar transactions) from a transaction and how to create a transaction object from a Txrep string. | [SEP-0011](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0011.md)| More examples and use cases can be found in the [test classes](../test). -An additional [example App](../example) is in progress. \ No newline at end of file +An additional [example App](../example) is in progress. diff --git a/documentation/sdk_api_doc.zip b/documentation/sdk_api_doc.zip index 307b2fe..d0c48dc 100644 Binary files a/documentation/sdk_api_doc.zip and b/documentation/sdk_api_doc.zip differ diff --git a/documentation/sdk_examples/README.md b/documentation/sdk_examples/README.md index a0221bf..8039ca8 100644 --- a/documentation/sdk_examples/README.md +++ b/documentation/sdk_examples/README.md @@ -25,5 +25,6 @@ The [Soneso open source Stellar SDK for Flutter](https://github.com/Soneso/stell | [Muxed accounts](muxed_account_payment.md) | In this example we will see how to use a muxed account in a payment operation.| [First-class multiplexed accounts](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0027.md)| | [SEP-0001: stellar.toml](sep-0001-toml.md) | In this example you can find out how to obtain data about an organization’s Stellar integration.| [SEP-0001](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md)| | [SEP-0002: Federation](sep-0002-federation.md) | This examples shows how to resolve a stellar address, a stellar account id, a transaction id and a forward by using the federation protocol. | [SEP-0002](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0002.md)| -| [SEP-0005: Key derivation](sdk_examples/sep-0005-key-derivation.md) | In this examples you can see how to generate 12 or 24 words mnemonics for different languages using the Flutter SDK, how to generate key pairs from a mnemonic (with and without BIP 39 passphrase) and how to generate key pairs from a BIP 39 seed. | [SEP-0005](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md)| +| [SEP-0005: Key derivation](sep-0005-key-derivation.md) | In this examples you can see how to generate 12 or 24 words mnemonics for different languages using the Flutter SDK, how to generate key pairs from a mnemonic (with and without BIP 39 passphrase) and how to generate key pairs from a BIP 39 seed. | [SEP-0005](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md)| +| [SEP-0011: Txrep](sep-0011-txrep.md) | This example shows how to to generate Txrep (human-readable low-level representation of Stellar transactions) from a transaction and how to create a transaction object from a Txrep string. | [SEP-0011](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0011.md)| diff --git a/documentation/sdk_examples/sep-0011-txrep.md b/documentation/sdk_examples/sep-0011-txrep.md new file mode 100644 index 0000000..f58afe1 --- /dev/null +++ b/documentation/sdk_examples/sep-0011-txrep.md @@ -0,0 +1,99 @@ + +### SEP-0011 - Txrep: human-readable low-level representation of Stellar transactions + +Txrep: human-readable low-level representation of Stellar transactions is described in [SEP-0011](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0011.md). + +In the following examples show how to generate Txrep from a transaction and how to create a transaction object from a Txrep string. + +FeeBumpTransaction and muxed accounts are currently not supported. + +### Generate Txrep from transaction + +```dart + +// Prepare accounts. +KeyPair sourceKeyPair = KeyPair.random(); +String sourceAccountId = sourceKeyPair.accountId; + +// Fund the source account +await FriendBot.fundTestAccount(sourceAccountId); + +// Load the account data including the sequence number +AccountResponse sourceAccount = await sdk.accounts.account(sourceAccountId); + +// Generate accountId for a new account to be created. +String newAccountId = KeyPair.random().accountId; + +// Build the CreateAccountOperation. +Operation createAccount = new CreateAccountOperationBuilder(newAccountId, "220.09").build(); + +// Add memo. +MemoText mt = MemoText("Enjoy this transaction"); + +// Create the transaction. +Transaction transaction = new TransactionBuilder(sourceAccount) + .addMemo(mt) + .addOperation(createAccount) + .build(); + +// Sign the transaction. +transaction.sign(sourceKeyPair, Network.TESTNET); + +// Generate and print the txrep +String txrep = TxRep.toTxRep(transaction); +print(txrep); +``` +**Result:** + +``` +type: ENVELOPE_TYPE_TX +tx.sourceAccount: GAVVTEXNKEQ7G7XVJJ2JMBIY5WUKE73PWFVMMIW4DY7Z2E6F7NXXIVUH +tx.fee: 100 +tx.seqNum: 238563958456321 +tx.timeBounds._present: false +tx.memo.type: MEMO_TEXT +tx.memo.text: "Enjoy this transaction" +tx.operations.len: 1 +tx.operation[0].sourceAccount._present: false +tx.operation[0].body.type: CREATE_ACCOUNT +tx.operation[0].body.createAccountOp.destination: GC5ICOW2G64VZXON6DNAWPZ46TZZYV6DYEKZE42KWTBMXCVNTS3EENHC +tx.operation[0].body.createAccountOp.startingBalance: 2200900000 +tx.signatures.len: 1 +tx.signatures[0].hint: c5fb6f74 +tx.signatures[0].signature: e0611076f402005942b27807c0702e0976c14c9a9bb8bc46d1c4740060b5125da1d02c2d9ee10b58acfdaa009f57867506d188d1ee0ab3d00877db22c4101709 +tx.ext.v: 0 +``` + +### Create a transaction object from a Txrep string + +```dart +String txRepString = ''' +type: ENVELOPE_TYPE_TX +tx.sourceAccount: GAVVTEXNKEQ7G7XVJJ2JMBIY5WUKE73PWFVMMIW4DY7Z2E6F7NXXIVUH +tx.fee: 100 +tx.seqNum: 238563958456321 +tx.timeBounds._present: false +tx.memo.type: MEMO_TEXT +tx.memo.text: "Enjoy this transaction" +tx.operations.len: 1 +tx.operation[0].sourceAccount._present: false +tx.operation[0].body.type: CREATE_ACCOUNT +tx.operation[0].body.createAccountOp.destination: GC5ICOW2G64VZXON6DNAWPZ46TZZYV6DYEKZE42KWTBMXCVNTS3EENHC +tx.operation[0].body.createAccountOp.startingBalance: 2200900000 +tx.signatures.len: 1 +tx.signatures[0].hint: c5fb6f74 +tx.signatures[0].signature: e0611076f402005942b27807c0702e0976c14c9a9bb8bc46d1c4740060b5125da1d02c2d9ee10b58acfdaa009f57867506d188d1ee0ab3d00877db22c4101709 +tx.ext.v: 0'''; + +// Create a transaction object by parsing the txRepString. +Transaction tx = TxRep.fromTxRep(txRepString); + +print(tx.sourceAccount.accountId); +// GAVVTEXNKEQ7G7XVJJ2JMBIY5WUKE73PWFVMMIW4DY7Z2E6F7NXXIVUH +print(tx.fee); +// 100 +print(tx.sequenceNumber); +// 238563958456321 +print(tx.operations.length); +// 1 +``` diff --git a/lib/src/sep/0011/txrep.dart b/lib/src/sep/0011/txrep.dart index 503565d..daf18c3 100644 --- a/lib/src/sep/0011/txrep.dart +++ b/lib/src/sep/0011/txrep.dart @@ -6,14 +6,12 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:decimal/decimal.dart'; import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart'; - import '../../transaction.dart'; import '../../memo.dart'; import '../../price.dart'; import '../../operation.dart'; import '../../util.dart'; import '../../key_pair.dart'; -import 'txrep_utils.dart'; class TxRep { static String toTxRep(AbstractTransaction tx) { @@ -249,87 +247,27 @@ class TxRep { } if (opType == 'CHANGE_TRUST') { String opPrefix = prefix + 'changeTrustOp.'; - Asset asset = decodeAsset(_removeComment(map[opPrefix + 'line'])); - String limit; - if (_removeComment(map[opPrefix + 'limit._present']) == 'true') { - limit = fromAmount(_removeComment(map[opPrefix + 'limit'])); - } - ChangeTrustOperationBuilder builder = - ChangeTrustOperationBuilder(asset, limit); - if (sourceAccountId != null) { - builder.setSourceAccount(sourceAccountId); - } - return builder.build(); + return _getChangeTrustOperation(sourceAccountId, opPrefix, map); } if (opType == 'ALLOW_TRUST') { String opPrefix = prefix + 'allowTrustOp.'; - String trustor = _removeComment(map[opPrefix + 'trustor']); - String assetCode = _removeComment(map[opPrefix + 'asset']); - int authtorize = int.parse(_removeComment(map[opPrefix + 'authorize'])); - AllowTrustOperationBuilder builder = - AllowTrustOperationBuilder(trustor, assetCode, authtorize); - if (sourceAccountId != null) { - builder.setSourceAccount(sourceAccountId); - } - return builder.build(); + return _getAllowTrustOperation(sourceAccountId, opPrefix, map); } if (opType == 'ACCOUNT_MERGE') { // account merge does not include 'accountMergeOp' prefix - String destination = - _removeComment(map['tx.operation[$index].body.destination']); - AccountMergeOperationBuilder builder = - AccountMergeOperationBuilder(destination); - if (sourceAccountId != null) { - builder.setSourceAccount(sourceAccountId); - } - return builder.build(); + return _getAccountMergeOperation(sourceAccountId, index, map); } if (opType == 'MANAGE_DATA') { String opPrefix = prefix + 'manageDataOp.'; - String dataName = _removeComment(map[opPrefix + 'dataName']); - Uint8List value; - if (_removeComment(map[opPrefix + 'dataValue._present']) == 'true') { - String valueHex = - fromAmount(_removeComment(map[opPrefix + 'dataValue'])); - value = Util.hexToBytes(valueHex); - } - ManageDataOperationBuilder builder = - ManageDataOperationBuilder(dataName, value); - if (sourceAccountId != null) { - builder.setSourceAccount(sourceAccountId); - } - return builder.build(); + return _getManageDataOperation(sourceAccountId, opPrefix, map); } if (opType == 'BUMP_SEQUENCE') { String opPrefix = prefix + 'bumpSequenceOp.'; - int bumpTo = int.parse(_removeComment(map[opPrefix + 'bumpTo'])); - BumpSequenceOperationBuilder builder = - BumpSequenceOperationBuilder(bumpTo); - if (sourceAccountId != null) { - builder.setSourceAccount(sourceAccountId); - } - return builder.build(); + return _getBumpSequenceOperation(sourceAccountId, opPrefix, map); } if (opType == 'MANAGE_BUY_OFFER') { String opPrefix = prefix + 'manageBuyOfferOp.'; - Asset selling = decodeAsset(_removeComment(map[opPrefix + 'selling'])); - Asset buying = decodeAsset(_removeComment(map[opPrefix + 'buying'])); - String amount = fromAmount(_removeComment(map[opPrefix + 'buyAmount'])); - int n = int.parse(_removeComment(map[opPrefix + 'price.n'])); - int d = int.parse(_removeComment(map[opPrefix + 'price.d'])); - if (d == 0) { - throw Exception( - 'price denominator can not be 0 in ' + opPrefix + 'price.d'); - } - Decimal dec = Decimal.parse(n.toString()) / Decimal.parse(d.toString()); - int offerId = int.parse(_removeComment(map[opPrefix + 'offerID'])); - ManageBuyOfferOperationBuilder builder = ManageBuyOfferOperationBuilder( - selling, buying, amount, dec.toString()); - builder.setOfferId(offerId.toString()); - if (sourceAccountId != null) { - builder.setSourceAccount(sourceAccountId); - } - return builder.build(); + return _getManageBuyOfferOperation(sourceAccountId, opPrefix, map); } throw Exception('invalid or unsupported [$prefix].type - $opType'); } @@ -352,7 +290,7 @@ class TxRep { } String startingBalance; try { - startingBalance = fromAmount(startingBalanceValue); + startingBalance = _fromAmount(startingBalanceValue); } catch (e) { throw Exception('invalid $opPrefix' + 'startingBalance'); } @@ -384,7 +322,7 @@ class TxRep { } Asset asset; try { - asset = decodeAsset(assetStr); + asset = _decodeAsset(assetStr); } catch (e) { throw Exception('invalid $opPrefix' + 'asset'); } @@ -397,7 +335,7 @@ class TxRep { } String amount; try { - amount = fromAmount(amountStr); + amount = _fromAmount(amountStr); } catch (e) { throw Exception('invalid $opPrefix' + 'amount'); } @@ -421,7 +359,7 @@ class TxRep { } Asset sendAsset; try { - sendAsset = decodeAsset(sendAssetStr); + sendAsset = _decodeAsset(sendAssetStr); } catch (e) { throw Exception('invalid $opPrefix' + 'sendAsset'); } @@ -435,7 +373,7 @@ class TxRep { } String sendMax; try { - sendMax = fromAmount(sendMaxStr); + sendMax = _fromAmount(sendMaxStr); } catch (e) { throw Exception('invalid $opPrefix' + 'sendMax'); } @@ -459,7 +397,7 @@ class TxRep { } Asset destAsset; try { - destAsset = decodeAsset(destAssetStr); + destAsset = _decodeAsset(destAssetStr); } catch (e) { throw Exception('invalid $opPrefix' + 'destAsset'); } @@ -473,7 +411,7 @@ class TxRep { } String destAmount; try { - destAmount = fromAmount(destAmountStr); + destAmount = _fromAmount(destAmountStr); } catch (e) { throw Exception('invalid $opPrefix' + 'destAmount'); } @@ -500,7 +438,7 @@ class TxRep { throw Exception('missing $opPrefix' + 'path[$i]'); } try { - Asset nextAsset = decodeAsset(nextAssetStr); + Asset nextAsset = _decodeAsset(nextAssetStr); path.add(nextAsset); } catch (e) { throw Exception('invalid $opPrefix' + 'path[$i]'); @@ -525,7 +463,7 @@ class TxRep { } Asset sendAsset; try { - sendAsset = decodeAsset(sendAssetStr); + sendAsset = _decodeAsset(sendAssetStr); } catch (e) { throw Exception('invalid $opPrefix' + 'sendAsset'); } @@ -539,7 +477,7 @@ class TxRep { } String sendAmount; try { - sendAmount = fromAmount(sendAmountStr); + sendAmount = _fromAmount(sendAmountStr); } catch (e) { throw Exception('invalid $opPrefix' + 'sendAmount'); } @@ -563,7 +501,7 @@ class TxRep { } Asset destAsset; try { - destAsset = decodeAsset(destAssetStr); + destAsset = _decodeAsset(destAssetStr); } catch (e) { throw Exception('invalid $opPrefix' + 'destAsset'); } @@ -577,7 +515,7 @@ class TxRep { } String destMin; try { - destMin = fromAmount(destMinStr); + destMin = _fromAmount(destMinStr); } catch (e) { throw Exception('invalid $opPrefix' + 'destMin'); } @@ -604,7 +542,7 @@ class TxRep { throw Exception('missing $opPrefix' + 'path[$i]'); } try { - Asset nextAsset = decodeAsset(nextAssetStr); + Asset nextAsset = _decodeAsset(nextAssetStr); path.add(nextAsset); } catch (e) { throw Exception('invalid $opPrefix' + 'path[$i]'); @@ -629,7 +567,7 @@ class TxRep { } Asset selling; try { - selling = decodeAsset(sellingStr); + selling = _decodeAsset(sellingStr); } catch (e) { throw Exception('invalid $opPrefix' + 'selling'); } @@ -642,7 +580,7 @@ class TxRep { } Asset buying; try { - buying = decodeAsset(buyingStr); + buying = _decodeAsset(buyingStr); } catch (e) { throw Exception('invalid $opPrefix' + 'buying'); } @@ -656,7 +594,7 @@ class TxRep { } String amount; try { - amount = fromAmount(amountStr); + amount = _fromAmount(amountStr); } catch (e) { throw Exception('invalid $opPrefix' + 'amount'); } @@ -721,6 +659,106 @@ class TxRep { return builder.build(); } + static ManageBuyOfferOperation _getManageBuyOfferOperation( + String sourceAccountId, String opPrefix, Map map) { + String sellingStr = _removeComment(map[opPrefix + 'selling']); + if (sellingStr == null) { + throw Exception('missing $opPrefix' + 'selling'); + } + Asset selling; + try { + selling = _decodeAsset(sellingStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'selling'); + } + if (selling == null) { + throw Exception('invalid $opPrefix' + 'selling'); + } + String buyingStr = _removeComment(map[opPrefix + 'buying']); + if (buyingStr == null) { + throw Exception('missing $opPrefix' + 'buying'); + } + Asset buying; + try { + buying = _decodeAsset(buyingStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'buying'); + } + if (buying == null) { + throw Exception('invalid $opPrefix' + 'buying'); + } + + String amountStr = _removeComment(map[opPrefix + 'buyAmount']); + if (amountStr == null) { + throw Exception('missing $opPrefix' + 'buyAmount'); + } + String buyAmount; + try { + buyAmount = _fromAmount(amountStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'buyAmount'); + } + if (buyAmount == null) { + throw Exception('invalid $opPrefix' + 'buyAmount'); + } + + String priceNStr = _removeComment(map[opPrefix + 'price.n']); + if (priceNStr == null) { + throw Exception('missing $opPrefix' + 'price.n'); + } + int n; + try { + n = int.parse(priceNStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'price.n'); + } + if (n == null) { + throw Exception('invalid $opPrefix' + 'price.n'); + } + + String priceDStr = _removeComment(map[opPrefix + 'price.d']); + if (priceDStr == null) { + throw Exception('missing $opPrefix' + 'price.d'); + } + int d; + try { + d = int.parse(priceDStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'price.d'); + } + if (d == null) { + throw Exception('invalid $opPrefix' + 'price.d'); + } + if (d == 0) { + throw Exception( + 'price denominator can not be 0 in ' + opPrefix + 'price.d'); + } + + Decimal dec = Decimal.parse(n.toString()) / Decimal.parse(d.toString()); + + String offerIdStr = _removeComment(map[opPrefix + 'offerID']); + if (offerIdStr == null) { + throw Exception('missing $opPrefix' + 'offerID'); + } + int offerId; + try { + offerId = int.parse(offerIdStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'offerID'); + } + if (offerId == null) { + throw Exception('invalid $opPrefix' + 'offerID'); + } + + ManageBuyOfferOperationBuilder builder = ManageBuyOfferOperationBuilder( + selling, buying, buyAmount, dec.toString()); + builder.setOfferId(offerId.toString()); + if (sourceAccountId != null) { + builder.setSourceAccount(sourceAccountId); + } + return builder.build(); + } + static CreatePassiveSellOfferOperation _getCreatePassiveSellOfferOperation( String sourceAccountId, String opPrefix, Map map) { String sellingStr = _removeComment(map[opPrefix + 'selling']); @@ -729,7 +767,7 @@ class TxRep { } Asset selling; try { - selling = decodeAsset(sellingStr); + selling = _decodeAsset(sellingStr); } catch (e) { throw Exception('invalid $opPrefix' + 'selling'); } @@ -742,7 +780,7 @@ class TxRep { } Asset buying; try { - buying = decodeAsset(buyingStr); + buying = _decodeAsset(buyingStr); } catch (e) { throw Exception('invalid $opPrefix' + 'buying'); } @@ -756,7 +794,7 @@ class TxRep { } String amount; try { - amount = fromAmount(amountStr); + amount = _fromAmount(amountStr); } catch (e) { throw Exception('invalid $opPrefix' + 'amount'); } @@ -1020,6 +1058,145 @@ class TxRep { return builder.build(); } + static ChangeTrustOperation _getChangeTrustOperation( + String sourceAccountId, String opPrefix, Map map) { + String assetStr = _removeComment(map[opPrefix + 'line']); + if (assetStr == null) { + throw Exception('missing $opPrefix' + 'line'); + } + Asset asset; + try { + asset = _decodeAsset(assetStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'line'); + } + if (asset == null) { + throw Exception('invalid $opPrefix' + 'line'); + } + String present = _removeComment(map[opPrefix + 'limit._present']); + if (present == null) { + throw Exception('missing $opPrefix' + 'limit._present'); + } + String limit; + if (present == 'true') { + String limitStr = _removeComment(map[opPrefix + 'limit']); + if (limitStr == null) { + throw Exception('missing $opPrefix' + 'limit'); + } + try { + limit = _fromAmount(limitStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'limit'); + } + } + ChangeTrustOperationBuilder builder = + ChangeTrustOperationBuilder(asset, limit); + if (sourceAccountId != null) { + builder.setSourceAccount(sourceAccountId); + } + return builder.build(); + } + + static AllowTrustOperation _getAllowTrustOperation( + String sourceAccountId, String opPrefix, Map map) { + String trustor = _removeComment(map[opPrefix + 'trustor']); + if (trustor == null) { + throw Exception('missing $opPrefix' + 'trustor'); + } + String assetCode = _removeComment(map[opPrefix + 'asset']); + if (assetCode == null) { + throw Exception('missing $opPrefix' + 'asset'); + } + String authStr = _removeComment(map[opPrefix + 'authorize']); + if (authStr == null) { + throw Exception('missing $opPrefix' + 'authorize'); + } + int authorize; + try { + authorize = int.parse(authStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'authorize'); + } + if (authorize < 0 || authorize > 2) { + throw Exception('invalid $opPrefix' + 'authorize'); + } + AllowTrustOperationBuilder builder = + AllowTrustOperationBuilder(trustor, assetCode, authorize); + if (sourceAccountId != null) { + builder.setSourceAccount(sourceAccountId); + } + return builder.build(); + } + + static AccountMergeOperation _getAccountMergeOperation( + String sourceAccountId, int index, Map map) { + String destination = + _removeComment(map['tx.operation[$index].body.destination']); + if (destination == null) { + throw Exception('missing tx.operation[$index].body.destination'); + } + AccountMergeOperationBuilder builder = + AccountMergeOperationBuilder(destination); + if (sourceAccountId != null) { + builder.setSourceAccount(sourceAccountId); + } + return builder.build(); + } + + static ManageDataOperation _getManageDataOperation( + String sourceAccountId, String opPrefix, Map map) { + String dataName = _removeComment(map[opPrefix + 'dataName']); + if (dataName == null) { + throw Exception('missing $opPrefix' + 'dataName'); + } + Uint8List value; + String present = _removeComment(map[opPrefix + 'dataValue._present']); + if (present == null) { + throw Exception('missing $opPrefix' + 'dataValue._present'); + } + if (present == 'true') { + String dataValueStr = _removeComment(map[opPrefix + 'dataValue']); + if (dataValueStr == null) { + throw Exception('missing $opPrefix' + 'dataValue'); + } + try { + value = Util.hexToBytes(dataValueStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'dataValue'); + } + } + ManageDataOperationBuilder builder = + ManageDataOperationBuilder(dataName, value); + if (sourceAccountId != null) { + builder.setSourceAccount(sourceAccountId); + } + return builder.build(); + } + + static BumpSequenceOperation _getBumpSequenceOperation( + String sourceAccountId, String opPrefix, Map map) { + String bumpToStr = _removeComment(map[opPrefix + 'bumpTo']); + if (bumpToStr == null) { + throw Exception('missing $opPrefix' + 'bumpTo'); + } + + int bumpTo; + try { + bumpTo = int.parse(bumpToStr); + } catch (e) { + throw Exception('invalid $opPrefix' + 'bumpTo'); + } + if (bumpTo == null) { + throw Exception('invalid $opPrefix' + 'bumpTo'); + } + + BumpSequenceOperationBuilder builder = BumpSequenceOperationBuilder(bumpTo); + if (sourceAccountId != null) { + builder.setSourceAccount(sourceAccountId); + } + return builder.build(); + } + static String _removeComment(String value) { if (value == null) { return null; @@ -1093,54 +1270,54 @@ class TxRep { _addLine('tx.operation[$index].sourceAccount._present', 'false', lines); } - _addLine('tx.operation[$index].body.type', txRepOpTypeUpperCase(operation), + _addLine('tx.operation[$index].body.type', _txRepOpTypeUpperCase(operation), lines); - String prefix = 'tx.operation[$index].body.${txRepOpType(operation)}'; + String prefix = 'tx.operation[$index].body.${_txRepOpType(operation)}'; if (operation is CreateAccountOperation) { _addLine('$prefix.destination', operation.destination, lines); - _addLine('$prefix.startingBalance', toAmount(operation.startingBalance), + _addLine('$prefix.startingBalance', _toAmount(operation.startingBalance), lines); } else if (operation is PaymentOperation) { _addLine('$prefix.destination', operation.destination.accountId, lines); - _addLine('$prefix.asset', encodeAsset(operation.asset), lines); - _addLine('$prefix.amount', toAmount(operation.amount), lines); + _addLine('$prefix.asset', _encodeAsset(operation.asset), lines); + _addLine('$prefix.amount', _toAmount(operation.amount), lines); } else if (operation is PathPaymentStrictReceiveOperation) { - _addLine('$prefix.sendAsset', encodeAsset(operation.sendAsset), lines); - _addLine('$prefix.sendMax', toAmount(operation.sendMax), lines); + _addLine('$prefix.sendAsset', _encodeAsset(operation.sendAsset), lines); + _addLine('$prefix.sendMax', _toAmount(operation.sendMax), lines); _addLine('$prefix.destination', operation.destination.accountId, lines); - _addLine('$prefix.destAsset', encodeAsset(operation.destAsset), lines); - _addLine('$prefix.destAmount', toAmount(operation.destAmount), lines); + _addLine('$prefix.destAsset', _encodeAsset(operation.destAsset), lines); + _addLine('$prefix.destAmount', _toAmount(operation.destAmount), lines); _addLine('$prefix.path.len', operation.path.length.toString(), lines); int assetIndex = 0; for (Asset asset in operation.path) { - _addLine('$prefix.path[$assetIndex]', encodeAsset(asset), lines); + _addLine('$prefix.path[$assetIndex]', _encodeAsset(asset), lines); assetIndex++; } } else if (operation is PathPaymentStrictSendOperation) { - _addLine('$prefix.sendAsset', encodeAsset(operation.sendAsset), lines); - _addLine('$prefix.sendAmount', toAmount(operation.sendAmount), lines); + _addLine('$prefix.sendAsset', _encodeAsset(operation.sendAsset), lines); + _addLine('$prefix.sendAmount', _toAmount(operation.sendAmount), lines); _addLine('$prefix.destination', operation.destination.accountId, lines); - _addLine('$prefix.destAsset', encodeAsset(operation.destAsset), lines); - _addLine('$prefix.destMin', toAmount(operation.destMin), lines); + _addLine('$prefix.destAsset', _encodeAsset(operation.destAsset), lines); + _addLine('$prefix.destMin', _toAmount(operation.destMin), lines); _addLine('$prefix.path.len', operation.path.length.toString(), lines); int assetIndex = 0; for (Asset asset in operation.path) { - _addLine('$prefix.path[$assetIndex]', encodeAsset(asset), lines); + _addLine('$prefix.path[$assetIndex]', _encodeAsset(asset), lines); assetIndex++; } } else if (operation is ManageSellOfferOperation) { - _addLine('$prefix.selling', encodeAsset(operation.selling), lines); - _addLine('$prefix.buying', encodeAsset(operation.buying), lines); - _addLine('$prefix.amount', toAmount(operation.amount), lines); + _addLine('$prefix.selling', _encodeAsset(operation.selling), lines); + _addLine('$prefix.buying', _encodeAsset(operation.buying), lines); + _addLine('$prefix.amount', _toAmount(operation.amount), lines); Price price = Price.fromString(operation.price); _addLine('$prefix.price.n', price.n.toString(), lines); _addLine('$prefix.price.d', price.d.toString(), lines); _addLine('$prefix.offerID', operation.offerId, lines); } else if (operation is CreatePassiveSellOfferOperation) { - _addLine('$prefix.selling', encodeAsset(operation.selling), lines); - _addLine('$prefix.buying', encodeAsset(operation.buying), lines); - _addLine('$prefix.amount', toAmount(operation.amount), lines); + _addLine('$prefix.selling', _encodeAsset(operation.selling), lines); + _addLine('$prefix.buying', _encodeAsset(operation.buying), lines); + _addLine('$prefix.amount', _toAmount(operation.amount), lines); Price price = Price.fromString(operation.price); _addLine('$prefix.price.n', price.n.toString(), lines); _addLine('$prefix.price.d', price.d.toString(), lines); @@ -1223,10 +1400,10 @@ class TxRep { _addLine('$prefix.signer._present', 'false', lines); } } else if (operation is ChangeTrustOperation) { - _addLine('$prefix.line', encodeAsset(operation.asset), lines); + _addLine('$prefix.line', _encodeAsset(operation.asset), lines); if (operation.limit != null) { _addLine('$prefix.limit._present', 'true', lines); - _addLine('$prefix.limit', toAmount(operation.limit), lines); + _addLine('$prefix.limit', _toAmount(operation.limit), lines); } else { _addLine('$prefix.limit._present', 'false', lines); } @@ -1251,9 +1428,9 @@ class TxRep { } else if (operation is BumpSequenceOperation) { _addLine('$prefix.bumpTo', operation.bumpTo.toString(), lines); } else if (operation is ManageBuyOfferOperation) { - _addLine('$prefix.selling', encodeAsset(operation.selling), lines); - _addLine('$prefix.buying', encodeAsset(operation.buying), lines); - _addLine('$prefix.buyAmount', toAmount(operation.amount), lines); + _addLine('$prefix.selling', _encodeAsset(operation.selling), lines); + _addLine('$prefix.buying', _encodeAsset(operation.buying), lines); + _addLine('$prefix.buyAmount', _toAmount(operation.amount), lines); Price price = Price.fromString(operation.price); _addLine('$prefix.price.n', price.n.toString(), lines); _addLine('$prefix.price.d', price.d.toString(), lines); @@ -1284,4 +1461,120 @@ class TxRep { _addLine('tx.signatures[$index].signature', Util.bytesToHex(signature.signature.signature), lines); } + + static String _txRepOpTypeUpperCase(Operation operation) { + int value = operation.toXdr().body.discriminant.value; + switch (value) { + case 0: + return 'CREATE_ACCOUNT'; + case 1: + return 'PAYMENT'; + case 2: + return 'PATH_PAYMENT_STRICT_RECEIVE'; + case 3: + return 'MANAGE_SELL_OFFER'; + case 4: + return 'CREATE_PASSIVE_SELL_OFFER'; + case 5: + return 'SET_OPTIONS'; + case 6: + return 'CHANGE_TRUST'; + case 7: + return 'ALLOW_TRUST'; + case 8: + return 'ACCOUNT_MERGE'; + case 9: + return 'INFLATION'; + case 10: + return 'MANAGE_DATA'; + case 11: + return 'BUMP_SEQUENCE'; + case 12: + return 'MANAGE_BUY_OFFER'; + case 13: + return 'PATH_PAYMENT_STRICT_SEND'; + default: + throw Exception("Unknown enum value: $value"); + } + } + + static String _txRepOpType(Operation operation) { + int value = operation.toXdr().body.discriminant.value; + switch (value) { + case 0: + return 'createAccountOp'; + case 1: + return 'paymentOp'; + case 2: + return 'pathPaymentStrictReceiveOp'; + case 3: + return 'manageSellOfferOp'; + case 4: + return 'createPassiveSellOfferOp'; + case 5: + return 'setOptionsOp'; + case 6: + return 'changeTrustOp'; + case 7: + return 'allowTrustOp'; + case 8: + return 'accountMergeOp'; + case 9: + return 'inflationOp'; + case 10: + return 'manageDataOp'; + case 11: + return 'bumpSequenceOp'; + case 12: + return 'manageBuyOfferOp'; + case 13: + return 'pathPaymentStrictSendOp'; + default: + throw Exception("Unknown enum value: $value"); + } + } + + static String _toAmount(String value) { + Decimal amount = Decimal.parse(value) * Decimal.parse('10000000.00'); + return amount.toString(); + } + + static String _fromAmount(String value) { + Decimal amount = Decimal.parse(value) / Decimal.parse('10000000.00'); + return amount.toString(); + } + + static String _encodeAsset(Asset asset) { + if (asset is AssetTypeNative) { + return Asset.TYPE_NATIVE; + } else if (asset is AssetTypeCreditAlphaNum) { + AssetTypeCreditAlphaNum creditAsset = asset; + return creditAsset.code + ":" + creditAsset.issuerId; + } else { + throw Exception("unsupported asset " + asset.type); + } + } + + static Asset _decodeAsset(String asset) { + if (asset == null) { + return null; + } + if (asset == Asset.TYPE_NATIVE) { + return Asset.NATIVE; + } else { + List components = asset.split(':'); + if (components.length != 2) { + return null; + } else { + String code = components[0].trim(); + String issuerId = components[1].trim(); + if (code.length <= 4) { + return AssetTypeCreditAlphaNum4(code, issuerId); + } else if (code.length <= 12) { + return AssetTypeCreditAlphaNum12(code, issuerId); + } + } + } + return null; + } } diff --git a/lib/src/sep/0011/txrep_utils.dart b/lib/src/sep/0011/txrep_utils.dart deleted file mode 100644 index 0dd3e27..0000000 --- a/lib/src/sep/0011/txrep_utils.dart +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2020 The Stellar Flutter SDK Authors. All rights reserved. -// Use of this source code is governed by a license that can be -// found in the LICENSE file. - -import 'package:stellar_flutter_sdk/src/asset_type_credit_alphanum12.dart'; -import 'package:stellar_flutter_sdk/src/asset_type_credit_alphanum4.dart'; -import 'package:stellar_flutter_sdk/src/operation.dart'; -import 'package:decimal/decimal.dart'; -import '../../assets.dart'; -import '../../asset_type_native.dart'; -import '../../asset_type_credit_alphanum.dart'; - -String txRepOpTypeUpperCase(Operation operation) { - int value = operation.toXdr().body.discriminant.value; - switch (value) { - case 0: - return 'CREATE_ACCOUNT'; - case 1: - return 'PAYMENT'; - case 2: - return 'PATH_PAYMENT_STRICT_RECEIVE'; - case 3: - return 'MANAGE_SELL_OFFER'; - case 4: - return 'CREATE_PASSIVE_SELL_OFFER'; - case 5: - return 'SET_OPTIONS'; - case 6: - return 'CHANGE_TRUST'; - case 7: - return 'ALLOW_TRUST'; - case 8: - return 'ACCOUNT_MERGE'; - case 9: - return 'INFLATION'; - case 10: - return 'MANAGE_DATA'; - case 11: - return 'BUMP_SEQUENCE'; - case 12: - return 'MANAGE_BUY_OFFER'; - case 13: - return 'PATH_PAYMENT_STRICT_SEND'; - default: - throw Exception("Unknown enum value: $value"); - } -} - -String txRepOpType(Operation operation) { - int value = operation.toXdr().body.discriminant.value; - switch (value) { - case 0: - return 'createAccountOp'; - case 1: - return 'paymentOp'; - case 2: - return 'pathPaymentStrictReceiveOp'; - case 3: - return 'manageSellOfferOp'; - case 4: - return 'createPassiveSellOfferOp'; - case 5: - return 'setOptionsOp'; - case 6: - return 'changeTrustOp'; - case 7: - return 'allowTrustOp'; - case 8: - return 'accountMergeOp'; - case 9: - return 'inflationOp'; - case 10: - return 'manageDataOp'; - case 11: - return 'bumpSequenceOp'; - case 12: - return 'manageBuyOfferOp'; - case 13: - return 'pathPaymentStrictSendOp'; - default: - throw Exception("Unknown enum value: $value"); - } -} - -String toAmount(String value) { - Decimal amount = Decimal.parse(value) * Decimal.parse('10000000.00'); - return amount.toString(); -} - -String fromAmount(String value) { - Decimal amount = Decimal.parse(value) / Decimal.parse('10000000.00'); - return amount.toString(); -} - -String encodeAsset(Asset asset) { - if (asset is AssetTypeNative) { - return Asset.TYPE_NATIVE; - } else if (asset is AssetTypeCreditAlphaNum) { - AssetTypeCreditAlphaNum creditAsset = asset; - return creditAsset.code + ":" + creditAsset.issuerId; - } else { - throw Exception("unsupported asset " + asset.type); - } -} - -Asset decodeAsset(String asset) { - if (asset == null) { - return null; - } - if (asset == Asset.TYPE_NATIVE) { - return Asset.NATIVE; - } else { - List components = asset.split(':'); - if (components.length != 2) { - return null; - } else { - String code = components[0].trim(); - String issuerId = components[1].trim(); - if (code.length <= 4) { - return AssetTypeCreditAlphaNum4(code, issuerId); - } else if (code.length <= 12) { - return AssetTypeCreditAlphaNum12(code, issuerId); - } - } - } - return null; -} diff --git a/lib/src/stellar_sdk.dart b/lib/src/stellar_sdk.dart index d7300fd..a8c5fc0 100644 --- a/lib/src/stellar_sdk.dart +++ b/lib/src/stellar_sdk.dart @@ -27,7 +27,7 @@ import 'requests/trades_request_builder.dart'; /// Main class of the flutter stellar sdk. class StellarSDK { - static const versionNumber = "1.0.4"; + static const versionNumber = "1.0.5"; static final StellarSDK PUBLIC = new StellarSDK("https://horizon.stellar.org"); diff --git a/pubspec.yaml b/pubspec.yaml index aa04e60..ecb25e6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: stellar_flutter_sdk description: A stellar blockchain sdk that query's horizon, build, signs and submits transactions to the stellar netweok. -version: 1.0.4 +version: 1.0.5 homepage: https://github.com/Soneso/stellar_flutter_sdk environment: diff --git a/test/examples_test.dart b/test/examples_test.dart index 916d69f..74955a3 100644 --- a/test/examples_test.dart +++ b/test/examples_test.dart @@ -1598,4 +1598,62 @@ void main() { keyPair1 = wallet.getKeyPair(index: 1); print("${keyPair1.accountId} : ${keyPair1.secretSeed}"); }); + + test('sep 0011 - txrep ', () async { + // Prepare accounts. + KeyPair sourceKeyPair = KeyPair.random(); + String sourceAccountId = sourceKeyPair.accountId; + + // fund the source account + await FriendBot.fundTestAccount(sourceAccountId); + + // load the account data including the sequence number + AccountResponse sourceAccount = await sdk.accounts.account(sourceAccountId); + + // generate accountId for a new account to be created. + String newAccountId = KeyPair.random().accountId; + + // Build the CreateAccountOperation. + Operation createAccount = + new CreateAccountOperationBuilder(newAccountId, "220.09").build(); + + // Add memo. + MemoText mt = MemoText("Enjoy this transaction"); + + // Create the transaction. + Transaction transaction = new TransactionBuilder(sourceAccount) + .addMemo(mt) + .addOperation(createAccount) + .build(); + + // Sign the transaction. + transaction.sign(sourceKeyPair, Network.TESTNET); + + // Generate and print the txrep + String txrep = TxRep.toTxRep(transaction); + print(txrep); + + String txRepString = ''' +type: ENVELOPE_TYPE_TX +tx.sourceAccount: GAVVTEXNKEQ7G7XVJJ2JMBIY5WUKE73PWFVMMIW4DY7Z2E6F7NXXIVUH +tx.fee: 100 +tx.seqNum: 238563958456321 +tx.timeBounds._present: false +tx.memo.type: MEMO_TEXT +tx.memo.text: "Enjoy this transaction" +tx.operations.len: 1 +tx.operation[0].sourceAccount._present: false +tx.operation[0].body.type: CREATE_ACCOUNT +tx.operation[0].body.createAccountOp.destination: GC5ICOW2G64VZXON6DNAWPZ46TZZYV6DYEKZE42KWTBMXCVNTS3EENHC +tx.operation[0].body.createAccountOp.startingBalance: 2200900000 +tx.signatures.len: 1 +tx.signatures[0].hint: c5fb6f74 +tx.signatures[0].signature: e0611076f402005942b27807c0702e0976c14c9a9bb8bc46d1c4740060b5125da1d02c2d9ee10b58acfdaa009f57867506d188d1ee0ab3d00877db22c4101709 +tx.ext.v: 0'''; + Transaction tx = TxRep.fromTxRep(txRepString); + print(tx.sourceAccount.accountId); + print(tx.fee); + print(tx.sequenceNumber); + print(tx.operations.length); + }); } diff --git a/test/sep0011_test.dart b/test/sep0011_test.dart index db748eb..fb9111b 100644 --- a/test/sep0011_test.dart +++ b/test/sep0011_test.dart @@ -6,7 +6,130 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart'; void main() { - test('transaction to txrep', () { + test('txrep to transaction qnd back to txrep', () { + String txRep = ''' +type: ENVELOPE_TYPE_TX +tx.sourceAccount: GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.fee: 1400 +tx.seqNum: 1102902109202 +tx.timeBounds._present: true +tx.timeBounds.minTime: 1595282368 +tx.timeBounds.maxTime: 1595284000 +tx.memo.type: MEMO_TEXT +tx.memo.text: "Enjoy this transaction" +tx.operations.len: 14 +tx.operation[0].sourceAccount._present: false +tx.operation[0].body.type: CREATE_ACCOUNT +tx.operation[0].body.createAccountOp.destination: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[0].body.createAccountOp.startingBalance: 22000000000000000000201112291981902020202021230019 +tx.operation[1].sourceAccount._present: true +tx.operation[1].sourceAccount: GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[1].body.type: PAYMENT +tx.operation[1].body.paymentOp.destination: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[1].body.paymentOp.asset: native +tx.operation[1].body.paymentOp.amount: 33333330000000000000201112291981902020202021233330 +tx.operation[2].sourceAccount._present: true +tx.operation[2].sourceAccount: GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[2].body.type: PAYMENT +tx.operation[2].body.paymentOp.destination: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[2].body.paymentOp.asset: USD:GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI +tx.operation[2].body.paymentOp.amount: 33333330000000000000201112291981902020202021233330 +tx.operation[3].sourceAccount._present: false +tx.operation[3].body.type: PATH_PAYMENT_STRICT_RECEIVE +tx.operation[3].body.pathPaymentStrictReceiveOp.sendAsset: IOM:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[3].body.pathPaymentStrictReceiveOp.sendMax: 20000000 +tx.operation[3].body.pathPaymentStrictReceiveOp.destination: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[3].body.pathPaymentStrictReceiveOp.destAsset: MOON:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[3].body.pathPaymentStrictReceiveOp.destAmount: 80000000 +tx.operation[3].body.pathPaymentStrictReceiveOp.path.len: 2 +tx.operation[3].body.pathPaymentStrictReceiveOp.path[0]: ECO:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[3].body.pathPaymentStrictReceiveOp.path[1]: ASTRO:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[4].sourceAccount._present: false +tx.operation[4].body.type: PATH_PAYMENT_STRICT_SEND +tx.operation[4].body.pathPaymentStrictSendOp.sendAsset: IOM:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[4].body.pathPaymentStrictSendOp.sendAmount: 4000000000 +tx.operation[4].body.pathPaymentStrictSendOp.destination: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[4].body.pathPaymentStrictSendOp.destAsset: MOON:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[4].body.pathPaymentStrictSendOp.destMin: 12000000000 +tx.operation[4].body.pathPaymentStrictSendOp.path.len: 2 +tx.operation[4].body.pathPaymentStrictSendOp.path[0]: ECO:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[4].body.pathPaymentStrictSendOp.path[1]: ASTRO:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[5].sourceAccount._present: false +tx.operation[5].body.type: SET_OPTIONS +tx.operation[5].body.setOptionsOp.inflationDest._present: true +tx.operation[5].body.setOptionsOp.inflationDest: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[5].body.setOptionsOp.clearFlags._present: true +tx.operation[5].body.setOptionsOp.clearFlags: 2 +tx.operation[5].body.setOptionsOp.setFlags._present: true +tx.operation[5].body.setOptionsOp.setFlags: 4 +tx.operation[5].body.setOptionsOp.masterWeight._present: true +tx.operation[5].body.setOptionsOp.masterWeight: 122 +tx.operation[5].body.setOptionsOp.lowThreshold._present: true +tx.operation[5].body.setOptionsOp.lowThreshold: 10 +tx.operation[5].body.setOptionsOp.medThreshold._present: true +tx.operation[5].body.setOptionsOp.medThreshold: 50 +tx.operation[5].body.setOptionsOp.highThreshold._present: true +tx.operation[5].body.setOptionsOp.highThreshold: 122 +tx.operation[5].body.setOptionsOp.homeDomain._present: true +tx.operation[5].body.setOptionsOp.homeDomain: "https://www.soneso.com/blubber" +tx.operation[5].body.setOptionsOp.signer._present: true +tx.operation[5].body.setOptionsOp.signer.key: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[5].body.setOptionsOp.signer.weight: 50 +tx.operation[6].sourceAccount._present: false +tx.operation[6].body.type: MANAGE_SELL_OFFER +tx.operation[6].body.manageSellOfferOp.selling: ECO:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[6].body.manageSellOfferOp.buying: native +tx.operation[6].body.manageSellOfferOp.amount: 82820000000 +tx.operation[6].body.manageSellOfferOp.price.n: 7 +tx.operation[6].body.manageSellOfferOp.price.d: 10 +tx.operation[6].body.manageSellOfferOp.offerID: 9298298398333 +tx.operation[7].sourceAccount._present: false +tx.operation[7].body.type: CREATE_PASSIVE_SELL_OFFER +tx.operation[7].body.createPassiveSellOfferOp.selling: ASTRO:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[7].body.createPassiveSellOfferOp.buying: MOON:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[7].body.createPassiveSellOfferOp.amount: 28280000000 +tx.operation[7].body.createPassiveSellOfferOp.price.n: 1 +tx.operation[7].body.createPassiveSellOfferOp.price.d: 2 +tx.operation[8].sourceAccount._present: false +tx.operation[8].body.type: CHANGE_TRUST +tx.operation[8].body.changeTrustOp.line: ASTRO:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[8].body.changeTrustOp.limit._present: true +tx.operation[8].body.changeTrustOp.limit: 100000000000 +tx.operation[9].sourceAccount._present: false +tx.operation[9].body.type: ALLOW_TRUST +tx.operation[9].body.allowTrustOp.trustor: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[9].body.allowTrustOp.asset: MOON +tx.operation[9].body.allowTrustOp.authorize: 1 +tx.operation[10].sourceAccount._present: false +tx.operation[10].body.type: ACCOUNT_MERGE +tx.operation[10].body.destination: GAB4GIAHEQ7C6UNG4U7KDTQZSMRP4ZWOPF4ZW5TARG6N7UBHJD5UMQZK +tx.operation[11].sourceAccount._present: false +tx.operation[11].body.type: MANAGE_DATA +tx.operation[11].body.manageDataOp.dataName: Sommer +tx.operation[11].body.manageDataOp.dataValue._present: true +tx.operation[11].body.manageDataOp.dataValue: 446965204df662656c2073696e6420686569df21 +tx.operation[12].sourceAccount._present: false +tx.operation[12].body.type: BUMP_SEQUENCE +tx.operation[12].body.bumpSequenceOp.bumpTo: 1102902109211 +tx.operation[13].sourceAccount._present: false +tx.operation[13].body.type: MANAGE_BUY_OFFER +tx.operation[13].body.manageBuyOfferOp.selling: MOON:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[13].body.manageBuyOfferOp.buying: ECO:GB2LBNNYUSZEJQF37MBBLXKGR4SBNEJHKMDDZ5EJL2ZRGGHEEJMNL3XX +tx.operation[13].body.manageBuyOfferOp.buyAmount: 120000000 +tx.operation[13].body.manageBuyOfferOp.price.n: 1 +tx.operation[13].body.manageBuyOfferOp.price.d: 5 +tx.operation[13].body.manageBuyOfferOp.offerID: 9298298398334 +tx.signatures.len: 1 +tx.signatures[0].hint: e42258d5 +tx.signatures[0].signature: e59cb71274c91bcc94f26b92a66357aba866f3f8c63d1879551937a24f1986af976e87e2387ef36e61cd94c9bc8771b04551e8bf8325d85775ae3647262bad02 +tx.ext.v: 0'''; + + AbstractTransaction transaction = TxRep.fromTxRep(txRep); + String txRepRes = TxRep.toTxRep(transaction); + assert(txRepRes == txRep); + }); + + /*test('fee bump transaction to txrep', () { KeyPair keyPairA = KeyPair.random(); String accountAId = keyPairA.accountId; Account a = Account(keyPairA, 1102902109201); @@ -16,18 +139,18 @@ void main() { Account b = Account(keyPairB, 19181981888); Operation createAccount = new CreateAccountOperationBuilder( - accountBId, "2200000000000000000020111229198190202020202.1230019") + accountBId, "2200000000000000000020111229198190202020202.1230019") .build(); Operation payment = new PaymentOperationBuilder(accountBId, Asset.NATIVE, - "3333333000000000000020111229198190202020202.123333") + "3333333000000000000020111229198190202020202.123333") .setSourceAccount(accountAId) .build(); Asset nonNativeAsset = AssetTypeCreditAlphaNum4( "USD", "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI"); Operation nonNativePayment = new PaymentOperationBuilder( - accountBId, - nonNativeAsset, - "3333333000000000000020111229198190202020202.123333") + accountBId, + nonNativeAsset, + "3333333000000000000020111229198190202020202.123333") .setSourceAccount(accountAId) .build(); @@ -37,26 +160,37 @@ void main() { Asset moonAsset = AssetTypeCreditAlphaNum4("MOON", keyPairA.accountId); List path = [ecoAsset, astroAsset]; PathPaymentStrictReceiveOperation strictReceive = - PathPaymentStrictReceiveOperationBuilder( - iomAsset, "2", accountBId, moonAsset, "8") - .setPath(path) - .build(); + PathPaymentStrictReceiveOperationBuilder( + iomAsset, "2", accountBId, moonAsset, "8") + .setPath(path) + .build(); PathPaymentStrictSendOperation strictSend = - PathPaymentStrictSendOperationBuilder( - iomAsset, "400", accountBId, moonAsset, "1200") - .setPath(path) - .build(); + PathPaymentStrictSendOperationBuilder( + iomAsset, "400", accountBId, moonAsset, "1200") + .setPath(path) + .build(); ManageSellOfferOperation manageSellOfferOperation = - ManageSellOfferOperationBuilder(ecoAsset, Asset.NATIVE, "8282", '0.7') - .setOfferId('9298298398333') - .build(); + ManageSellOfferOperationBuilder(ecoAsset, Asset.NATIVE, "8282", '0.7') + .setOfferId('9298298398333') + .build(); + ManageBuyOfferOperation manageBuyOfferOperation = + ManageBuyOfferOperationBuilder(moonAsset, ecoAsset, "12", '0.2') + .setOfferId('9298298398334') + .build(); CreatePassiveSellOfferOperation createPassiveSellOfferOperation = - CreatePassiveSellOfferOperationBuilder( - astroAsset, moonAsset, "2828", '0.5') - .build(); + CreatePassiveSellOfferOperationBuilder( + astroAsset, moonAsset, "2828", '0.5') + .build(); + + String limit = "10000"; + ChangeTrustOperation changeTrustOperation = + ChangeTrustOperationBuilder(astroAsset, limit).build(); + + AllowTrustOperation allowTrustOperation = + AllowTrustOperationBuilder(accountBId, "MOON", 1).build(); TimeBounds tb = TimeBounds(1595282368, 1595284000); MemoText mt = MemoText("Enjoy this transaction"); @@ -78,6 +212,22 @@ void main() { .setHomeDomain("https://www.soneso.com/blubber") .build(); + AccountMergeOperation accountMergeOperation = AccountMergeOperationBuilder( + accountBId).build(); + + String key = "Sommer"; + String value = "Die Möbel sind heiß!"; + + List list = value.codeUnits; + Uint8List valueBytes = Uint8List.fromList(list); + + ManageDataOperation manageDataOperation = + ManageDataOperationBuilder(key, valueBytes).build(); + + BumpSequenceOperation bumpSequenceOperation = + BumpSequenceOperationBuilder(a.sequenceNumber + 10).build(); + + Transaction transaction = new TransactionBuilder(a) .addTimeBounds(tb) .addMemo(mt) @@ -89,106 +239,22 @@ void main() { .addOperation(setOptionsOperation) .addOperation(manageSellOfferOperation) .addOperation(createPassiveSellOfferOperation) + .addOperation(changeTrustOperation) + .addOperation(allowTrustOperation) + .addOperation(accountMergeOperation) + .addOperation(manageDataOperation) + .addOperation(bumpSequenceOperation) + .addOperation(manageBuyOfferOperation) .build(); transaction.sign(keyPairA, Network.TESTNET); - String txrep = TxRep.toTxRep(transaction); - print(txrep); - }); + KeyPair keyPairC = KeyPair.random(); + String accountCId = keyPairC.accountId; + Account c = Account(keyPairB, 19181981888); - test('txrep to transaction', () { - String txRep = ''' -type: ENVELOPE_TYPE_TX -tx.sourceAccount: GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.fee: 800 -tx.seqNum: 1102902109202 -tx.timeBounds._present: true -tx.timeBounds.minTime: 1595282368 -tx.timeBounds.maxTime: 1595284000 -tx.memo.type: MEMO_TEXT -tx.memo.text: "Enjoy this transaction" -tx.operations.len: 8 -tx.operation[0].sourceAccount._present: false -tx.operation[0].body.type: CREATE_ACCOUNT -tx.operation[0].body.createAccountOp.destination: GBTU43IBHZW2QOMQBGFYIJGOMSAKQCEMDHDICDHXQJLBFWDRW2J4GTTI -tx.operation[0].body.createAccountOp.startingBalance: 22000000000000000000201112291981902020202021230019 -tx.operation[1].sourceAccount._present: true -tx.operation[1].sourceAccount: GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[1].body.type: PAYMENT -tx.operation[1].body.paymentOp.destination: GBTU43IBHZW2QOMQBGFYIJGOMSAKQCEMDHDICDHXQJLBFWDRW2J4GTTI -tx.operation[1].body.paymentOp.asset: native -tx.operation[1].body.paymentOp.amount: 33333330000000000000201112291981902020202021233330 -tx.operation[2].sourceAccount._present: true -tx.operation[2].sourceAccount: GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[2].body.type: PAYMENT -tx.operation[2].body.paymentOp.destination: GBTU43IBHZW2QOMQBGFYIJGOMSAKQCEMDHDICDHXQJLBFWDRW2J4GTTI -tx.operation[2].body.paymentOp.asset: USD:GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI -tx.operation[2].body.paymentOp.amount: 33333330000000000000201112291981902020202021233330 -tx.operation[3].sourceAccount._present: false -tx.operation[3].body.type: PATH_PAYMENT_STRICT_RECEIVE -tx.operation[3].body.pathPaymentStrictReceiveOp.sendAsset: IOM:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[3].body.pathPaymentStrictReceiveOp.sendMax: 20000000 -tx.operation[3].body.pathPaymentStrictReceiveOp.destination: GBTU43IBHZW2QOMQBGFYIJGOMSAKQCEMDHDICDHXQJLBFWDRW2J4GTTI -tx.operation[3].body.pathPaymentStrictReceiveOp.destAsset: MOON:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[3].body.pathPaymentStrictReceiveOp.destAmount: 80000000 -tx.operation[3].body.pathPaymentStrictReceiveOp.path.len: 2 -tx.operation[3].body.pathPaymentStrictReceiveOp.path[0]: ECO:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[3].body.pathPaymentStrictReceiveOp.path[1]: ASTRO:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[4].sourceAccount._present: false -tx.operation[4].body.type: PATH_PAYMENT_STRICT_SEND -tx.operation[4].body.pathPaymentStrictSendOp.sendAsset: IOM:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[4].body.pathPaymentStrictSendOp.sendAmount: 4000000000 -tx.operation[4].body.pathPaymentStrictSendOp.destination: GBTU43IBHZW2QOMQBGFYIJGOMSAKQCEMDHDICDHXQJLBFWDRW2J4GTTI -tx.operation[4].body.pathPaymentStrictSendOp.destAsset: MOON:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[4].body.pathPaymentStrictSendOp.destMin: 12000000000 -tx.operation[4].body.pathPaymentStrictSendOp.path.len: 2 -tx.operation[4].body.pathPaymentStrictSendOp.path[0]: ECO:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[4].body.pathPaymentStrictSendOp.path[1]: ASTRO:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[5].sourceAccount._present: false -tx.operation[5].body.type: SET_OPTIONS -tx.operation[5].body.setOptionsOp.inflationDest._present: true -tx.operation[5].body.setOptionsOp.inflationDest: GBTU43IBHZW2QOMQBGFYIJGOMSAKQCEMDHDICDHXQJLBFWDRW2J4GTTI -tx.operation[5].body.setOptionsOp.clearFlags._present: true -tx.operation[5].body.setOptionsOp.clearFlags: 2 -tx.operation[5].body.setOptionsOp.setFlags._present: true -tx.operation[5].body.setOptionsOp.setFlags: 4 -tx.operation[5].body.setOptionsOp.masterWeight._present: true -tx.operation[5].body.setOptionsOp.masterWeight: 122 -tx.operation[5].body.setOptionsOp.lowThreshold._present: true -tx.operation[5].body.setOptionsOp.lowThreshold: 10 -tx.operation[5].body.setOptionsOp.medThreshold._present: true -tx.operation[5].body.setOptionsOp.medThreshold: 50 -tx.operation[5].body.setOptionsOp.highThreshold._present: true -tx.operation[5].body.setOptionsOp.highThreshold: 122 -tx.operation[5].body.setOptionsOp.homeDomain._present: true -tx.operation[5].body.setOptionsOp.homeDomain: "https://www.soneso.com/blubber" -tx.operation[5].body.setOptionsOp.signer._present: true -tx.operation[5].body.setOptionsOp.signer.key: GBTU43IBHZW2QOMQBGFYIJGOMSAKQCEMDHDICDHXQJLBFWDRW2J4GTTI -tx.operation[5].body.setOptionsOp.signer.weight: 50 -tx.operation[6].sourceAccount._present: false -tx.operation[6].body.type: MANAGE_SELL_OFFER -tx.operation[6].body.manageSellOfferOp.selling: ECO:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[6].body.manageSellOfferOp.buying: native -tx.operation[6].body.manageSellOfferOp.amount: 82820000000 -tx.operation[6].body.manageSellOfferOp.price.n: 7 -tx.operation[6].body.manageSellOfferOp.price.d: 10 -tx.operation[6].body.manageSellOfferOp.offerID: 9298298398333 -tx.operation[7].sourceAccount._present: false -tx.operation[7].body.type: CREATE_PASSIVE_SELL_OFFER -tx.operation[7].body.createPassiveSellOfferOp.selling: ASTRO:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[7].body.createPassiveSellOfferOp.buying: MOON:GDNKJ544DQUUBAJESDL3ZMQ6T3EMHWEQKLKYJD2OUZFIQRDOETE3UJZS -tx.operation[7].body.createPassiveSellOfferOp.amount: 28280000000 -tx.operation[7].body.createPassiveSellOfferOp.price.n: 1 -tx.operation[7].body.createPassiveSellOfferOp.price.d: 2 -tx.signatures.len: 1 -tx.signatures[0].hint: 6e24c9ba -tx.signatures[0].signature: f790c47d6d2b44843c98700f20275272fc8a951e82d12b6f56b393cc75f8dab116736503242f355a9fd037da186fe7bf86b0765166df3fba52ccec1aa813be05 -tx.ext.v: 0'''; - - AbstractTransaction transaction = TxRep.fromTxRep(txRep); - String txRepRes = TxRep.toTxRep(transaction); - print(txRepRes); - assert(txRepRes == txRep); - }); + FeeBumpTransaction feeBump = FeeBumpTransactionBuilder(transaction).setFeeAccount(accountCId).setBaseFee(100000).build(); + String txrep = TxRep.toTxRep(feeBump); + print(txrep); + });*/ }