pycardano: 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 <module> 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)

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16

Most upvoted comments

Thanks @cffls for your help! 😃