def test_tx_fee_less_than_base_fee(self): inner_keypair = Keypair.from_secret( "SBKTIFHJSS3JJWEZO2W74DZSA45WZU56LOL3AY7GAW63BXPEJQFYV53E") inner_source = Account(inner_keypair.public_key, 7) destination = "GDQERENWDDSQZS7R7WKHZI3BSOYMV3FSWR7TFUYFTKQ447PIX6NREOJM" amount = "2000.0000000" inner_tx = (TransactionBuilder(inner_source, Network.TESTNET_NETWORK_PASSPHRASE, 50, v1=True).append_payment_op( destination=destination, amount=amount, asset_code="PMN").add_time_bounds( 0, 0).build()) inner_tx.sign(inner_keypair) fee_source = Keypair.from_secret( "SB7ZMPZB3YMMK5CUWENXVLZWBK4KYX4YU5JBXQNZSK2DP2Q7V3LVTO5V") base_fee = 60 with pytest.raises( ValueError, match="Invalid `base_fee`, it should be at least 100 stroops.", ): TransactionBuilder.build_fee_bump_transaction( fee_source.public_key, base_fee, inner_tx, Network.TESTNET_NETWORK_PASSPHRASE, )
async def test_check_memo_required_with_fee_bump_transaction_async( self, httpserver): self.__inject_mock_server(httpserver) horizon_url = httpserver.url_for("/") keypair = Keypair.from_secret( "SDQXFKA32UVQHUTLYJ42N56ZUEM5PNVVI4XE7EA5QFMLA2DHDCQX3GPY") account = Account(keypair.public_key, 1) async with Server(horizon_url, AiohttpClient()) as server: transaction = (TransactionBuilder( account, v1=True).append_payment_op( self.DESTINATION_ACCOUNT_MEMO_REQUIRED_A, "10", "PMN" ).append_path_payment_strict_send_op( self.DESTINATION_ACCOUNT_MEMO_REQUIRED_C, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_account_merge_op( self.DESTINATION_ACCOUNT_MEMO_REQUIRED_D).add_text_memo( "hello, world").build()) transaction.sign(keypair) fee_bump_tx = TransactionBuilder.build_fee_bump_transaction( fee_source=Keypair.random().public_key, base_fee=200, inner_transaction_envelope=transaction, network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, ) await server.submit_transaction(fee_bump_tx)
async def test_check_memo_required_with_memo_muxed_account_async( self, httpserver): self.__inject_mock_server(httpserver) horizon_url = httpserver.url_for("/") keypair = Keypair.from_secret( "SDQXFKA32UVQHUTLYJ42N56ZUEM5PNVVI4XE7EA5QFMLA2DHDCQX3GPY") account = Account(keypair.public_key, 1) async with Server(horizon_url, AiohttpClient()) as server: transaction = (TransactionBuilder(account).append_payment_op( self.DESTINATION_ACCOUNT_MEMO_REQUIRED_A, "10", "PMN").append_path_payment_strict_receive_op( self.DESTINATION_ACCOUNT_MEMO_REQUIRED_B, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_path_payment_strict_send_op( self.DESTINATION_ACCOUNT_MEMO_REQUIRED_C, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_account_merge_op( self.DESTINATION_ACCOUNT_MEMO_REQUIRED_D).add_text_memo( "hello, world").build()) transaction.sign(keypair) await server.submit_transaction(transaction)
async def test_check_memo_required_with_fetch_account_error_raise_async( self, httpserver): self.__inject_mock_server(httpserver) horizon_url = httpserver.url_for("/") keypair = Keypair.from_secret( "SDQXFKA32UVQHUTLYJ42N56ZUEM5PNVVI4XE7EA5QFMLA2DHDCQX3GPY") account = Account(keypair.public_key, 1) async with Server(horizon_url, AiohttpClient()) as server: transaction = (TransactionBuilder(account).append_payment_op( self.DESTINATION_ACCOUNT_FETCH_ERROR, "10", "PMN").append_path_payment_strict_receive_op( self.DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_path_payment_strict_send_op( self.DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_account_merge_op( self.DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) transaction.sign(keypair) with pytest.raises(BadRequestError) as err: await server.submit_transaction(transaction) assert err.value.status == 400
def test_check_memo_required_with_account_not_found_sync(self, httpserver): self.__inject_mock_server(httpserver) horizon_url = httpserver.url_for("/") server = Server(horizon_url) keypair = Keypair.from_secret( "SDQXFKA32UVQHUTLYJ42N56ZUEM5PNVVI4XE7EA5QFMLA2DHDCQX3GPY") account = Account(keypair.public_key, 1) transaction = (TransactionBuilder(account).append_payment_op( self.DESTINATION_ACCOUNT_NO_FOUND, "10", "PMN").append_path_payment_strict_receive_op( self.DESTINATION_ACCOUNT_NO_FOUND, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_path_payment_strict_send_op( self.DESTINATION_ACCOUNT_NO_FOUND, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_account_merge_op( self.DESTINATION_ACCOUNT_NO_FOUND).build()) transaction.sign(keypair) server.submit_transaction(transaction)
def test_to_txrep_return_memo(self): keypair = Keypair.from_secret( "SAHGKA7QJB6SRFDZSPZDEEIOEHUHTQS4XVN4IMR5YCKBPEN5A6YNKKUO") account = Account(keypair.public_key, 46489056724385792) transaction_builder = TransactionBuilder( source_account=account, network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, base_fee=100, ) transaction_builder.add_return_hash_memo( "ef14f82df770697f56789b4db4e59d1ece902484739ba167cf99fae319ebcc34") transaction_builder.add_time_bounds(1535756672, 1567292672) transaction_builder.append_payment_op( destination= "GBAF6NXN3DHSF357QBZLTBNWUTABKUODJXJYYE32ZDKA2QBM2H33IK6O", asset_code="USD", asset_issuer= "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI", amount="40.0004", source=keypair.public_key, ) te = transaction_builder.build() te.sign(keypair) txrep = to_txrep(te) assert txrep == get_txrep_file("test_to_txrep_return_memo.txt") assert (from_txrep( txrep, Network.TESTNET_NETWORK_PASSPHRASE).to_xdr() == te.to_xdr())
def test_to_source_muxed_xdr(self): inner_keypair = Keypair.from_secret( "SBKTIFHJSS3JJWEZO2W74DZSA45WZU56LOL3AY7GAW63BXPEJQFYV53E") inner_source = Account(inner_keypair.public_key, 7) destination = "GDQERENWDDSQZS7R7WKHZI3BSOYMV3FSWR7TFUYFTKQ447PIX6NREOJM" amount = "2000.0000000" inner_tx = (TransactionBuilder(inner_source, Network.TESTNET_NETWORK_PASSPHRASE, 200, v1=True).append_payment_op( destination=destination, amount=amount, asset_code="PMN").add_time_bounds( 0, 0).build()) inner_tx.sign(inner_keypair) fee_source = Keypair.from_secret( "SB7ZMPZB3YMMK5CUWENXVLZWBK4KYX4YU5JBXQNZSK2DP2Q7V3LVTO5V") fee_source2 = Keypair.from_secret( "SCVFDAOOXWR5TSPZF5U2MIE6V7M4LTOCNCD624Q6AEVBZ2XMH7HOWFZL") base_fee = 200 fee_bump_tx = TransactionBuilder.build_fee_bump_transaction( fee_source.public_key, base_fee, inner_tx, Network.TESTNET_NETWORK_PASSPHRASE, ) fee_bump_tx.sign(fee_source) xdr = "AAAABQAAAADgSJG2GOUMy/H9lHyjYZOwyuyytH8y0wWaoc596L+bEgAAAAAAAAGQAAAAAgAAAAAcVPE+R/n1VnbIrAK3tu5yVfTkhkqVz04ShWk2SrF3wAAAAMgAAAAAAAAACAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAADgSJG2GOUMy/H9lHyjYZOwyuyytH8y0wWaoc596L+bEgAAAAAAAAAEqBfIAAAAAAAAAAABSrF3wAAAAEAordQh63kT50muRLVYaWW7Pgtt8i1tc4t9Bv9MWFWFN3WfTHSU2Jxv7hedjZEyfBPvaS/XnwvYJFcHgPDd1JkNAAAAAAAAAAHov5sSAAAAQKu/RuArXn/P13IIJ8WlnVDStwOquXM0CsWzA4ooZY6gqJ3k1EfmMVIJ0cir0bMTJD9r+g2IUZCANU7wdC38PA0=" assert fee_bump_tx.to_xdr() == xdr restore_te = FeeBumpTransactionEnvelope.from_xdr( xdr, Network.TESTNET_NETWORK_PASSPHRASE) assert restore_te == fee_bump_tx assert restore_te.transaction == fee_bump_tx.transaction assert restore_te.transaction == FeeBumpTransaction.from_xdr( fee_bump_tx.transaction.to_xdr_object().to_xdr(), Network.TESTNET_NETWORK_PASSPHRASE, ) assert (restore_te.to_xdr() == TransactionBuilder.from_xdr( xdr, Network.TESTNET_NETWORK_PASSPHRASE).to_xdr()) assert (restore_te.transaction._fee_source_muxed.to_xdr() == fee_source.xdr_muxed_account().to_xdr()) restore_te.transaction.fee_source = fee_source2.public_key assert restore_te.transaction.fee_source.public_key == fee_source2.public_key assert restore_te.transaction._fee_source_muxed is None
async def test_check_memo_required_with_no_destination_operation_async( self, httpserver): self.__inject_mock_server(httpserver) horizon_url = httpserver.url_for("/") keypair = Keypair.from_secret( "SDQXFKA32UVQHUTLYJ42N56ZUEM5PNVVI4XE7EA5QFMLA2DHDCQX3GPY") account = Account(keypair.public_key, 1) async with Server(horizon_url, AiohttpClient()) as server: transaction = (TransactionBuilder(account).append_manage_data_op( "Hello", "world").build()) transaction.sign(keypair) await server.submit_transaction(transaction)
async def test_check_memo_required_with_two_operation_with_same_destination_async( self, httpserver): self.__inject_mock_server(httpserver) horizon_url = httpserver.url_for("/") keypair = Keypair.from_secret( "SDQXFKA32UVQHUTLYJ42N56ZUEM5PNVVI4XE7EA5QFMLA2DHDCQX3GPY") account = Account(keypair.public_key, 1) async with Server(horizon_url, AiohttpClient()) as server: transaction = (TransactionBuilder(account).append_payment_op( self.DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, "10", "PMN").append_path_payment_strict_receive_op( self.DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_path_payment_strict_send_op( self.DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, "PMN", None, "10", "BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2", "1", [], ).append_account_merge_op( self.DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) transaction.sign(keypair) with pytest.raises( AccountRequiresMemoError, match= "Destination account requires a memo in the transaction.", ) as err: await server.submit_transaction(transaction) assert err.value.account_id == self.DESTINATION_ACCOUNT_MEMO_REQUIRED_D assert err.value.operation_index == 3
from kuknos_sdk import Server, TransactionBuilder, Signer, Network, Keypair server = Server(horizon_url="https://hz1-test.kuknos.org/") root_keypair = Keypair.from_secret("SA6XHAH4GNLRWWWF6TEVEWNS44CBNFAJWHWOPZCVZOUXSQA7BOYN7XHC") root_account = server.load_account(account_id=root_keypair.public_key) secondary_keypair = Keypair.from_secret("SAMZUAAPLRUH62HH3XE7NVD6ZSMTWPWGM6DS4X47HLVRHEBKP4U2H5E7") secondary_signer = Signer.ed25519_public_key(account_id=secondary_keypair.public_key, weight=1) transaction = TransactionBuilder( source_account=root_account, network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, base_fee=100) \ .append_set_options_op( master_weight=1, # set master key weight low_threshold=1, med_threshold=2, # a payment is medium threshold high_threshold=2, # make sure to have enough weight to add up to the high threshold! signer=secondary_signer) \ .set_timeout(30) \ .build() # only need to sign with the root signer as the 2nd signer won't # be added to the account till after this transaction completes transaction.sign(root_keypair) response = server.submit_transaction(transaction) print(response) # now create a payment with the account that has two signers destination = "GBA5SMM5OYAOOPL6R773MV7O3CCLUDVLCWHIVVL3W4XTD3DA5FJ4JSEZ" transaction = TransactionBuilder( source_account=root_account,
from kuknos_sdk import TransactionBuilder, Network, Keypair, Account root_keypair = Keypair.from_secret( "SA6XHAH4GNLRWWWF6TEVEWNS44CBNFAJWHWOPZCVZOUXSQA7BOYN7XHC") # Create an Account object from an address and sequence number. root_account = Account(account_id=root_keypair.public_key, sequence=1) transaction = TransactionBuilder( source_account=root_account, network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, base_fee=100).add_text_memo("Happy birthday!").append_payment_op( destination="GASOCNHNNLYFNMDJYQ3XFMI7BYHIOCFW3GJEOWRPEGK2TDPGTG2E5EDW", amount="2000", asset_code="PMN").set_timeout(30).build()
def test_to_xdr_v0(self): sequence = 1 source = Account( "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", sequence ) builder = TransactionBuilder( source, Network.TESTNET_NETWORK_PASSPHRASE, base_fee=150, v1=False ) builder.add_text_memo("Kuknos Python SDK") builder.add_time_bounds(1565590000, 1565600000) te = ( builder.append_create_account_op( "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", "5.5", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", ) .append_change_trust_op( "XCN", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", "100000", ) .append_payment_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", "12.25", "PMN", ) .append_path_payment_strict_receive_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", "PMN", None, "100", "XCN", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", "1000.5", [ Asset( "USD", "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", ), Asset( "BTC", "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", ), ], ) .append_path_payment_strict_send_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", "PMN", None, "100", "XCN", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", "1000.5", [ Asset( "USD", "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", ), Asset( "BTC", "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", ), ], ) .append_allow_trust_op( "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", "XCN", True ) .append_set_options_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", 1, 6, 20, 20, 20, 20, "kuknoscn.org", Signer.ed25519_public_key( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", 10 ), "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", ) .append_ed25519_public_key_signer( "GCN4HBZGFPOAI5DF4YQIS6OBC6KJKDC7CJSS5B4FWEXMJSWOPEYABLSD", 5 ) .append_hashx_signer( bytes.fromhex( "3389e9f0f1a65f19736cacf544c2e825313e8447f569233bb8db39aa607c8000" ), 10, ) .append_pre_auth_tx_signer( bytes.fromhex( "2db4b22ca018119c5027a80578813ffcf582cda4aa9e31cd92b43cfa4fc5a000" ), 10, ) .append_inflation_op() .append_manage_data_op("hello", "overcat") .append_bump_sequence_op(10) .append_manage_buy_offer_op( "XCN", "GCN4HBZGFPOAI5DF4YQIS6OBC6KJKDC7CJSS5B4FWEXMJSWOPEYABLSD", "PMN", None, "10.5", "11.25", ) .append_manage_sell_offer_op( "PMN", None, "XCN", "GCN4HBZGFPOAI5DF4YQIS6OBC6KJKDC7CJSS5B4FWEXMJSWOPEYABLSD", "10.5", Price(8, 9), 10086, ) .append_create_passive_sell_offer_op( "XCN", "GCN4HBZGFPOAI5DF4YQIS6OBC6KJKDC7CJSS5B4FWEXMJSWOPEYABLSD", "PMN", None, "10.5", "11.25", ) .append_account_merge_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF" ) .append_claim_claimable_balance_op( "00000000da0d57da7d4850e7fc10d2a9d0ebc731f7afb40574c03395b17d49149b91f5be" ) .append_create_claimable_balance_op( Asset.native(), "100", [ Claimant( destination="GCXGGIREYPENNT3LYFRD5I2SDALFWM3NKKLIQD3DMJ63ML5N3FG4OQQG" ) ], ) .build() ) xdr = "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" assert te.to_xdr() == xdr xdr_signed = "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" signer = Keypair.from_secret( "SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH" ) te.sign(signer) assert te.to_xdr() == xdr_signed restore_te = TransactionBuilder.from_xdr( xdr_signed, Network.TESTNET_NETWORK_PASSPHRASE ).build() assert restore_te.to_xdr() == xdr assert source.sequence == sequence + 1 restore_builder = TransactionBuilder.from_xdr( xdr_signed, Network.TESTNET_NETWORK_PASSPHRASE ) assert restore_builder.v1 is False assert restore_builder.build().to_xdr() == xdr
from kuknos_sdk import Server, TransactionBuilder, Keypair, ClaimPredicate, Claimant, Asset, Network sponsor_secret = "SAOJHTVFCYVKUMPNQI7RUSI566GKWXP7RXOHP4SV6JAVUQKSIWGPZFPJ" claimant_secret = "SBOLGU7D7A7MTY4JZ3WZUKSKB6NZBQFNQG3BZT4HZW4AAVZJRG7TWXGQ" sponsor_keypair = Keypair.from_secret(sponsor_secret) claimant_keypair = Keypair.from_secret(claimant_secret) server = Server("https://hz1-test.kuknos.org/") network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE # Create Claimable Balance sponsor_account = server.load_account(sponsor_keypair.public_key) predicate_left = ClaimPredicate.predicate_before_relative_time(60 * 60 * 24 * 7) predicate_right = ClaimPredicate.predicate_not( ClaimPredicate.predicate_before_relative_time(60 * 3)) predicate = ClaimPredicate.predicate_and(predicate_left, predicate_right) claimant = Claimant(destination=claimant_keypair.public_key, predicate=predicate) create_claimable_balance_te = TransactionBuilder( source_account=sponsor_account, network_passphrase=network_passphrase).append_create_claimable_balance_op( asset=Asset.native(), amount="100", claimants=[claimant], source=sponsor_keypair.public_key).build() create_claimable_balance_te.sign(sponsor_keypair) create_claimable_balance_resp = server.submit_transaction( create_claimable_balance_te)
class TestPayKuknosUri: @pytest.mark.parametrize( "destination, amount, asset, memo, callback, message, network_passphrase, origin_domain, signature, signer, uri", [ ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", None, TextMemo("skdjfasf"), None, "pay me with paymons", None, "someDomain.com", None, "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=skdjfasf&memo_type=MEMO_TEXT&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=tbsLtlK%2FfouvRWk2UWFP47yHYeI1g1NEC%2FfEQvuXG6V8P%2BbeLxplYbOVtTk1g94Wp97cHZ3pVJy%2FtZNYobl3Cw%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", None, TextMemo("skdjfasf"), None, "pay me with paymons", None, "someDomain.com", "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=skdjfasf&memo_type=MEMO_TEXT&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=tbsLtlK%2FfouvRWk2UWFP47yHYeI1g1NEC%2FfEQvuXG6V8P%2BbeLxplYbOVtTk1g94Wp97cHZ3pVJy%2FtZNYobl3Cw%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", None, TextMemo("skdjfasf"), None, "pay me with paymons", None, "someDomain.com", None, None, "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=skdjfasf&memo_type=MEMO_TEXT&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", None, TextMemo("skdjfasf"), None, "pay me with paymons", None, "someDomain.com", "testSignature", None, "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=skdjfasf&memo_type=MEMO_TEXT&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=testSignature", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", None, TextMemo("skdjfasf"), None, "_" * 300, None, "someDomain.com", None, "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=skdjfasf&memo_type=MEMO_TEXT&msg=____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________&origin_domain=someDomain.com&signature=d94yTtEX0PMUDwwYfzLuVx6luuEiVlxyVQrkR%2FM4NIundX2VqXLVE%2F6FX8G5x4MZ1qc0u661t4Rfu5SKyTIjCg%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", None, NoneMemo(), None, "pay me with paymons", None, "someDomain.com", None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=t9glBiurQ1MUyk3T5BIqxgJwbrNT1ZDIzQ6aKDJmPgM8heN1NEk%2FusfMq6lSaqtxUuysfZisgPM8TiSY0ckyCw%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", None, TextMemo("skdjfasf"), None, "pay me with paymons", None, "someDomain.com", None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=skdjfasf&memo_type=MEMO_TEXT&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=tbsLtlK%2FfouvRWk2UWFP47yHYeI1g1NEC%2FfEQvuXG6V8P%2BbeLxplYbOVtTk1g94Wp97cHZ3pVJy%2FtZNYobl3Cw%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", Asset.native(), TextMemo("skdjfasf"), None, "pay me with paymons", None, "someDomain.com", None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=skdjfasf&memo_type=MEMO_TEXT&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=tbsLtlK%2FfouvRWk2UWFP47yHYeI1g1NEC%2FfEQvuXG6V8P%2BbeLxplYbOVtTk1g94Wp97cHZ3pVJy%2FtZNYobl3Cw%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", Asset.native(), IdMemo(10086), None, "pay me with paymons", None, "someDomain.com", None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=10086&memo_type=MEMO_ID&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=cTyphv1qE6YqnwKu1O4psEzA8O6vNYu%2FDbvGb0Nu53V%2FL8U7LoGknX1JOGLsgGjOXdfAmJbG8rxF8I9DD%2BvjCA%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", Asset.native(), HashMemo( "218a3ef357324c496d07f7d7b31be5c2a11a1ac44af9b81938a6c5b9c0684af4" ), None, "pay me with paymons", None, "someDomain.com", None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=IYo%2B81cyTEltB%2FfXsxvlwqEaGsRK%2BbgZOKbFucBoSvQ%3D&memo_type=MEMO_HASH&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=CzqyewhypDQGCBlvxSj%2BazIm5iS2GDqcOlBeXT%2FB7s8ogEXC8hNa6HjOArdbTkvNCUvTl6lgyKOj%2FeeNE%2BA5DQ%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", Asset.native(), ReturnHashMemo( "218a3ef357324c496d07f7d7b31be5c2a11a1ac44af9b81938a6c5b9c0684af4" ), None, "pay me with paymons", None, "someDomain.com", None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&memo=IYo%2B81cyTEltB%2FfXsxvlwqEaGsRK%2BbgZOKbFucBoSvQ%3D&memo_type=MEMO_RETURN&msg=pay%20me%20with%20paymons&origin_domain=someDomain.com&signature=4OJcgA0qxjUhqJL79MeK%2BkHgNjdKXiGhufvw2yjcXud7Ce%2BG53N0qSUpgXDT8OBDQRrI7Pe%2FUPu0%2FCM7DJkXAQ%3D%3D", ), ( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "120.1234567", Asset( "Hello", "GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF" ), TextMemo("Hello World"), "https://example.com/callback", "pay me with paymons", Network.TESTNET_NETWORK_PASSPHRASE, "someDomain.com", "testSignature", Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&asset_code=Hello&asset_issuer=GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF&memo=Hello%20World&memo_type=MEMO_TEXT&callback=url%3Ahttps%3A%2F%2Fexample.com%2Fcallback&msg=pay%20me%20with%20paymons&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=k5cDMVTD2W2lKeEGqakjOTf3aPuPumlr8ObBOvauaa2QiXqa7%2Bw9WRgtmo6NaPXdOoFG5ScUIp9k7PdeuPieCw%3D%3D", ), ], ) def test_to_uri( self, destination, amount, asset, memo, callback, message, network_passphrase, origin_domain, signature, signer, uri, ): uri_builder = PayKuknosUri( destination=destination, amount=amount, asset=asset, memo=memo, callback=callback, message=message, network_passphrase=network_passphrase, origin_domain=origin_domain, signature=signature, ) if signer: uri_builder.sign(signer) assert uri_builder.to_uri() == uri assert PayKuknosUri.from_uri(uri_builder.to_uri()).to_uri() == uri def test_message_too_long_raise(self): message = "_" * 301 with pytest.raises(ValueError, match="Message must not exceed 300 characters."): PayKuknosUri( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", message=message, ) def test_invalid_memo_raise(self): memo = "invalid memo" with pytest.raises(ValueError, match="Invalid memo."): PayKuknosUri( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", memo=memo ) def test_equal(self): assert PayKuknosUri( "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO" ) == PayKuknosUri("GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO") def test_invalid_callback_raise(self): uri = "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&asset_code=Hello&asset_issuer=GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF&memo=Hello%20World&memo_type=MEMO_TEXT&callback=https%3A%2F%2Fexample.com%2Fcallback&msg=pay%20me%20with%20paymons&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=k5cDMVTD2W2lKeEGqakjOTf3aPuPumlr8ObBOvauaa2QiXqa7%2Bw9WRgtmo6NaPXdOoFG5ScUIp9k7PdeuPieCw%3D%3D" with pytest.raises(ValueError, match="`callback` should start with `url:`."): PayKuknosUri.from_uri(uri) def test_invalid_scheme_raise(self): uri = "invalid+web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&asset_code=Hello&asset_issuer=GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF&memo=Hello%20World&memo_type=MEMO_TEXT&callback=url%3Ahttps%3A%2F%2Fexample.com%2Fcallback&msg=pay%20me%20with%20paymons&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=k5cDMVTD2W2lKeEGqakjOTf3aPuPumlr8ObBOvauaa2QiXqa7%2Bw9WRgtmo6NaPXdOoFG5ScUIp9k7PdeuPieCw%3D%3D" # TODO: recheck: Kuknos URI scheme should be `web+kuknos`, but got `invalid+web+kuknos`. with pytest.raises(ValueError, match="Kuknos URI scheme should be"): PayKuknosUri.from_uri(uri) def test_invalid_path_raise(self): uri = "web+kuknos:invalid_pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&asset_code=Hello&asset_issuer=GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF&memo=Hello%20World&memo_type=MEMO_TEXT&callback=url%3Ahttps%3A%2F%2Fexample.com%2Fcallback&msg=pay%20me%20with%20paymons&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=k5cDMVTD2W2lKeEGqakjOTf3aPuPumlr8ObBOvauaa2QiXqa7%2Bw9WRgtmo6NaPXdOoFG5ScUIp9k7PdeuPieCw%3D%3D" with pytest.raises( ValueError, match="Kuknos URI path should be `pay`, but got `invalid_pay`." ): PayKuknosUri.from_uri(uri) def test_destination_missing_raise(self): uri = "web+kuknos:pay?amount=120.1234567&asset_code=Hello&asset_issuer=GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF&memo=Hello%20World&memo_type=MEMO_TEXT&callback=url%3Ahttps%3A%2F%2Fexample.com%2Fcallback&msg=pay%20me%20with%20paymons&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=k5cDMVTD2W2lKeEGqakjOTf3aPuPumlr8ObBOvauaa2QiXqa7%2Bw9WRgtmo6NaPXdOoFG5ScUIp9k7PdeuPieCw%3D%3D" with pytest.raises(ValueError, match="`destination` is missing from uri."): PayKuknosUri.from_uri(uri) def test_memo_no_value_raise(self): uri = "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&asset_code=Hello&asset_issuer=GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF&memo_type=MEMO_TEXT&callback=url%3Ahttps%3A%2F%2Fexample.com%2Fcallback&msg=pay%20me%20with%20paymons&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=k5cDMVTD2W2lKeEGqakjOTf3aPuPumlr8ObBOvauaa2QiXqa7%2Bw9WRgtmo6NaPXdOoFG5ScUIp9k7PdeuPieCw%3D%3D" with pytest.raises(ValueError, match="`memo` is missing from uri."): PayKuknosUri.from_uri(uri) def test_invalid_memo_type_raise(self): uri = "web+kuknos:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=120.1234567&asset_code=Hello&asset_issuer=GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF&memo=Hello%20World&memo_type=MEMO_TEXT_INVALID&callback=url%3Ahttps%3A%2F%2Fexample.com%2Fcallback&msg=pay%20me%20with%20paymons&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=k5cDMVTD2W2lKeEGqakjOTf3aPuPumlr8ObBOvauaa2QiXqa7%2Bw9WRgtmo6NaPXdOoFG5ScUIp9k7PdeuPieCw%3D%3D" with pytest.raises(ValueError, match="Invalid `memo_type`."): PayKuknosUri.from_uri(uri)
class TestKuknosTransactionKuknosUri: xdr = "AAAAAP+yw+ZEuNg533pUmwlYxfrq6/BoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH//////////AAAAAAAAAAA=" tx = TransactionEnvelope.from_xdr(xdr, Network.TESTNET_NETWORK_PASSPHRASE) fee_bump_tx_xdr = "AAAABQAAAABDqliFalCjYChydhLNq0lXO8PWMJGrsl8naSePTpNR2wAAAAAAAAGQAAAAAgAAAABzdv3ojkzWHMD7KUoXhrPx0GH18vHKV0ZfqpMiEblG1gAAADIAAAAAAAAwOgAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAGqka26GRAaOmdSR+9P28gWFYn/iQ3GPQsRGAuT5bF4JAAAAAAAAAAA7msoAAAAAAAAAAAERuUbWAAAAQM7EA7Pnkeyqzpa+KgOTDLX66WKBk32BSF4NocSAeOkfUDwIODWvgcToOAH4+jaeOqdWhmxjC0mQ0vO3V14u7AwAAAAAAAAAAU6TUdsAAABAt2wuZNUirKyCkv7Sc3B1kk5CUPt7eitpaZjRBpbrW1p8wVIZ6IsaRzzIVHP7YpGhzbPImXkahlolf0IL3sevBA==" fee_bump_tx = FeeBumpTransactionEnvelope.from_xdr( fee_bump_tx_xdr, Network.TESTNET_NETWORK_PASSPHRASE ) @pytest.mark.parametrize( "tx, replace, callback, pubkey, message, network_passphrase, origin_domain, signature, signer, uri", [ ( tx, [ Replacement( "sourceAccount", "X", "account on which to create the trustline" ) ], None, None, None, None, None, None, None, "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline", ), ( tx, None, None, None, None, None, None, None, None, "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D", ), ( tx, [ Replacement( "sourceAccount", "X", "account on which to create the trustline" ), Replacement("seqNum", "Y", "sequence for sourceAccount"), ], None, None, None, None, None, None, None, "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%2CseqNum%3AY%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline%2CY%3Asequence%20for%20sourceAccount", ), ( tx, [ Replacement( "sourceAccount", "X", "account on which to create the trustline" ) ], None, None, None, None, None, None, "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement( "sourceAccount", "X", "account on which to create the trustline" ) ], None, None, None, None, None, "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement( "sourceAccount", "X", "account on which to create the trustline" ) ], None, None, None, None, None, "testSignature", None, "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=testSignature", ), ( tx, [ Replacement( "sourceAccount", "X", "account on which to create the trustline" ) ], None, None, None, None, None, None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement( "sourceAccount", "X", "account on which to create the trustline" ) ], "https://someSigningService.com", "GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS", "a" * 300, Network.TESTNET_NETWORK_PASSPHRASE, "someDomain.com", "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D", ), ( fee_bump_tx, [ Replacement( "sourceAccount", "X", "account on which to create the trustline" ) ], "https://someSigningService.com", "GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS", "a" * 300, Network.TESTNET_NETWORK_PASSPHRASE, "someDomain.com", "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+kuknos:tx?xdr=AAAABQAAAABDqliFalCjYChydhLNq0lXO8PWMJGrsl8naSePTpNR2wAAAAAAAAGQAAAAAgAAAABzdv3ojkzWHMD7KUoXhrPx0GH18vHKV0ZfqpMiEblG1gAAADIAAAAAAAAwOgAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAGqka26GRAaOmdSR%2B9P28gWFYn%2FiQ3GPQsRGAuT5bF4JAAAAAAAAAAA7msoAAAAAAAAAAAERuUbWAAAAQM7EA7Pnkeyqzpa%2BKgOTDLX66WKBk32BSF4NocSAeOkfUDwIODWvgcToOAH4%2BjaeOqdWhmxjC0mQ0vO3V14u7AwAAAAAAAAAAU6TUdsAAABAt2wuZNUirKyCkv7Sc3B1kk5CUPt7eitpaZjRBpbrW1p8wVIZ6IsaRzzIVHP7YpGhzbPImXkahlolf0IL3sevBA%3D%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=x968yy0d2uFhx6At2mxd6yK%2F0ZZCz%2F%2FQT%2BmlIXmJwI6xe4RuYVIPZpROVx8HUYtcL9ok%2FrwzlQHnBg%2F%2Ba0YYDg%3D%3D", ), ], ) def test_to_uri( self, tx, replace, callback, pubkey, message, network_passphrase, origin_domain, signature, signer, uri, ): uri_builder = TransactionKuknosUri( transaction_envelope=tx, replace=replace, callback=callback, pubkey=pubkey, message=message, network_passphrase=network_passphrase, origin_domain=origin_domain, signature=signature, ) if signer: uri_builder.sign(signer) assert uri_builder.to_uri() == uri restore_uri = TransactionKuknosUri.from_uri( uri_builder.to_uri(), Network.TESTNET_NETWORK_PASSPHRASE ).to_uri() if network_passphrase is None: restore_uri = restore_uri.replace( "&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015", "" ) assert restore_uri == uri def test_message_too_long_raise(self): message = "_" * 301 with pytest.raises(ValueError, match="Message must not exceed 300 characters."): TransactionKuknosUri( transaction_envelope=self.tx, message=message, ) def test_equal(self): assert TransactionKuknosUri( transaction_envelope=self.tx ) == TransactionKuknosUri(transaction_envelope=self.tx) def test_invalid_callback_raise(self): uri = "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=https%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises(ValueError, match="`callback` should start with `url:`."): TransactionKuknosUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) def test_missing_network_passphrase_raise(self): uri = "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=https%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises(ValueError, match="`network_passphrase` is required."): TransactionKuknosUri.from_uri(uri, None) def test_invalid_scheme_raise(self): uri = "invalid+web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" # TODO: recheck: Kuknos URI scheme should be `web+kuknos`, but got `invalid+web+kuknos`. with pytest.raises(ValueError, match="Kuknos URI scheme should be"): TransactionKuknosUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) def test_invalid_path_raise(self): uri = "web+kuknos:invalid_tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises( ValueError, match="Kuknos URI path should be `tx`, but got `invalid_tx`." ): TransactionKuknosUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) # TODO: add more tests def test_invalid_replace_raise(self): uri = "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BY%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises(ValueError, match="Invalid `replace`."): TransactionKuknosUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) def test_missing_xdr_passphrase_raise(self): uri = "web+kuknos:tx?callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises(ValueError, match="`xdr` is missing from uri."): TransactionKuknosUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) def test_network_passphrase_mismatch_raise(self): uri = "web+kuknos:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises( ValueError, match="The `network_passphrase` in the function parameter does not " "match the `network_passphrase` in the uri.", ): TransactionKuknosUri.from_uri(uri, Network.PUBLIC_NETWORK_PASSPHRASE)
from kuknos_sdk import Server, TransactionBuilder, Keypair, Network sponsor_secret = "SAOJHTVFCYVKUMPNQI7RUSI566GKWXP7RXOHP4SV6JAVUQKSIWGPZFPJ" new_account_secret = "SCN5D72JHQAHUHGIA23SLS3LBYCPHJWD7HLYNJRBBZIG4PD74UCGQBYM" sponsor_keypair = Keypair.from_secret(sponsor_secret) newly_created_keypair = Keypair.from_secret(new_account_secret) server = Server("https://hz1-test.kuknos.org") network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE # Sponsoring Account Creation # https://github.com/kuknos/kuknos-protocol/blob/master/core/cap-0033.md#example-sponsoring-account-creation sponsor_account = server.load_account(sponsor_keypair.public_key) sponsoring_account_creation_te = TransactionBuilder( source_account=sponsor_account, network_passphrase=network_passphrase ).append_begin_sponsoring_future_reserves_op( sponsored_id=newly_created_keypair.public_key, source=sponsor_keypair.public_key ).append_create_account_op( destination=newly_created_keypair.public_key, starting_balance="10", source=sponsor_keypair.public_key ).append_end_sponsoring_future_reserves_op( source=newly_created_keypair.public_key ).build() sponsoring_account_creation_te.sign(sponsor_keypair) sponsoring_account_creation_te.sign(new_account_secret) sponsoring_account_creation_resp = server.submit_transaction(sponsoring_account_creation_te) print(sponsoring_account_creation_resp)
def test_to_txrep_full_tx(self): keypair = Keypair.from_secret( "SAHGKA7QJB6SRFDZSPZDEEIOEHUHTQS4XVN4IMR5YCKBPEN5A6YNKKUO") account = Account(keypair.public_key, 46489056724385792) transaction_builder = TransactionBuilder( source_account=account, network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, base_fee=200, ) transaction_builder.add_text_memo("Enjoy this transaction") transaction_builder.add_time_bounds(1535756672, 1567292672) transaction_builder.append_create_account_op( destination= "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI", starting_balance="10", source=keypair.public_key, ) transaction_builder.append_payment_op( destination= "GBAF6NXN3DHSF357QBZLTBNWUTABKUODJXJYYE32ZDKA2QBM2H33IK6O", asset_code="USD", asset_issuer= "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI", amount="40.0004", source=keypair.public_key, ), transaction_builder.append_path_payment_strict_receive_op( destination= "GBAF6NXN3DHSF357QBZLTBNWUTABKUODJXJYYE32ZDKA2QBM2H33IK6O", send_code="USD", send_issuer= "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI", send_max="10", dest_code="XCN", dest_issuer= "GAYE5SDEM5JIEMGQ7LBMQVRQRVJB6A5E7AZVLJYFL3CNHLZX24DFD35F", dest_amount="5.125", path=[ Asset( "Hello", "GD3RXMK2GHSXXEHPBZY5IL7VW5BXQEDJMCD4KVMXOH2GRFKDGZXR5PFO" ), Asset.native(), Asset( "MOEW", "GBR765FQTCAJLLJGZVYLXCFAOZI6ORTHPDPOOHJOHFRZ5GHNVYGK4IFM" ), ], source=keypair.public_key, ) transaction_builder.append_manage_sell_offer_op( selling_code="XCN", selling_issuer= "GAYE5SDEM5JIEMGQ7LBMQVRQRVJB6A5E7AZVLJYFL3CNHLZX24DFD35F", buying_code="USD", buying_issuer= "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI", amount="100.123", price=Price(n=7, d=1), offer_id=12345, source=keypair.public_key, ) transaction_builder.append_create_passive_sell_offer_op( selling_code="XCN", selling_issuer= "GAYE5SDEM5JIEMGQ7LBMQVRQRVJB6A5E7AZVLJYFL3CNHLZX24DFD35F", buying_code="USD", buying_issuer= "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI", amount="100.123", price="7.1", source=keypair.public_key, ) transaction_builder.append_set_options_op( inflation_dest= "GCVAZXCGX3HLHZ6P5WKEPE3U2YJMTLMPTZJFGY67MTNPSOA4COKVJ6AF", clear_flags=3, set_flags=3, master_weight=255, low_threshold=1, med_threshold=2, high_threshold=3, home_domain="kuknos.org", signer=Signer.ed25519_public_key( "GAO3YIWNOBP4DN3ORDXYXTUMLF5S54OK4PKAFCWE23TTOML4COGQOIYA", 255), source=keypair.public_key, ) transaction_builder.append_set_options_op(home_domain="kuknoscn.org") transaction_builder.append_pre_auth_tx_signer( "0ab0c76b1c661db0e829abfdd9e32e6ce3c11f756bdf71aa23846582106c1783", 5) transaction_builder.append_hashx_signer( "0d64fac556c1545616b3c915a4ec215239500bce287007cff038b6020950af46", 10) transaction_builder.append_change_trust_op( asset_code="XCN", asset_issuer= "GAYE5SDEM5JIEMGQ7LBMQVRQRVJB6A5E7AZVLJYFL3CNHLZX24DFD35F", limit="1000", source=keypair.public_key, ) transaction_builder.append_allow_trust_op( trustor="GDU64QWPRTO3LW5VGZPTLRR6QROFWV36XG5QT4C6FZBPHQBBFYRCWZTZ", asset_code="CAT", authorize=True, source=keypair.public_key, ) transaction_builder.append_account_merge_op( destination= "GDU64QWPRTO3LW5VGZPTLRR6QROFWV36XG5QT4C6FZBPHQBBFYRCWZTZ", source=keypair.public_key, ) transaction_builder.append_manage_data_op("Hello", "Kuknos", source=keypair.public_key) transaction_builder.append_manage_data_op("World", None, source=keypair.public_key) transaction_builder.append_bump_sequence_op(bump_to=46489056724385800, source=keypair.public_key) transaction_builder.append_manage_buy_offer_op( selling_code="XCN", selling_issuer= "GAYE5SDEM5JIEMGQ7LBMQVRQRVJB6A5E7AZVLJYFL3CNHLZX24DFD35F", buying_code="USD", buying_issuer= "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI", amount="100.123", price="7.1", source=keypair.public_key, ) transaction_builder.append_path_payment_strict_send_op( destination= "GBAF6NXN3DHSF357QBZLTBNWUTABKUODJXJYYE32ZDKA2QBM2H33IK6O", send_code="USD", send_issuer= "GAZFEVBSEGJJ63WPVVIWXLZLWN2JYZECECGT6GUNP4FJDVZVNXWQWMYI", send_amount="10", dest_code="XCN", dest_issuer= "GAYE5SDEM5JIEMGQ7LBMQVRQRVJB6A5E7AZVLJYFL3CNHLZX24DFD35F", dest_min="5.125", path=[ Asset( "Hello", "GD3RXMK2GHSXXEHPBZY5IL7VW5BXQEDJMCD4KVMXOH2GRFKDGZXR5PFO" ), Asset.native(), Asset( "MOEW", "GBR765FQTCAJLLJGZVYLXCFAOZI6ORTHPDPOOHJOHFRZ5GHNVYGK4IFM" ), ], source=keypair.public_key, ) transaction_builder.append_inflation_op() te = transaction_builder.build() te.sign(keypair) te.sign_hashx( bytes.fromhex( "8b73f9e12fcc8cd9580a2a26aec14d6175aa1ff45e76b816618635d03f3256b8" )) txrep = to_txrep(te) assert txrep == get_txrep_file("test_to_txrep_full_tx.txt") assert (from_txrep( txrep, Network.TESTNET_NETWORK_PASSPHRASE).to_xdr() == te.to_xdr())
def test_to_xdr_v1(self): sequence = 1 source = Account( "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", sequence ) builder = TransactionBuilder( source, Network.TESTNET_NETWORK_PASSPHRASE, base_fee=150, v1=True ) builder.add_text_memo("Kuknos Python SDK") builder.add_time_bounds(1565590000, 1565600000) te = ( builder.append_create_account_op( "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", "5.5", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", ) .append_change_trust_op( "XCN", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", "100000", ) .append_payment_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", "12.25", "PMN", ) .append_path_payment_strict_receive_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", "PMN", None, "100", "XCN", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", "1000.5", [ Asset( "USD", "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", ), Asset( "BTC", "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", ), ], ) .append_path_payment_strict_send_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", "PMN", None, "100", "XCN", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", "1000.5", [ Asset( "USD", "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", ), Asset( "BTC", "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", ), ], ) .append_allow_trust_op( "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", "XCN", True ) .append_set_options_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", 1, 6, 20, 20, 20, 20, "kuknoscn.org", Signer.ed25519_public_key( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF", 10 ), "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM", ) .append_ed25519_public_key_signer( "GCN4HBZGFPOAI5DF4YQIS6OBC6KJKDC7CJSS5B4FWEXMJSWOPEYABLSD", 5 ) .append_hashx_signer( bytes.fromhex( "3389e9f0f1a65f19736cacf544c2e825313e8447f569233bb8db39aa607c8000" ), 10, ) .append_pre_auth_tx_signer( bytes.fromhex( "2db4b22ca018119c5027a80578813ffcf582cda4aa9e31cd92b43cfa4fc5a000" ), 10, ) .append_inflation_op() .append_manage_data_op("hello", "overcat") .append_bump_sequence_op(10) .append_manage_buy_offer_op( "XCN", "GCN4HBZGFPOAI5DF4YQIS6OBC6KJKDC7CJSS5B4FWEXMJSWOPEYABLSD", "PMN", None, "10.5", "11.25", ) .append_manage_sell_offer_op( "PMN", None, "XCN", "GCN4HBZGFPOAI5DF4YQIS6OBC6KJKDC7CJSS5B4FWEXMJSWOPEYABLSD", "10.5", Price(8, 9), 10086, ) .append_create_passive_sell_offer_op( "XCN", "GCN4HBZGFPOAI5DF4YQIS6OBC6KJKDC7CJSS5B4FWEXMJSWOPEYABLSD", "PMN", None, "10.5", "11.25", ) .append_account_merge_op( "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF" ) .append_claim_claimable_balance_op( "00000000da0d57da7d4850e7fc10d2a9d0ebc731f7afb40574c03395b17d49149b91f5be" ) .append_create_claimable_balance_op( Asset.native(), "100", [ Claimant( destination="GCXGGIREYPENNT3LYFRD5I2SDALFWM3NKKLIQD3DMJ63ML5N3FG4OQQG" ) ], ) .append_clawback_op( Asset( "XCN", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM" ), "GCXGGIREYPENNT3LYFRD5I2SDALFWM3NKKLIQD3DMJ63ML5N3FG4OQQG", "100", ) .append_clawback_claimable_balance_op( "00000000929b20b72e5890ab51c24f1cc46fa01c4f318d8d33367d24dd614cfdf5491072" ) .append_set_trust_line_flags_op( "GCXGGIREYPENNT3LYFRD5I2SDALFWM3NKKLIQD3DMJ63ML5N3FG4OQQG", Asset( "XCN", "GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM" ), TrustLineFlags.AUTHORIZED_FLAG, ) .build() ) xdr_unsigned = te.to_xdr() signer = Keypair.from_secret( "SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH" ) te.sign(signer) restore_builder = TransactionBuilder.from_xdr( te.to_xdr(), Network.TESTNET_NETWORK_PASSPHRASE ) assert restore_builder.v1 is True assert restore_builder.build().to_xdr() == xdr_unsigned