Giter Site home page Giter Site logo

python-cardano / pycardano Goto Github PK

View Code? Open in Web Editor NEW
210.0 12.0 60.0 3.64 MB

A lightweight Cardano library in Python

Home Page: https://pycardano.readthedocs.io

License: MIT License

Python 98.38% Makefile 0.35% Shell 0.78% Haskell 0.49%
blockchain cardano library python hacktoberfest

pycardano's Introduction


PyCardano

PyPi version PyPI pyversions PyPI download month

PyCardano codecov Documentation Status

Discord Twitter

PyCardano is a Cardano library written in Python. It allows users to create and sign transactions without depending on third-party Cardano serialization tools, such as cardano-cli and cardano-serialization-lib, making it a lightweight library, which is simple and fast to set up in all types of environments.

Current goal of this project is to enable developers to write off-chain code and tests in pure Python for Plutus DApps. Nevertheless, we see the potential in expanding this project to a full Cardano node client, which could be beneficial for faster R&D iterations.

Features

  • Shelly address
  • Transaction builder
  • Transaction signing
  • Multi-asset
  • Chain backend integration
  • Fee calculation
  • UTxO selection
  • Native script
  • Native token
  • Metadata
  • Plutus script
  • Staking certificates
  • Reward withdraw
  • Mnemonic
  • HD Wallet
  • Byron Address
  • Pool certificate
  • Protocol proposal update

Installation

Install the library using pip:

pip install pycardano

Documentation

https://pycardano.readthedocs.io/en/latest/

Examples

Full stack DApp

A full stack testnet DApp is hosted on replit: https://pycardano.cffls.repl.co/

To learn more details, go to the DApp page.

Transaction creation and signing

Expand code
"""Build a transaction using transaction builder"""

from blockfrost import ApiUrls
from pycardano import *

# Use testnet
network = Network.TESTNET

# Read keys to memory
# Assume there is a payment.skey file sitting in current directory
psk = PaymentSigningKey.load("payment.skey")
# Assume there is a stake.skey file sitting in current directory
ssk = StakeSigningKey.load("stake.skey")

pvk = PaymentVerificationKey.from_signing_key(psk)
svk = StakeVerificationKey.from_signing_key(ssk)

# Derive an address from payment verification key and stake verification key
address = Address(pvk.hash(), svk.hash(), network)

# Create a BlockFrost chain context
context = BlockFrostChainContext("your_blockfrost_project_id", base_url=ApiUrls.preprod.value)

# Create a transaction builder
builder = TransactionBuilder(context)

# Tell the builder that transaction input will come from a specific address, assuming that there are some ADA and native
# assets sitting at this address. "add_input_address" could be called multiple times with different address.
builder.add_input_address(address)

# Get all UTxOs currently sitting at this address
utxos = context.utxos(address)

# We can also tell the builder to include a specific UTxO in the transaction.
# Similarly, "add_input" could be called multiple times.
builder.add_input(utxos[0])

# Send 1.5 ADA and a native asset (CHOC) in quantity of 2000 to an address.
builder.add_output(
    TransactionOutput(
        Address.from_primitive(
            "addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x"
        ),
        Value.from_primitive(
            [
                1500000,
                {
                    bytes.fromhex(
                        "57fca08abbaddee36da742a839f7d83a7e1d2419f1507fcbf3916522"  # Policy ID
                    ): {
                        b"CHOC": 2000  # Asset name and amount
                    }
                },
            ]
        ),
    )
)

# We can add multiple outputs, similar to what we can do with inputs.
# Send 2 ADA and a native asset (CHOC) in quantity of 200 to ourselves
builder.add_output(
    TransactionOutput(
        address,
        Value.from_primitive(
            [
                2000000,
                {
                    bytes.fromhex(
                        "57fca08abbaddee36da742a839f7d83a7e1d2419f1507fcbf3916522"  # Policy ID
                    ): {
                        b"CHOC": 200  # Asset name and amount
                    }
                },
            ]
        ),
    )
)

# Create final signed transaction
signed_tx = builder.build_and_sign([psk], change_address=address)

# Submit signed transaction to the network
context.submit_tx(signed_tx)

See more usages under examples.

Development

Click to expand

Workspace setup

Clone the repository:

git clone https://github.com/Python-Cardano/pycardano.git

PyCardano uses poetry to manage its dependencies. Install poetry for osx / linux / bashonwindows:

curl -sSL https://install.python-poetry.org | python3 -

Go to poetry installation for more details.

Change directory into the repo, install all dependencies using poetry, and you are all set!

cd pycardano && poetry install

When testing or running any program, it is recommended to enter a poetry shell in which all python dependencies are automatically configured: poetry shell.

Test

PyCardano uses pytest for unit testing.

Run all tests: make test

Run all tests in a specific test file: poetry run pytest test/pycardano/test_transaction.py

Run a specific test function: poetry run pytest -k "test_transaction_body"

Run a specific test function in a test file: poetry run pytest test/pycardano/test_transaction.py -k "test_transaction_body"

Test coverage

We use Coverage to calculate the test coverage.

Test coverage could be generated by: make cov

A html report could be generated and opened in browser by: make cov-html

Style guidelines

The package uses Google style docstring.

Code could be formatted with command: make format

The code style could be checked by flake8: make qa

Docs generation

The majority of package documentation is created by the docstrings in python files. We use sphinx with Read the Docs theme to generate the html pages.

Build docs and open the docs in browser:

make docs

Donation and Sponsor

If you find this project helpful, please consider donate or sponsor us. Your donation and sponsor will allow us to spend more time on improving PyCardano and adding more features in the future.

You can support us by 1) sponsoring through Github, or 2) donating ADA to our ADA Handle pycardano or to the address below:

addr1vxa4qadv7hk2dd3jtz9rep7sp92lkgwzefwkmsy3qkglq5qzv8c0d

Sponsors ❤️

pycardano's People

Contributors

astrojarred avatar b3nac avatar bhatt-deep avatar cffls avatar chrissiwaffler avatar daehan-koreapool avatar dependabot[bot] avatar henryyuanheng-wang avatar juliusfrost avatar kingh242 avatar mateusap1 avatar mrvfino avatar nielstron avatar petervg avatar thaddeusdiamond avatar thaopt avatar theeldermillenial avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pycardano's Issues

Txs failing to build when there's minimal (but sufficient) coin.

I have an address that contains 2.830539 ada. If i build a tx to send 1.6 ada, it fails with insufficient funds error. When I send 1.4 ada it works as expected. Seems the same with addresses holding more ada, as well.

Since the tx fee should be ~0.16 ada and I'm sending more than the minimum 1 ada, I think this is a bug.

pycardano.exception.UTxOSelectionException: All UTxO selectors failed.
0|crypto |     Requested output:
0|crypto |      {'coin': 1795556, 'multi_asset': {}} 
0|crypto |     Pre-selected inputs:
0|crypto |      {'coin': 2830539, 'multi_asset': {}} 
0|crypto |     Additional UTxO pool:
0|crypto |      [] 

Unfulfilled amount error

Hello Jerry. Hope all is good.

Maybe this is solved in a recent commit but pip is still giving me version 0.5.0. Please push if you tackled this.

I'm still getting funky errors on creating txs with addresses that have one or few txs. The index of available inputs is out of line. I'm guessing this is a problem with blockfrost's context. One address has 2 txs, indexed at 1 and 3 respectively. I'm pretty sure that's causing txs to fail.

Screen Shot 2022-06-29 at 19 23 38

Screen Shot 2022-06-29 at 19 23 22

FR: Add support for OpShin scripts (including evaluation internally)

Is your feature request related to a problem? Please describe.
Cardano developers may have a hard time debugging their script, as they need to understand the whole Haskell/Pluto stack.

Describe the solution you'd like
Imperator/pyscc provides a compiler from a subset of Python into UPLC, in other words a programming language for Smart Contracts on Cardano, that IS python.

When adding an Imperator script as a script input to a transaction in pycardano, this means that we could offer to evaluate the script during transaction building and signing. This includes being able to step through the Script as it is being evaluated with a normal python debugger.

What we need for this?

  • A flag to signify an added script input is actually an imperator script (and the corresponding source file)
  • A hook inside the transaction builder that calls the imperator script with the correct parameters (i.e. ScriptContext etc)
  • Optimally: Imperator/pycardano using the same definition for PlutusData

Describe alternatives you've considered
None

Additional context
I am developing Imperator/pyscc and can provide the parts of support that are needed on that end. Currently, I am looking into integrating the pycardano definitions of PlutusData and looking for a place where I could add the evaluation hook.

How to consume UTxO from script address

Hi, this looks like a great tool. But I don't see that it supports consuming UTxOs from script addresses?
To do that with the CLI, you need to set

--tx-in-script-file FILE 
--tx-in-datum-file FILE 
--tx-in-redeemer-file FILE

but I don't see any corresponding arguments for that in this tool.
Is there any convenient way of doing this (possibly adding some source code) with the tool as it is now?
If this is not supported yet, what is the roadmap for adding this feature?
Thanks.

Mint tokens with plutus script

I want to mint tokens with a plutus script I created, but I did not find any examples describing how to do so.

If it is already possible to mint tokens with plutus scripts I think the documentation is lacking references to that feature, I didn't find any examples on this repository or in the documentation.

As a work-around, I attempted to change the scripts from the transaction builder manually, but it returned me an exception saying I can't set attribute 'scripts'. Any plans on implementing that feature or is it there already, but I missed something? Also, any suggestions, however, for what to do meanwhile?

FR: Potential inputs for TxBuilder

Is your feature request related to a problem? Please describe.
When interacting with multi-address wallets, there may be tons of addresses that a user wallet controls. Usually few of them hold UTxOs. Moreover browser wallets usually cache these UTxOs anyways and allow to retreive them quickly.

When using pycardano to build a transaction for a browser wallet using TxBuilder, currently the only way for interaction is adding all of these addresses to the input_addresses. The TxBuilder will then fetch all addresses again - which may be very slow.

If you add all UTxOs of the wallet as inputs than necessarily all inputs will be consumed, which is also not necessarily desireable as it may make the transaction too big and decreases parallelizability of future tx builds (generating only one output)

Describe the solution you'd like
A parameter to the TxBuilder that allows passing a list of UTxOs that may be used by the coin selection algorithm, but do not need to be included in the tx as inputs.

Describe alternatives you've considered
None

Additional context
Useful when building dApps with PyCardano.

CIP30 defines a function that will return all UTxOs from the wallet: https://cips.cardano.org/cips/cip30/#apigetutxosamountcborvalueundefinedpaginatepaginateundefinedpromisetransactionunspentoutputnull

AWESOME thank you thank you

Although I primarily use JS in our stack, I'm going to give your work a try. Been super frustrated with car-ser-lib, especially how it doesn't handle the newer coinsperutxoword change. Similarly frustrated with running a sync'd node just to mint tokens and sign txs.

Looking forward and thank you. If I'm able to work with it, then I'll see about wrapping it in JS via https://www.npmjs.com/package/python-bridge

PyCardano does not correctly load nested PlutusData from cbor where the keys are unions of PlutusData

Describe the bug
When trying to load a ScriptContext from a CBOR description of it, the data is not correctly unwrapped.
The issue is that the redeemers dict is a dict from Redeemer to PlutusDatum, where Redeemer is a Union of PlutusData objects. Currently, only direct PlutusData objects are unrolled but not recursively defined container and union types.

To Reproduce

from opshin.prelude import ScriptContext
x = ScriptContext.from_cbor("d8799fd8799f9fd8799fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffd8799fd8799fd87a9f581cdbe769758f26efb21f008dc097bb194cffc622acc37fcefc5372eee3ffd87a80ffa140a1401a00989680d87a9f5820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dffd87a80ffffff809fd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a14000d87980d87a80ffffa140a14000a140a1400080a0d8799fd8799fd87980d87a80ffd8799fd87b80d87a80ffff80a1d87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffd87980a15820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd8799f5820746957f0eb57f2b11119684e611a98f373afea93473fefbb7632d579af2f6259ffffd87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffff")
# only here the script fails as it finds weird ConstrData remainders
x.to_json()

Error when submit NFT minting tx

Discussed in #164

Originally posted by reitcircles February 14, 2023
When submitting tx for the NFT minting of this example: https://github.com/Python-Cardano/pycardano/blob/main/examples/native_token.py

blockfrost.utils.ApiError: {'error': 'Bad Request', 'message': '"transaction read error RawCborDecodeError [DecoderErrorDeserialiseFailure \\"Byron Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding TxAux.\\\\nExpected 2, but found 4.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 43 \\"expected string\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 43 \\"expected string\\")]"', 'status_code': 400}

Does anyone have a clue how to solve this ?

Can't add "msg" metadata to transaction

Describe the bug
I'm trying to add some metadata to an ADA transaction, just like this:

metadata = {674: {'msg': ['some text here']}}
builder = TransactionBuilder(chain_context)

builder.auxiliary_data = AuxiliaryData(
    data=AlonzoMetadata(metadata=Metadata(metadata))
)
# also tried like this:
# builder.auxiliary_data = AuxiliaryData(data=Metadata(metadata))

# (...)

tx_body = builder.build(change_address=change_address)
tx = Transaction(tx_body, TransactionWitnessSet())

Logs
When I try to sign the tx using the nami web wallet, this is the error I'm getting on their log:

code: 2
info: "Wallet could not send the tx."
message: "transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (FromAlonzoUtxowFail (WrappedShelleyEraFailure (MissingTxMetadata (AuxiliaryDataHash {unsafeAuxiliaryDataHash = SafeHash \"ef64ed78c9efa5555caaec0f3987bc03b71de2890763b3a55108f19fd4d191a9\"}))))])"

How to calculate exact tx fee

Forgive me if you already explained this in docs or examples.. how can I calculate the exact tx fee for given tx inputs, outputs, and metadata?

It would be cool to have a method like Transaction.estimate_fee(builder) so I can inform users what they need to pay, especially considering their metadata inputs.

Move to hashlib for hashing

Is your feature request related to a problem? Please describe.
pycardano currently uses NaCL for its hashing primitives. This is usually a large library, that is also not pure python and often causes long builds during installation.

Describe the solution you'd like
The python standard library hashlib supports sha256 etc. It would be nice if they can be used by default.

Describe alternatives you've considered
None

Additional context
I want to make development on Cardano as accessible as possible and a short and easily installable toolchain is part of that.

Multiple addresses from same keys

Hey guys, not sure if this is the same question from those wanting wallets.

Is it possible to create multiple addresses from the same keys with this lib?

Cannot get proper policy hash from policy

I'm loading my policy skey from the json that is created when you generate the keys from cardano-cli. I can read those fine. When I create a vkey from it, the result is identical to loading the vkey from json. So I know the skey and vkey are jiving. When I use the InvalidHereafter, I produce the same hash as the policy script. So the policy skey, vkey, and script are all jiving. But when I go to spit out the hash from the policy, I cannot get it to match my policy id. I can see from the tx submission that that is exactly what its complaining about. Its saying I dont have enough assets from policy a, and trying to mint assets from policy b. Below is the code to create the key, the tx submission error, and the signed tx data:

submit_transaction(data)
'transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (UtxoFailure (FromAlonzoUtxoFail (ValueNotConservedUTxO (Value 1996994043 (fromList [(PolicyID {policyID = ScriptHash "a3680e96ca0587893b878040be0adb3a08646c721a46b74fd25433c0"},fromList [(43617264616e6f4275647a464230303233,1)])])) (Value 1996994043 (fromList [(PolicyID {policyID = ScriptHash "810cdc9a4e4106e5fec73d25c417948af4fb18b9ec9c6b791074ef9f"},fromList [(43617264616e6f4275647a464230303233,1)])])))))])'

def compile_policy(mint_skey, mint_script):
policy = None
if mint_skey != None:
sk = PaymentSigningKey.from_json(json.dumps(mint_skey)) ### THE mint_skey VARIABLE HAS THE POLICY SKEY JSON
vk = PaymentVerificationKey.from_signing_key(sk)
pub_key_policy = ScriptPubkey(vk.hash())
if 'scripts' in mint_script:
must_before_slot = InvalidHereAfter(mint_script['scripts'][0]['slot']) #### THE mint_script VARIABLE HAS THE POLICY SCRIPT JSON
policy = ScriptAll([pub_key_policy, must_before_slot])
else:
policy = ScriptAll([pub_key_policy])
return policy

signed_tx
{'auxiliary_data': AuxiliaryData(data=AlonzoMetadata(metadata={721: {'840e45a88f8ef46222878306752d04df46fb01e38f4392304426ad70': {'CardanoBudzFB0023': {'image': 'ipfs://QmQNQKP6jYpW83jNTtnGoiqtLBWyN3w12dLakvMGUpAcvG',
'name': 'Cardano '
'Budz '
'Fully '
'Baked '
'#23'}}}},
native_scripts=None,
plutus_scripts=None)),
'transaction_body': {'auxiliary_data_hash': AuxiliaryDataHash(hex='2387f7e567e24e5e4bf6e1e3728af43743144ab7e342ad858ad4028dcc0ee183'),
'certificates': None,
'collateral': None,
'fee': 193582,
'inputs': [{'index': 0,
'transaction_id': TransactionId(hex='57254cdf2de7e38527080e5caa28898877571150a20acc5c231b09bdda934bb5')}],
'mint': {ScriptHash(hex='a3680e96ca0587893b878040be0adb3a08646c721a46b74fd25433c0'): {AssetName(b'CardanoBudzFB0023'): 1}},
'network_id': None,
'outputs': [{'address': addr_test1vpyzhkrtv4xprtj9z5e3p3e2jrp56wzlaxpaxk7p3gxrdmcph2gph,
'amount': {'coin': 1994800461, 'multi_asset': {}},
'datum_hash': None},
{'address': addr_test1vpyzhkrtv4xprtj9z5e3p3e2jrp56wzlaxpaxk7p3gxrdmcph2gph,
'amount': {'coin': 2000000,
'multi_asset': {ScriptHash(hex='810cdc9a4e4106e5fec73d25c417948af4fb18b9ec9c6b791074ef9f'): {AssetName(b'CardanoBudzFB0023'): 1}}},
'datum_hash': None}],
'required_signers': None,
'script_data_hash': None,
'ttl': 76898386,
'update': None,
'validity_start': None,
'withdraws': None},
'transaction_witness_set': {'bootstrap_witness': None,
'native_scripts': [ScriptAll(_TYPE=1,
native_scripts=[ScriptPubkey(_TYPE=0,
key_hash=VerificationKeyHash(hex='83eac3c8076a56efe62854089cd36fdb6b1157adc6a0a085c0992c37')),
InvalidHereAfter(_TYPE=5,
after=81927623)])],
'plutus_data': None,
'plutus_script': None,
'redeemer': None,
'vkey_witnesses': [{'signature': b'\xb3\xb85#\xa8\xb0\x89\xceJ\x8e<\x1b\xac\x8e\xa2XU\xca\xe7\xc9'
b'\xb0i\xbepc\xb1\x07\xc0\xc8\xe3\xda\x93J\xd3\x85\xf0'
b'\x002\x03\xd7\x99\x9c\x83\xdb)`\x80\xc0D\xf1\r\xd1\xa6\xd4\x01/'
b'\t\xda!\x9a9h\xe8\x06',
'vkey': {"type": "PaymentVerificationKeyShelley_ed25519", "description": "Payment Verification Key", "cborHex": "58202027d9ab58c8b6ef08c158078bf753426cf0e4cdb4cb5d64159cd6f4aa299a7b"}},
{'signature': b'\t\n{w\xcbs\xa5\xf5zZ\xec\xfd\xb6O%\xd6\x1d\x04\xcc\xd2'
b'\xcb\x8f\x99#\xfe\xca\x9eDx@\x17Xn\xb8\xa3\xe1\xc0\x9e\xca\x1b'
b"u\xa4.\xeb\xe9\x0f3\x8d\x8a\x8aH'\xd4\xb3\xa7\xb3\xea\xe9\x00}"
b'c\xafL\t',
'vkey': {"type": "PaymentVerificationKeyShelley_ed25519", "description": "Payment Verification Key", "cborHex": "5820d36baff3fc391af90089b347e4db9c785c6716843d366b222028413baf6897d4"}}]},
'valid': True}

Tx submit errors with PlutusV2 script cbor derived from Helios

Describe the bug
Fails to claim UTxOs sitting at always succeed contract address. Blockfrost either returns malformed script errors or deserialize errors ("TooMuchSpace")

To Reproduce
See locking and claiming scripts: https://github.com/Et9797/pycardano-issue
Cbor hex obtained from Helios: 54530100003222233335734600893124c4c9312511
The Helios contract:
`spending always_true

func main() -> Bool {
true
}
`
Environment and software version (please complete the following information):
Pycardano 0.6.2.
Preview testnet

BabbageOutputTooSmallUTxO Error

Are there any other considerations from recent updates to address?

Hi Jer, I'm getting the following error. Minting a simple nft with only a name set in metadata, from a fresh address holding 4143577 lovelace from one tx.

Error:

PythonShellError: blockfrost.utils.ApiError: {'error': 'Bad Request', 'message': '"transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (UtxoFailure (BabbageOutputTooSmallUTxO [((Addr Mainnet (KeyHashObj (KeyHash \\"66f5708f512e120269b683cf9f7c793563c5d68169d74e47b7421768\\")) (StakeRefBase (KeyHashObj (KeyHash \\"9af6d40c5a217958b20d04f82955a2b670de001c43c0184b01cf8e5f\\"))),Value 874930 (fromList [(PolicyID {policyID = ScriptHash \\"7973f8eab6e52074c7b19b0f1305a7c85afb1ab5df2c6c92d169301d\\"},fromList [(6c6f77206b6579,1)])]),NoDatum,SNothing),Coin 1150770)]))])"', 'status_code': 400}
"TransactionBuilder(context=<pycardano.backend.blockfrost.BlockFrostChainContext object at 0x10c557dc0>, utxo_selectors=[<pycardano.coinselection.RandomImproveMultiAsset object at 0x10e1561d0>, <pycardano.coinselection.LargestFirstSelector object at 0x10e156140>], execution_memory_buffer=0.2, execution_step_buffer=0.2, ttl=72640326, validity_start=None, auxiliary_data=AuxiliaryData(data=AlonzoMetadata(metadata={721: {'7973f8eab6e52074c7b19b0f1305a7c85afb1ab5df2c6c92d169301d': {'low key': {'name': 'low key', 'minted_on': '26/09/2022 18:20:23'}}}}, native_scripts=None, plutus_scripts=None)), native_scripts=[ScriptAll(_TYPE=1, native_scripts=[ScriptPubkey(_TYPE=0, key_hash=VerificationKeyHash(hex='2839e42112cf20d6d78304a9954e08e677d86aa8e06e1d26ba91470d')), InvalidHereAfter(_TYPE=5, after=72640326)])], mint={ScriptHash(hex='7973f8eab6e52074c7b19b0f1305a7c85afb1ab5df2c6c92d169301d'): {AssetName(b'low key'): 1}}, required_signers=None, collaterals=[], certificates=None, withdrawals=None, reference_inputs=set(), _inputs=[], _excluded_inputs=[], _input_addresses=[addr1q9n02uy02yhpyqnfk6pul8mu0y6k83wks95awnj8kappw6y67m2qck3p09vtyrgylq54tg4kwr0qq8zrcqvykqw03e0s0k3z4z], _outputs=[], _fee=0, _datums={}, _collateral_return=None, _total_collateral=None, _inputs_to_redeemers={}, _minting_script_to_redeemers=[], _inputs_to_scripts={}, _reference_scripts=[], _should_estimate_execution_units=None) <pycardano.backend.blockfrost.BlockFrostChainContext object at 0x10c557dc0>"

Blockfrost ApiError 5 ADA redeeming transaction fortytwo.py example

Describe the bug
Sending 10ADA to the fortytwo plutus script on TESTNET works fine.
However, when I attempt to redeem 5ADA from the plutus script I get a blockfrost ApiError.

To Reproduce
fortytwo_test.py script under Additional context

Logs
Traceback (most recent call last):
File "fortytwo_test.py", line 120, in
submit_tx(signed_tx)
File "fortytwo_test.py", line 42, in submit_tx
chain_context.submit_tx(tx.to_cbor())
File "/home/zlac116/anaconda3/envs/cardanopy/lib/python3.8/site-packages/pycardano/backend/blockfrost.py", line 205, in submit_tx
self.api.transaction_submit(f.name)
File "/home/zlac116/anaconda3/envs/cardanopy/lib/python3.8/site-packages/blockfrost/utils.py", line 63, in error_wrapper
raise ApiError(request_response)
blockfrost.utils.ApiError: {'error': 'Bad Request', 'message': '"transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (FromAlonzoUtxowFail (PPViewHashesDontMatch (SJust (SafeHash \"0067cdab1a1ae15069bf96bd33cbc059ec3a8677ab198b56081e6716016b0410\")) (SJust (SafeHash \"2b25c2e22a3a08ca6d9fa0bcf299dff8ab2d5cbe06d5c2c5e255af46bb16cafd\"))))])"', 'status_code': 400}

Expected behavior
5ADA is sent to the taker address

Environment and software version (please complete the following information):

  • OS: Ubuntu 20.04.3 LTS
  • PyCardano Version 0.6.0
  • Python Version 3.8.12

Additional context
python script: fortytwo_test.py

'''
Off-chain code of taker and giver in fortytwo

'''
import os
import cbor2
from retry import retry
from dotenv import load_dotenv

import pycardano as pc

load_dotenv()

NETWORK = pc.Network.TESTNET

def get_env_val(key):
    val = os.getenv(key)
    if not val:
        raise Exception(f"Environment variable {key} is not set!")
    return val

payment_skey = pc.PaymentSigningKey.load(get_env_val("PAYMENT_KEY_PATH"))
payment_vkey = pc.PaymentVerificationKey.from_signing_key(payment_skey)

stake_skey = pc.StakeSigningKey.load(get_env_val('STAKE_KEY_PATH'))
stake_vkey = pc.StakeVerificationKey.from_signing_key(stake_skey)

chain_context = pc.BlockFrostChainContext(
    project_id=get_env_val("BLOCKFROST_ID"), network=NETWORK
)

@retry(delay=20)
def wait_for_tx(tx_id):
    chain_context.api.transaction(tx_id)
    print(f"Transaction {tx_id} has been successfully included in the blockchain.")

def submit_tx(tx):
    print("############### Transaction created ###############")
    print(tx)
    print(tx.to_cbor())
    print("############### Submitting transaction ###############")
    chain_context.submit_tx(tx.to_cbor())
    wait_for_tx(str(tx.id))


def utxos(address):
    amount = sum([i.output.amount.coin for i in chain_context.utxos(str(address)) if not i.output.amount.multi_asset is None]) / 1e6
    print(f'UTXO value: {amount} ADA')

def find_collateral(target_address):
    for utxo in chain_context.utxos(str(target_address)):
        # A collateral should contain no multi asset
        if not utxo.output.amount.multi_asset:
            return utxo
    return None

def create_collateral(target_address, skey):
    collateral_builder = pc.TransactionBuilder(chain_context)

    collateral_builder.add_input_address(target_address)
    collateral_builder.add_output(pc.TransactionOutput(target_address, 5000000))

    submit_tx(collateral_builder.build_and_sign([skey], target_address))

# ----------- Giver sends 10 ADA to a script address ---------------
with open("fortytwo.plutus", "r") as f:
    script_hex = f.read()
    forty_two_script = cbor2.loads(bytes.fromhex(script_hex))

script_hash = pc.plutus_script_hash(forty_two_script)

script_address = pc.Address(script_hash, network=NETWORK)

giver_address = pc.Address(payment_vkey.hash(), stake_vkey.hash(), network=NETWORK)

builder = pc.TransactionBuilder(chain_context)
builder.add_input_address(giver_address)
datum = pc.PlutusData()  # A Unit type "()" in Haskell
builder.add_output(
    pc.TransactionOutput(script_address, 10000000, datum_hash=pc.datum_hash(datum))
)

utxos(giver_address)

signed_tx = builder.build_and_sign([payment_skey], giver_address)

submit_tx(signed_tx)



# ----------- Taker takes 10 ADA from the script address ---------------

# taker_address could be any address. In this example, we will use the same address as giver.
taker_address = giver_address

# Notice that transaction builder will automatically estimate execution units (num steps & memory) for a redeemer if
# no execution units are provided in the constructor of Redeemer.
# Put integer 42 (the secret that unlocks the fund) in the redeemer.
redeemer = pc.Redeemer(pc.RedeemerTag.SPEND, 42)

utxo_to_spend = chain_context.utxos(str(script_address))[1]

builder = pc.TransactionBuilder(chain_context)
builder.add_script_input(utxo_to_spend, forty_two_script, datum, redeemer)

# Send 5 ADA to taker address. The remaining ADA (~4.7) will be sent as change.
take_output = pc.TransactionOutput(taker_address, 5000000)
builder.add_output(take_output)

non_nft_utxo = find_collateral(taker_address)

if non_nft_utxo is None:
    create_collateral(taker_address, payment_skey)
    non_nft_utxo = find_collateral(taker_address)

builder.collaterals.append(non_nft_utxo)

signed_tx = builder.build_and_sign([payment_skey], taker_address)

submit_tx(signed_tx)
utxos(giver_address)

How to use payment extended signing key

Hi,

I would like to use your library. I create my own keys with the script written below. In the end, my payment and stake signing keys are extended versions. I think it's caused by using mnemonics to generate keys. However, in the library, NaCL cannot use them.

Do you have any idea to use or convert PaymentExtendedSigningKeyShelley_ed25519_bip32?

#Root Private Key
cardano-wallet key from-recovery-phrase Shelley < mnemonics.prv > root.prv

#Root Public Key
cardano-address key public --with-chain-code < root.prv > root.pub

# Address Private and Public Keys
cat root.prv \
  | cardano-wallet key child 1852H/1815H/0H/0/0 \
  | tee address.prv \
  | cardano-wallet key public --with-chain-code > address.pub
cardano-cli key convert-cardano-address-key --shelley-payment-key --signing-key-file address.prv --out-file payment.skey
cardano-cli key verification-key --signing-key-file payment.skey --verification-key-file extended_payment.vkey
cardano-cli key non-extended-key --extended-verification-key-file extended_payment.vkey --verification-key-file payment.vkey

#Staking Private and Public Keys
cat root.prv \
  | cardano-wallet key child 1852H/1815H/0H/2/0 \
  | tee staking.prv \
  | cardano-wallet key public --with-chain-code > staking.pub
cardano-cli key convert-cardano-address-key --shelley-stake-key --signing-key-file staking.prv --out-file stake.skey
cardano-cli key verification-key --signing-key-file stake.skey --verification-key-file extended_stake.vkey
cardano-cli key non-extended-key --extended-verification-key-file extended_stake.vkey --verification-key-file stake.vkey

cardano-cli address build --payment-verification-key-file payment.vkey --stake-verification-key-file stake.vkey --out-file address.addr --mainnet

Can't submit transaction with raw json datum

I am trying to make a transaction with datum attached to output (not spend a plutus UTxO, but like making a regular transaction with --tx-out-datum-embed-file from cardano-cli). This is required when submitting to many of the existing smart contracts on Cardano.
This is my attempt:

network = pc.Network.TESTNET 
psk = pc.PaymentExtendedSigningKey.load("testnet-keys/00.skey")
ssk = pc.StakeExtendedSigningKey.load('testnet-keys/stake.skey')
pvk = pc.PaymentExtendedVerificationKey.from_signing_key(psk)
svk = pc.StakeExtendedVerificationKey.from_signing_key(ssk)
address = pc.Address(pvk.hash(), svk.hash(), network)
context = pc.BlockFrostChainContext(my_token, network)
builder = pc.TransactionBuilder(context)

builder.add_input_address(address)

datum = {b'fields': [], b'constructor': 0}
datum_hash = pc.DatumHash(blake2b(cbor2.dumps(datum, default=default_encoder), 32, encoder=RawEncoder))


builder.add_output(pc.TransactionOutput(pc.Address.from_primitive("addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x"),
                         amount=pc.Value(10000000), datum_hash=datum_hash))


tx_body = builder.build(change_address=address)
signature = psk.sign(tx_body.hash())
vk_witnesses = [pc.VerificationKeyWitness(pvk, signature)]
tx = pc.Transaction(tx_body, pc.TransactionWitnessSet(vkey_witnesses=vk_witnesses,
                           plutus_data=[datum]))

context.submit_tx(tx.to_cbor())

It results in a node error on submission saying the fees were miscalculated:

ApiError: {'error': 'Bad Request', 'message': '"transaction submit error ShelleyTxValidationError ShelleyBasedEraAlonzo (ApplyTxError [UtxowFailure (PPViewHashesDontMatch SNothing (SJust (SafeHash \\"7e58e4a25bc56a14475e7461cda5aeeaf59fff97285560887a9eedd2ddea1d9f\\"))),UtxowFailure (WrappedShelleyEraFailure (UtxoFailure (FeeTooSmallUTxO (Coin 169241) (Coin 168317))))])"', 'status_code': 400}

I can temporarily fix this by making sure datum is included when calculating fee (this will need a proper fix):

class MyTransactionBuilder(pc.TransactionBuilder):     
    def _build_fake_witness_set(self) -> pc.TransactionWitnessSet:
        return pc.TransactionWitnessSet(
            vkey_witnesses=self._build_fake_vkey_witnesses(),
            native_scripts=self.native_scripts,
            plutus_data=self.plutus_data
        )
# ... add stuff to builder
builder.plutus_data = [datum]
# .. build tx and submit

Then I am left with this error:

ApiError: {'error': 'Bad Request', 'message': '"transaction submit error ShelleyTxValidationError ShelleyBasedEraAlonzo (ApplyTxError [UtxowFailure (PPViewHashesDontMatch SNothing (SJust (SafeHash \\"7e58e4a25bc56a14475e7461cda5aeeaf59fff97285560887a9eedd2ddea1d9f\\")))])"', 'status_code': 400}

A quick google search indicate that this might have something to do with the cost model not being in protocol params. It doesn't look like the protocol params are used anywhere when creating the transaction with TransactionBuilder ? I know Plutus is not all supported yet in PyCardano, but I thought adding a raw json datum to the transaction would be different.

  • PyCardano Version 0.2.0

Thanks.

Error: The seed must be exactly 32 bytes long

Describe the bug
From a mnemonic (32 words length) i have derived a set of payment.vkey/skey and stake.skey/vkey. Using these keys i am trying to now sign the tx. in pycardano using interface:builder.build_and_sign.

(same transaction works for cardano-cli to be clear.So there is some limitation to this library for being used for transaction)

To Reproduce

Logs
The seed must be exactly 32 bytes long

Expected behavior

  • should have signed using the keys.
  • I have used the same set of keys in cardano-cli, so they are correct.

Environment and software version (please complete the following information):

  • OS: [ubuntu latest]
  • PyCardano Version latest

Additional context
Add any other context about the problem here.

Fix blockfrost chain context integration

Describe the bug
There has been a recent version update in the blockfrost-python API (https://github.com/blockfrost/blockfrost-python/releases/tag/0.4.4), which fixes a duplicated function signature (see the issue here: blockfrost/blockfrost-python#19).

The wrong version of the method epoch_latest_parameters required an integer parameter (epoch). That's the method currently used by the latest release of this source code.

I've noticed that this bug has been fixed in a recent commit here: cffls@47b2c96#diff-71818a1931481d2eb048a946ae222df8bda01be6eec751f263acaab097b8e86b, but still not released.

Upon a fresh install of the pycardano package, this is causing the BlockFrostChainContext to crash when trying to retrieve protocol parameters since the blockfrost API has been updated.

To Reproduce
Install pycardano using pip and try to build a transaction using the blockfrost chain context.

Logs
Here's some python traceback:

File ~/python/example/env/lib/python3.10/site-packages/pycardano/txbuilder.py:620, in TransactionBuilder.build(self, change_address)
    618 for i, selector in enumerate(self.utxo_selectors):
    619     try:
--> 620         selected, _ = selector.select(
    621             additional_utxo_pool,
    622             [TransactionOutput(None, unfulfilled_amount)],
    623             self.context,
    624         )
    625         for s in selected:
    626             selected_amount += s.output.amount

File ~/python/example/env/lib/python3.10/site-packages/pycardano/coinselection.py:271, in RandomImproveMultiAsset.select(self, utxos, outputs, context, max_input_count, include_max_fee, respect_min_utxo)
    260 def select(
    261     self,
    262     utxos: List[UTxO],
   (...)
    268 ) -> Tuple[List[UTxO], Value]:
    269     # Shallow copy the list
    270     remaining = list(utxos)
--> 271     max_fee = max_tx_fee(context) if include_max_fee else 0
    272     request_sum = Value(max_fee)
    273     for o in outputs:

File ~/python/example/env/lib/python3.10/site-packages/pycardano/utils.py:57, in max_tx_fee(context)
     46 def max_tx_fee(context: ChainContext) -> int:
     47     """Calculate the maximum possible transaction fee based on protocol parameters.
     48 
     49     Args:
   (...)
     53         int: Maximum possible tx fee in lovelace.
     54     """
     55     return fee(
     56         context,
---> 57         context.protocol_param.max_tx_size,
     58         context.protocol_param.max_tx_ex_steps,
     59         context.protocol_param.max_tx_ex_mem,
     60     )

File ~/python/example/env/lib/python3.10/site-packages/pycardano/backend/blockfrost.py:79, in BlockFrostChainContext.protocol_param(self)
     76 @property
     77 def protocol_param(self) -> ProtocolParameters:
     78     if not self._protocol_param or self._check_epoch_and_update():
---> 79         params = self.api.epoch_latest_parameters(self.epoch)
     80         self._protocol_param = ProtocolParameters(
     81             min_fee_constant=int(params.min_fee_b),
     82             min_fee_coefficient=int(params.min_fee_a),
   (...)
    105             coins_per_utxo_word=int(params.coins_per_utxo_word),
    106         )
    107     return self._protocol_param

File ~/python/example/env/lib/python3.10/site-packages/blockfrost/utils.py:61, in request_wrapper.<locals>.error_wrapper(*args, **kwargs)
     60 def error_wrapper(*args, **kwargs):
---> 61     request_response: Response = func(*args, **kwargs)
     62     if request_response.status_code != 200:
     63         raise ApiError(request_response)

TypeError: epoch_latest_parameters() takes 1 positional argument but 2 were given

Inputs do not conform to spec when attempting to sign with nami.

Describe the bug
When sending a transaction to the browser to sign with nami it says the inputs do not conform to this spec or are otherwise invalid.

To Reproduce
Here is the function that generates the transaction to send to nami. It is converted to cbor as detailed in the examples before sending.

def build_mash_transaction(data):
     input_addresses = [Address.from_primitive(bytes.fromhex(sender)) for sender in data['senders']]
     change_address = Address.from_primitive(bytes.fromhex(data["change_address"]))
     their_asset1 = Asset() # Create an asset object to hold the NFT names and amounts
     their_asset2 = Asset()
     nft1 = AssetName(bytes(data['nft1_name'], 'utf-8')) # get NFT 1 name
     nft2 = AssetName(bytes(data['nft2_name'], 'utf-8')) # get NFT 2 name
 
     their_asset1[nft1] = 1 # set token amount to 1
     their_asset2[nft2] = 1
 
     asset = Asset()
     nft_name = "Test" + str(shortuuid.random(length=8))
     nft_asset = AssetName(bytes(nft_name, 'utf-8'))
     asset[nft_asset] = 1
     their_nfts = MultiAsset()
     nft = MultiAsset()
     their_nfts[ScriptHash.from_primitive(data['nft1_policy'])] = their_asset1
     their_nfts[ScriptHash.from_primitive(data['nft2_policy'])] = their_asset2
     nft[policyid] = asset

     metadata = {
       721: {
         policyid.payload.hex(): {
           nft_name: {
             "name": data['name'],
             "image": "ipfs://", #TODO
             "description": data['description'],
             "creator": data['creator'],
             "publisher": "Me",
             "contents": data['nft1_name'] + " | " + data['nft2_name'],
             "smart contract address": "FIX ME" #TODO
           }
         }
       }
     }

     auxiliary_data = AuxiliaryData(AlonzoMetadata(metadata=Metadata(metadata)))
     native_scripts = [policy]
     builder = TransactionBuilder(chain_context)

     address1 = "<address>"
     address2 = "<address>"

     for input_address in input_addresses:
       builder.add_input_address(input_address)
     builder.mint = nft
     builder.native_scripts = native_scripts
     builder.auxiliary_data = auxiliary_data
     builder.add_output(TransactionOutput(input_addresses[0], Value(2000000, nft)))
     builder.add_output(TransactionOutput(address2, Value(4000000, their_nfts)))
     builder.add_output(TransactionOutput(address1, 2000000))

     tx_body = builder.build(change_address=change_address)
     signature = policy_skey.sign(tx_body.hash())
     return Transaction(tx_body, TransactionWitnessSet(vkey_witnesses=[VerificationKeyWitness(policy_vkey, signature)]))

I have tried using both an empty witness set and the one shown in the code above but to no avail.

Logs
Error code says: {code: -1, info: 'Inputs do not conform to this spec or are otherwise invalid.'}

Expected behavior
Transaction should be able to be signed by the nami wallet

Environment and software version (please complete the following information):

  • OS: Ubuntu 20.04
  • PyCardano Version 0.5.0

Additional context
N/A

Document how to add reference_inputs when using TransactionBuilder

It's not obvious how to set values for the reference_inputs property and add it to a TransactionBody, as per the API here:
https://pycardano.readthedocs.io/en/latest/api/pycardano.transaction.html#pycardano.transaction.TransactionBody

I can add another TransactionBuilder property like ttl as part of the object instantiation, e.g.
builder = TransactionBuilder(context=context, ttl=2024)

But trying to do the same for the reference_inputs property results in:

builder = TransactionBuilder(context=context, reference_inputs=["testing1", "testing2"])
>>>TypeError: TransactionBuilder.__init__() got an unexpected keyword argument 'reference_inputs'

Trying to add it to the object after it's instantiated also fails to build a proper transaction:

builder = TransactionBuilder(context)`
builder.__set__(self, reference_inputs, {"test1", "test2"})
>>>AttributeError: 'TransactionBuilder' object has no attribute '__set__'. Did you mean: '__eq__'?

This code here appears to be adding reference_inputs to the utxo.output.script but, again, not sure where to initially set that reference input value.

if utxo.output.script:
self._inputs_to_scripts[utxo] = utxo.output.script
self.reference_inputs.add(utxo.input)
self._reference_scripts.append(utxo.output.script)

Size mismatch when decoding errors on fairly simple multi_asset transaction with builder

Describe the bug
I'm essentially following the example for tx_builder in github. I'm not seeing anything that jumps out in the signedTX that looks wrong, the builder is identifying the correct UTXOs and formating the proper outputs. Put I'm getting a 400 error from Blockfrost:

blockfrost.utils.ApiError: {'error': 'Bad Request', 'message': '"transaction read error RawCborDecodeError [DecoderErrorDeserialiseFailure \\"Byron Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding TxAux.\\\\nExpected 2, but found 4.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 155 \\"expected string\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 155 \\"expected string\\")]"', 'status_code': 400}

To Reproduce

import util
from pycardano import (
    Address,
    PaymentSigningKey,
    PaymentVerificationKey,
    Transaction,
    TransactionBody,
    TransactionBuilder,
    TransactionInput,
    TransactionOutput,
    TransactionWitnessSet,
    Value,
    VerificationKeyWitness,
    BlockFrostChainContext,
    Network
)
network = Network.MAINNET
context = BlockFrostChainContext(util.blockfrostHeader['project_id'], network)
address = 'addr1v9f3a4hf85s2xdy355n7sv0qjkp6mj785zca2m03cqvt4kqqthpgu'
toAddress = 'addr1vy65aqt766xp9u38hy2l7t2ejqtep8g799efh9uwu78askgpfzqtr'

sk = PaymentSigningKey.load("payment.skey")

builder = TransactionBuilder(context)
builder.add_input_address(Address.from_primitive(address))
# utxos = context.utxos(str(address))
# builder.add_input(utxos[0])
# builder.add_input(utxos[1])
builder.add_output(
    TransactionOutput(
        Address.from_primitive(
            toAddress
        ),
        Value.from_primitive(
            [
                1500000,
                {
                    bytes.fromhex(
                        "c9281d64953f9531304e57e4ff15111f0c5445167c0b413f70fcd50c"  # Policy ID
                    ): {
                        b"RUNE": 20  # Asset name and amount
                    }
                },
            ]
        ),
    )
)

signedTX = builder.build_and_sign([sk], change_address=address)
context.submit_tx(signedTX.to_cbor())

Logs
signed_tx (not cbor-ed):

{
  'auxiliary_data': None,
  'transaction_body': {
    'auxiliary_data_hash': None,
    'certificates': None,
    'collateral': None,
    'collateral_return': None,
    'fee': 171793,
    'inputs': [
      {
        'index': 1,
        'transaction_id': TransactionId(hex='8f0443226a68631d2919ec32378d4f7aa231a054d027260c9bebdc90b513a550'),
      },
      {
        'index': 0,
        'transaction_id': TransactionId(hex='a1887376d765595098fb049ddff4c950dabb5184989836c1599d64bbf0d67759'),
      },
    ],
    'mint': None,
    'network_id': None,
    'outputs': [
      {
        'address': addr1vy65aqt766xp9u38hy2l7t2ejqtep8g799efh9uwu78askgpfzqtr,
        'amount': {
          'coin': 1500000,
          'multi_asset': {ScriptHash(hex='c9281d64953f9531304e57e4ff15111f0c5445167c0b413f70fcd50c'): {AssetName(b'RUNE'): 20}},
        },
        'datum': None,
        'datum_hash': None,
        'post_alonzo': False,
        'script': None,
      },
      {
        'address': 'addr1v9f3a4hf85s2xdy355n7sv0qjkp6mj785zca2m03cqvt4kqqthpgu',
        'amount': {
          'coin': 10382691,
          'multi_asset': {ScriptHash(hex='c9281d64953f9531304e57e4ff15111f0c5445167c0b413f70fcd50c'): {AssetName(b'RUNE'): 980}},
        },
        'datum': None,
        'datum_hash': None,
        'post_alonzo': False,
        'script': None,
      },
    ],
    'reference_inputs': None,
    'required_signers': None,
    'script_data_hash': None,
    'total_collateral': None,
    'ttl': None,
    'update': None,
    'validity_start': None,
    'withdraws': None,
  },
  'transaction_witness_set': {
    'bootstrap_witness': None,
    'native_scripts': None,
    'plutus_data': None,
    'plutus_v1_script': None,
    'plutus_v2_script': None,
    'redeemer': None,
    'vkey_witnesses': [
      {
        ************REDACTED*************************
      },
    ],
  },
  'valid': True,
}

Expected behavior
Expect the transaction to go through

Environment and software version (please complete the following information):

  • OS: Win 11 for dev, Ubuntu 20.4.5 prod
  • PyCardano 0.6.3

Additional context
Add any other context about the problem here.

Burn token example

Hello, thank you for this package.

Are you able to provide an example of burning a token using the tx-builder?

Thanks

Docs for adding arbitrary datum value to .add_output method

This works:

builder.add_output(TransactionOutput.from_primitive([outaddress, 99900000]))

But now I would like to add some arbitrary datum value to my output UTXO. This doesn't work:

builder.add_output(TransactionOutput.from_primitive([outaddress, 99900000, datum="my_value"]))

Neither does:

builder.add_output(TransactionOutput(Datum.from_primitive("my_value")))

Example on transaction builder doesn't provide some detail

Describe the bug
A wonderful package i must say, the issue I have right now is with the example on using the transaction builder.

To Reproduce
This block shows how to add an output but the comment is confusing

builder.add_output(
    TransactionOutput(
        address,
        Value.from_primitive(
            [
                2000000,
                {
                    bytes.fromhex(
                        "57fca08abbaddee36da742a839f7d83a7e1d2419f1507fcbf3916522"
                    ): {  # Policy ID
                        b"CHOC": 200
                    }
                },
            ]
        ),
    )
)

is the b"CHOC" part the policy-id or "57fca08abbaddee36da742a839f7d83a7e1d2419f1507fcbf3916522". Whichever it is, what exactly is the other?

decodeVerKeyDSIGN: wrong length, expected 32 bytes but got 0

I am trying to make a simple transaction (lovelace only) using generated payment keys.

CHAIN_CONTEXT = BlockFrostChainContext(
  project_id=BLOCKFROST_PROJECT_ID,
  network=Network.TESTNET,
  base_url="https://cardano-preprod.blockfrost.io/api",
)

payment_key_pair = PaymentKeyPair.generate()
payment_signing_key = payment_key_pair.signing_key
payment_verification_key = payment_key_pair.verification_key

stake_key_pair = StakeKeyPair.generate()
stake_signing_key = stake_key_pair.signing_key
stake_verification_key = stake_key_pair.verification_key

address = Address(payment_part=payment_verification_key.hash(),
                         staking_part=stake_verification_key.hash(),
                         network=Network.TESTNET)

toAddress = "..."
...
builder = TransactionBuilder(CHAIN_CONTEXT)
builder.add_input_address(address)
builder.add_output(TransactionOutput.from_primitive([toAddress, 10000000]))
builder.build_and_sign([payment_signing_key], change_address=address)

The tx can be built, but submitting it to Blockfrost throws this error

blockfrost.utils.ApiError: {'error': 'Bad Request', 'message': '"transaction read error RawCborDecodeError [DecoderErrorDeserialiseFailure \\"Byron Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding TxAux.\\\\nExpected 2, but found 4.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 193 \\"decodeVerKeyDSIGN: wrong length, expected 32 bytes but got 0\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (Deseria', 'status_code': 400}

I can see it hast to do with the verification key and I bet I am doing something wrong. Any idea what it is? 🙇

Guidance creating address from keys saved as JSON

Hey guys. I'm having trouble getting an address. I've tried variations but haven't got it right yet. Please look and lmk what I'm doing wrong.

Here's my code, currently trying the from_cbor method:

jsonsecret = json.loads(secret)
pkey  = jsonsecret['payment']['verification']['cborHex']
skey  = jsonsecret['stake']['verification']['cborHex']

print(pkey, skey)

base_address = Address(payment_part=PaymentVerificationKey.from_cbor(pkey), staking_part=StakeVerificationKey.from_cbor(skey), network=Network.MAINNET)

Here's my JSON string example:

secret = '{"payment":{"signing":{"type":"PaymentSigningKeyShelley_ed25519","description":"PaymentSigningKeyShelley_ed25519","cborHex":"5820b56b0401431a24f97fab426148c5c55b9df2c3aa69f24e5c50465fc8ae0bdf30"},"verification":{"type":"PaymentVerificationKeyShelley_ed25519","description":"PaymentVerificationKeyShelley_ed25519","cborHex":"582091f7710e0d57878b94b18f8d3bb9c2d884e055ca6797607e020eb7d6af222648"}},"stake":{"signing":{"type":"StakeSigningKeyShelley_ed25519","description":"StakeSigningKeyShelley_ed25519","cborHex":"5820c0337fcfb1504f14fa83352fa2a1a86d71bc74789b687baa03bd386d23255c18"},"verification":{"type":"StakeVerificationKeyShelley_ed25519","description":"StakeVerificationKeyShelley_ed25519","cborHex":"5820427693e248678fd3b58dc9c08ba4afa2876477bf79d3720bdc604be12d2bed5e"}}}'

Error message:

err: PythonShellError: pycardano.exception.InvalidAddressInputException: Cannot construct a shelley address from a combination of payment part: {"type": "PaymentVerificationKeyShelley_ed25519", "description": "PaymentVerificationKeyShelley_ed25519", "cborHex": "582091f7710e0d57878b94b18f8d3bb9c2d884e055ca6797607e020eb7d6af222648"} and stake part: {"type": "StakeVerificationKeyShelley_ed25519", "description": "StakeVerificationKeyShelley_ed25519", "cborHex": "5820427693e248678fd3b58dc9c08ba4afa2876477bf79d3720bdc604be12d2bed5e"}
      at PythonShell.parseError (/Users/34r7h/a/cardano-python-js/node_modules/python-shell/index.js:295:21)
      at terminateIfNeeded (/Users/34r7h/a/cardano-python-js/node_modules/python-shell/index.js:190:32)
      at ChildProcess.<anonymous> (/Users/34r7h/a/cardano-python-js/node_modules/python-shell/index.js:182:13)
      at ChildProcess.emit (events.js:314:20)
      at Process.ChildProcess._handle.onexit (internal/child_process.js:276:12)
      ----- Python Traceback -----
      File "/Users/34r7h/a/cardano-python-js/python/getaddress.py", line 26, in <module>
        base_address = Address(payment_part=PaymentVerificationKey.from_cbor((pkey)), staking_part=StakeVerificationKey.from_cbor(skey), network=Network.MAINNET)
      File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/address.py", line 201, in __init__
        self._address_type = self._infer_address_type()
      File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/address.py", line 233, in _infer_address_type

FR: Add gift contract example to /examples

Is your feature request related to a problem? Please describe.
The documentation now features a sample introduction with the eopsin gift contract, but the /examples folder only contains the old example with the Haskell 42 contract.

Describe the solution you'd like
The gift contract example should be added.

Describe alternatives you've considered
None

Additional context
#144

Minting error at end of example

Heya, I'm getting an error at the chain_context.submit_tx(signed_tx.to_cbor()) function, seemingly around deserialization. Maybe something with keys but I can't understand from the following log where to fix. Thanks for any help!

File "/Users/347h/a/cardano-python-js/python/mint.py", line 227, in <module>
    chain_context.submit_tx(signed_tx.to_cbor())
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/backend/blockfrost.py", line 161, in submit_tx
    self.api.transaction_submit(f.name)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/blockfrost/utils.py", line 63, in error_wrapper
    raise ApiError(request_response)
blockfrost.utils.ApiError: {'error': 'Bad Request', 'message': '"transaction read error RawCborDecodeError [DecoderErrorDeserialiseFailure \\"Byron Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding TxAux.\\\\nExpected 2, but found 4.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 79 \\"expected string\\")]"', 'status_code': 400}

Here's the script i'm running, slightly modified for local testing and to take existing payment, stake, and signing keys,


import pathlib
import sys
import json
from pycardano import *

if len(sys.argv) > 1:
    ### args = [address, data, blockfrostid]
    args = list(sys.argv[1:])
    sendaddress = args[0]
    tokenmetadata = json.loads(args[1])
    bf = args[2]

else:
    args = [
      "addr1qxvqw3xawj76533sf8dl4rkm9sn5t4vfzwgtc0mvmcjpmgee8dlsen8n464ucw69acfgdxgguscgfl5we3rwts4s57assqu5h5",
{}, "mainnetXXXXXXRdtWqh2SNVLNqfQbhlNmTbza",]
    # print("no args", sys.argv)
    sendaddress = args[0]
    tokenmetadata = args[1]
    bf = args[2]


# Copy your BlockFrost project ID below. Go to https://blockfrost.io/ for more information.
BLOCK_FROST_PROJECT_ID = bf
NETWORK = Network.MAINNET

chain_context = BlockFrostChainContext(
    project_id=BLOCK_FROST_PROJECT_ID, network=NETWORK
)

PROJECT_ROOT = "nft"
root = pathlib.Path(PROJECT_ROOT)
root.mkdir(parents=True, exist_ok=True)
key_dir = root / "keys"
key_dir.mkdir(exist_ok=True)

def load_or_create_key_pair(base_dir, base_name):
    skey_path = base_dir / f"{base_name}.skey"
    vkey_path = base_dir / f"{base_name}.vkey"

    if skey_path.exists():
        skey = PaymentSigningKey.load(str(skey_path))
        vkey = PaymentVerificationKey.from_signing_key(skey)
    else:
        key_pair = PaymentKeyPair.generate()
        key_pair.signing_key.save(str(skey_path))
        key_pair.verification_key.save(str(vkey_path))
        skey = key_pair.signing_key
        vkey = key_pair.verification_key
    return skey, vkey


pkey = PaymentVerificationKey.load("testpayment.vkey")
skey = PaymentSigningKey.load("testpayment.skey")
spkey = PaymentVerificationKey.load("teststake.vkey")

address = Address(
    payment_part=pkey.hash(), staking_part=spkey.hash(), network=Network.MAINNET
)

policy_skey, policy_vkey = load_or_create_key_pair(key_dir, "policy")
pub_key_policy = ScriptPubkey(policy_vkey.hash())
must_before_slot = InvalidHereAfter(chain_context.last_block_slot + 10000)
policy = ScriptAll([pub_key_policy, must_before_slot])
policy_id = policy.hash()
with open(root / "policy.id", "a+") as f:
    f.truncate(0)
    f.write(str(policy_id))

my_asset = Asset()
nft1 = AssetName(b"MY_NFT_1")
nft2 = AssetName(b"MY_NFT_2")
my_asset[nft1] = 1
my_asset[nft2] = 1
my_nft = MultiAsset()
my_nft[policy_id] = my_asset
native_scripts = [policy]

my_nft_alternative = MultiAsset.from_primitive(
    {
        policy_id.payload: { 
            b"MY_NFT_1": 1,  b"MY_NFT_2": 1, 
        }
    }
)

assert my_nft == my_nft_alternative

metadata = {
    721: {
        policy_id.payload.hex(): {
            "MY_NFT_1": {
                "description": "This is my first NFT thanks to PyCardano",
                "name": "PyCardano NFT example token 1",
                "id": 1,
                "image": "ipfs://QmRhTTbUrPYEw3mJGGhQqQST9k86v1DPBiTTWJGKDJsVFw",
            },
            "MY_NFT_2": {
                "description": "This is my second NFT thanks to PyCardano",
                "name": "PyCardano NFT example token 2",
                "id": 2,
                "image": "ipfs://QmRhTTbUrPYEw3mJGGhQqQST9k86v1DPBiTTWJGKDJsVFw",
            },
        }
    }
}
auxiliary_data = AuxiliaryData(AlonzoMetadata(metadata=Metadata(metadata)))

builder = TransactionBuilder(chain_context)
builder.add_input_address(address)
builder.ttl = must_before_slot.after
builder.mint = my_nft
builder.native_scripts = native_scripts
builder.auxiliary_data = auxiliary_data
min_val = min_lovelace(Value(0, my_nft), chain_context)
builder.add_output(TransactionOutput(sendaddress, Value(min_val, my_nft)))
signed_tx = builder.build_and_sign([skey, policy_skey], change_address=address)

print("############### Submitting transaction ###############")
chain_context.submit_tx(signed_tx.to_cbor())

The decoded CBOR

[{0: [[h'9322A59F6217108AC921633F273574CDE9CA216A5C4677396B643B37E41E1767', 0], [h'D316D93709E4333D971F948A4D8E6EEAA68343E6CB65A25A18D31B6975EDAF11', 0]], 1: [["addr1qxvqw3xawj76533sf8dl4rkm9sn5t4vfzwgtc0mvmcjpmgee8dlsen8n464ucw69acfgdxgguscgfl5we3rwts4s57assqu5h5", [1448244, {h'371EF437DEE7FFB7F3E9114D99C69EA6E32995D9AC1AB2A4A9B3DB41': {h'4D595F4E46545F31': 1, h'4D595F4E46545F32': 1}}]], [h'01D6B2022CA04D892444CAA9C5C650E29D15272B43083B8449394DD78F9BBABB874FFAE9114744EF5A90485EC955DA03145A58FD3ED2FBB2D5', 4912157]], 2: 201889, 3: 61914215, 7: h'F520A8A8AD15203B8834AE126CAF1D94EBB6B6BADF101DACC60B59BF7D016713', 9: {h'371EF437DEE7FFB7F3E9114D99C69EA6E32995D9AC1AB2A4A9B3DB41': {h'4D595F4E46545F31': 1, h'4D595F4E46545F32': 1}}}, {0: [[h'7823A205BD851586A623644C620010BFAAD6A9C4ADBEA06F1385012778EA36F0', h'B34D4200A0A1E99D246A34BAB8C87699381F1A3F3B64AA890F8CE1284E9400FC062B2BF56A34058662501AB0D6B18AB7F0B2FD2A4B0B4E5131148ACDC390CF0A'], [h'99ED574592B366C8BA57D149415B0A1BC98C6031D696921C4F23B2AE4359D616', h'3249FD069D1BF76198ECAE653246C14412CB6DE2467F7E502872656F6C6AD961DF183DF26029FC85274AE17465D499EC765874C6826BEE24B4FD3A6F792E5100']], 1: [[1, [[0, h'DA3C74EE098DEE1A8E7E3043315742CC7A484D8F8A299FEA4BD35716'], [5, 61914215]]]]}, true, 259({0: {721: {"371ef437dee7ffb7f3e9114d99c69ea6e32995d9ac1ab2a4a9b3db41": {"MY_NFT_1": {"description": "This is my first NFT thanks to PyCardano", "name": "PyCardano NFT example token 1", "id": 1, "image": "ipfs://QmRhTTbUrPYEw3mJGGhQqQST9k86v1DPBiTTWJGKDJsVFw"}, "MY_NFT_2": {"description": "This is my second NFT thanks to PyCardano", "name": "PyCardano NFT example token 2", "id": 2, "image": "ipfs://QmRhTTbUrPYEw3mJGGhQqQST9k86v1DPBiTTWJGKDJsVFw"}}}}})]

Can't get extendedpaymenykey from cbor

Describe the bug
I'm trying to get an extended payment key from it's string representation that was generated on the frontend. I was thinking the following code should work
extended_payment_skey = PaymentExtendedSigningKey.from_cbor(body.skey)

where body.skey is in the form ed25519bip32_sk1........... which is an extended key.

but I keep getting this error.


/site-packages/pycardano/serialization.py", line 316, in from_cbor
    payload = bytes.fromhex(payload)
ValueError: non-hexadecimal number found in fromhex() arg at position 8

Wrong variable used in the Plutus Smart Contract Tutorial

Describe the bug
in the last step of the Smart Contract Tutorial when we want to submit the transaction for the “taker”, the variable self.extended_payment_skey is used but not defined.

To Reproduce
follow the steps by the tutorial

Expected behavior
The signing key of the second key pair should be used to sign and submit the transaction: payment_skey_2.
I don't know where the variable self.extended_payment_skey should come from.

Environment and software version (please complete the following information):
shouldn't be relevant

  • OS: MacOS 13.3.1
  • PyCardano Version 0.8.0

Additional context
Currently, learning opshin & pycardano, so I maybe I could have misunderstood something

Error: input UTxOs cannot cover the transaction outputs and tx fee

First of all, thank you for this awesome library! You guys rock!

I am trying to mint some NFT and the example is failing for me. I have to believe its a bug and not a user error since I haven't altered any of the code.

Describe the bug
The example for native token minting is failing with an error due to mismatching ADA amounts in the change output and the fee.

Input: 1000 ADA
Change: 998.355763 ADA
Fee: 2.520477 ADA

Fee + Change = 1000.87624 ADA

To Reproduce
Run the original example script, top up the generated payment address using the faucet and run again
https://github.com/cffls/pycardano/blob/main/examples/native_token.py

Logs

Traceback (most recent call last):
  File "/Users/robin/projects/../mint_example.py", line 162, in <module>
    signed_tx = builder.build_and_sign([payment_skey, policy_skey], change_address=address)
  File "/usr/local/lib/python3.9/site-packages/pycardano/txbuilder.py", line 691, in build_and_sign
    tx_body = self.build(change_address=change_address)
  File "/usr/local/lib/python3.9/site-packages/pycardano/txbuilder.py", line 667, in build
    self._add_change_and_fee(change_address)
  File "/usr/local/lib/python3.9/site-packages/pycardano/txbuilder.py", line 351, in _add_change_and_fee
    changes = self._calc_change(
  File "/usr/local/lib/python3.9/site-packages/pycardano/txbuilder.py", line 286, in _calc_change
    raise InvalidTransactionException(
pycardano.exception.InvalidTransactionException: The input UTxOs cannot cover the transaction outputs and tx fee.
Inputs: [{'input': {'index': 0,
 'transaction_id': TransactionId(hex='009c527e...9b2e1')},
 'output': {'address': addr_test1vz...,
 'amount': 1000000000,
 'datum_hash': None}}]
Outputs: [{'address': addr_test1vz...,
 'amount': {'coin': 1448244,
 'multi_asset': {ScriptHash(hex='90ddf27...981c8'): {AssetName(b'MY_NFT_1'): 1, AssetName(b'MY_NFT_2'): 1}}},
 'datum_hash': None}, {'address': addr_test1vz...,
 'amount': 998355763,
 'datum_hash': None}]
fee: 2520477

Expected behavior
The Change/Fee calculation should match the Input and the Tx should be valid

Environment and software version (please complete the following information):

  • OS: Mac OS 10.15
  • PyCardano Version 0.3.1

Thanks!

How to get a pending tx id

Hi @cffls, i'm able to run the example to create and send a tx via blockfrost but I'd like the txid to be returned from context submit_tx. I naively tried tx = context.submit_tx(signed_tx.to_cbor()) but returns None

I made a pr assuming it's that simple but didn't test. please advise if there's a way already.

Include API responses in submit_tx method

Is your feature request related to a problem? Please describe.
Yes, When a transaction is submitted, If there's an error in any part of it, the response from APIs will be helpful in decoding and re-submitting.

Describe the solution you'd like
Modify the submit_tx method to capture the API response and include it in the return value. Here's an example of how this could be done:

from blockfrost import ApiError

def submit_tx(self, cbor: Union[bytes, str]) -> dict:
    if isinstance(cbor, str):
        cbor = bytes.fromhex(cbor)
    with tempfile.NamedTemporaryFile(delete=False) as f:
        f.write(cbor)
    try:
        response: dict = self.api.tx_submit(f.name)
    except ApiError as e:
        os.remove(f.name)
        raise ValueError(f"Failed to submit transaction. Error code: {e.status_code}. Error message: {e.message}")
    os.remove(f.name)
    return {
        'response': response,
    }

Describe alternatives you've considered
None

Additional context
None

config option to choose local cardano-node for transactions

Currently you can change the Network setting from Network.TESTNET to Network.MAINNET.

Firstly, it's not clear which "Testnet" version this if referring to. It looks like its the "legacy" (broken) testnet still being run by IOG but could (should) be the PREVIEW and/or the PREPROD network. It would be nice if you choose explicitely.

Secondly, it would be nice not to rely on a third-party API like Blockfrost for chain data but instead use your own copy from your cardano-node db. Can we add a config option to PyCardano to use a local node for its read/write transactions?

Verification and Signing Keys Bug

Greetings!

I'm trying to sign a transaction with keys I got from an HDWallet. I need to sign it with stake and spend (payment) keys. So I get vkeys like (wallet is HDWallet object):
Stake:

hdwallet_stake = wallet.derive_from_path("m/1852'/1815'/0'/2/0")
stake_public_key = hdwallet_stake.public_key
stake_vk = StakeVerificationKey.from_primitive(stake_public_key)

Spend:

hdwallet_spend = wallet.derive_from_path("m/1852'/1815'/0'/0/0")
spend_public_key = hdwallet_spend.public_key
spend_vk = PaymentVerificationKey.from_primitive(spend_public_key)

The vkeys I got above are exactly the ones I need (their hashes are the same hashes that can be found in the RequiredSigners of a transaction)

After that I get signing keys:
spend_sk = SigningKey.from_primitive(spend_public_key)
stake_sk = SigningKey.from_primitive(stake_public_key)

Signing transaction with this set of keys results in InvalidWitnessUTXOW error on submit.

I tried to get back vkeys from skeys to compare it:

spend_vk_2 = PaymentVerificationKey.from_signing_key(spend_sk)
stake_vk_2 = StakeVerificationKey.from_signing_key(stake_sk)

spend_vk and spend_vk_2 as well as stake_vk and stake_vk_2 hashes are completely different.

I've tried to change Signing key to extended version or to StakeSigningKey/PaymentSigningKey and to me it didn't make any changes.

I would also like to get in touch with Jerry, if it's possible, to ask a few questions about cardano and this library as well as kindly ask for a helping hand with one of my projects.

I can give all the needed information.

Thanks in advance!

Querying UTxOs from an address never used before raises an exception

If I create a new address and don't send any tokens to it, I would expect to get an empty list as a result if I used chain_context.utxos(address). Currently, however, it raises the following exception:

      |   File "/usr/local/lib/python3.9/site-packages/blockfrost/utils.py", line 83, in recursive_append
      |     raise ApiError(request_response)
      | blockfrost.utils.ApiError: {'status_code': 404, 'error': 'Not Found', 'message': 'The requested component has not been found.'}

This is a snippet if you want to reproduce the error,

import pycardano as pyc

skey = pyc.PaymentSigningKey.generate()
vkey = pyc.PaymentVerificationKey.from_signing_key(skey)

address = pyc.Address(payment_part=vkey.hash(), network=pyc.Network.TESNET)

chain_context = pyc.BlockfrostChainContext(...)

print(chain_context.utxos(str(address)))

This will give us the exception written above, but I would expect to get an empty list instead.

Blockfrost is not reliable

A lot of development time has been lost or misappropriated due to inaccuracies from blockfrost. Can we connect with another service?

Raw tx: how to configure change address with exact calculation

Hi Jer, while waiting to see about the other issue, I've begun creation of a raw tx to send tokens. My script is building fine but I'm getting a ValueNotConservedUTxO error for not including a change address with the correct calculation. I assume that as long as the outputs are equal to the outputs + fee we're good to go but unsure how to calc and what should be done about other assets from the same tx but not going to the recipient. Can you give a bit of guidance this please? I don't see this in the raw examples for token or tx.

Many thanks!

Improve Efficiency of Kupo UTxO Query by Directly Querying Unspent UTxOs

Is your feature request related to a problem? Please describe.
Yes, the current implementation of the _utxos_kupo method in the library is inefficient, as it retrieves all UTxOs and then filters for unspent ones. This can be slow and resource-intensive, especially when working with a large number of UTxOs.

Describe the solution you'd like
I propose updating the _utxos_kupo method to directly query unspent UTxOs using the ?unspent flag in the endpoint. This would allow the library to retrieve only the unspent UTxOs, improving the efficiency and performance of the query.

Describe alternatives you've considered
NA

Additional context
NA

Allow str addresses as change address in txbuilder

Is your feature request related to a problem? Please describe.
A lot of users put a Bech32 encoded address as change address in the txbuilder, which seems natural as a user flow. The returned error message is usually quite obscure.

Describe the solution you'd like
For this reason, the txbuilder should check and optimally directly transform a passed-in str object to a PyCardano address object to reduce friction.

Describe alternatives you've considered
The txbuilder could check that it is passed an address object and raise an error otherwise. The process to transform a string to address is very automatizable though, so it should directly apply this.

Additional context
CC @while0x1

minting, .cbor() error after update

Hi Jer, I just upgraded to 0.6 and tested. I'm getting a new error around the minvalue function. Here's the traceback:


PythonShellError: AttributeError: 'BlockFrostChainContext' object has no attribute 'to_cbor'
0|crypto | at PythonShell.parseError (/Users/34r7h/a/cardano-python-js/node_modules/python-shell/index.js:295:21)
0|crypto | at terminateIfNeeded (/Users/34r7h/a/cardano-python-js/node_modules/python-shell/index.js:190:32)
0|crypto | at ChildProcess. (/Users/34r7h/a/cardano-python-js/node_modules/python-shell/index.js:182:13)
0|crypto | at ChildProcess.emit (events.js:314:20)
0|crypto | at Process.ChildProcess._handle.onexit (internal/child_process.js:276:12)
0|crypto | ----- Python Traceback -----
0|crypto | File "/Users/34r7h/a/cardano-python-js/python/mint.py", line 235, in
0|crypto | min_val = min_lovelace(Value(0, my_nft), chain_context)
0|crypto | File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/utils.py", line 119, in min_lovelace
0|crypto | return min_lovelace_post_alonzo(output, context)
0|crypto | File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/utils.py", line 166, in min_lovelace_post_alonzo
0|crypto | constant_overhead + len(output.to_cbor("bytes")) {
0|crypto | traceback: 'Traceback (most recent call last):\n' +
0|crypto | ' File "/Users/34r7h/a/cardano-python-js/python/mint.py", line 235, in \n' +
0|crypto | ' min_val = min_lovelace(Value(0, my_nft), chain_context)\n' +
0|crypto | ' File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/utils.py", line 119, in min_lovelace\n' +
0|crypto | ' return min_lovelace_post_alonzo(output, context)\n' +
0|crypto | ' File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/utils.py", line 166, in min_lovelace_post_alonzo\n' +
0|crypto | ' constant_overhead + len(output.to_cbor("bytes"))\n' +
0|crypto | "AttributeError: 'BlockFrostChainContext' object has no attribute 'to_cbor'\n",


Error sending simple Tx of tADA

Describe the bug
I'm trying to send some tADA on PreProd context but I got this error. If I try to send the same CBOR signed Tx using curl and Blockfrost API I get the same error.

To Reproduce
My code (it is a simple example test):


NETWORK = Network.TESTNET

context = BlockFrostChainContext(
    project_id="preproXXXXXXXXXXXXXXXXXXXXXX",
    network=NETWORK,
    base_url="https://cardano-preprod.blockfrost.io/api",
)

# Read keys to memory
# Assume there is a payment.skey file sitting in current directory
psk = PaymentSigningKey.load("payment.skey")
pvk = PaymentVerificationKey.from_signing_key(psk)

# Wallet in the node (sender)
wallet = "addr_test1vrppcwynx539fvwpppXXXXXXXXXXXXXXXXX"

# Create a transaction builder
builder = TransactionBuilder(context)

# Tell the builder that transaction input will come from a specific address, assuming that there are some ADA and native
builder.add_input_address(wallet)

# Get all UTxOs currently sitting at this address
utxos = context.utxos(wallet)

builder.add_input(utxos[0])

# Nami Wallet
nami ="addr_test1qr6uhu9ru5v0XXXXXXXXXXXXXXXX"

# Send 1.5 ADA to a Nami address.
builder.add_output( TransactionOutput.from_primitive([nami, 1500000]))

# Create final signed transaction
signed_tx = builder.build_and_sign([psk], change_address=billetera)

print("############### Transaction created ###############")
print(f"Tx: {signed_tx}")
print(f"Signed Tx: {signed_tx.to_cbor()}")

# Submit signed transaction to the network
print("############### Submitting transaction ###############")
context.submit_tx(signed_tx.to_cbor())

Error


blockfrost.utils.ApiError: {'error': 'Bad Request', 'message': '"transaction read error RawCborDecodeError [DecoderErrorDeserialiseFailure \\"Byron Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding TxAux.\\\\nExpected 2, but found 4.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 1 \\"Size mismatch when decoding \\\\nRecord RecD.\\\\nExpected 4, but found 3.\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 108 \\"expected string\\"),DecoderErrorDeserialiseFailure \\"Shelley Tx\\" (DeserialiseFailure 108 \\"expected string\\")]"', 'status_code': 400}

Environment and software version (please complete the following information):

  • OS: [Ubuntu 20.04.3 LTS]
  • PyCardano Version [0.6.2]

TransactionBuilder doesn't work if there's no index 0 utxo input

Hello there, hope all is smooth.

I'm running into an issue with the builder.. seems I need > 1 utxo in order to send ada to an address.

Successful tx has utxo data like this:

[{'input': {'index': 1,
 'transaction_id': TransactionId(hex='41cb004bec7051621b19b46aea28f0657a586a05ce2013152ea9b9f1a5614cc7')},
 'output': {'address': addr1qytqt3v9ej3kzefxcy8f59h9atf2knracnj5snkgtaea6p4r8g3mu652945v3gldw7v88dn5lrfudx0un540ak9qt2kqhfjl0d,
 'amount': 2991353,
 'datum_hash': None}}, {'input': {'index': 0,
 'transaction_id': TransactionId(hex='ed2d5e7738f12dfbf988b8f634812b26dd805e53fa633c0d4d2d8df6e2a74596')},
 'output': {'address': addr1qytqt3v9ej3kzefxcy8f59h9atf2knracnj5snkgtaea6p4r8g3mu652945v3gldw7v88dn5lrfudx0un540ak9qt2kqhfjl0d,
 'amount': 1000000,
 'datum_hash': None}}]

Unsuccessful utxo data looks like this:

[{'input': {'index': 1,
 'transaction_id': TransactionId(hex='9d255cdacd8a575ee86f4ad0a61b14c7be037c623059f71b1bc9ce8d4e53cb6c')},
 'output': {'address': addr1qytqt3v9ej3kzefxcy8f59h9atf2knracnj5snkgtaea6p4r8g3mu652945v3gldw7v88dn5lrfudx0un540ak9qt2kqhfjl0d,
 'amount': 2821804,
 'datum_hash': None}}]

So, I suspect the problem comes when there's no index[0] in the list.. and I get this UTxOSelectionException error:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/txbuilder.py", line 658, in build
    selected, _ = selector.select(
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/coinselection.py", line 109, in select
    additional, _ = self.select(
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/coinselection.py", line 94, in select
    raise InsufficientUTxOBalanceException("UTxO Balance insufficient!")
pycardano.exception.InsufficientUTxOBalanceException: UTxO Balance insufficient!

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/zzzz/a/carpy-js/python/createtx.py", line 30, in <module>
    signed_tx = builder.build_and_sign([sk], change_address=address)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/txbuilder.py", line 767, in build_and_sign
    tx_body = self.build(change_address=change_address)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pycardano/txbuilder.py", line 690, in build
    raise UTxOSelectionException(
pycardano.exception.UTxOSelectionException: All UTxO selectors failed.
Requested output:
 {'coin': 1158901, 'multi_asset': {}} 
Pre-selected inputs:
 {'coin': 0, 'multi_asset': {}} 
Additional UTxO pool:
 [{'input': {'index': 1,
 'transaction_id': TransactionId(hex='41cb004bec7051621b19b46aea28f0657a586a05ce2013152ea9b9f1a5614cc7')},
 'output': {'address': addr1qytqt3v9ej3kzefxcy8f59h9atf2knracnj5snkgtaea6p4r8g3mu652945v3gldw7v88dn5lrfudx0un540ak9qt2kqhfjl0d,
 'amount': 2991353,
 'datum_hash': None}}] 
Unfulfilled amount:
 {'coin': -1832452, 'multi_asset': {}}

My script is essentially the same as your example:

from pycardano import BlockFrostChainContext, Network, PaymentSigningKey, PaymentVerificationKey, Address, TransactionBuilder, TransactionOutput, Value

network = Network.MAINNET
context = BlockFrostChainContext("mainnetqEZ4wDDoRdtWqh2SNVLNqfQbhlNmTbza", network)

sk = PaymentSigningKey.from_cbor('abcdef0123456789')
vk = PaymentVerificationKey.from_signing_key(sk)
address = Address.from_primitive('addr1qytqt3v9ej3kzefxcy8f59h9atf2knracnj5snkgtaea6p4r8g3mu652945v3gldw7v88dn5lrfudx0un540ak9qt2kqhfjl0d')

builder = TransactionBuilder(context)
builder.add_input_address(address)
utxos = context.utxos(str(address))

# builder.add_input(utxos[0])
builder.add_output(
    TransactionOutput(
        Address.from_primitive(
"addr1qyady0evsaxqsfmz0z8rvmq62fmuas5w8n4m8z6qcm4wrt3e8dlsen8n464ucw69acfgdxgguscgfl5we3rwts4s57ashysyee"
        ),
        Value.from_primitive(
            [
                1000000,
            ]
        ),
    )
)
signed_tx = builder.build_and_sign([sk], change_address=address)
context.submit_tx(signed_tx.to_cbor())

Specification of the Redeemer Tag necessary?

The Redeemer is currently annotated with a tag (SPEND, MINT, etc)

I am wondering if this is necessary? Should this tag not be automatically derived? When building transactions with the CLI, you never specify the tag. I would assume that the tag can be derived automatically depending on the position of its occurrence.

If you agree, I will open a PR that removes the need to specify this tag, reducing the friction for the TxBuilder.

Which Blockfrost network to use?

I'm getting 404 errors from the Blockfrost API when trying to implement the Transaction builder example code.

Can you please clarify which network my Blockfrost project should be using to sync with the pyCardano network.TESTNET option? There's a choice between Testnet, Preprod, and Preview
Screen Shot 2022-10-27 at 1 27 27 PM

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.