<p>This is the code to build a transaction to register a Stake Pool using <code>cardano-serialization-lib</code>:</p>
<pre><code>export const submitRegistrationTransaction = async (
  mnemonic, 
  poolCertificate
) =&gt; {

  //const privateKey = await getPrivateKeyFromEntropy(entropy);
  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 stakeAddr = CardanoWasm.RewardAddress.new(
    CardanoWasm.NetworkInfo.testnet().network_id(),
    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(&quot;addr_test&quot;);

  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(poolCertificate);
  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);

  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());

}
</code></pre>
<p>This is how you create the stake pool certificate:</p>
<pre><code>export const createStakePoolCertificate = (
  mnemonic, 
  stakePoolName, 
  relayIP, 
  relayPort, 
  pledge, 
  cost,
  poolMarginNumerator,
  poolMarginDenominator
) =&gt; {

  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 stakeAddr = CardanoWasm.RewardAddress.new(
    CardanoWasm.NetworkInfo.testnet().network_id(),
    CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash())
  )

  const privateKey = deriveSpendPrivateKey(accountKey, 0).to_raw_key();

  // Declare variables for pool params
  const operatorKeyHash = utxoPubKey.to_raw_key().hash().to_bech32('ed25519_pk');

  const nodeKeyGenKES = cardanocliJs.nodeKeyGenKES(stakePoolName);
  const nodeKeyGen = cardanocliJs.nodeKeyGen(stakePoolName);
  const nodeOperationalCert = cardanocliJs.nodeIssueOpCert(stakePoolName);
  const vrfKeyAccount = cardanocliJs.nodeKeyGenVRF(stakePoolName);

  //const kesKeyFile = readFileSync(nodeKeyGenKES.vkey, 'utf-8');
  //console.log(CardanoWasm.KESSignature.from_bytes(Buffer.from(JSON.parse(kesKeyFile).cborHex), &quot;hex&quot;));
  const vrfKeyFile = readFileSync(vrfKeyAccount.vkey, 'utf-8');
  //const vrfKeyHash = CardanoWasm.VRFKeyHash.from_hex(JSON.parse(vrfKeyFile).cborHex.substring(4));
  const vrfKeyHash = CardanoWasm.VRFKeyHash.from_bytes(fromHex(JSON.parse(vrfKeyFile).cborHex.substring(4)));
   
  const poolMargin = CardanoWasm.UnitInterval.new(
    CardanoWasm.BigNum.from_str(poolMarginNumerator), // numerator
    CardanoWasm.BigNum.from_str(poolMarginDenominator)  // denominator
  );

  const poolOwner = CardanoWasm.Ed25519KeyHashes.new();
  poolOwner.add(CardanoWasm.Ed25519KeyHash.from_bech32(operatorKeyHash));

  const relays = CardanoWasm.Relays.new();
  const relay = CardanoWasm.Relay.new_single_host_addr(
    CardanoWasm.SingleHostAddr.new(
      relayPort,
      CardanoWasm.Ipv4.new(new Uint8Array(relayIP.split('.').map((segment) =&gt; Number.parseInt(segment, 10))))
    )
  );
  relays.add(relay);

  // Prepare pool params
  const poolParams = CardanoWasm.PoolParams.new(
    CardanoWasm.Ed25519KeyHash.from_bech32(operatorKeyHash),
    vrfKeyHash,
    CardanoWasm.BigNum.from_str(pledge),
    CardanoWasm.BigNum.from_str(cost),
    poolMargin,
    stakeAddr,
    poolOwner,
    relays
  );

  // Create Pool Registration
  const poolRegistration = CardanoWasm.PoolRegistration.new(poolParams);
  console.log(poolRegistration);
  // Generate Pool Certificate
  const poolCertificate = CardanoWasm.Certificate.new_pool_registration(poolRegistration)
  return poolCertificate;
}
</code></pre>

CC BY-SA 4.0
2023-01-27T00:23:12.000Z
2023-01-27T00:23:12.000Z
Falcon Stakepool
<p>I have built the following transaction to register a stake pool:</p>
<pre><code>{
  &quot;txHash&quot;: &quot;0f874a3cb0ba57ed68a79601781eb9e29666f7f2f0e72ab4ed1f3cc9fcea7dee&quot;,
  &quot;ttl&quot;: 60310548,
  &quot;txSize&quot;: 386,
  &quot;inputs&quot;: [
    {
      &quot;txHash&quot;: &quot;ca0bca365de069364e4c74b818d8a1c4faebbb095e05635fbab5b1033ede3270&quot;,
      &quot;index&quot;: 0
    }
  ],
  &quot;outputs&quot;: [
    {
      &quot;outputIndex&quot;: 0,
      &quot;address&quot;: &quot;addr_test1qpypxf9jma2lkmzwn6nhypmynqpmus7x0ekwp2jfdrxqgm8vecdcssszlmywlyn7h0nkqp9jyk8p7dhjecglhu9m75xq4mwl9v&quot;,
      &quot;policyId&quot;: &quot;ada&quot;,
      &quot;assetName&quot;: &quot;ada&quot;,
      &quot;quantity&quot;: &quot;499827547&quot;
    }
  ],
  &quot;metadata&quot;: {},
  &quot;outputTotals&quot;: [
    {
      &quot;policyId&quot;: &quot;ada&quot;,
      &quot;assetName&quot;: &quot;ada&quot;,
      &quot;quantity&quot;: &quot;499827547&quot;
    }
  ],
  &quot;fee&quot;: &quot;172453&quot;,
  &quot;outputAda&quot;: &quot;499827547&quot;,
  &quot;inputTotalEstimation&quot;: &quot;500000000&quot;
}
</code></pre>
<p>The transaction has the stake pool certificate attached to it, that's why it consumes an additional 500 ADA for the deposit.</p>
<p>The problem I have is I'm getting the following error when trying to submit it to the blockchain:</p>
<pre><code>The sum of inputs, outputs and fee is not zero. (ValueNotConservedError)

    [
      {
        &quot;valueNotConserved&quot;: {
          &quot;consumed&quot;: {
            &quot;coins&quot;: 1000000000,
            &quot;assets&quot;: {}
          },
          &quot;produced&quot;: {
            &quot;coins&quot;: 500000000,
            &quot;assets&quot;: {}
          }
        }
      }
    ]
</code></pre>
<p>The input UTxO has a value of 1000 ADA, which is more than enough to cover the stake pool deposit fee. And, in addition to this, if I do...</p>
<pre><code>output + fee + deposit = 499827547 + 172453 + 500000000 = 1000000000
</code></pre>
<p>...I get the value of the input of the transaction (so nothing's being lost in theory).</p>
<p>What am I doing wrong?</p>

<stake-pool><cardano-serialization-lib><utxo><tx>
How can I build a transaction to register a stake pool using Cardano Serialization Lib?
# How can I build a transaction to register a stake pool using Cardano Serialization Lib?

(Asked by: username_0 on 2022-06-08)

I have built the following transaction to register a stake pool:
{
  "txHash": "0f874a3cb0ba57ed68a79601781eb9e29666f7f2f0e72ab4ed1f3cc9fcea7dee",
  "ttl": 60310548,
  "txSize": 386,
  "inputs": [
    {
      "txHash": "ca0bca365de069364e4c74b818d8a1c4faebbb095e05635fbab5b1033ede3270",
      "index": 0
    }
  ],
  "outputs": [
    {
      "outputIndex": 0,
      "address": "addr_test1qpypxf9jma2lkmzwn6nhypmynqpmus7x0ekwp2jfdrxqgm8vecdcssszlmywlyn7h0nkqp9jyk8p7dhjecglhu9m75xq4mwl9v",
      "policyId": "ada",
      "assetName": "ada",
      "quantity": "499827547"
    }
  ],
  "metadata": {},
  "outputTotals": [
    {
      "policyId": "ada",
      "assetName": "ada",
      "quantity": "499827547"
    }
  ],
  "fee": "172453",
  "outputAda": "499827547",
  "inputTotalEstimation": "500000000"
}

The transaction has the stake pool certificate attached to it, that's why it consumes an additional 500 ADA for the deposit.
The problem I have is I'm getting the following error when trying to submit it to the blockchain:
The sum of inputs, outputs and fee is not zero. (ValueNotConservedError)

    [
      {
        "valueNotConserved": {
          "consumed": {
            "coins": 1000000000,
            "assets": {}
          },
          "produced": {
            "coins": 500000000,
            "assets": {}
          }
        }
      }
    ]

The input UTxO has a value of 1000 ADA, which is more than enough to cover the stake pool deposit fee. And, in addition to this, if I do...
output + fee + deposit = 499827547 + 172453 + 500000000 = 1000000000

...I get the value of the input of the transaction (so nothing's being lost in theory).
What am I doing wrong?

**Tags:** stake-pool, cardano-serialization-lib, utxo, tx

---

## 1 answers

### Answer by username_0 on 2023-01-27. Score: 0 | Accepted answer

This is the code to build a transaction to register a Stake Pool using cardano-serialization-lib:
export const submitRegistrationTransaction = async (
  mnemonic, 
  poolCertificate
) => {

  //const privateKey = await getPrivateKeyFromEntropy(entropy);
  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 stakeAddr = CardanoWasm.RewardAddress.new(
    CardanoWasm.NetworkInfo.testnet().network_id(),
    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(poolCertificate);
  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);

  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());

}

This is how you create the stake pool certificate:
export const createStakePoolCertificate = (
  mnemonic, 
  stakePoolName, 
  relayIP, 
  relayPort, 
  pledge, 
  cost,
  poolMarginNumerator,
  poolMarginDenominator
) => {

  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 stakeAddr = CardanoWasm.RewardAddress.new(
    CardanoWasm.NetworkInfo.testnet().network_id(),
    CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash())
  )

  const privateKey = deriveSpendPrivateKey(accountKey, 0).to_raw_key();

  // Declare variables for pool params
  const operatorKeyHash = utxoPubKey.to_raw_key().hash().to_bech32('ed25519_pk');

  const nodeKeyGenKES = cardanocliJs.nodeKeyGenKES(stakePoolName);
  const nodeKeyGen = cardanocliJs.nodeKeyGen(stakePoolName);
  const nodeOperationalCert = cardanocliJs.nodeIssueOpCert(stakePoolName);
  const vrfKeyAccount = cardanocliJs.nodeKeyGenVRF(stakePoolName);

  //const kesKeyFile = readFileSync(nodeKeyGenKES.vkey, 'utf-8');
  //console.log(CardanoWasm.KESSignature.from_bytes(Buffer.from(JSON.parse(kesKeyFile).cborHex), "hex"));
  const vrfKeyFile = readFileSync(vrfKeyAccount.vkey, 'utf-8');
  //const vrfKeyHash = CardanoWasm.VRFKeyHash.from_hex(JSON.parse(vrfKeyFile).cborHex.substring(4));
  const vrfKeyHash = CardanoWasm.VRFKeyHash.from_bytes(fromHex(JSON.parse(vrfKeyFile).cborHex.substring(4)));
   
  const poolMargin = CardanoWasm.UnitInterval.new(
    CardanoWasm.BigNum.from_str(poolMarginNumerator), // numerator
    CardanoWasm.BigNum.from_str(poolMarginDenominator)  // denominator
  );

  const poolOwner = CardanoWasm.Ed25519KeyHashes.new();
  poolOwner.add(CardanoWasm.Ed25519KeyHash.from_bech32(operatorKeyHash));

  const relays = CardanoWasm.Relays.new();
  const relay = CardanoWasm.Relay.new_single_host_addr(
    CardanoWasm.SingleHostAddr.new(
      relayPort,
      CardanoWasm.Ipv4.new(new Uint8Array(relayIP.split('.').map((segment) => Number.parseInt(segment, 10))))
    )
  );
  relays.add(relay);

  // Prepare pool params
  const poolParams = CardanoWasm.PoolParams.new(
    CardanoWasm.Ed25519KeyHash.from_bech32(operatorKeyHash),
    vrfKeyHash,
    CardanoWasm.BigNum.from_str(pledge),
    CardanoWasm.BigNum.from_str(cost),
    poolMargin,
    stakeAddr,
    poolOwner,
    relays
  );

  // Create Pool Registration
  const poolRegistration = CardanoWasm.PoolRegistration.new(poolParams);
  console.log(poolRegistration);
  // Generate Pool Certificate
  const poolCertificate = CardanoWasm.Certificate.new_pool_registration(poolRegistration)
  return poolCertificate;
}


---

