1

i am trying to create a staking transaction using cardano serialisation library and then use blockfrost/infinito to send the transaction. I am using @emurgo/cardano-serialization-lib-nodejs: ^9.1.4,

Here's my code :

    createStakeTx(inputs, ttl, poolID, isFirstStake) {
        try {
            // let rootKey = Cardano.Bip32PrivateKey.from_bytes(Buffer.from(this.masterKey, 'hex'));
            console.log(
                'inside create stake tx ::::::::::::::'
            )
            console.log('inputs :', inputs); 
            console.log('ttl :', ttl);
            console.log('poolId :', poolID);
            console.log('isFirstStake :', isFirstStake);

            // this.masterKey = Cardano.Bip32PrivateKey.from_bip39_entropy(Buffer.from(entropy, 'hex'), Buffer.from(''))
            let rootKey = this.masterKey 
            let spend = rootKey
                .derive(harden(1852))
                .derive(harden(1815))
                .derive(harden(0))
                .derive(this.accountKey) // 1
                .derive(this.addressKey); // 34
            let spendPubKey = spend.to_public();

            let stake = rootKey
                .derive(harden(1852))
                .derive(harden(1815))
                .derive(harden(0))
                .derive(2)
                .derive(0);
            let stakePubkey = stake.to_public();
            let ed = stake.to_public().to_raw_key().hash();
            // console.log('ed :', ed);
            let spendCred = Cardano.StakeCredential.from_keyhash(spend.to_public().to_raw_key().hash());
            let stakeCred = Cardano.StakeCredential.from_keyhash(stake.to_public().to_raw_key().hash());

            // testnet: 0, mainnet: 1
            let address = Cardano.BaseAddress.new(1, spendCred, stakeCred).to_address().to_bech32();

            let linearFee = Cardano.LinearFee.new(
                Cardano.BigNum.from_str('44'),
                Cardano.BigNum.from_str('155381')
            );
            let txBuilder = Cardano.TransactionBuilder.new(
                linearFee,
                Cardano.BigNum.from_str('1000000'),
                Cardano.BigNum.from_str('500000000'),
                Cardano.BigNum.from_str('2000000'),
                16384,
                5000,
            );

            let sumInputs = bigNumber(0);
            inputs.forEach(element => {
                txBuilder.add_key_input(
                    spendPubKey.to_raw_key().hash(),
                    Cardano.TransactionInput.new(
                        Cardano.TransactionHash.from_bytes(Buffer.from(element.tx_id, 'hex')),
                        element.vout
                    ),
                    Cardano.Value.new(Cardano.BigNum.from_str(element.amount))
                );
                sumInputs = sumInputs.plus(element.amount);
            });

            txBuilder.set_ttl(ttl + 2000);

            let poolKeyHash = Cardano.Ed25519KeyHash.from_bytes(Buffer.from(poolID, 'hex'));
            let certs = Cardano.Certificates.new();

            if (isFirstStake == true) {
                certs.add(
                    Cardano.Certificate.new_stake_registration(
                        Cardano.StakeRegistration.new(
                            stakeCred
                        )
                    )
                );                
            }

            certs.add(
                Cardano.Certificate.new_stake_delegation(
                    Cardano.StakeDelegation.new(
                        stakeCred,
                        poolKeyHash
                    )
                )
            );

            txBuilder.set_certs(certs);

            let estimate = this.estimateStakeTxFee(inputs, poolID, isFirstStake);
            txBuilder.set_fee(Cardano.BigNum.from_str(bigNumber(estimate.fee).toFixed()));

            let changeAmount = bigNumber(0);
            console.log('sumInputs :', sumInputs)
            if (isFirstStake == true) {
                changeAmount = sumInputs.minus(KEY_DEPOSIT).minus(estimate.fee);
            } else {
                changeAmount = sumInputs.minus(estimate.fee);
            }

            console.log('changeAmount :', changeAmount)
            txBuilder.add_output(
                Cardano.TransactionOutput.new(
                    Cardano.Address.from_bech32(address),
                    Cardano.Value.new(Cardano.BigNum.from_str(changeAmount.toFixed()))
                )
            );

            let txBody = txBuilder.build();

            const txHash = Cardano.hash_transaction(txBody);

            let witnesses = Cardano.TransactionWitnessSet.new();
            let vkeyWitnesses = Cardano.Vkeywitnesses.new();
            vkeyWitnesses.add(Cardano.make_vkey_witness(
                txHash,
                spend.to_raw_key(),
            ));

            vkeyWitnesses.add(Cardano.make_vkey_witness(
                 txHash,
                 stake.to_raw_key(),
            ));

            witnesses.set_vkeys(vkeyWitnesses);

            let tx = Cardano.Transaction.new(
                txBody,
                witnesses,
            );
            console.log('rawtx ::', Buffer.from(tx.to_bytes()).toString('hex'))
            const res = await client.txSubmit(Buffer.from(tx.to_bytes()).toString('hex'));
            return res;
        } catch (err) {
            console.log('err: ', err);
            return null;
        }
    }    

// Error when i use blockfrost :

transaction submit error ShelleyTxValidationError ShelleyBasedEraAlonzo (ApplyTxError [UtxowFailure (WrappedShelleyEraFailure (MissingVKeyWitnessesUTXOW (WitHashes (fromList [KeyHash "some hash"])))),DelegsFailure (DelplFailure (DelegFailure (StakeDelegationImpossibleDELEG (KeyHashObj (KeyHash "somehash")))))])

//Error when i use infinito : {"type":"jsonwsp/fault","version":"1.0","servicename":"ogmios","fault":{"code":"client","string":"Invalid request: DeserialiseFailure 539 "Size mismatch when decoding \nRecord RecD.\nExpected 4, but found 3."."},"reflection":{"requestId":"XEpib"}} is an unknown result

Maybe i am just creating the transaction in a wrong way or my keys might be messed up.

Open to any help and suggestions

Thanks

2 Answers 2

1

The error message "MissingVKeyWitnessesUTXOW" means you need to sign with the right key. For delegation transaction, you need to sign with both your stake.skey and payment.skey.

2
  • It might be helpful to future readers if I clarify that contracts have types. You must specify that its a staking one or it will default to something else.
    – KryptoKing
    Mar 2, 2022 at 17:46
  • Hi @kawan, I am a little new to this isn't this exactly what i am doing here : ```` javascript let spendCred = Cardano.StakeCredential.from_keyhash(spend.to_public().to_raw_key().hash()); let stakeCred = Cardano.StakeCredential.from_keyhash(stake.to_public().to_raw_key().hash()); vkeyWitnesses.add(Cardano.make_vkey_witness( txHash, spend.to_raw_key(), )); vkeyWitnesses.add(Cardano.make_vkey_witness( txHash, stake.to_raw_key(), )); ```` Mar 4, 2022 at 7:48
0

This is the code I use to submit a staking transaction:

export const createDelegationCertificate = (
  mnemonic
) => {

  const entropy = mnemonicToEntropy(mnemonic);

  const rootKey = CardanoWasm.Bip32PrivateKey.from_bip39_entropy(
    Buffer.from(entropy, 'hex'),
    Buffer.from(''),
  );

  const accountKey = rootKey
    .derive(harden(1852)) // purpose
    .derive(harden(1815)) // coin type
    .derive(harden(0)); // account #0

  const utxoPubKey = accountKey
    .derive(0) // external
    .derive(0)
    .to_public();

  const stakeKey = accountKey
    .derive(2) // chimeric
    .derive(0)
    .to_public();

  const operatorKeyHash = utxoPubKey.to_raw_key().hash().to_bech32('ed25519_pk');
  const poolKeyHash = CardanoWasm.Ed25519KeyHash.from_bech32(operatorKeyHash);

  const stakeCredential = CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash());
  
  const stakeDelegation = CardanoWasm.StakeDelegation.new(stakeCredential, poolKeyHash);

  // Generate Stake Certificate
  const delegationCertificate = CardanoWasm.Certificate.new_stake_delegation(stakeDelegation)

  return delegationCertificate;

}

After that, create the transaction using the delegation certificate from the previous step:

export const submitDelegationTransaction = async (
  mnemonic, 
  delegationCertificate
) => {

  const entropy = mnemonicToEntropy(mnemonic);

  const rootKey = CardanoWasm.Bip32PrivateKey.from_bip39_entropy(
    Buffer.from(entropy, 'hex'),
    Buffer.from(''),
  );
  
  const accountKey = rootKey
    .derive(harden(1852)) // purpose
    .derive(harden(1815)) // coin type
    .derive(harden(0)); // account #0

  const utxoPubKey = accountKey
    .derive(0) // external
    .derive(0)
    .to_public();

  const stakeKey = accountKey
    .derive(2) // chimeric
    .derive(0)
    .to_public();

  const baseAddr = CardanoWasm.BaseAddress.new(
    CardanoWasm.NetworkInfo.testnet().network_id(),
    CardanoWasm.StakeCredential.from_keyhash(utxoPubKey.to_raw_key().hash()),
    CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash()),
  );

  const privateKey = deriveSpendPrivateKey(accountKey, 0).to_raw_key();
  const stakingPrivateKey = deriveStakePrivateKey(accountKey, 0).to_raw_key();
  const fromAddress = baseAddr.to_address().to_bech32("addr_test");

  const params = await getParameters();

  const linearFee = CardanoWasm.LinearFee.new(
    CardanoWasm.BigNum.from_str(params.data.genesis.shelley.protocolParams.minFeeA.toString()),
    CardanoWasm.BigNum.from_str(params.data.genesis.shelley.protocolParams.minFeeB.toString())
  );
  
  const txBuilderConfig = CardanoWasm.TransactionBuilderConfigBuilder.new()
    .fee_algo(linearFee)
    .pool_deposit(CardanoWasm.BigNum.from_str(params.data.genesis.shelley.protocolParams.poolDeposit.toString()))
    .key_deposit(CardanoWasm.BigNum.from_str(params.data.genesis.shelley.protocolParams.keyDeposit.toString()))
    .max_value_size(params.data.genesis.alonzo.maxValueSize)
    .max_tx_size(params.data.genesis.shelley.protocolParams.maxTxSize)
    .coins_per_utxo_word(CardanoWasm.BigNum.from_str(params.data.genesis.alonzo.lovelacePerUTxOWord.toString()))
    .build();

  // instantiate the tx builder with the Cardano protocol parameters - these may change later on
  const txBuilder = CardanoWasm.TransactionBuilder.new(txBuilderConfig);

  const unspentOutputs = await getTxUnspentOutputs(fromAddress);
   
  // pointer address
  const shelleyChangeAddress = CardanoWasm.Address.from_bech32(fromAddress);

  // set the time to live - the absolute slot value before the tx becomes invalid
  const slot = await getSlotNo();
  txBuilder.set_ttl(slot.data.cardano.tip.slotNo + 10000);

  const certificates = CardanoWasm.Certificates.new();
  certificates.add(delegationCertificate);
  txBuilder.set_certs(certificates);

  // add a keyhash input - for ADA held in a Shelley-era normal address (Base, Enterprise, Pointer)
  txBuilder.add_inputs_from(unspentOutputs, 2); // LargestFirstMultiAsset

  // calculate the min fee required and send any change to an address
  txBuilder.add_change_if_needed(shelleyChangeAddress)

  // once the transaction is ready, we build it to get the tx body without witnesses
  const txBody = txBuilder.build()
  const txHash = CardanoWasm.hash_transaction(txBody);
  const witnesses = CardanoWasm.TransactionWitnessSet.new();
  const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new();
  const vkeyWitness1 = CardanoWasm.make_vkey_witness(txHash, privateKey);
  const vkeyWitness2 = CardanoWasm.make_vkey_witness(txHash, stakingPrivateKey);

  vkeyWitnesses.add(vkeyWitness1);
  vkeyWitnesses.add(vkeyWitness2);

  witnesses.set_vkeys(vkeyWitnesses);
  witnesses.set_native_scripts(CardanoWasm.NativeScripts.new());
  
  // create the finalized transaction with witnesses
  const transaction = CardanoWasm.Transaction.new(
    txBody,
    witnesses,
    undefined, // transaction metadata
  );

  return toHexString(transaction.to_bytes());

}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.