user1053: If we create a transaction body in the backend using cardano-cli for example, and only for signing the transaction would like to get the frontend wallet to sign it, how is this done in the best possible way ?

user4137: Basically, there are two ways:

After getting the transaction in the frontend, load the CBOR to CSL (cardano-serialization-lib), and proceed from there.

The workflow would be: build the transaction with cardano-cli, sign it with some random wallet stored in backend, send the signed transaction CBOR to frontend, deserialize the CBOR to CSL Transaction object, remove the witness you signed with in backend, call any wallet extension (eternl, nami..) injected signTx() method and get current user witness, add it to the CSL Transaction object and submit it with submitTx() wallet method.
This worked fine for me with Alonzo era and simple transactions, but had some problems with complicated (inline datums, plutusV2 scripts, etc.) Babbage era transactions. You kind of need to dig deep into CSL to make it work. So I chose the second, easier approach.

Build the transaction using cardano-cli, send it to the frontend in order to retrieve a witness, return both transaction CBOR and witness to backend, assemble the transaction and submit it.

In depth steps:

cardano-cli transaction build-raw --babbage-era --cddl-format ...

build instead of build-raw also works. Do not forget to add --cddl-format flag, in order for frontend sign() method to understand the format. Also you do not need to sign the transaction, only build it.

Send built transaction CBOR to frontend

encodedTxVkeyWitnesses = await window.signTx(cbor, true)


Call signTx() wallet method with received CBOR as a parameter. You will get the encdoded witness, which is basically a proof that user signed the transaction. The resulting hex string will look like this: a10081825820cccfe2be401c85342497f6e1e4a241629790b0fb7f2af5f18441779d11f25b1f5840c38a93d63faac9335ecc2f24ead7ca2d46a6637f354ee707bb06eb8192af2fa6a676fb72f8772cd1c42b491ec6dfc798c76b61c55dc4eecab362e71ffab26305

await handleSubmit(cbor, encodedTxVkeyWitnesses)

Call your backend from frontend, and pass the very same transaction CBOR you received from backend and recently acquired witness CBOR.

$witnessCbor = substr($encodedTxVkeyWitnesses, strpos($encodedTxVkeyWitnesses,'825820'), (strlen($encodedTxVkeyWitnesses) - strpos($encodedTxVkeyWitnesses,'825820')));

Note: example is in PHP. signTx() method returns a bit different witness structure than required by cardano-cli. You basically need to strip first 6 chars from the string. They represent data structure, in this case some nested arrays and stuff. You can look at structure differences here, by comparing both stripped and not stripped hex witness CBOR strings: https://cbor.nemo157.com/
6.
transactionCborFile = '
        {
            "type": "Unwitnessed Tx BabbageEra",
            "description": "Ledger Cddl Format",
            "cborHex": "<transaction CBOR here>"
        }
    ';
    $witnessCborFile = '
        {
            "type": "TxWitness BabbageEra",
            "description": "Key Witness ShelleyEra",
            "cborHex": "<stripped witness CBOR here>"
        }
    ';

Create two files as described above containing transaction and witness CBOR, as cardano-cli assemble command require files as parameters.

cardano-cli transaction assemble --tx-body-file <transaction_cbor_file_path> --witness-file <witness_cbor_file_path> --out-file tx.assembled

Use cardano-cli assemble command to combine both transaction and witness CBOR in order to receive a signed transaction. At this point, transaction is signed and is ready to be submitted.

cardano-cli transaction submit $MAGIC --tx-file tx.assembled

That's it!

1053: <p>If we create a transaction body in the backend using cardano-cli for example, and only for signing the transaction would like to get the frontend wallet to sign it, how is this done in the best possible way ?</p>
 4137: <p>Basically, there are two ways:</p>
<ol>
<li>After getting the transaction in the frontend, load the CBOR to CSL (cardano-serialization-lib), and proceed from there.</li>
</ol>
<p>The workflow would be: build the transaction with cardano-cli, sign it with some random wallet stored in backend, send the signed transaction CBOR to frontend, deserialize the CBOR to CSL Transaction object, remove the witness you signed with in backend, call any wallet extension (eternl, nami..) injected signTx() method and get current user witness, add it to the CSL Transaction object and submit it with submitTx() wallet method.</p>
<p>This worked fine for me with Alonzo era and simple transactions, but had some problems with complicated (inline datums, plutusV2 scripts, etc.) Babbage era transactions. You kind of need to dig deep into CSL to make it work. So I chose the second, easier approach.</p>
<ol start="2">
<li>Build the transaction using cardano-cli, send it to the frontend in order to retrieve a witness, return both transaction CBOR and witness to backend, assemble the transaction and submit it.</li>
</ol>
<p>In depth steps:</p>
<ol>
<li><code>cardano-cli transaction build-raw --babbage-era --cddl-format ...</code></li>
</ol>
<p>build instead of build-raw also works. Do not forget to add --cddl-format flag, in order for frontend sign() method to understand the format. Also you do not need to sign the transaction, only build it.</p>
<ol start="2">
<li><p>Send built transaction CBOR to frontend</p>
</li>
<li><p><code>encodedTxVkeyWitnesses = await window.signTx(cbor, true)</code></p>
</li>
</ol>
<p>Call signTx() wallet method with received CBOR as a parameter. You will get the encdoded witness, which is basically a proof that user signed the transaction. The resulting hex string will look like this: <code>a10081825820cccfe2be401c85342497f6e1e4a241629790b0fb7f2af5f18441779d11f25b1f5840c38a93d63faac9335ecc2f24ead7ca2d46a6637f354ee707bb06eb8192af2fa6a676fb72f8772cd1c42b491ec6dfc798c76b61c55dc4eecab362e71ffab26305</code></p>
<ol start="4">
<li><code>await handleSubmit(cbor, encodedTxVkeyWitnesses)</code></li>
</ol>
<p>Call your backend from frontend, and pass the very same transaction CBOR you received from backend and recently acquired witness CBOR.</p>
<ol start="5">
<li><code>$witnessCbor = substr($encodedTxVkeyWitnesses, strpos($encodedTxVkeyWitnesses,'825820'), (strlen($encodedTxVkeyWitnesses) - strpos($encodedTxVkeyWitnesses,'825820')));</code></li>
</ol>
<p>Note: example is in PHP. signTx() method returns a bit different witness structure than required by cardano-cli. You basically need to strip first 6 chars from the string. They represent data structure, in this case some nested arrays and stuff. You can look at structure differences here, by comparing both stripped and not stripped hex witness CBOR strings: <a href="https://cbor.nemo157.com/" rel="nofollow noreferrer">https://cbor.nemo157.com/</a></p>
<p>6.</p>
<pre><code>transactionCborFile = '
        {
            &quot;type&quot;: &quot;Unwitnessed Tx BabbageEra&quot;,
            &quot;description&quot;: &quot;Ledger Cddl Format&quot;,
            &quot;cborHex&quot;: &quot;&lt;transaction CBOR here&gt;&quot;
        }
    ';
    $witnessCborFile = '
        {
            &quot;type&quot;: &quot;TxWitness BabbageEra&quot;,
            &quot;description&quot;: &quot;Key Witness ShelleyEra&quot;,
            &quot;cborHex&quot;: &quot;&lt;stripped witness CBOR here&gt;&quot;
        }
    ';
</code></pre>
<p>Create two files as described above containing transaction and witness CBOR, as cardano-cli assemble command require files as parameters.</p>
<ol start="7">
<li><code>cardano-cli transaction assemble --tx-body-file &lt;transaction_cbor_file_path&gt; --witness-file &lt;witness_cbor_file_path&gt; --out-file tx.assembled</code></li>
</ol>
<p>Use cardano-cli assemble command to combine both transaction and witness CBOR in order to receive a signed transaction. At this point, transaction is signed and is ready to be submitted.</p>
<ol start="8">
<li><code>cardano-cli transaction submit $MAGIC --tx-file tx.assembled</code></li>
</ol>
<p>That's it!</p>
