From bee6c444656a10d65298f538b14c905ffd859ade Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 3 Sep 2024 11:49:19 -0400 Subject: [PATCH 1/3] feat: start adding sveltekit --- src/pages/example--gift-card.mdx | 204 +++++++++++++------------------ 1 file changed, 87 insertions(+), 117 deletions(-) diff --git a/src/pages/example--gift-card.mdx b/src/pages/example--gift-card.mdx index 49db6a8..6b05c18 100644 --- a/src/pages/example--gift-card.mdx +++ b/src/pages/example--gift-card.mdx @@ -15,16 +15,14 @@ Let's build a UI to send and redeem a gift card using smart contracts on Cardano - [x] Writing `Aiken` inter-dependent `mint` & `spend` validators. - [x] Parameterizing validators. -- [x] Using [Lucid](https://lucid.spacebudz.io/) with [Blockfrost](https://blockfrost.io). +- [x] Using [Blaze](https://github.com/butaneprotocol/blaze-cardano/) with Blockfrost. - ★ We'll once again be using the `Blockfrost` provider. So have your - Blockfrost API key ready. - -- [x] Using [Deno fresh](https://fresh.deno.dev/). - - ★ You can install deno using these - [instructions](https://deno.land/manual@v1.29.1/getting_started/installation). + ★ You can easily get access by using [Demeter](https://demeter.run). Go + there and grab your API key by creating a Blockfrost instance on their + dashboard. +- [x] Using [SvelteKit](https://kit.svelte.dev/). + ★ Make sure you have Node.js installed. When encountering an unfamiliar syntax or concept, do not hesitate to refer to @@ -379,152 +377,121 @@ aiken build ## Building a frontend With the easy part out of the way we can start building a frontend to interact with our -smart contracts in the browser. Deno fresh is an interesting project for building -web applications in Deno. +smart contracts in the browser. SvelteKit is an interesting framework for building +web applications. ### Setting up -Let's generate a Deno fresh project in the same directory as our Aiken project. +Let's generate a SvelteKit project in the same directory as our Aiken project. ```sh -deno run -A -r https://fresh.deno.dev . +npm create svelte@latest . ``` -When prompted to enable Tailwind CSS say yes. - -We need lucid and we should probably add an alias for better looking imports. -Let's edit `import_map.json`. - -```json filename="import_map.json" {11-12} -{ - "imports": { - "$fresh/": "...", - "preact": "...", - "preact/": "...", - "preact-render-to-string": "...", - "@preact/signals": "...", - "@preact/signals-core": "...", - "twind": "...", - "twind/": "...", - "lucid/": "https://deno.land/x/lucid@0.9.5/", - "~/": "./" - } -} + + When prompted use the current directory, continue even though directory is not + empty, choose a skeleton project, use Svelte 5, and enable typescript. + + +We need to add Blaze now. + +```sh +npm add @blaze-cardano/sdk@latest @blaze-cardano/core@latest @blaze-cardano/query@latest @blaze-cardano/uplc@latest @blaze-cardano/wallet@latest @blaze-cardano/tx@latest ``` -We can delete a few things that come with the starter template that we don't need. +Let's add tailwindcss to our project. ```sh -rm islands/Counter.tsx -rm -rf routes/api -rm routes/\[name\].tsx +npx svelte-add@latest tailwindcss ``` -Let's also add some reusable components to our project. +When prompted just say yes to everything. -```tsx filename="components/Button.tsx" -import { JSX } from "preact"; -import { IS_BROWSER } from "$fresh/runtime.ts"; +Let's also add some reusable components to our project. -export function Button(props: JSX.HTMLAttributes) { - return ( - ``` -### Home page - -Everything we'll be doing with validators and transactions will happen fully client side. -This means we can just have our route render a single `island` component and then -we can write all of our code in this island for the most part. +```svelte filename="src/lib/components/Input.svelte" + + +
+ + +
``` -Now inside of `routes/index.tsx` we can import our new island and render it. +### Home page -```tsx filename="routes/index.tsx" {3,29} -import { Head } from "$fresh/runtime.ts"; +Everything we'll be doing with validators and transactions will happen fully client side. +This means we can just have our app render a single `+page.svelte` component and then +we can write all of our code in this page component for the most part. -import Oneshot from "~/islands/Oneshot"; +Let's edit `src/routes/+page.svelte` to contain the following code. -export default function Home() { - return ( - <> - - One Shot - +```svelte filename="src/routes/+page.svelte" + + One Shot + -
-
-

- Make a one shot minting and lock contract -

+
+
+

Make a one shot minting and lock contract

-

Redeem

-
-            TODO: Render non-parameterized redeem validator
-          
+

Redeem

+
+      TODO: Render non-parameterized redeem validator
+    
-

Gift Card

-
-            TODO: Render non-parameterized gift_card validator
-          
-
+

Gift Card

+
+      TODO: Render non-parameterized gift_card validator
+    
+
- -
- - ); -} +
Oneshot
+
``` -You can replace everything that was in `routes/index.tsx` with the above code. -We've left some `TODO`'s in the code to remind us to render the validators. We'll render +We've left a `TODO` in the code to remind us to render the validator. We'll render the compiled aiken code as a hex encoded string. There not much of a reason to do this, it's just kinda cool to see. Next we should load the `plutus.json` file and get the compiled aiken code. Let's create -a file called `utils.ts` and add the following code. +a file called `lib/utils.ts` and add the following code. -```ts filename="utils.ts" +```ts filename="lib/utils.ts" import { MintingPolicy, SpendingValidator } from "lucid/mod.ts"; import blueprint from "~/plutus.json" assert { type: "json" }; @@ -780,7 +747,10 @@ export function applyParams( return { redeem: { type: "PlutusV2", script: applyDoubleCborEncoding(redeem) }, - giftCard: { type: "PlutusV2", script: applyDoubleCborEncoding(giftCard) }, + giftCard: { + type: "PlutusV2", + script: applyDoubleCborEncoding(giftCard), + }, policyId, lockAddress, }; From 7e383c6bf3bc3904af177644740c60d8ff9f1b8f Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 3 Sep 2024 12:34:44 -0400 Subject: [PATCH 2/3] feat: made it all the way to init blaze --- src/pages/example--gift-card.mdx | 242 +++++++++++++------------------ 1 file changed, 104 insertions(+), 138 deletions(-) diff --git a/src/pages/example--gift-card.mdx b/src/pages/example--gift-card.mdx index 6b05c18..888af20 100644 --- a/src/pages/example--gift-card.mdx +++ b/src/pages/example--gift-card.mdx @@ -492,24 +492,15 @@ Next we should load the `plutus.json` file and get the compiled aiken code. Let' a file called `lib/utils.ts` and add the following code. ```ts filename="lib/utils.ts" -import { MintingPolicy, SpendingValidator } from "lucid/mod.ts"; - -import blueprint from "~/plutus.json" assert { type: "json" }; +import blueprint from "../../plutus.json" assert { type: "json" }; export type Validators = { - redeem: SpendingValidator; - giftCard: MintingPolicy; + giftCard: string; }; export function readValidators(): Validators { - const redeem = blueprint.validators.find((v) => v.title === "oneshot.redeem"); - - if (!redeem) { - throw new Error("Redeem validator not found"); - } - const giftCard = blueprint.validators.find( - (v) => v.title === "oneshot.gift_card" + (v) => v.title === "oneshot.gift_card.spend" ); if (!giftCard) { @@ -517,173 +508,148 @@ export function readValidators(): Validators { } return { - redeem: { - type: "PlutusV2", - script: redeem.compiledCode, - }, - giftCard: { - type: "PlutusV2", - script: giftCard.compiledCode, - }, + giftCard: giftCard.compiledCode, }; } ``` There's nothing particularly special here. We're just reading the `plutus.json` file -and finding the compiled code for the `redeem` and `gift_card` validators. We're also -exporting a type for the validators so we can use it in our island later. Having this function +and finding the compiled code for the `gift_card` validator. We're also +exporting a type for the validators so we can use it in our page later. Having this function potentially throw an error is just a way to signal to us that we've done something wrong. -Let's import our new `readValidators` file into our `routes/index.tsx` file and use it to -in a server side handler. This will allow us to access the data in the `Home` page component +Let's import our new `readValidators` file into `src/routes/+page.server.ts` file and use it to +in a server side loader. This will allow us to access the data in the `+page.svelte` page component as page props which we'll then use to render the validator's compiled code. -```tsx filename="routes/index.tsx" {2,5,7-9,11-17} /validators/4-8 /PageProps/ /data/ -import { Head } from "$fresh/runtime.ts"; -import { Handlers, PageProps } from "$fresh/server.ts"; - -import Oneshot from "~/islands/Oneshot"; -import { readValidators, Validators } from "~/utils.ts"; +```ts filename="src/routes/+page.server.ts" +import { readValidators } from "$lib/utils"; +import type { PageServerLoad } from "./$types"; -interface Data { - validators: Validators; -} +export const load: PageServerLoad = async () => { + const validator = readValidators().giftCard; -export const handler: Handlers = { - GET(_req, ctx) { - const validators = readValidators(); - - return ctx.render({ validators }); - }, + return { validator }; }; +``` -export default function Home({ data }: PageProps) { - const { validators } = data; +```svelte filename="src/routes/+page.svelte" + -

Redeem

-
-            {validators.redeem.script}
-          
+ + One Shot + -

Gift Card

-
-            {validators.giftCard.script}
-          
- +
+
+

Make a one shot minting and lock contract

- -
- - ); -} -``` +

Gift Card

+
{data.validator}
+
-Your editor will probably complain and say that `Oneshot` doesn't accept a `validators` prop. -We'll fix that in a moment. +
Oneshot
+ +``` -### The island +### The App It's about time we start the real party and we've made it to the juicy part. In this island -we'll capture some user input, apply some params to our raw validators, and execute some transactions. +we'll capture some user input, apply some params to our raw validator, and execute some transactions. To keep things simple we'll assume [eternl](https://eternl.io/) is setup in your browser. Another thing we'll do to keep things simple is have the gift card be sent to ourselves when minted. This way we can test the redeeming of the gift card without having to send it to someone else or using a second wallet. #### Token name -We should give `Oneshot`'s props a type and capture the `token_name` so we can use it to -apply some params to the raw validators. Lucid also requires initialization so let's get -some boilerplate out of the way. +We need to capture the `token_name` so we can use it to apply some params to the raw validators. +Blaze also requires initialization so let's get some boilerplate out of the way. -```tsx filename="islands/Oneshot.tsx" -import { useEffect, useState } from "preact/hooks"; -import { Blockfrost, Lucid } from "lucid/mod.ts"; +```svelte filename="src/routes/+page.svelte" + - return ( -
- {!lucid ? ( -
- setBlockfrostAPIKey(e.currentTarget.value)} - > - Blockfrost API Key - - - -
- ) : ( -
- setTokenName(e.currentTarget.value)} - > - Token Name - - - {tokenName && } -
- )} -
- ); -} + + One Shot + + +
+
+

Make a one shot minting and lock contract

+ +

Gift Card

+
{data.validator}
+
+ +
+ {#if blaze} +
+ + Blockfrost API Key + + + +
+ {:else} +
+ Token Name + + {#if tokenName.length > 0} + + {/if} +
+ {/if} +
+
``` #### Apply params From 987ba33a86d7d38090e2db16e8230c22d009f6fa Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 3 Sep 2024 12:37:05 -0400 Subject: [PATCH 3/3] chore: keep it simple --- src/pages/example--gift-card.mdx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/example--gift-card.mdx b/src/pages/example--gift-card.mdx index 888af20..c79c9ad 100644 --- a/src/pages/example--gift-card.mdx +++ b/src/pages/example--gift-card.mdx @@ -15,11 +15,10 @@ Let's build a UI to send and redeem a gift card using smart contracts on Cardano - [x] Writing `Aiken` inter-dependent `mint` & `spend` validators. - [x] Parameterizing validators. -- [x] Using [Blaze](https://github.com/butaneprotocol/blaze-cardano/) with Blockfrost. +- [x] Using [Blaze](https://github.com/butaneprotocol/blaze-cardano/) with [Blockfrost](https://blockfrost.io). - ★ You can easily get access by using [Demeter](https://demeter.run). Go - there and grab your API key by creating a Blockfrost instance on their - dashboard. + ★ We'll once again be using the `Blockfrost` provider. So have your + Blockfrost API key ready. - [x] Using [SvelteKit](https://kit.svelte.dev/). ★ Make sure you have Node.js installed.