From 36634178351457a3ee0e8629c57b7319b6e5d0f9 Mon Sep 17 00:00:00 2001 From: Quentin Burg Date: Fri, 15 Dec 2023 15:20:03 +0100 Subject: [PATCH 01/22] :wrench: update NFT example contracts for staging --- .github/workflows/docker-nft-ghostnet-staging.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-nft-ghostnet-staging.yml b/.github/workflows/docker-nft-ghostnet-staging.yml index f01985d..84ec959 100644 --- a/.github/workflows/docker-nft-ghostnet-staging.yml +++ b/.github/workflows/docker-nft-ghostnet-staging.yml @@ -34,8 +34,8 @@ jobs: set -x ls -last . sed -i 's|http://localhost:8000|https://staging.gas-station-api.marigold.dev|g' ./examples/nft/.env - sed -i 's|KT1Re88VMEJ7TLHTkXSHQYZQTD3MP3k7j6Ar|KT199yuNkHQKpy331A6fvWJtQ1uan9uya2jx|g' ./examples/nft/.env - sed -i 's|KT1Rp1rgfwS25XrWU6fUnR8cw6KMZBhDvXdq|KT1MLMXwFEMcfByGbGcQ9ow3nsrQCkLbcRAu|g' ./examples/nft/.env + sed -i 's|KT1Re88VMEJ7TLHTkXSHQYZQTD3MP3k7j6Ar|KT1CvtrTV7snJSxdnQqhSqh47tyVYhm4ACGB|g' ./examples/nft/.env + sed -i 's|KT1Rp1rgfwS25XrWU6fUnR8cw6KMZBhDvXdq|KT1K9X6QpuTWFX4S5ieDGTUdjAHQtiFRH5op|g' ./examples/nft/.env cat ./examples/nft/.env - name: Set up Docker Buildx From d345f1c568334b13a9524c9d0a86a10418486ae7 Mon Sep 17 00:00:00 2001 From: Quentin Burg Date: Mon, 18 Dec 2023 11:06:05 +0100 Subject: [PATCH 02/22] :recycle: add apiURL variables && update constructor of GasStation class --- index.ts | 16 ++++++++++++++-- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/index.ts b/index.ts index d23b985..e64fe3a 100644 --- a/index.ts +++ b/index.ts @@ -30,11 +30,21 @@ export type PermitOperation = { transferHash: string; }; +export const GAS_STATION_PUBLIC_API_GHOSTNET = + "https://ghostnet.gas-station-api.marigold.dev/operation"; +export const GAS_STATION_PUBLIC_API_MAINNET = + "https://gas-station-api.marigold.dev/operation"; + export class GasStation { url: string; - constructor(settings: Settings) { - this.url = settings.apiURL; + /** + * + * @param settings (optional) object + * - apiURL: the URL of Gas Station API. /!\ For this version, the URL must redirect to the endpoint /operation + */ + constructor(settings?: Settings) { + this.url = settings?.apiURL || GAS_STATION_PUBLIC_API_GHOSTNET; } async postOperations(sender: string, ops: Array) { @@ -62,6 +72,8 @@ export class GasStation { } } +const g = new GasStation(); + export class PermitContract { address: string; tezos: TezosToolkit; diff --git a/package-lock.json b/package-lock.json index c26bc5a..b62fd09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@marigold-dev/gas-station-lib", - "version": "0.0.4", + "version": "0.0.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@marigold-dev/gas-station-lib", - "version": "0.0.4", + "version": "0.0.6", "license": "LGPL 3", "dependencies": { "@taquito/michel-codec": "17.3.0", diff --git a/package.json b/package.json index 63c7145..6582f17 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@marigold-dev/gas-station-lib", - "version": "0.0.4", + "version": "0.0.6", "description": "Interact with a gas station API and produce TZIP 17 permits", "main": "./dist/index.js", "files": [ From ee914fde01d933a63ef06690b4d9c02a55480174 Mon Sep 17 00:00:00 2001 From: Quentin Burg Date: Mon, 18 Dec 2023 11:06:34 +0100 Subject: [PATCH 03/22] :arrow_up: bump gas-station deps --- examples/nft/package-lock.json | 8 ++++---- examples/nft/package.json | 2 +- examples/nft/src/lib/MintingComponent.svelte | 6 +++--- examples/nft/src/lib/StakingComponent.svelte | 10 +++------- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/examples/nft/package-lock.json b/examples/nft/package-lock.json index c3fd780..0fd6d0e 100644 --- a/examples/nft/package-lock.json +++ b/examples/nft/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@airgap/beacon-sdk": "^4.0.4", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@marigold-dev/gas-station-lib": "^0.0.4", + "@marigold-dev/gas-station-lib": "^0.0.6", "@picocss/pico": "^1.5.10", "@taquito/beacon-wallet": "17.3.0", "@taquito/michel-codec": "17.3.0", @@ -602,9 +602,9 @@ } }, "node_modules/@marigold-dev/gas-station-lib": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@marigold-dev/gas-station-lib/-/gas-station-lib-0.0.4.tgz", - "integrity": "sha512-szNFJyv1943FaBri/RB5k0ctpX31zBUaL4bIEsuRRndN+j0SzkJ/cwEGh6WhKtqKai7VjaS30fpkotwwNix9SQ==", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@marigold-dev/gas-station-lib/-/gas-station-lib-0.0.6.tgz", + "integrity": "sha512-lXw7QPvRq+rvb2In7Ta56SzYwyBS8S0lk0f4hPgNawmZY5odkEo13m0AaZgNCzisE9CYUSWxhVj4BHnAbeGFaw==", "dependencies": { "@taquito/michel-codec": "17.3.0", "@taquito/rpc": "17.3.0", diff --git a/examples/nft/package.json b/examples/nft/package.json index fce4edc..d855517 100644 --- a/examples/nft/package.json +++ b/examples/nft/package.json @@ -21,7 +21,7 @@ }, "type": "module", "dependencies": { - "@marigold-dev/gas-station-lib": "^0.0.4", + "@marigold-dev/gas-station-lib": "^0.0.6", "@airgap/beacon-sdk": "^4.0.4", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@picocss/pico": "^1.5.10", diff --git a/examples/nft/src/lib/MintingComponent.svelte b/examples/nft/src/lib/MintingComponent.svelte index 75b61bd..20b34cc 100644 --- a/examples/nft/src/lib/MintingComponent.svelte +++ b/examples/nft/src/lib/MintingComponent.svelte @@ -1,7 +1,7 @@ -

Permit demo

+

Gas station demo

+ +

This simple dApp can be used by a user having no ꜩ in their wallet, with operations being relayed + by the Gas Station. Minting a new NFT is done by a single call to the smart contract. + However, stashing an NFT to another smart contract requires a transfer, which must be + authorized off-chain through the signature of a permit.

+
{#if $myAccount == undefined} - You're not connected. +

Please connect to use the dApp.

{:else}{#await getPKH() then pkh}

NFTs in your wallet

+

{PUBLIC_PERMIT}

-

Staked NFTs

+

Stashed NFTs

+

{PUBLIC_STAKING_CONTRACT}

{/await} @@ -61,6 +69,6 @@ } p { - font-size: 16px; + font-size: 18px; } From d454a082a016845a386d37a76ba17d489e0ed49c Mon Sep 17 00:00:00 2001 From: Arthur Guillon Date: Tue, 9 Jan 2024 15:29:13 +0100 Subject: [PATCH 09/22] New contract for stash too --- .github/workflows/docker-nft-ghostnet-staging.yml | 1 - examples/nft/.env | 2 +- examples/nft/src/lib/StakingComponent.svelte | 9 +++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker-nft-ghostnet-staging.yml b/.github/workflows/docker-nft-ghostnet-staging.yml index 6759b9f..34000c0 100644 --- a/.github/workflows/docker-nft-ghostnet-staging.yml +++ b/.github/workflows/docker-nft-ghostnet-staging.yml @@ -34,7 +34,6 @@ jobs: set -x ls -last . sed -i 's|http://localhost:8000|https://staging.gas-station-api.marigold.dev|g' ./examples/nft/.env - sed -i 's|KT1Rp1rgfwS25XrWU6fUnR8cw6KMZBhDvXdq|KT1K9X6QpuTWFX4S5ieDGTUdjAHQtiFRH5op|g' ./examples/nft/.env cat ./examples/nft/.env - name: Set up Docker Buildx diff --git a/examples/nft/.env b/examples/nft/.env index 24511d9..943432c 100644 --- a/examples/nft/.env +++ b/examples/nft/.env @@ -2,5 +2,5 @@ PUBLIC_TZKT_API=https://api.ghostnet.tzkt.io PUBLIC_TEZOS_RPC=https://ghostnet.smartpy.io PUBLIC_GAS_STATION_API=http://localhost:8000 PUBLIC_PERMIT=KT1HUdxmgZUw21ED9gqELVvCty5d1ff41p7J -PUBLIC_STAKING_CONTRACT=KT1Rp1rgfwS25XrWU6fUnR8cw6KMZBhDvXdq # GHOSTNET : KT1MLMXwFEMcfByGbGcQ9ow3nsrQCkLbcRAu +PUBLIC_STAKING_CONTRACT=KT1VVotciVbvz1SopVfoXsxXcpyBBSryQgEn PUBLIC_APP_BASE_URL=http://localhost:5173 diff --git a/examples/nft/src/lib/StakingComponent.svelte b/examples/nft/src/lib/StakingComponent.svelte index 125c3c4..76f90dc 100644 --- a/examples/nft/src/lib/StakingComponent.svelte +++ b/examples/nft/src/lib/StakingComponent.svelte @@ -53,7 +53,7 @@ const activeAccount = await wallet.client.getActiveAccount(); if (!activeAccount) { - throw new Error('No active account, cannot stake') + throw new Error('No active account, cannot stash.') } const permit_op = await permit_contract.permitCall({ @@ -64,8 +64,9 @@ console.log(permit_op); console.log("ok"); const staking_contract = await Tezos.wallet.at(PUBLIC_STAKING_CONTRACT); - const staking_op = await staking_contract.methods.stake( + const staking_op = await staking_contract.methods.stash( 1, + token_id, user_address ).toTransferParams(); @@ -92,14 +93,14 @@
-
{#if user_tokens.length == 0} -

You don't have any tokens staked.

+

You don't have any tokens stashed.

{:else} {#each user_tokens as token, i}
From 5e19c7566a5abe863f3edef1e4dd04a0899eb591 Mon Sep 17 00:00:00 2001 From: Arthur Guillon Date: Tue, 9 Jan 2024 15:31:11 +0100 Subject: [PATCH 10/22] Maintain a set of minted token ids and randomize minting and stashing for a more pleasing user experience! --- examples/nft/src/lib/MintingComponent.svelte | 11 +++++++++-- examples/nft/src/lib/StakingComponent.svelte | 13 +++++++++---- examples/nft/src/routes/+page.svelte | 7 +++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/examples/nft/src/lib/MintingComponent.svelte b/examples/nft/src/lib/MintingComponent.svelte index 8096c92..d9dd283 100644 --- a/examples/nft/src/lib/MintingComponent.svelte +++ b/examples/nft/src/lib/MintingComponent.svelte @@ -4,11 +4,14 @@ import { PUBLIC_GAS_STATION_API, PUBLIC_PERMIT, PUBLIC_TZKT_API } from '$env/static/public'; export let user_address = ''; + export let available_token_ids = new Set(); // to be shared with StakingComponent - - const token_id = 0; let user_tokens: any[] = []; + function randomInt(max) { + return Math.floor(Math.random() * max); + } + function IPFSLinkToHTTPS(url: string) { return url.replace("ipfs://", "https://ipfs.io/ipfs/"); } @@ -24,6 +27,7 @@ }; function mint(user_address: string) { + const token_id = randomInt(6); (async () => { const gas_api = new GasStation({ apiURL: PUBLIC_GAS_STATION_API @@ -52,6 +56,9 @@ subTezos(() => { get_tokens(user_address) }); + + // Maintain the set of available token IDs to pick one in the stash operation + $: available_token_ids = new Set(user_tokens.map(token => token.token.tokenId));
diff --git a/examples/nft/src/lib/StakingComponent.svelte b/examples/nft/src/lib/StakingComponent.svelte index 76f90dc..1f6697f 100644 --- a/examples/nft/src/lib/StakingComponent.svelte +++ b/examples/nft/src/lib/StakingComponent.svelte @@ -5,8 +5,7 @@ import { SigningType } from "@airgap/beacon-types"; export let user_address = ''; - - const token_id = 0; + export let available_token_ids = new Set(); let user_tokens: any[] = []; @@ -24,7 +23,12 @@ }); }; - function stake(user_address: string) { + function stash(user_address: string) { + const n = available_token_ids?.size; + if (n === 0) { + return; + } + const token_id = [...available_token_ids][Math.floor(Math.random() * n)]; // √ Build the transfer // √ Build the permit // √ Ask to sign the permit @@ -89,8 +93,9 @@ subTezos(() => { get_tokens(PUBLIC_STAKING_CONTRACT) }); - + $: console.log(available_token_ids); +
diff --git a/examples/nft/src/lib/StakingComponent.svelte b/examples/nft/src/lib/StakingComponent.svelte index 1f6697f..179d1d0 100644 --- a/examples/nft/src/lib/StakingComponent.svelte +++ b/examples/nft/src/lib/StakingComponent.svelte @@ -107,12 +107,16 @@ {#if user_tokens.length == 0}

You don't have any tokens stashed.

{:else} - {#each user_tokens as token, i} -
- Token thumnail -
{token.balance}
-
- {/each} +
+ {#each user_tokens as token, i} +
+ {#if Object.hasOwn(token.token, "metadata")} + Token thumnail +
{token.balance}
+ {/if} +
+ {/each} +
{/if}
From b31ff1aafdec542eac7a76bc44df1f9e315a1495 Mon Sep 17 00:00:00 2001 From: Arthur Guillon Date: Tue, 9 Jan 2024 15:35:10 +0100 Subject: [PATCH 12/22] Pardon my French --- index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.ts b/index.ts index 8e02ed2..48e1514 100644 --- a/index.ts +++ b/index.ts @@ -126,7 +126,7 @@ export class PermitContract { const permit_bytes = packDataBytes(permit_data, permit_type).bytes; console.info("Permit bytes :", permit_bytes); - console.info("Transfert hash : ", transfer_hash); + console.info("Transfer hash : ", transfer_hash); return { bytes: permit_bytes, transfer_hash: transfer_hash }; } @@ -137,7 +137,7 @@ export class PermitContract { .permit([[op.publicKey, op.signature, op.transferHash]]) .toTransferParams(); - console.info("Transfert params", call); + console.info("Transfer params", call); return call; } From 3dff2581e3e14ed88efb2883984400bdcfb03284 Mon Sep 17 00:00:00 2001 From: lykimq Date: Thu, 18 Jan 2024 11:38:14 +0100 Subject: [PATCH 13/22] index/comment: add comments --- index.ts | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/index.ts b/index.ts index 48e1514..ca40b7e 100644 --- a/index.ts +++ b/index.ts @@ -4,15 +4,33 @@ import { packDataBytes } from "@taquito/michel-codec"; import blake2b from "blake2b"; import { hex2buf } from "@taquito/utils"; +/** + * index.ts: Interacting with the Tezos blockchain, allowing users to perform token + * transfers and obtain permits for certain operations. + */ + +/** + * Settings: Describes the settings object expected by the GasStation class, with an apiURL property. + */ + export type Settings = { apiURL: string; }; +/** + * Operation: Represents a generic blockchain operation with destination and parameters properties + */ + export type Operation = { destination: string; parameters: any; }; +/** + * TransferOperation: Represents a specific type of operation for transferring tokens, + * with from_ as the sender's address and an array of transactions (txs) + */ + export type TransferOperation = { from_: string; txs: [ @@ -24,6 +42,11 @@ export type TransferOperation = { ]; }; +/** + * PermitOperation: Represents an operation for obtaining a permit, + * including publicKey, signature, and transferHash properties + */ + export type PermitOperation = { publicKey: string; signature: string; @@ -32,9 +55,14 @@ export type PermitOperation = { export const GAS_STATION_PUBLIC_API_GHOSTNET = "https://ghostnet.gas-station-api.marigold.dev"; + export const GAS_STATION_PUBLIC_API_MAINNET = "https://gas-station-api.marigold.dev"; +/** + * GasStation is responsible for interacting with a remote API to post blockchain operations. + */ + export class GasStation { url: string; @@ -42,11 +70,17 @@ export class GasStation { * * @param settings (optional) object * - apiURL: the URL of Gas Station API. /!\ For this version, the URL must redirect to the endpoint /operation + * + * Takes a Settings object and initializes the url property. */ constructor(settings?: Settings) { this.url = settings?.apiURL || GAS_STATION_PUBLIC_API_GHOSTNET; } + /** + * postOperations: Sends a POST request to the specified API endpoint with the provided operations. + */ + async postOperations(sender: string, ops: Array) { const post_content = { sender_address: sender, @@ -67,11 +101,26 @@ export class GasStation { return await response.json(); } + /** + * postOperation: A convenience method to post a single operation using postOperations + */ + postOperation(sender: string, op: Operation) { return this.postOperations(sender, [op]); } } +/** + * PermitContract: interacts with a Tezos smart contract, specifically for + * generating permits related to token transfers. + * + * It uses the Tezos toolkit to interact with the Tezos blockchain, + * fetch contract information, and perform operations. + * + * The code utilizes various Tezos-specific functions and conventions for + * encoding data, hashing, and interacting with smart contracts + */ + export class PermitContract { address: string; tezos: TezosToolkit; @@ -81,12 +130,21 @@ export class PermitContract { this.tezos = tezos; } + /** + * getCounter: Retrieves the counter value from the contract's storage + */ + async getCounter() { const contract = await this.tezos.wallet.at(this.address); // @ts-ignore return (await contract.storage()).extension.counter.c[0]; } + /** + * generatePermit: Generates a permit for a given transfer operation by computing a + * transfer hash and constructing permit data. + */ + async generatePermit(transfer: TransferOperation) { // @ts-ignore const rpcClient = new RpcClient(this.tezos._rpc, "main"); @@ -130,6 +188,11 @@ export class PermitContract { return { bytes: permit_bytes, transfer_hash: transfer_hash }; } + /** + * permitCall: Calls the permit entrypoint on the contract with + * the provided permit operation parameters. + */ + async permitCall(op: PermitOperation) { const contract = await this.tezos.wallet.at(this.address); From e8b4c10bfb0fddfc1cfa777673e8f9c92066c6de Mon Sep 17 00:00:00 2001 From: lykimq Date: Thu, 18 Jan 2024 11:49:52 +0100 Subject: [PATCH 14/22] Minting: add progress bar and formating code --- examples/nft/src/lib/MintingComponent.svelte | 125 +++++++++++++------ 1 file changed, 90 insertions(+), 35 deletions(-) diff --git a/examples/nft/src/lib/MintingComponent.svelte b/examples/nft/src/lib/MintingComponent.svelte index 92411a7..e1336f0 100644 --- a/examples/nft/src/lib/MintingComponent.svelte +++ b/examples/nft/src/lib/MintingComponent.svelte @@ -1,71 +1,121 @@
- + {#if isMinting} +
+
+
+ {/if}
@@ -74,10 +124,17 @@ {:else}
{#each user_tokens as token, i} -
+
{#if Object.hasOwn(token.token, "metadata")} - Token thumnail -
{token.balance}
+ Token thumnail +
+ {token.balance} +
{/if}
{/each} @@ -85,5 +142,3 @@ {/if}
- - From 0a7ed1f260fe991c423dfcc4dd5d480ff8829c3d Mon Sep 17 00:00:00 2001 From: lykimq Date: Thu, 18 Jan 2024 12:48:41 +0100 Subject: [PATCH 15/22] Doc: add Readme for the example --- examples/nft/README.md | 85 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/examples/nft/README.md b/examples/nft/README.md index 336b83c..f48502d 100644 --- a/examples/nft/README.md +++ b/examples/nft/README.md @@ -1,5 +1,82 @@ -Toy example of a webapp using a gas station API and a permit contract. The webapp lets you mint a -NFT and transfer it to a “staking” contract even if you have no tez in your wallet. +# Gas Station Demo -The contracts are already deployed on Ghostnet. Their addresses, together with the API URL, can be -changed in the `.env` file. +This project is a toy web application demonstrating the use of a Gas Station API and a Permit Contract on the Tezos blockchain. + +The web app allows you to mint a non-fungible token (NFT) and transfer it to a "staking" contract, even if you have no Tez in your wallet. + + +## Prerequisites + +Before you start, ensure you have the following installed on your machine: + +- Node.js [Download and Install Node.js](https://nodejs.org/) +- npm (Node Package Manager): Comes with Node.js installation +- NVM (Node Version Manager): This is the key component for managing Node.js versions. + To install NVM for managing Node.js versions, refer to the official installation guide + at [install](https://github.com/nvm-sh/nvm#installing-and-updating) + +## Gas Station API and Permit Contract + +This project interacts with a Gas Station API and a Permit Contract on the Tezos blockchain. The contracts are already deloyed on Ghostnet. Configure the follwing in the `.env` file: + +- **Gas Station API URL**: the URL of the Gas Station API that handles the reply of operations to the blockchain. +- **Permit Contract Address**: the address of the Permit Contract on the Tezos blockchain. This contract is used for authorizing and signing operations. +- **Staking Contract Address (Optional)**: If applicable, the address of the Staking Contract where NFTs can be transferred. + +**Note:** The contract addresses and API URL can be changed in the `.env` file. + + +## Configuration + +Copy the `.env.example` file to `.env` and update the following variables based on your deployment: + +```dotenv +PUBLIC_TZKT_API=https://api.ghostnet.tzkt.io +PUBLIC_TEZOS_RPC=https://ghostnet.smartpy.io + +# Gas Station API URL +PUBLIC_GAS_STATION_API=http://localhost:8000 + +# Permit Contract Address +PUBLIC_PERMIT=KT1HUdxmgZUw21ED9gqELVvCty5d1ff41p7J + +# Staking Contract Address (if applicable) +PUBLIC_STAKING_CONTRACT=KT1VVotciVbvz1SopVfoXsxXcpyBBSryQgEn + +PUBLIC_APP_BASE_URL=http://localhost:5173 +``` + +## Running locally + +Navigate to the project folder: + +``` +cd examples/nft +``` + +To install dependencies, run: + +``` +npm install +``` +or + +``` +npm i +``` + +To launch the project on your localhost using a development server, run: + +``` +npm run dev +``` + +Your project will be accessible at http://localhost:5173/ + +To clean up generated files, run: + +``` +rm -rf node_modules +``` + +Make sure to configure the `.env` file with the appropriate values before running the project locally. \ No newline at end of file From 997e0f530aa88cbb227b0cb06eb4f1c1182ffdae Mon Sep 17 00:00:00 2001 From: lykimq Date: Thu, 18 Jan 2024 12:57:46 +0100 Subject: [PATCH 16/22] Git: add ignore .d.ts and .js --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ffeeff4..d312076 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /examples/nft/node_modules/ /examples/nft/.svelte-kit/ node_modules -dist \ No newline at end of file +dist +**/*d.ts +**/*.js \ No newline at end of file From 53d47f800b92b1cb0490be0281422687bafd1dde Mon Sep 17 00:00:00 2001 From: lykimq Date: Thu, 18 Jan 2024 13:32:01 +0100 Subject: [PATCH 17/22] HomePage: rework the introduction display on the webpage, formatting --- examples/nft/src/routes/+page.svelte | 66 ++++++++++++++++++---------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/examples/nft/src/routes/+page.svelte b/examples/nft/src/routes/+page.svelte index 16ce00d..aa5660b 100644 --- a/examples/nft/src/routes/+page.svelte +++ b/examples/nft/src/routes/+page.svelte @@ -2,43 +2,50 @@ import { myAccount, getPKH, Tezos } from "$lib/tezos"; import MintingComponent from "$lib/MintingComponent.svelte"; import StakingComponent from "$lib/StakingComponent.svelte"; - import { PUBLIC_PERMIT, PUBLIC_STAKING_CONTRACT } from '$env/static/public'; + import { PUBLIC_PERMIT, PUBLIC_STAKING_CONTRACT } from "$env/static/public"; // Shared between the two components let available_token_ids; -

Gas station demo

+
+

Gas Station Demo

-

This simple dApp can be used by a user having no ꜩ in their wallet, with operations being relayed - by the Gas Station. Minting a new NFT is done by a single call to the smart contract. - However, stashing an NFT to another smart contract requires a transfer, which must be - authorized off-chain through the signature of a permit.

+

+ This user-friendly dApp is tailored for individuals without any ꜩ in their + wallets. Operations are seamlessly relayed through the Gas Station. +

-
- {#if $myAccount == undefined} -

Please connect to use the dApp.

- {:else}{#await getPKH() then pkh} -
-

NFTs in your wallet

-

{PUBLIC_PERMIT}

- -
-
-

Stashed NFTs

-

{PUBLIC_STAKING_CONTRACT}

- -
- {/await} - {/if} -
+

+ Minting a new NFT is achieved with a single call to the smart + contract. However, stashing an NFT to another smart contract requires + a transfer, which must be authorized off-chain through the signature of a permit. +

+ +
+ {#if $myAccount === undefined} +

Please connect to use the dApp.

+ {:else}{#await getPKH() then pkh} +
+

NFTs in your wallet

+

{PUBLIC_PERMIT}

+ +
+
+

Stashed NFTs

+

{PUBLIC_STAKING_CONTRACT}

+ +
+ {/await} + {/if} +
+
From e42b6e3b046689450b32692247344c8010db18a4 Mon Sep 17 00:00:00 2001 From: lykimq Date: Thu, 18 Jan 2024 13:42:18 +0100 Subject: [PATCH 18/22] Doc: refine the README of the project --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0892a1f..2030c48 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Tezos Gas Station library -This library helps you -- use [Marigold's Gas Station API](https://github.com/marigold-dev/gas-station) in TypeScript -- create [TZIP-17 permit contracts](https://tzip.tezosagora.org/proposal/tzip-17/), which are FA2 - contracts that can be manipulated by a 3rd party (such as the gas station API). Permits are signed +This library facilitates the following: +- Utilizing [Marigold's Gas Station API](https://github.com/marigold-dev/gas-station) in TypeScript. +- Creating [TZIP-17 permit contracts](https://tzip.tezosagora.org/proposal/tzip-17/), which are FA2 + contracts capable of being manipulated by a 3rd party, such as the gas station API. Permits are signed off-chain and can be posted and executed by anyone. -A toy webapp example is available in the `examples/` directory. +An example of a toy web app is provided in the `examples/nft` directory. -Contributions welcome at https://github.com/marigold-dev/gas-station-lib. +Feel free to contribute and provide feedback on https://github.com/marigold-dev/gas-station-lib. From cdde4c7ee4ac87c2f58910d5c003c4a7f1941a15 Mon Sep 17 00:00:00 2001 From: lykimq Date: Mon, 22 Jan 2024 11:44:51 +0100 Subject: [PATCH 19/22] Console_log: remove console_log, and formatting --- examples/nft/src/lib/MintingComponent.svelte | 4 - examples/nft/src/lib/StakingComponent.svelte | 112 ++++++++++--------- 2 files changed, 62 insertions(+), 54 deletions(-) diff --git a/examples/nft/src/lib/MintingComponent.svelte b/examples/nft/src/lib/MintingComponent.svelte index e1336f0..6340d08 100644 --- a/examples/nft/src/lib/MintingComponent.svelte +++ b/examples/nft/src/lib/MintingComponent.svelte @@ -66,8 +66,6 @@ ]) .toTransferParams(); - console.log(mint_op); - mintingProgress = 50; // Set progress to 50% after Taquito operation const response = await gas_api.postOperation(user_address, { @@ -75,8 +73,6 @@ parameters: mint_op.parameter, }); - console.log(response); - mintingProgress = 100; // Set progress to 100% after gas station operation } catch (error) { console.error("Minting failed:", error); diff --git a/examples/nft/src/lib/StakingComponent.svelte b/examples/nft/src/lib/StakingComponent.svelte index 179d1d0..afc8120 100644 --- a/examples/nft/src/lib/StakingComponent.svelte +++ b/examples/nft/src/lib/StakingComponent.svelte @@ -1,10 +1,19 @@ +
- +
@@ -109,10 +116,17 @@ {:else}
{#each user_tokens as token, i} -
+
{#if Object.hasOwn(token.token, "metadata")} - Token thumnail -
{token.balance}
+ Token thumnail +
+ {token.balance} +
{/if}
{/each} @@ -120,5 +134,3 @@ {/if}
- - From 36a91b2c88f387311099cb613d54a043a6f8c6bf Mon Sep 17 00:00:00 2001 From: lykimq Date: Mon, 22 Jan 2024 13:31:33 +0100 Subject: [PATCH 20/22] StakingComponent: add progress bar for stashing button, add comments --- examples/nft/src/lib/StakingComponent.svelte | 158 +++++++++++++------ 1 file changed, 112 insertions(+), 46 deletions(-) diff --git a/examples/nft/src/lib/StakingComponent.svelte b/examples/nft/src/lib/StakingComponent.svelte index afc8120..6b2c03b 100644 --- a/examples/nft/src/lib/StakingComponent.svelte +++ b/examples/nft/src/lib/StakingComponent.svelte @@ -13,15 +13,31 @@ } from "$env/static/public"; import { SigningType } from "@airgap/beacon-types"; + /** + * [user_address] stores the user's address + */ export let user_address = ""; + + /** + * [available_token_ids] a set of available token IDs + * + */ export let available_token_ids = new Set(); let user_tokens: any[] = []; + /** + * Convert an IPFS to HTTPS + * @param url + */ function IPFSLinkToHTTPS(url: string) { return url.replace("ipfs://", "https://ipfs.io/ipfs/"); } + /** + * Fetch and store the user's token balances based on their address + * @param address + */ function get_tokens(address: string) { return fetch( `${PUBLIC_TZKT_API}/v1/tokens/balances?account=${address}&token.contract=${PUBLIC_PERMIT}&balance.gt=0`, @@ -34,11 +50,23 @@ }); } + // Add progress bar for stashing + let isStashing = true; + let stashProgress = 0; + + /** + * [stash] function responsible for performing the "stash" operation, + * which involves creating a permit for transferring a token to a staking + * contract and sending it to the API. + * @param user_address + */ function stash(user_address: string) { const n = available_token_ids?.size; if (n === 0) { return; } + + // Randomly selects a token ID from available tokens; const token_id = [...available_token_ids][Math.floor(Math.random() * n)]; // √ Build the transfer // √ Build the permit @@ -47,56 +75,86 @@ // √ Build the transfer operation // √ Send it to the API (async () => { - const gas_api = new GasStation({ - apiURL: PUBLIC_GAS_STATION_API, - }); - const permit_contract = new PermitContract(PUBLIC_PERMIT, Tezos); + isStashing = true; + + try { + // Initializes a Gas Station instance using the provided API URL + const gas_api = new GasStation({ + apiURL: PUBLIC_GAS_STATION_API, + }); + + // Initializes a PermitContract instance using the public permit address and the + // Tezos object + const permit_contract = new PermitContract(PUBLIC_PERMIT, Tezos); + + // Generate permit data for the transfer operation. Specifies the sender, + // receiver (staking contract), token ID, and amount + const permit_data = await permit_contract.generatePermit({ + from_: user_address, + txs: [ + { + to_: PUBLIC_STAKING_CONTRACT, + token_id: token_id, + amount: 1, + }, + ], + }); + + // Request signature for permit + const signature = ( + await ( + await wallet.client + ).requestSignPayload({ + signingType: SigningType.MICHELINE, + payload: permit_data.bytes, + }) + ).signature; + const activeAccount = await wallet.client.getActiveAccount(); + + if (!activeAccount) { + throw new Error("No active account, cannot stash."); + } - const permit_data = await permit_contract.generatePermit({ - from_: user_address, - txs: [ + // Permit call and staking contract operation + const permit_op = await permit_contract.permitCall({ + publicKey: activeAccount.publicKey, + signature: signature, + transferHash: permit_data.transfer_hash, + }); + + // Get the staking contract instance + const staking_contract = await Tezos.wallet.at(PUBLIC_STAKING_CONTRACT); + + // Build a transfer operation for stashing the token in the staking contract + const staking_op = await staking_contract.methods + .stash(1, token_id, user_address) + .toTransferParams(); + + stashProgress = 50; + + // Post Operations to Gas Station API + const response = await gas_api.postOperations(user_address, [ { - to_: PUBLIC_STAKING_CONTRACT, - token_id: token_id, - amount: 1, + destination: permit_op.to, + parameters: permit_op.parameter, }, - ], - }); + { + destination: staking_op.to, + parameters: staking_op.parameter, + }, + ]); - const signature = ( - await ( - await wallet.client - ).requestSignPayload({ - signingType: SigningType.MICHELINE, - payload: permit_data.bytes, - }) - ).signature; - const activeAccount = await wallet.client.getActiveAccount(); - - if (!activeAccount) { - throw new Error("No active account, cannot stash."); + stashProgress = 100; + } catch (error) { + console.error("Stashing failed:", error); + console.error("URL being fetched:", PUBLIC_GAS_STATION_API); + stashProgress = 0; + } finally { + // Introduce a short delay before setting isStashing to false + // to give Svelte time to update the UI + await new Promise((resolve) => setTimeout(resolve, 0)); + isStashing = false; } - - const permit_op = await permit_contract.permitCall({ - publicKey: activeAccount.publicKey, - signature: signature, - transferHash: permit_data.transfer_hash, - }); - const staking_contract = await Tezos.wallet.at(PUBLIC_STAKING_CONTRACT); - const staking_op = await staking_contract.methods - .stash(1, token_id, user_address) - .toTransferParams(); - - const response = await gas_api.postOperations(user_address, [ - { - destination: permit_op.to, - parameters: permit_op.parameter, - }, - { - destination: staking_op.to, - parameters: staking_op.parameter, - }, - ]); })(); } @@ -107,7 +165,15 @@
- + +
From f3a7b008a9cabf2017db331a22ae982156886ebb Mon Sep 17 00:00:00 2001 From: Quentin Burg Date: Mon, 22 Jan 2024 16:24:54 +0100 Subject: [PATCH 21/22] :bug: fix progress bar display --- examples/nft/src/lib/MintingComponent.svelte | 16 +++++++++++++++- examples/nft/src/lib/Nav.svelte | 2 +- examples/nft/src/lib/StakingComponent.svelte | 20 +++++++++++++++++--- examples/nft/src/routes/+page.svelte | 11 ----------- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/examples/nft/src/lib/MintingComponent.svelte b/examples/nft/src/lib/MintingComponent.svelte index 6340d08..1a6ae3b 100644 --- a/examples/nft/src/lib/MintingComponent.svelte +++ b/examples/nft/src/lib/MintingComponent.svelte @@ -17,7 +17,7 @@ /*** Generate a random integer up to a given maximum value. * It uses to generate a random token ID when minting. */ - function randomInt(max) { + function randomInt(max: number) { return Math.floor(Math.random() * max); } @@ -138,3 +138,17 @@ {/if}
+ + \ No newline at end of file diff --git a/examples/nft/src/lib/Nav.svelte b/examples/nft/src/lib/Nav.svelte index de31c0a..e1b1fd7 100644 --- a/examples/nft/src/lib/Nav.svelte +++ b/examples/nft/src/lib/Nav.svelte @@ -1,7 +1,7 @@