Interpreter for partial transactions using lucid-cardano
npm install lucid-cardano-partialtxA library to export partial transactions (unbalanced, unsigned) and get them
signed and submitted by a frontend PAB. This enables you to re-use your regular
Haskell Contracts while still having a deployment solution for production -
effortlessly.
- Write all your contracts in the familiar Contract monad.
- Leverage excellent Haskell-side testing solutions for your Contracts, using
Plutip, Contract models etc.
- Run your contracts on the testnet (and mainnet!) utilizing a lightweight
frontend PAB (i.e Lucid) - without ever
having to rewrite your contracts or any extra logic. Simply re-use your
Haskell contracts, call .sign and .submit on the frontend.
- Extremely lightweight, easy to test in local development environment. If using
bot-plutus-interface
(BPI), all you need is cardano-node and plutus-chain-index.
bot-plutus-interface and plutus-partial-tx as dependencies to your project.cardano-node, cardano-cli, and a corresponding version (same one used by bot-plutus-interface) of plutus-chain-index in $PATH.cardano-node and chain-index running in the background and properly synced. You may simply copy over the provided testnet directory and use the scripts there to set up for testnet. Remember to create the db directory inside testnet/chain-index first! Usually, if using nix, plutus-chain-index is already included by BPI. All you have to do is make it available in the nix shell by adding project.hsPkgs.plutus-chain-index.components.exes.plutus-chain-index to nativeBuildInputs.
> NOTE: The provided example is using the preview testnet.
> NOTE: You need to use a vasil compliant version of BPI. Mainline BPI does not support vasil yet. Use the gergely/vasil branch instead.
How you set up the frontend is entirely upto you, as long as it can query the Haskell server to obtain a PartialTx - and use it with lucid-cardano and lucid-cardano-partialtx, it's enough.
See Berry-Pool/lucid for adding Lucid to your dependency.
> NOTE: You need to use a vasil compliant version of Lucid. Mainline Lucid does not support vasil yet. Use the vasil branch instead.
For lucid-cardano-partialtx (provided by this repo), there are three ways to import it:
Install the npm package:
``sh`
npm install lucid-cardano-partialtx
Import in your file:
`sh`
import { mkPartialTxInterpreter } from "lucid-cardano-partialtx";
> Aside: You can use webpack or similar to bundle your Node project to run on the browser. See full example that does this
Simply import from deno.land:
`ts`
import { mkPartialTxInterpreter } from "https://deno.land/x/lucid_partialtx@0.1.3/mod.ts";
> Aside: You can use ESBuild or similar to bundle your Deno project to run on the browser. However, you should to replace the deno.land imports with the browser package url if running in a browser environment. See lucid-partialtx/build.ts that does something similar (but only after generating a node package).
`hs
import { mkPartialTxInterpreter } from "https://unpkg.com/lucid-cardano-partialtx@0.1.3/web/mod.js";
`
> Aside: This is pure JS directly running in the browser: probably not too practical for large projects.
Once you have the environment set up, your haskell server merely has to use BPI (bot-plutus-interface) to run your contracts returning PartialTxs. You can hook up BPI with the testnet quite easily: simply copy over the BPI.Testnet.Setup module from the example.
A simple servant server, showcasing a simple Contract usage, can be found in example/Main.
All you have to do is create a PartialTxInterpreter by passing your Lucid instance to mkPartialTxInterpreter, make an API call to receieve the PartialTx from your server, and pass it through the interpreter. The resulting Lucid Tx can be modified further or directly signed, and submitted.
See example/frontend/src/index.ts.
There is a full example with servant, BPI, and Lucid that can run a dummy
minting contract on the testnet. Check the haskell code
in the example directory. The gist is that you can copy over
BPI.Testnet.Setup and use the exposed interface to create PartialTxs in the
context of the testnet.
The example frontend is in example/frontend.
To run the project and run stuff on the testnet, head inside the nix shell by
doing nix develop, and follow these steps:
You need to fill a config.json and put it in example/frontend/config.json, this
config should contain your blockfrost API access key:
`json`
{
"blockfrostUrl": "TESTNET_BLOCKFROST_URL",
"blockfrostProjId": "TESTNET_BLOCKFROST_PROJID"
}
> Aside: Of course, you can also deploy all this in production by switching out
> to connect to the mainnet in the BPI setup and blockfrost setup.
and plutus-chain-index in backgroundYou'll also need cardano-node and chain-index running in the background,
properly connected to testnet.
Unless the directory testnet/chain-index/db already exists, you should create an empty directory: mkdir testnet/chain-index/db.
All you have to do run make services.
This will take some time to sync. You can see the node logs in
testnet/node.log and chain index logs in testnet/cix.log. You can query themake query-tip
node sync progress by running .
> Note: Remember to stop these background services when you're done! Use
> make stop-services to do so.
Firstly, run make build-lucid-lib at the root project directory.
Now, head inside the example/frontend directory and run the following commands:
- npm i - to install all the npm dependenciesnpx webpack
- - to build the project
Alternatively, if you've already done npm i and have the node_modules frommake build-frontend
it - you can run from the root project path.
Once the node has synced and all the previous steps have been completed, run
make serve. Head to localhost:8080 to see a beautiful frontend with a
singular dummy minting button!
Note: You'll need a wallet supporting Vasil for signing and submission to
work properly. In particular, mainline Nami does not support vasil yet. You need to use the vasil branch of nami.
Here's the core idea:
Keep your Haskell contracts as they are, just make them return UnbalancedTxLedger.Constraints.mkTx
using.ScriptLookups
Alternatively, make them return the and the TxConstraints.
Use either of the two functions provided within this repo to create a
PartialTx:
`hs
import Plutus.Contract.PartialTx
mkPartialTx ::
( FromData (DatumType a)
, ToData (DatumType a)
, ToData (RedeemerType a)
) =>
ScriptLookups a ->
TxConstraints (RedeemerType a) (DatumType a) ->
Either MkTxError PartialTx
unbalancedToPartial :: UnbalancedTx -> PartialTx
`
> Aside: For your Haskell side tests (Plutip or EmulatorTrace), you'd still want
> to submit the UnbalancedTx - which you can still do usingsubmitUnbalancedTx
> .mkTx
> So calling followed by submitUnbalancedTx is effectively the same assubmitTx
> just calling (and similar).
In the frontend, bind your Lucid instance to mkPartialTxInterpreter, and use the resulting PartialTxInterpreter to interpret a PartialTx:
`ts
type PartialTxInterpreter = (x: PartialTx) => Tx;
mkPartialTxInterpreter(lucid: Lucid): PartialTxInterpreter;
`
Once you obtain a Lucid Tx, it's as simple as signing and submitting it, which
looks like:
`ts``
const signedTx = await tx.sign().complete();
return signedTx.submit();