diff --git a/demos/client-example/package.json b/demos/client-example/package.json index d25937465a..cd83e00e09 100644 --- a/demos/client-example/package.json +++ b/demos/client-example/package.json @@ -1,50 +1,51 @@ { - "name": "@prosopo/client-example", - "version": "0.1.11", - "private": true, - "dependencies": { - "@emotion/react": "^11.9.3", - "@emotion/styled": "^11.9.3", - "@mui/material": "^5.9.1", - "@mui/styles": "^5.9.1", - "@prosopo/procaptcha-react": "^0.1.11", - "@testing-library/jest-dom": "^5.16.4", - "bootstrap": "^5.2.2", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "5.0.1", - "typescript": "^4.7.4", - "web-vitals": "^2.1.4" - }, - "devDependencies": { - "cypress": "^10.8.0", - "cypress-promise": "^1.1.0", - "eslint-config-react-app": "^7.0.1" - }, - "scripts": { - "start": "PORT=3001 react-scripts start", - "build": "NODE_ENV=production react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", - "cypress:open": "cypress open", - "lint:fix": "npx eslint . --fix" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } + "name": "@prosopo/client-example", + "version": "0.1.11", + "private": true, + "dependencies": { + "@emotion/react": "^11.9.3", + "@emotion/styled": "^11.9.3", + "@mui/material": "^5.9.1", + "@prosopo/procaptcha-react": "^0.1.11", + "@testing-library/jest-dom": "^5.16.4", + "@types/react-dom": "^18.2.4", + "bootstrap": "^5.2.2", + "electron": "25.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "5.0.1", + "typescript": "^4.7.4", + "web-vitals": "^2.1.4" + }, + "devDependencies": { + "cypress": "^10.8.0", + "cypress-promise": "^1.1.0", + "eslint-config-react-app": "^7.0.1" + }, + "scripts": { + "start": "PORT=3001 react-scripts start", + "build": "NODE_ENV=production react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject", + "cypress:open": "cypress open", + "lint:fix": "npx eslint . --fix" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } } diff --git a/demos/client-example/src/index.tsx b/demos/client-example/src/index.tsx index ac4d5eeb82..c0c7b88bb3 100644 --- a/demos/client-example/src/index.tsx +++ b/demos/client-example/src/index.tsx @@ -1,16 +1,16 @@ import React from 'react' -import ReactDOM from 'react-dom' import App from './App' import reportWebVitals from './reportWebVitals' - import CssBaseline from '@mui/material/CssBaseline' +import ReactDOM from 'react-dom/client' + +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) -ReactDOM.render( +root.render( - , - document.getElementById('root') as HTMLElement + ) // If you want to start measuring performance in your app, pass a function diff --git a/demos/demo-nft-marketplace/.babelrc b/demos/demo-nft-marketplace/.babelrc deleted file mode 100644 index dad233a84f..0000000000 --- a/demos/demo-nft-marketplace/.babelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "presets": ["next/babel"], - "plugins": ["css-modules-transform"] - } - \ No newline at end of file diff --git a/demos/demo-nft-marketplace/.nvmrc b/demos/demo-nft-marketplace/.nvmrc deleted file mode 100644 index cab13a7963..0000000000 --- a/demos/demo-nft-marketplace/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v14.17.0 diff --git a/demos/demo-nft-marketplace/README.md b/demos/demo-nft-marketplace/README.md deleted file mode 100644 index 083a5ed7c8..0000000000 --- a/demos/demo-nft-marketplace/README.md +++ /dev/null @@ -1,101 +0,0 @@ -## What is the Demo NFT Marketplace? - -The Demo NFT Marketplace is an open source NFT marketplace built by Rarible DAO using the Rarible Protocol. Prosopo has -forked and modified the marketplace to work -with [OpenBrush's PSP34 contracts](https://github.com/Supercolony-net/openbrush-contracts/tree/main/examples/psp34). The -PSP34 contracts have also been modified so that they -are [protected](https://github.com/prosopo/demo-nft-marketplace/blob/57fe32a36d2988d3076835fc3ebe3a4dad60efa3/contracts/lib.rs#L209) -by Prosopo's human verification system. - -### Demo - -[Checkout the demo app!](https://nft.demo.prosopo.io/) - -Main demo page -Product demo page - -### How to run locally - -#### 1. Build the contract - -The contract metadata JSON is required to interact with the contract from the frontend. You can obtain this by building -the contract sources. Run the following command from the root of the repository to build the contract. - -```bash -docker run --rm -it -v $(pwd):/sources paritytech/contracts-ci-linux:production \ - cargo +nightly contract build --manifest-path=/sources/contracts/Cargo.toml -``` - -#### 2. Install packages and set up environment variables - -```bash -npm install - -npm run setup -``` - -#### 3. Fund your test account - -##### 3a. Wallet Setup - -You will need to have a test account present in a polkadot wallet. Choose either -[talisman](https://chrome.google.com/webstore/detail/talisman-polkadot-wallet/fijngjgcjhjmmpcmkeiomlglpeiijkld) -, [subwallet](https://chrome.google.com/webstore/detail/subwallet-polkadot-extens/onhogfjeacnfoofkfgppdlbmlmnplgbn) -or [polkadotjs](https://polkadot.js.org/extension/). Please only install *one* wallet in your browser! Once you have -installed a wallet, create an account. - -##### 3b. Send some funds to your wallet - -Go to [polkadot apps](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/accounts) and select the -development endpoint (`ws://localhost:9944`). - -![Select endpoint](assets/img-endpoint.png) - -Send some funds from one of the test accounts (Alice etc.) to your test account. - -![Send funds](assets/img-send-funds.png) - -#### 4. Start the app - -```bash -npm run dev -``` - -### How it works - -The NFT marketplace is composed of two parts - the website and the smart contract backend. A user is requested to -connect their web3 account when they enter the marketplace. - -![Selecting an account](https://raw.githubusercontent.com/prosopo/demo-nft-marketplace/article/.github/images/screenshot3.png) - -Once an account has been selected they can begin the purchase process. Upon clicking buy, the website frontend checks if -the user's account has previously completed captcha challenges by calling -the [prosopo protocol contract](https://github.com/prosopo/protocol/). If the user has answered the majority of -previous captcha challenges correctly within a certain timeframe, they will be allowed to purchase an NFT. Otherwise, -they will be shown a captcha challenge. - -#### `is_human` checks in Dapp frontend website - -The frontend checks the `is_human` function in the protocol contract before it allows the user to purchase. - -https://github.com/prosopo/demo-nft-marketplace/blob/65669d7d3909bb287718b028e95e013f1c29ee78/src/components/Modal/CheckoutModal.tsx#L49-L59 - -#### `is_human` checks in NFT Contract - -There are additional checks in the NFT marketplace smart contract that prevent the user from calling the NFT marketplace -contract directly, bypassing the frontend checks. - -The threshold number of captcha that the user must correctly answer is set to 80% within the last 5 minutes. In this -example, the values are fixed in the NFT contract however they could reside elsewhere. - -https://github.com/prosopo/demo-nft-marketplace/blob/65669d7d3909bb287718b028e95e013f1c29ee78/contracts/lib.rs#L78-L79 - -https://github.com/prosopo/demo-nft-marketplace/blob/65669d7d3909bb287718b028e95e013f1c29ee78/contracts/lib.rs#L203-L217 - -https://github.com/prosopo/demo-nft-marketplace/blob/65669d7d3909bb287718b028e95e013f1c29ee78/contracts/lib.rs#L186-L200 - -### Flow - -The entire process flow can be visualised as follows. - -![Main CAPTCHA flow](https://www.prosopo.io/static/maincaptchaflow.jpg) diff --git a/demos/demo-nft-marketplace/assets/img-endpoint.png b/demos/demo-nft-marketplace/assets/img-endpoint.png deleted file mode 100644 index 712c82b8f7..0000000000 Binary files a/demos/demo-nft-marketplace/assets/img-endpoint.png and /dev/null differ diff --git a/demos/demo-nft-marketplace/assets/img-send-funds.png b/demos/demo-nft-marketplace/assets/img-send-funds.png deleted file mode 100644 index 3ca871fe4c..0000000000 Binary files a/demos/demo-nft-marketplace/assets/img-send-funds.png and /dev/null differ diff --git a/demos/demo-nft-marketplace/contracts/Cargo.toml b/demos/demo-nft-marketplace/contracts/Cargo.toml deleted file mode 100644 index 61acc8933b..0000000000 --- a/demos/demo-nft-marketplace/contracts/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "demo_nft_contract" -version = "0.2.0" -authors = ["Sinisa Canak sinisa.canak997@gmail.com"] -edition = "2021" - -[dependencies] -ink = { git = "https://github.com/paritytech/ink", rev="4655a8b4413cb50cbc38d1b7c173ad426ab06cde", default-features = false } -scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } -openbrush = { rev="14ff655a0d83440f40c57c82d9a33e4c5b981da7", git = "https://github.com/727-Ventures/openbrush-contracts", default-features = false, features = ["psp34", "ownable"] } -prosopo = { git = "https://github.com/prosopo/protocol", branch = "inkv4", default-features = false, features = ["ink-as-dependency"] } -base64 = { version = "0.13.0", default-features = false, features = ["alloc"] } -serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } -serde-json-core = { version = "0.4.0", default-features = false, features = ["heapless"] } - -[lib] -name = "demo_nft_contract" -path = "lib.rs" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", -] - -[features] -default = ["std"] -std = [ - "ink/std", - "scale/std", - "scale-info/std", - "openbrush/std", - "prosopo/std", - "serde-json-core/std" -] -ink-as-dependency = [] diff --git a/demos/demo-nft-marketplace/contracts/lib.rs b/demos/demo-nft-marketplace/contracts/lib.rs deleted file mode 100644 index 8337ea8024..0000000000 --- a/demos/demo-nft-marketplace/contracts/lib.rs +++ /dev/null @@ -1,341 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] -#![feature(min_specialization)] - -mod nfts; - -#[brush::contract] -pub mod demo_nft_contract { - use base64; - use serde_json_core as serde_json; - - use openbrush::{ - contracts::{ - ownable::*, - psp34::extensions::{enumerable::*, metadata::*}, - }, - modifiers, - }; - - use ink::prelude::format; - use ink::prelude::{string::String, vec::Vec}; - use ink::storage::traits::SpreadAllocate; - - use prosopo::ProsopoRef; - - use crate::nfts::{self, *}; - - #[derive( - Default, - SpreadAllocate, - PSP34Storage, - PSP34MetadataStorage, - PSP34EnumerableStorage, - OwnableStorage, - )] - #[ink(storage)] - pub struct DemoNFT { - #[PSP34StorageField] - psp34: PSP34Data, - next_id: u8, - #[PSP34MetadataStorageField] - metadata: PSP34MetadataData, - #[PSP34EnumerableStorageField] - enumdata: PSP34EnumerableData, - #[OwnableStorageField] - ownable: OwnableData, - // / The address of the prosopo bot protection contract - prosopo_account: AccountId, - } - - #[derive(scale::Encode, scale::Decode)] - #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] - pub struct NFT { - id: Id, - owner: AccountId, - token_uri: String, - on_sale: bool, - price: u128, - } - - enum Attribute { - TokenUri, - OnSale, - Price, - } - - impl Attribute { - pub fn to_key(&self) -> Vec { - let key = match self { - Attribute::TokenUri => "token_uri", - Attribute::OnSale => "on_sale", - Attribute::Price => "price", - }; - - String::from(key).into_bytes() - } - } - - pub const HUMAN_THRESHOLD: u8 = 50; - pub const RECENCY: u32 = 5 * 60 * 1000; // 5 mins - - fn bool_to_bytes(value: bool) -> Vec { - match value { - true => Vec::from([1]), - false => Vec::from([0]), - } - } - - fn bytes_to_bool(value: Vec) -> bool { - value.last().unwrap().to_be() == 1 - } - - impl PSP34 for DemoNFT {} - - impl PSP34Metadata for DemoNFT {} - impl PSP34Enumerable for DemoNFT {} - impl Ownable for DemoNFT {} - - impl DemoNFT { - #[ink(constructor)] - pub fn new(prosopo_account: AccountId) -> Self { - ink::codegen::initialize_contract(|instance: &mut Self| { - let caller = instance.env().caller(); - instance._init_with_owner(caller); - instance.prosopo_account = prosopo_account; - instance.mint_all(); - }) - } - - #[ink(message)] - #[modifiers(only_owner)] - pub fn mint_all(&mut self) -> Result<(), PSP34Error> { - for ele in nfts::Nfts::get_list() { - self.mint(ele.name, ele.description, ele.image); - } - Ok(()) - } - - #[ink(message)] - #[modifiers(only_owner)] - pub fn mint( - &mut self, - name: String, - description: String, - image: String, - ) -> Result { - let id = Id::U8(self.next_id); - self._mint_to(self.env().caller(), id.clone())?; - self.next_id += 1; - let metadata = Metadata { - name, - description, - image, - }; - let stringified = serde_json::to_string::(&metadata).unwrap(); - let encoded = base64::encode(stringified); - - let token_uri = format!("data:application/json;base64,{}", encoded); - self._set_attribute( - id.clone(), - Attribute::TokenUri.to_key(), - token_uri.into_bytes(), - ); - self._set_attribute(id.clone(), Attribute::OnSale.to_key(), bool_to_bytes(true)); - self._set_attribute( - id.clone(), - Attribute::Price.to_key(), - Vec::from(Nfts::PRICE.to_be_bytes()), - ); - - Ok(id) - } - - #[ink(message)] - pub fn token_uri(&self, token: Id) -> String { - let token_uri_attribute = self - .get_attribute(token, Attribute::TokenUri.to_key()) - .unwrap(); - String::from_utf8(token_uri_attribute).unwrap() - } - - // // // // // // // // // // // // // nonstandard nft smart contract functionalities // // // // // // // // // // // // // - fn get_token_by_index( - &self, - owner: &Option, - index: &u128, - ) -> Result { - self.enumdata.enumerable.get_by_index(owner, index) - } - - #[ink(message)] - pub fn on_sale(&self, token: Id) -> bool { - let on_sale_attribute = self - .get_attribute(token, Attribute::OnSale.to_key()) - .unwrap(); - bytes_to_bool(on_sale_attribute) - } - - #[ink(message)] - pub fn price(&self, token: Id) -> u128 { - let price_attribute = self - .get_attribute(token, Attribute::Price.to_key()) - .unwrap(); - u128::from_be_bytes(price_attribute.try_into().unwrap()) - } - - /// Calls the `Prosopo` contract to check if `user` is human - #[ink(message)] - pub fn is_human(&self, user: AccountId) -> Result { - let prosopo_instance: ProsopoRef = - ink_env::call::FromAccountId::from_account_id(self.prosopo_account); - let is_human = prosopo_instance.dapp_operator_is_human_user(user, HUMAN_THRESHOLD); - // check that the captcha was completed within the last X seconds - let last_correct_captcha = prosopo_instance.dapp_operator_last_correct_captcha(user); - - if is_human.is_err() || last_correct_captcha.is_err() { - return Ok(false); - } - - return Ok(last_correct_captcha.unwrap().before_ms <= RECENCY && is_human.unwrap()); - } - - #[ink(message, payable)] - pub fn buy(&mut self, token: Id) -> Result<(), PSP34Error> { - let token_owner = self.owner_of(token.clone()); - - let amount_sent: Balance = self.env().transferred_value(); - let caller = self.env().caller(); - - let is_human = self.is_human(caller); - - let res = match is_human { - Ok(true) => Ok(()), - Ok(false) => Err(PSP34Error::Custom(String::from( - "User not considered human", - ))), - Err(x) => Err(x), - }; - - if res.is_err() { - return res; - } - - if amount_sent < Nfts::PRICE { - self.env().transfer(caller, amount_sent).ok(); - return Err(PSP34Error::Custom(String::from("Amount sent too low."))); - } else if amount_sent > Nfts::PRICE { - self.env().transfer(caller, amount_sent - Nfts::PRICE).ok(); - } - - if token_owner.is_none() { - self.env().transfer(caller, amount_sent).ok(); - return Err(PSP34Error::Custom(String::from("Token not found."))); - } - - let owner = token_owner.unwrap(); - - if !self.on_sale(token.clone()) { - self.env().transfer(caller, amount_sent).ok(); - return Err(PSP34Error::Custom(String::from("Token not for sale."))); - } - - self.psp34 - .operator_approvals - .insert((owner.clone(), caller.clone(), Some(token.clone())), &()); - - self.transfer(caller, token.clone(), Vec::new()); - - self._set_attribute(token, Attribute::OnSale.to_key(), bool_to_bytes(false)); - - self.env().transfer(owner, amount_sent).ok(); - - Ok(()) - } - - /// Fetches newest tokens - #[ink(message)] - pub fn get_tokens( - &self, - page_size: Option, - offset: Option, - owner: Option, - ) -> Result, PSP34Error> { - let size: u128 = page_size.unwrap_or(20).into(); - - if size == 0 { - return Err(PSP34Error::Custom(String::from( - "Invalid page size! Must be 1 or greater.", - ))); - } - - let mut tokens = Vec::new(); - - let _offset = if offset.is_some() { offset.unwrap() } else { 0 }; - let balance: u128 = if owner.is_some() { - self.balance_of(owner.unwrap()).into() - } else { - self.total_supply() - }; - - if balance <= _offset { - return Ok(tokens); - } - - let mut i = balance - _offset - 1; - let end = if balance > _offset + size { - balance - _offset - size - } else { - 0 - }; - - while let Ok(token) = self.get_token_by_index(&owner, &i) { - let id = token.clone(); - let owner = if owner.is_some() { - owner.unwrap() - } else { - self._owner_of(&token.clone()).unwrap() - }; - - let token_uri = self.token_uri(token.clone()); - let on_sale = self.on_sale(token.clone()); - let price = self.price(token.clone()); - - tokens.push(NFT { - id, - owner, - token_uri, - on_sale, - price, - }); - - if i == 0 { - break; - } - i -= 1; - if i < end { - break; - } - } - - Ok(tokens) - } - } - - #[cfg(test)] - mod tests { - use core::cmp::Ordering; - use ink; - - use super::*; - - #[ink::test] - fn test_new_works() { - let operator_account = AccountId::from([0x1; 32]); - let contract = DemoNFT::new(operator_account); - assert_eq!( - contract.prosopo_account.cmp(&operator_account), - Ordering::Equal - ); - } - } -} diff --git a/demos/demo-nft-marketplace/contracts/nfts.rs b/demos/demo-nft-marketplace/contracts/nfts.rs deleted file mode 100644 index b1e1fe491e..0000000000 --- a/demos/demo-nft-marketplace/contracts/nfts.rs +++ /dev/null @@ -1,35 +0,0 @@ -use ink::prelude::{format, string::String, vec::Vec}; -use serde::Serialize; - -#[derive(Serialize)] -pub struct Metadata { - pub name: String, - pub description: String, - pub image: String, -} - -// TODO: make this generatable (upload images and generate structs with new urls) -// TODO: simplify/lessen the amount of content -pub mod Nfts { - use super::*; - - const DESC: &'static str = "Generated via https://bigheads.io/"; - - pub const PRICE: u128 = 2_000_000_000_000; - - pub fn get_list() -> Vec { - let mut index = 0; - let arr = [0; 100]; - Vec::from(arr.map(|_| { - index += 1; - Metadata { - name: format!("bighead#{}", index), - description: String::from(DESC), - image: format!( - "https://prosopo.github.io/demo-assets/nft-marketplace/img/bighead-{}.svg", - index - ), - } - })) - } -} diff --git a/demos/demo-nft-marketplace/custom.d.ts b/demos/demo-nft-marketplace/custom.d.ts deleted file mode 100644 index e622aa1c5e..0000000000 --- a/demos/demo-nft-marketplace/custom.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare module '*.svg' { - const content: any - export default content -} - -declare module '*.png' { - const content: any - export default content -} diff --git a/demos/demo-nft-marketplace/docker/contracts.demo.dockerfile b/demos/demo-nft-marketplace/docker/contracts.demo.dockerfile deleted file mode 100644 index de2366614f..0000000000 --- a/demos/demo-nft-marketplace/docker/contracts.demo.dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -ARG BASE=paritytech -ARG TAG=latest - -FROM ${BASE}/contracts-ci-linux:${TAG} as builder -RUN mkdir -p /usr/src/docker -COPY ./docker/contracts.deploy.demo.sh /usr/src/docker/ -COPY ./docker/contracts.deploy.contract.sh /usr/src/docker/ -COPY ./demos/demo-nft-marketplace/contracts/ /usr/src/build/contracts -ENV SUBSTRATE_ENDPOINT=ws://substrate-node -ENV SUBSTRATE_PORT=9944 -ENV DEPLOYER_SURI=//Alice -ENV DEMO_CONTRACT_SOURCE=/usr/src/demos/demo-nft-marketplace/contracts -ENV DEMO_CONTRACT_WASM=./target/ink/demo_nft_contract.wasm -ENV DEMO_CONTRACT_CONSTRUCTOR=new -ENV DEMO_CONTRACT_ARGS_OWNER=5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY -ENV DEMO_CONTRACT_ENDOWMENT=0 -WORKDIR /usr/src/demos/demo-nft-marketplace/contracts -RUN echo $(ls -lah /usr/src/build/contracts) -WORKDIR /usr/src/build/contracts -ARG ARCHITECTURE=x86_64 -RUN /usr/local/rustup/toolchains/nightly-${ARCHITECTURE}-unknown-linux-gnu/bin/cargo metadata --format-version 1 --manifest-path Cargo.toml -#RUN cargo +nightly contract build -WORKDIR /usr/src -RUN chmod +x /usr/src/docker/contracts.deploy.demo.sh -CMD ["bash", "/usr/src/docker/contracts.deploy.demo.sh"] diff --git a/demos/demo-nft-marketplace/docker/contracts.deploy.contract.sh b/demos/demo-nft-marketplace/docker/contracts.deploy.contract.sh deleted file mode 100755 index 88d7814fd5..0000000000 --- a/demos/demo-nft-marketplace/docker/contracts.deploy.contract.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash -# flags -function usage() { - cat < /usr/src/.env diff --git a/demos/demo-nft-marketplace/env.development b/demos/demo-nft-marketplace/env.development deleted file mode 100644 index 1ea607fde5..0000000000 --- a/demos/demo-nft-marketplace/env.development +++ /dev/null @@ -1,9 +0,0 @@ -NEXT_PUBLIC_API_BASE_URL=http://localhost:3000 -NEXT_PUBLIC_API_PATH_PREFIX=/v1/prosopo -NEXT_PUBLIC_CONTRACT_ADDRESS=5FwHfbt6ua4GKcbNVwZvvgTwXszp1yj8G3LvK2FtaEtbmAJ9 -NEXT_PUBLIC_CONTRACT_URL=ws://localhost:9944 -MAIN_ACCOUNT_MNEMONIC=bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice -IMAGE_DOMAINS=ipfs.io,prosopo.github.io -NEXT_PUBLIC_WEB2= -NEXT_PUBLIC_PROSOPO_CONTRACT_ADDRESS=5DoZy6B2ekXLdCWA9b2Kk5EdC8LZg3PeyJQW4h8vSMZa8tvS -NEXT_DAPP_NAME=demo_nft_marketplace diff --git a/demos/demo-nft-marketplace/netlify.toml b/demos/demo-nft-marketplace/netlify.toml deleted file mode 100644 index 1a1c999960..0000000000 --- a/demos/demo-nft-marketplace/netlify.toml +++ /dev/null @@ -1,13 +0,0 @@ -[build] - environment = { AWS_LAMBDA_JS_RUNTIME = "nodejs14.x" } - publish = "out" - command = "yarn run build-prod" - -[template] - incoming-hooks = ["Contentful"] - -[template.environment] - AWS_LAMBDA_JS_RUNTIME = "nodejs14.x" - NEXT_PUBLIC_CONTRACT_ID = "REPLACE_WITH_CONTRACT_ADDRESS" - NEXT_PUBLIC_NETWORK_ID = "REPLACE_WITH_NETWORK_ID" - NEXT_PUBLIC_NETWORK_NAME = "REPLACE_WITH_NETWORK_NAME" \ No newline at end of file diff --git a/demos/demo-nft-marketplace/next-env.d.ts b/demos/demo-nft-marketplace/next-env.d.ts deleted file mode 100644 index 2532e77aba..0000000000 --- a/demos/demo-nft-marketplace/next-env.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/demos/demo-nft-marketplace/next.config.js b/demos/demo-nft-marketplace/next.config.js deleted file mode 100644 index f47a58e44e..0000000000 --- a/demos/demo-nft-marketplace/next.config.js +++ /dev/null @@ -1,40 +0,0 @@ -const NodePolyfillPlugin = require('node-polyfill-webpack-plugin') -const withImages = require('next-images') -const webpack = require('webpack') - -module.exports = { - ...withImages({ - swcMinify: true, - esModule: true, - webpack: (config, { isServer }) => { - if (!isServer) { - config.resolve.fallback = { - ...config.resolve.fallback, - fs: require.resolve('browserify-fs'), - } - } - - config.plugins = [ - ...config.plugins, - new NodePolyfillPlugin(), - new webpack.ContextReplacementPlugin(/mocha|typescript|redspot|express/), - new webpack.IgnorePlugin({ - resourceRegExp: /ts-node|perf_hooks/, - }), - ] - - return config - }, - }), - images: { - domains: process.env.IMAGE_DOMAINS.split(','), - disableStaticImages: true, - dangerouslyAllowSVG: true, - }, - typescript: { - ignoreBuildErrors: true, - }, - serverRuntimeConfig: { - MAIN_ACCOUNT_MNEMONIC: process.env.MAIN_ACCOUNT_MNEMONIC, - }, -} diff --git a/demos/demo-nft-marketplace/package.json b/demos/demo-nft-marketplace/package.json deleted file mode 100644 index 8a4c37319c..0000000000 --- a/demos/demo-nft-marketplace/package.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "name": "@prosopo/demo-nft-marketplace", - "version": "0.1.11", - "private": true, - "scripts": { - "dev": "next dev -p 3002", - "build": "next build", - "build-prod": "next build", - "start": "next start -p 3002", - "build:tailwind": "postcss src/scss/base.scss -o src/scss/tailwind.scss", - "lint": "eslint '*/**/*.{ts,tsx}'", - "lint:fix": "npx eslint . --fix", - "find:unused": "next-unused", - "setup": "node ./scripts/setup.js" - }, - "dependencies": { - "@emotion/react": "^11.9.3", - "@emotion/styled": "^11.9.3", - "@headlessui/react": "^1.6.6", - "@heroicons/react": "^1.0.6", - "@polkadot/api": "9.13.6", - "@polkadot/api-contract": "9.13.6", - "@polkadot/rpc-provider": "9.13.6", - "@polkadot/util": "10.3.1", - "@polkadot/util-crypto": "10.3.1", - "@prosopo/procaptcha-react": "^0.1.10", - "axios": "^0.27.2", - "browserify-fs": "^1.0.0", - "ethereum-blockies-base64": "^1.0.2", - "next": "12.1.4", - "next-images": "^1.8.4", - "node-polyfill-webpack-plugin": "^2.0.0", - "react": "^17.0.0", - "react-currency-input-field": "^3.6.4", - "react-dom": "^17.0.0", - "react-hot-toast": "^2.3.0", - "react-top-loading-bar": "^2.1.0", - "webpack": "^5.73.0" - }, - "devDependencies": { - "@babel/core": "^7.18.9", - "@polkadot/types": "9.13.6", - "@tailwindcss/forms": "^0.5.2", - "@types/node": "^18.0.6", - "@types/react": "^18.0.15", - "autoprefixer": "^10.4.7", - "babel-loader": "^8.2.5", - "babel-plugin-css-modules-transform": "^1.6.2", - "electron": "^19.0.8", - "fs-extra": "^10.1.0", - "husky": "^8.0.1", - "next-unused": "^0.0.6", - "postcss": "^8.4.14", - "postcss-preset-env": "^7.7.2", - "prettier": "^2.7.1", - "react-docgen-typescript-loader": "^3.7.2", - "tailwindcss": "^3.1.6", - "typescript": "^4.7.4" - }, - "resolutions": { - "did-jwt": "5.1.0" - }, - "husky": { - "hooks": { - "pre-commit": "npx lint-staged" - } - }, - "lint-staged": { - "*.{ts,tsx}": [ - "eslint" - ] - }, - "next-unused": { - "alias": {}, - "include": [ - "src/*" - ], - "exclude": [], - "entrypoints": [ - "src/pages" - ] - } -} diff --git a/demos/demo-nft-marketplace/postcss.config.js b/demos/demo-nft-marketplace/postcss.config.js deleted file mode 100644 index fef1b2256d..0000000000 --- a/demos/demo-nft-marketplace/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/demos/demo-nft-marketplace/public/favicon.ico b/demos/demo-nft-marketplace/public/favicon.ico deleted file mode 100644 index 4965832f2c..0000000000 Binary files a/demos/demo-nft-marketplace/public/favicon.ico and /dev/null differ diff --git a/demos/demo-nft-marketplace/public/vercel.svg b/demos/demo-nft-marketplace/public/vercel.svg deleted file mode 100644 index fbf0e25a65..0000000000 --- a/demos/demo-nft-marketplace/public/vercel.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - \ No newline at end of file diff --git a/demos/demo-nft-marketplace/scripts/setup.js b/demos/demo-nft-marketplace/scripts/setup.js deleted file mode 100644 index 98da34d94c..0000000000 --- a/demos/demo-nft-marketplace/scripts/setup.js +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path') -const fse = require('fs-extra') - -const abiPath = './contracts/target/ink/metadata.json' - -async function copyAbi() { - await Promise.all([fse.copy(abiPath, './src/api/abi.json', { overwrite: true })]) -} - -function getEnvFile(filename = '.env', filepath = './') { - const env = process.env.NODE_ENV || 'development' - return path.join(filepath, `${filename}.${env}`) -} - -async function copyEnvFile() { - const tplEnvFile = getEnvFile('env') - const envFile = getEnvFile('.env') - await fse.copy(tplEnvFile, envFile, { overwrite: false }) -} - -async function setup() { - console.log('Copying contract abi...') - await copyAbi() - - console.log('Writing .env file...') - await copyEnvFile() - - process.exit() -} - -setup() diff --git a/demos/demo-nft-marketplace/src/api/DemoNFTContract.ts b/demos/demo-nft-marketplace/src/api/DemoNFTContract.ts deleted file mode 100644 index 92641985c1..0000000000 --- a/demos/demo-nft-marketplace/src/api/DemoNFTContract.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { Abi, ContractPromise } from '@polkadot/api-contract' -import { ApiPromise, SubmittableResult } from '@polkadot/api' -import { ProviderInterface } from '@polkadot/rpc-provider/types' -import { Signer, SubmittableResultValue } from '@polkadot/api/types' - -import { AsyncFactory, encodeStringArgs, unwrap } from './prosopoTemp' -import abiJson from './abi.json' - -class DemoNFTContract extends AsyncFactory { - private api: ApiPromise - private abi: Abi - private contract: ContractPromise - private account - - public address: string - - /** - * @param address - * @param account - * @param providerInterface - */ - public async init(address: string, providerInterface: ProviderInterface) { - this.api = await ApiPromise.create({ provider: providerInterface }) - this.abi = new Abi(abiJson, this.api.registry.getChainProperties()) - this.contract = new ContractPromise(this.api, this.abi, address) - this.address = address - return this - } - - public getContract(): ContractPromise { - return this.contract - } - - public getAccount() { - return this.account - } - - public setAccount(account): void { - this.account = account - } - - public async query( - method: string, - args: any[], - value?: number | string - ): Promise<{ data?: T | null; gasRequired?: string }> { - try { - const abiMessage = this.abi.findMessage(method) - const response = await this.contract.query[method]( - this.account?.address, - { value }, - ...encodeStringArgs(abiMessage, args) - ) - // console.log('QUERY RESPONSE', response); - if (response.result.isOk) { - if (response.output) { - return { - data: unwrap(response.output.toHuman()) as undefined as T, - gasRequired: response.gasRequired.toString(), - } - } else { - return { gasRequired: response.gasRequired.toString() } - } - } else { - if (response.result.asErr.isModule) { - const decoded = this.api.registry.findMetaError(response.result.asErr.asModule) - - throw new Error(`${decoded.section}.${decoded.name}`) - } else { - throw new Error(response.result.asErr.toString()) - } - } - } catch (e) { - console.error('ERROR', e) - return {} - } - } - - // https://polkadot.js.org/docs/api/cookbook/tx/ - // https://polkadot.js.org/docs/api/start/api.tx.subs/ - public async transaction( - signer: Signer, - method: string, - args: any[], - value?: number | string, - gasLimit?: number | string - ): Promise< - SubmittableResultValue & { - blockHash?: string - } - > { - // TODO if DEBUG==true || env.development - // const queryBeforeTx = await this.query(method, args); - - // console.log('QUERY BEFORE TX....................', queryBeforeTx); - - const abiMessage = this.abi.findMessage(method) - - const extrinsic = this.contract.tx[method]({ value, gasLimit }, ...encodeStringArgs(abiMessage, args)) - - // this.api.setSigner(signer); - // const response = await buildTx(this.api.registry, extrinsic, this.account.address, { signer }); - // console.log("buildTx RESPONSE", response); - // return; - - return new Promise((resolve, reject) => { - extrinsic - .signAndSend(this.account.address, { signer }, (result: SubmittableResult) => { - const { dispatchError, dispatchInfo, events, internalError, status, txHash, txIndex } = result - - // console.log('TX STATUS', status.type); - // console.log('IS FINALIZED', status?.isFinalized); - // console.log('IN BLOCK', status?.isInBlock); - // console.log('EVENTS', events); - - if (internalError) { - console.error('internalError', internalError) - reject(internalError) - - return - } - - if (dispatchError) { - console.error('dispatchError', dispatchError) - const error = dispatchError.registry.findMetaError(dispatchError.asModule) - console.log(`${error.section}.${error.name}`) - reject(error) - - return - } - - // [Ready, InBlock, Finalized...] - - // Instant seal ON. - if (status?.isInBlock) { - const blockHash = status.asInBlock.toHex() - - resolve({ - dispatchError, - dispatchInfo, - events, - internalError, - status, - txHash, - txIndex, - blockHash, - }) - } - - // TODO - // Instant seal OFF. - // if (status?.isFinalized) { - - // const blockHash = status.asFinalized.toHex(); - - // resolve({ dispatchError, dispatchInfo, events, internalError, status, txHash, txIndex, blockHash }); - - // } - }) - .catch((e) => { - console.error('signAndSend ERROR', e) - reject(e) - }) - }) - } -} - -export default DemoNFTContract diff --git a/demos/demo-nft-marketplace/src/api/abi.json b/demos/demo-nft-marketplace/src/api/abi.json deleted file mode 100644 index 6eec836fcd..0000000000 --- a/demos/demo-nft-marketplace/src/api/abi.json +++ /dev/null @@ -1,1902 +0,0 @@ -{ - "source": { - "hash": "0x4868f198f800de8a74057c4d8880129fa3c6d362abba343c78a54052070f2360", - "language": "ink! 3.3.1", - "compiler": "rustc 1.64.0-nightly" - }, - "contract": { - "name": "demo_nft_contract", - "version": "0.1.0", - "authors": [ - "Sinisa Canak sinisa.canak997@gmail.com" - ] - }, - "V3": { - "spec": { - "constructors": [ - { - "args": [ - { - "label": "prosopo_account", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [], - "label": "new", - "payable": false, - "selector": "0x9bae9d5e" - } - ], - "docs": [], - "events": [], - "messages": [ - { - "args": [], - "docs": [], - "label": "mint_all", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 23 - }, - "selector": "0xfb8ab4e9" - }, - { - "args": [ - { - "label": "name", - "type": { - "displayName": [ - "String" - ], - "type": 25 - } - }, - { - "label": "description", - "type": { - "displayName": [ - "String" - ], - "type": 25 - } - }, - { - "label": "image", - "type": { - "displayName": [ - "String" - ], - "type": 25 - } - } - ], - "docs": [], - "label": "mint", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 26 - }, - "selector": "0xcfdd9aa2" - }, - { - "args": [ - { - "label": "token", - "type": { - "displayName": [ - "Id" - ], - "type": 1 - } - } - ], - "docs": [], - "label": "token_uri", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "String" - ], - "type": 25 - }, - "selector": "0x5b64e66a" - }, - { - "args": [ - { - "label": "token", - "type": { - "displayName": [ - "Id" - ], - "type": 1 - } - } - ], - "docs": [], - "label": "on_sale", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "bool" - ], - "type": 27 - }, - "selector": "0x4fc0ad74" - }, - { - "args": [ - { - "label": "token", - "type": { - "displayName": [ - "Id" - ], - "type": 1 - } - } - ], - "docs": [], - "label": "price", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "u128" - ], - "type": 6 - }, - "selector": "0xd4bd7bc1" - }, - { - "args": [ - { - "label": "user", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Calls the `Prosopo` contract to check if `user` is human" - ], - "label": "is_human", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 28 - }, - "selector": "0x47e27f92" - }, - { - "args": [ - { - "label": "token", - "type": { - "displayName": [ - "Id" - ], - "type": 1 - } - } - ], - "docs": [], - "label": "buy", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [ - "Result" - ], - "type": 23 - }, - "selector": "0x15d62801" - }, - { - "args": [ - { - "label": "page_size", - "type": { - "displayName": [ - "Option" - ], - "type": 29 - } - }, - { - "label": "offset", - "type": { - "displayName": [ - "Option" - ], - "type": 30 - } - }, - { - "label": "owner", - "type": { - "displayName": [ - "Option" - ], - "type": 20 - } - } - ], - "docs": [ - " Fetches newest tokens" - ], - "label": "get_tokens", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 31 - }, - "selector": "0xba045271" - }, - { - "args": [], - "docs": [ - " Returns the collection `Id` of the NFT token.", - "", - " This can represents the relationship between tokens/contracts/pallets." - ], - "label": "PSP34::collection_id", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "psp34_external", - "CollectionIdOutput" - ], - "type": 1 - }, - "selector": "0xffa27a5f" - }, - { - "args": [ - { - "label": "owner", - "type": { - "displayName": [ - "psp34_external", - "AllowanceInput1" - ], - "type": 8 - } - }, - { - "label": "operator", - "type": { - "displayName": [ - "psp34_external", - "AllowanceInput2" - ], - "type": 8 - } - }, - { - "label": "id", - "type": { - "displayName": [ - "psp34_external", - "AllowanceInput3" - ], - "type": 14 - } - } - ], - "docs": [ - " Returns `true` if the operator is approved by the owner to withdraw `id` token.", - " If `id` is `None`, returns `true` if the operator is approved to withdraw all owner's tokens." - ], - "label": "PSP34::allowance", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "psp34_external", - "AllowanceOutput" - ], - "type": 27 - }, - "selector": "0x4790f55a" - }, - { - "args": [ - { - "label": "operator", - "type": { - "displayName": [ - "psp34_external", - "ApproveInput1" - ], - "type": 8 - } - }, - { - "label": "id", - "type": { - "displayName": [ - "psp34_external", - "ApproveInput2" - ], - "type": 14 - } - }, - { - "label": "approved", - "type": { - "displayName": [ - "psp34_external", - "ApproveInput3" - ], - "type": 27 - } - } - ], - "docs": [ - " Approves `operator` to withdraw the `id` token from the caller's account.", - " If `id` is `None` approves or disapproves the operator for all tokens of the caller.", - "", - " On success a `Approval` event is emitted.", - "", - " # Errors", - "", - " Returns `SelfApprove` error if it is self approve.", - "", - " Returns `NotApproved` error if caller is not owner of `id`." - ], - "label": "PSP34::approve", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "psp34_external", - "ApproveOutput" - ], - "type": 23 - }, - "selector": "0x1932a8b0" - }, - { - "args": [], - "docs": [ - " Returns current NFT total supply." - ], - "label": "PSP34::total_supply", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "psp34_external", - "TotalSupplyOutput" - ], - "type": 6 - }, - "selector": "0x628413fe" - }, - { - "args": [ - { - "label": "id", - "type": { - "displayName": [ - "psp34_external", - "OwnerOfInput1" - ], - "type": 1 - } - } - ], - "docs": [ - " Returns the owner of the token if any." - ], - "label": "PSP34::owner_of", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "psp34_external", - "OwnerOfOutput" - ], - "type": 20 - }, - "selector": "0x1168624d" - }, - { - "args": [ - { - "label": "to", - "type": { - "displayName": [ - "psp34_external", - "TransferInput1" - ], - "type": 8 - } - }, - { - "label": "id", - "type": { - "displayName": [ - "psp34_external", - "TransferInput2" - ], - "type": 1 - } - }, - { - "label": "data", - "type": { - "displayName": [ - "psp34_external", - "TransferInput3" - ], - "type": 7 - } - } - ], - "docs": [ - " Transfer approved or owned token from caller.", - "", - " On success a `Transfer` event is emitted.", - "", - " # Errors", - "", - " Returns `TokenNotExists` error if `id` does not exist.", - "", - " Returns `NotApproved` error if `from` doesn't have allowance for transferring.", - "", - " Returns `SafeTransferCheckFailed` error if `to` doesn't accept transfer." - ], - "label": "PSP34::transfer", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "psp34_external", - "TransferOutput" - ], - "type": 23 - }, - "selector": "0x3128d61b" - }, - { - "args": [ - { - "label": "owner", - "type": { - "displayName": [ - "psp34_external", - "BalanceOfInput1" - ], - "type": 8 - } - } - ], - "docs": [ - " Returns the balance of the owner.", - "", - " This represents the amount of unique tokens the owner has." - ], - "label": "PSP34::balance_of", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "psp34_external", - "BalanceOfOutput" - ], - "type": 4 - }, - "selector": "0xcde7e55f" - }, - { - "args": [ - { - "label": "id", - "type": { - "displayName": [ - "psp34metadata_external", - "GetAttributeInput1" - ], - "type": 1 - } - }, - { - "label": "key", - "type": { - "displayName": [ - "psp34metadata_external", - "GetAttributeInput2" - ], - "type": 7 - } - } - ], - "docs": [ - " Returns the attribute of `id` for the given `key`.", - "", - " If `id` is a collection id of the token, it returns attributes for collection." - ], - "label": "PSP34Metadata::get_attribute", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "psp34metadata_external", - "GetAttributeOutput" - ], - "type": 34 - }, - "selector": "0xf19d48d1" - }, - { - "args": [ - { - "label": "index", - "type": { - "displayName": [ - "psp34enumerable_external", - "TokenByIndexInput1" - ], - "type": 6 - } - } - ], - "docs": [ - " Returns a token `Id` at a given `index` of all the tokens stored by the contract.", - " Use along with `total_supply` to enumerate all tokens." - ], - "label": "PSP34Enumerable::token_by_index", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "psp34enumerable_external", - "TokenByIndexOutput" - ], - "type": 26 - }, - "selector": "0xcd0340d0" - }, - { - "args": [ - { - "label": "owner", - "type": { - "displayName": [ - "psp34enumerable_external", - "OwnersTokenByIndexInput1" - ], - "type": 8 - } - }, - { - "label": "index", - "type": { - "displayName": [ - "psp34enumerable_external", - "OwnersTokenByIndexInput2" - ], - "type": 6 - } - } - ], - "docs": [ - " Returns a token `Id` owned by `owner` at a given `index` of its token list.", - " Use along with `balance_of` to enumerate all of ``owner``'s tokens." - ], - "label": "PSP34Enumerable::owners_token_by_index", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "psp34enumerable_external", - "OwnersTokenByIndexOutput" - ], - "type": 26 - }, - "selector": "0x3bcfb511" - }, - { - "args": [], - "docs": [ - " Returns the address of the current owner." - ], - "label": "Ownable::owner", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "ownable_external", - "OwnerOutput" - ], - "type": 8 - }, - "selector": "0x4fa43c8c" - }, - { - "args": [], - "docs": [ - " Leaves the contract without owner. It will not be possible to call", - " owner's functions anymore. Can only be called by the current owner.", - "", - " NOTE: Renouncing ownership will leave the contract without an owner,", - " thereby removing any functionality that is only available to the owner.", - "", - " On success a `OwnershipTransferred` event is emitted.", - "", - " # Errors", - "", - " Panics with `CallerIsNotOwner` error if caller is not owner" - ], - "label": "Ownable::renounce_ownership", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "ownable_external", - "RenounceOwnershipOutput" - ], - "type": 35 - }, - "selector": "0x5e228753" - }, - { - "args": [ - { - "label": "new_owner", - "type": { - "displayName": [ - "ownable_external", - "TransferOwnershipInput1" - ], - "type": 8 - } - } - ], - "docs": [ - " Transfers ownership of the contract to a `new_owner`.", - " Can only be called by the current owner.", - "", - " On success a `OwnershipTransferred` event is emitted.", - "", - " # Errors", - "", - " Panics with `CallerIsNotOwner` error if caller is not owner.", - "", - " Panics with `NewOwnerIsZero` error if new owner's address is zero." - ], - "label": "Ownable::transfer_ownership", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "ownable_external", - "TransferOwnershipOutput" - ], - "type": 35 - }, - "selector": "0x11f43efd" - } - ] - }, - "storage": { - "struct": { - "fields": [ - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0xca887b42fe11b9165176009de2f405a7b125d635c1cce6b1c20dbec072c3df76", - "ty": 0 - } - }, - "name": "token_owner" - }, - { - "layout": { - "cell": { - "key": "0xcb887b42fe11b9165176009de2f405a7b125d635c1cce6b1c20dbec072c3df76", - "ty": 11 - } - }, - "name": "owned_tokens_count" - }, - { - "layout": { - "cell": { - "key": "0xcc887b42fe11b9165176009de2f405a7b125d635c1cce6b1c20dbec072c3df76", - "ty": 12 - } - }, - "name": "operator_approvals" - }, - { - "layout": { - "cell": { - "key": "0xcd887b42fe11b9165176009de2f405a7b125d635c1cce6b1c20dbec072c3df76", - "ty": 6 - } - }, - "name": "total_supply" - }, - { - "layout": { - "enum": { - "dispatchKey": "0xce887b42fe11b9165176009de2f405a7b125d635c1cce6b1c20dbec072c3df76", - "variants": { - "0": { - "fields": [ - { - "layout": { - "cell": { - "key": "0xcf887b42fe11b9165176009de2f405a7b125d635c1cce6b1c20dbec072c3df76", - "ty": 15 - } - }, - "name": null - } - ] - }, - "1": { - "fields": [] - } - } - } - }, - "name": "_reserved" - } - ] - } - }, - "name": "psp34" - }, - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000000", - "ty": 2 - } - }, - "name": "next_id" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x2378c55a0f00e79375687565b21164fce62724c7b4567f680eb360fbb4f90f50", - "ty": 16 - } - }, - "name": "attributes" - }, - { - "layout": { - "enum": { - "dispatchKey": "0x2478c55a0f00e79375687565b21164fce62724c7b4567f680eb360fbb4f90f50", - "variants": { - "0": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x2578c55a0f00e79375687565b21164fce62724c7b4567f680eb360fbb4f90f50", - "ty": 15 - } - }, - "name": null - } - ] - }, - "1": { - "fields": [] - } - } - } - }, - "name": "_reserved" - } - ] - } - }, - "name": "metadata" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0391b8a2812992ad5b7f27efbc1900a7844ca2e0c7d339f1fb06830ec558f750", - "ty": 18 - } - }, - "name": "id_to_index" - }, - { - "layout": { - "cell": { - "key": "0x0491b8a2812992ad5b7f27efbc1900a7844ca2e0c7d339f1fb06830ec558f750", - "ty": 21 - } - }, - "name": "index_to_id" - } - ] - } - }, - "name": "enumerable" - }, - { - "layout": { - "enum": { - "dispatchKey": "0x0591b8a2812992ad5b7f27efbc1900a7844ca2e0c7d339f1fb06830ec558f750", - "variants": { - "0": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0691b8a2812992ad5b7f27efbc1900a7844ca2e0c7d339f1fb06830ec558f750", - "ty": 15 - } - }, - "name": null - } - ] - }, - "1": { - "fields": [] - } - } - } - }, - "name": "_reserved" - } - ] - } - }, - "name": "enumdata" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x8cd6e4a382bfd8c05974e694dac962833b58a5d56cc64ad5d8451dcbda63b387", - "ty": 8 - } - }, - "name": "owner" - }, - { - "layout": { - "enum": { - "dispatchKey": "0x8dd6e4a382bfd8c05974e694dac962833b58a5d56cc64ad5d8451dcbda63b387", - "variants": { - "0": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x8ed6e4a382bfd8c05974e694dac962833b58a5d56cc64ad5d8451dcbda63b387", - "ty": 15 - } - }, - "name": null - } - ] - }, - "1": { - "fields": [] - } - } - } - }, - "name": "_reserved" - } - ] - } - }, - "name": "ownable" - }, - { - "layout": { - "cell": { - "key": "0x0100000000000000000000000000000000000000000000000000000000000000", - "ty": 8 - } - }, - "name": "prosopo_account" - } - ] - } - }, - "types": [ - { - "id": 0, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 10, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 1 - }, - { - "name": "V", - "type": 8 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 1, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 2, - "typeName": "u8" - } - ], - "index": 0, - "name": "U8" - }, - { - "fields": [ - { - "type": 3, - "typeName": "u16" - } - ], - "index": 1, - "name": "U16" - }, - { - "fields": [ - { - "type": 4, - "typeName": "u32" - } - ], - "index": 2, - "name": "U32" - }, - { - "fields": [ - { - "type": 5, - "typeName": "u64" - } - ], - "index": 3, - "name": "U64" - }, - { - "fields": [ - { - "type": 6, - "typeName": "u128" - } - ], - "index": 4, - "name": "U128" - }, - { - "fields": [ - { - "type": 7, - "typeName": "Vec" - } - ], - "index": 5, - "name": "Bytes" - } - ] - } - }, - "path": [ - "contracts", - "traits", - "psp34", - "psp34", - "Id" - ] - } - }, - { - "id": 2, - "type": { - "def": { - "primitive": "u8" - } - } - }, - { - "id": 3, - "type": { - "def": { - "primitive": "u16" - } - } - }, - { - "id": 4, - "type": { - "def": { - "primitive": "u32" - } - } - }, - { - "id": 5, - "type": { - "def": { - "primitive": "u64" - } - } - }, - { - "id": 6, - "type": { - "def": { - "primitive": "u128" - } - } - }, - { - "id": 7, - "type": { - "def": { - "sequence": { - "type": 2 - } - } - } - }, - { - "id": 8, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 9, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_env", - "types", - "AccountId" - ] - } - }, - { - "id": 9, - "type": { - "def": { - "array": { - "len": 32, - "type": 2 - } - } - } - }, - { - "id": 10, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 9, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_primitives", - "Key" - ] - } - }, - { - "id": 11, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 10, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 8 - }, - { - "name": "V", - "type": 4 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 12, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 10, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 13 - }, - { - "name": "V", - "type": 15 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 13, - "type": { - "def": { - "tuple": [ - 8, - 8, - 14 - ] - } - } - }, - { - "id": 14, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 1 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 1 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 15, - "type": { - "def": { - "tuple": [] - } - } - }, - { - "id": 16, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 10, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 17 - }, - { - "name": "V", - "type": 7 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 17, - "type": { - "def": { - "tuple": [ - 1, - 7 - ] - } - } - }, - { - "id": 18, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 10, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 19 - }, - { - "name": "V", - "type": 6 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 19, - "type": { - "def": { - "tuple": [ - 20, - 1 - ] - } - } - }, - { - "id": 20, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 8 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 8 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 21, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 10, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 22 - }, - { - "name": "V", - "type": 1 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 22, - "type": { - "def": { - "tuple": [ - 20, - 6 - ] - } - } - }, - { - "id": 23, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 15 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 24 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 15 - }, - { - "name": "E", - "type": 24 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 24, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 25, - "typeName": "String" - } - ], - "index": 0, - "name": "Custom" - }, - { - "index": 1, - "name": "SelfApprove" - }, - { - "index": 2, - "name": "NotApproved" - }, - { - "index": 3, - "name": "TokenExists" - }, - { - "index": 4, - "name": "TokenNotExists" - }, - { - "fields": [ - { - "type": 25, - "typeName": "String" - } - ], - "index": 5, - "name": "SafeTransferCheckFailed" - } - ] - } - }, - "path": [ - "contracts", - "traits", - "errors", - "psp34", - "PSP34Error" - ] - } - }, - { - "id": 25, - "type": { - "def": { - "primitive": "str" - } - } - }, - { - "id": 26, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 1 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 24 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 1 - }, - { - "name": "E", - "type": 24 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 27, - "type": { - "def": { - "primitive": "bool" - } - } - }, - { - "id": 28, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 27 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 24 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 27 - }, - { - "name": "E", - "type": 24 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 29, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 2 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 2 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 30, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 6 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 6 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 31, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 32 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 24 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 32 - }, - { - "name": "E", - "type": 24 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 32, - "type": { - "def": { - "sequence": { - "type": 33 - } - } - } - }, - { - "id": 33, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "id", - "type": 1, - "typeName": "Id" - }, - { - "name": "owner", - "type": 8, - "typeName": "AccountId" - }, - { - "name": "token_uri", - "type": 25, - "typeName": "String" - }, - { - "name": "on_sale", - "type": 27, - "typeName": "bool" - }, - { - "name": "price", - "type": 6, - "typeName": "u128" - } - ] - } - }, - "path": [ - "demo_nft_contract", - "demo_nft_contract", - "NFT" - ] - } - }, - { - "id": 34, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 7 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 7 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 35, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 15 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 36 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 15 - }, - { - "name": "E", - "type": 36 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 36, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "CallerIsNotOwner" - }, - { - "index": 1, - "name": "NewOwnerIsZero" - } - ] - } - }, - "path": [ - "contracts", - "traits", - "errors", - "ownable", - "OwnableError" - ] - } - } - ] - } -} \ No newline at end of file diff --git a/demos/demo-nft-marketplace/src/api/demoApi.ts b/demos/demo-nft-marketplace/src/api/demoApi.ts deleted file mode 100644 index c43516db3c..0000000000 --- a/demos/demo-nft-marketplace/src/api/demoApi.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { Balance } from '@polkadot/types/interfaces' -import { Keyring, WsProvider } from '@polkadot/api' -import { Signer } from '@polkadot/types/types' -import { SubmittableResultValue } from '@polkadot/api/types' -import { formatBalance } from '@polkadot/util' -import DemoNFTContract from './DemoNFTContract' -import config from 'config' -import getConfig from 'next/config' - -const keyring = new Keyring({ type: 'sr25519' }) - -const wsProvider = new WsProvider(config.dappUrl) - -const CONNECT_RETRIES = 5 - -const contractPromise: Promise = getDemoContract() - -async function getDemoContract(): Promise { - await new Promise((resolve, reject) => { - let retries = CONNECT_RETRIES - - wsProvider.on('connected', () => resolve(undefined)) - wsProvider.on('error', () => { - if (--retries == 0) { - wsProvider.disconnect() - reject(`WS Connect failed after ${CONNECT_RETRIES} tries`) - } - }) - }) - - return DemoNFTContract.create(config.dappAccount, wsProvider) -} - -const ID_KEY = 'U8' // key = U8 | U16 | U32 | U64 | U128 | Bytes -const UNIT = 1000000000000n -export const MAX_BALANCE_FAUCET = 10n * UNIT -export const MIN_BALANCE_FAUCET = 4n * UNIT - -const { serverRuntimeConfig } = getConfig() - -function toId(id: string) { - return { [ID_KEY]: id } -} - -type GetTokensContractResponse = { - id: { - [key: string]: string - } - owner: string - tokenUri: string - onSale: boolean - price: string -}[] - -export type TokenMetadata = { - name: string - description: string - image: string -} - -export type Token = { - id: string - owner: string - meta: TokenMetadata - onSale: boolean - price: string -} - -function readMetadata(tokenUri: string): TokenMetadata { - const decodedRequestBodyString = Buffer.from(tokenUri.replace('data:application/json;base64,', ''), 'base64') - const meta = JSON.parse(decodedRequestBodyString.toString()) - return meta -} - -export function formatPrice(price: string) { - return formatBalance((price || '0').replaceAll(',', ''), { decimals: 12, withSiFull: true }) -} - -export const PAGE_SIZE = 24 - -function withContract

( - fun: (contract: DemoNFTContract, ...props: P) => Promise, - fallbackVal: R -): (...props: P) => Promise { - return (...props: P) => contractPromise.then((contract) => fun(contract, ...props)).catch(() => fallbackVal) -} - -function getVoid() { - return -} - -const demoApi = { - setAccount: withContract(async (contract, account): Promise => { - return contract.setAccount(account) - }, getVoid()), - getAccount: withContract(async (contract): Promise<{ address: string }> => { - return contract.getAccount() - }, null), - getBalance: withContract(async (contract, address?: string): Promise => { - const { data } = (await contract - .getContract() - .api.query.system.account(contract.getAccount()?.address || address)) as any - - return data - }, null), - /** - * BACKEND ONLY! - */ - faucet: withContract(async (contract, to: string): Promise => { - if (!serverRuntimeConfig.MAIN_ACCOUNT_MNEMONIC) { - throw new Error('Main account not set. Contact the developers.') - } - - const from = keyring.addFromMnemonic(serverRuntimeConfig.MAIN_ACCOUNT_MNEMONIC) - - const balance: any = await demoApi.getBalance(to) - const free = BigInt((balance?.free?.toHuman() || '0').replaceAll(',', '')) - - if (free > MIN_BALANCE_FAUCET) { - throw new Error('Balance high enough. Transfer not allowed.') - } - - const amount = MAX_BALANCE_FAUCET - free - - return new Promise((resolve, reject) => { - contract['api'].tx.balances.transfer(to, amount).signAndSend(from, async (result) => { - const { dispatchError, internalError, status } = result - if (internalError) { - reject(internalError) - - return - } - - if (dispatchError) { - const error = dispatchError.registry.findMetaError(dispatchError.asModule) - reject(error) - - return - } - - // Instant seal ON. - if (status.isInBlock) { - resolve(amount.toString()) - } - - // Instant seal OFF. - // if (status.isFinalized) { - // resolve(); - // } - }) - }) - }, null), - getTokens: withContract( - async (contract, pageSize = 20, pageIndex = 0, owner = undefined as string | undefined): Promise => { - const { data: tokens } = await contract.query('getTokens', [ - pageSize, - pageIndex, - owner, - ]) - - return tokens.map(({ id, owner, tokenUri, onSale, price }) => ({ - id: id[ID_KEY], - owner, - meta: readMetadata(tokenUri), - onSale, - price: price.replaceAll(',', ''), - })) - }, - [] - ), - getToken: withContract(async (contract, id: string): Promise => { - const { data: owner } = await contract.query('psp34::ownerOf', [toId(id)]) - - const { data: tokenUri } = await contract.query('tokenUri', [toId(id)]) - - const meta = readMetadata(tokenUri) - - const { data: onSale } = await contract.query('onSale', [toId(id)]) - - const { data: price } = await contract.query('price', [toId(id)]) - - return { - id, - owner, - meta, - onSale, - price: price.replaceAll(',', ''), - } - }, null), - estimateBuyGasFees: withContract(async (contract, id: string): Promise => { - const { gasRequired } = await contract.query('buy', [toId(id)]) - - return gasRequired.replaceAll(',', '') - }, null), - buy: withContract( - async ( - contract, - signer: Signer, - id: string, - gas: string - ): Promise => { - const { data } = await contract.query('price', [toId(id)]) - - const price = data.replaceAll(',', '') - - return contract.transaction(signer, 'buy', [toId(id)], price, gas) - }, - null - ), - isHuman: withContract(async (contract): Promise => { - const { data } = await contract.query('isHuman', [contract.getAccount().address, 60, 20000]) - - return data - }, false), -} - -export default demoApi diff --git a/demos/demo-nft-marketplace/src/api/prosopoTemp.ts b/demos/demo-nft-marketplace/src/api/prosopoTemp.ts deleted file mode 100644 index bfca1714ee..0000000000 --- a/demos/demo-nft-marketplace/src/api/prosopoTemp.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { AbiMessage } from '@polkadot/api-contract/types' -import { AnyJson } from '@polkadot/types/types/codec' -import { blake2AsU8a } from '@polkadot/util-crypto' -import { isHex, isU8a } from '@polkadot/util' - -export abstract class AsyncFactory { - constructor() { - throw new Error('Use `create` factory method') - } - - public static async create(...args: any[]) { - return await Object.create(this.prototype).init(...args) - } - - public abstract init(...args: any[]): Promise -} - -/** Encodes arguments that should be hashes using blake2AsU8a - * @return encoded arguments - */ -export function encodeStringArgs(methodObj: AbiMessage, args: T[]): T[] { - const encodedArgs: T[] = [] - // args must be in the same order as methodObj['args'] - const typesToHash = ['Hash'] - methodObj.args.forEach((methodArg, idx) => { - const argVal = args[idx] - // hash values that have been passed as strings - if (typesToHash.indexOf(methodArg.type.type) > -1 && !(isU8a(argVal) || isHex(argVal))) { - encodedArgs.push(blake2AsU8a(argVal as unknown as string) as unknown as T) - } else { - encodedArgs.push(argVal) - } - }) - return encodedArgs -} - -/** Unwrap a query respons from a contract - * @return {AnyJson} unwrapped - */ -export function unwrap(item: AnyJson): AnyJson { - const prop = 'Ok' - if (item && typeof item === 'object') { - if (prop in item) { - return item[prop] - } - } - return item -} diff --git a/demos/demo-nft-marketplace/src/assets/icons/loading.svg b/demos/demo-nft-marketplace/src/assets/icons/loading.svg deleted file mode 100644 index 2f2d709a7e..0000000000 --- a/demos/demo-nft-marketplace/src/assets/icons/loading.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/demos/demo-nft-marketplace/src/assets/icons/reload.svg b/demos/demo-nft-marketplace/src/assets/icons/reload.svg deleted file mode 100644 index df22f9fe39..0000000000 --- a/demos/demo-nft-marketplace/src/assets/icons/reload.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/demos/demo-nft-marketplace/src/assets/icons/subwallet.svg b/demos/demo-nft-marketplace/src/assets/icons/subwallet.svg deleted file mode 100644 index ebfade2799..0000000000 --- a/demos/demo-nft-marketplace/src/assets/icons/subwallet.svg +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/demos/demo-nft-marketplace/src/assets/images/cover.png b/demos/demo-nft-marketplace/src/assets/images/cover.png deleted file mode 100644 index ea64367681..0000000000 Binary files a/demos/demo-nft-marketplace/src/assets/images/cover.png and /dev/null differ diff --git a/demos/demo-nft-marketplace/src/assets/images/polkadotjs.png b/demos/demo-nft-marketplace/src/assets/images/polkadotjs.png deleted file mode 100644 index 5e6352cc30..0000000000 Binary files a/demos/demo-nft-marketplace/src/assets/images/polkadotjs.png and /dev/null differ diff --git a/demos/demo-nft-marketplace/src/assets/images/talisman.png b/demos/demo-nft-marketplace/src/assets/images/talisman.png deleted file mode 100644 index 46738c1911..0000000000 Binary files a/demos/demo-nft-marketplace/src/assets/images/talisman.png and /dev/null differ diff --git a/demos/demo-nft-marketplace/src/assets/index.ts b/demos/demo-nft-marketplace/src/assets/index.ts deleted file mode 100644 index c49b64b4dd..0000000000 --- a/demos/demo-nft-marketplace/src/assets/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { default as ReloadIcon } from './icons/reload.svg' -export { default as CoverPhoto } from './images/cover.png' -export { default as LoadingIcon } from './icons/loading.svg' -export { default as Logo } from './logo.svg' -export { default as PolkadotJsIcon } from './images/polkadotjs.png' -export { default as TalismanIcon } from './images/talisman.png' -export { default as SubwalletIcon } from './icons/subwallet.svg' diff --git a/demos/demo-nft-marketplace/src/assets/logo.svg b/demos/demo-nft-marketplace/src/assets/logo.svg deleted file mode 100644 index 74feb63fb0..0000000000 --- a/demos/demo-nft-marketplace/src/assets/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - Prosopo Store - - - - - \ No newline at end of file diff --git a/demos/demo-nft-marketplace/src/components/Avatar/Avatar.tsx b/demos/demo-nft-marketplace/src/components/Avatar/Avatar.tsx deleted file mode 100644 index 7d27174b02..0000000000 --- a/demos/demo-nft-marketplace/src/components/Avatar/Avatar.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import Image from 'components/Image/Image' -import React, { FC } from 'react' -import makeBlockie from 'ethereum-blockies-base64' - -type Props = { - additionalClasses?: string - sizeClasses?: string - username: string - disableHoverEffects?: boolean -} - -const Avatar: FC = ({ additionalClasses = '', sizeClasses, username, disableHoverEffects }) => { - return ( -

- avatar -
- ) -} -export default Avatar diff --git a/demos/demo-nft-marketplace/src/components/Button/Button.tsx b/demos/demo-nft-marketplace/src/components/Button/Button.tsx deleted file mode 100644 index 2c57518baa..0000000000 --- a/demos/demo-nft-marketplace/src/components/Button/Button.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { LoadingIcon } from 'assets' -import Image from 'components/Image/Image' -import React, { FC } from 'react' -// TODO: add clicked, hover effects - -const BASE_BUTTON = - 'max-h-10 text-white font-semibold outline-none rounded shadow font-normal focus:outline-none border border-transparent items-center py-2' - -export enum ButtonType { - Primary = 'bg-gradient-to-b from-primary-start to-primary-stop bg-origin-border', - Secondary = 'bg-secondary border-gray-600', - Main = 'bg-main border-gray-600', -} - -type Props = { - title?: string - // TODO change type? - icon?: string - iconRight?: boolean - onClick?: React.MouseEventHandler - type?: ButtonType - fullWidth?: boolean - customClasses?: string - equalPadding?: boolean - loading?: boolean -} - -const Button: FC = ({ - customClasses = '', - onClick, - icon, - title, - iconRight, - fullWidth, - equalPadding, - type = ButtonType.Primary, - loading, -}) => ( - -) -export default Button diff --git a/demos/demo-nft-marketplace/src/components/Button/index.ts b/demos/demo-nft-marketplace/src/components/Button/index.ts deleted file mode 100644 index 148a2ebfe1..0000000000 --- a/demos/demo-nft-marketplace/src/components/Button/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default, ButtonType } from './Button' diff --git a/demos/demo-nft-marketplace/src/components/HorizontalCard/HorizontalCard.tsx b/demos/demo-nft-marketplace/src/components/HorizontalCard/HorizontalCard.tsx deleted file mode 100644 index 4c37eb19ba..0000000000 --- a/demos/demo-nft-marketplace/src/components/HorizontalCard/HorizontalCard.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import Image from 'components/Image/Image' -import Link from 'components/Link' -import React from 'react' - -type Props = { - imageUrl: string - title: React.ReactNode - subtitle?: React.ReactNode - actions?: React.ReactNode - mainBackground?: boolean - linkTo?: string -} - -function HorizontalCard({ imageUrl, title, subtitle, actions, mainBackground = false, linkTo }: Props) { - const Wrapper = linkTo ? ({ children }) => {children} : ({ children }) => <>{children} - - return ( -
- -
-
- -
-
-
-
- {actions &&
{actions}
} -
-
-
-
- ) -} -export default HorizontalCard diff --git a/demos/demo-nft-marketplace/src/components/HorizontalCard/index.ts b/demos/demo-nft-marketplace/src/components/HorizontalCard/index.ts deleted file mode 100644 index 673862a727..0000000000 --- a/demos/demo-nft-marketplace/src/components/HorizontalCard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './HorizontalCard' diff --git a/demos/demo-nft-marketplace/src/components/Image/Image.tsx b/demos/demo-nft-marketplace/src/components/Image/Image.tsx deleted file mode 100644 index 96c458aafd..0000000000 --- a/demos/demo-nft-marketplace/src/components/Image/Image.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import NextImage, { ImageProps } from 'next/image' -import React from 'react' - -type Props = Omit - -const shimmer = (w = 100, h = 100) => ` - - - - - - - - - - - -` - -const toBase64 = (str: string) => - typeof window === 'undefined' ? Buffer.from(str).toString('base64') : window.btoa(str) - -function Image({ alt, ...props }: Props) { - return ( -
- {props.src && ( - - )} -
- ) -} - -export default Image diff --git a/demos/demo-nft-marketplace/src/components/Link/Link.tsx b/demos/demo-nft-marketplace/src/components/Link/Link.tsx deleted file mode 100644 index d0b37e3450..0000000000 --- a/demos/demo-nft-marketplace/src/components/Link/Link.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import NextLink from 'next/link' -import React, { FC, ReactNode, useCallback } from 'react' - -//TODO: add other types -export enum LinkType { - Main = 'text-white font-bold', - Secondary = 'text-gray-600 font-semibold', - Primary = 'text-transparent bg-clip-text bg-gradient-to-b from-primary-start to-primary-stop hover:from-primary-stop ', -} - -type Props = { - to?: string - title?: string - type?: LinkType - onClick?: (e?: any) => void - children?: ReactNode -} - -const Link: FC = ({ to, title = '', type = LinkType.Primary, children, onClick }) => { - const renderComponent = useCallback(() => { - return {children ?? title} - }, [children, type, title]) - if (onClick) { - return ( - ( -
- {children ?? title} -
- ) ?? renderComponent() - ) - } - return ( - - {renderComponent()} - - ) -} -export default Link diff --git a/demos/demo-nft-marketplace/src/components/Link/index.ts b/demos/demo-nft-marketplace/src/components/Link/index.ts deleted file mode 100644 index 518d372981..0000000000 --- a/demos/demo-nft-marketplace/src/components/Link/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Link' diff --git a/demos/demo-nft-marketplace/src/components/Modal/CheckoutModal.tsx b/demos/demo-nft-marketplace/src/components/Modal/CheckoutModal.tsx deleted file mode 100644 index 4dbc75a473..0000000000 --- a/demos/demo-nft-marketplace/src/components/Modal/CheckoutModal.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import { BN } from '@polkadot/util' -import { CaptchaComponent, ProsopoConsumer } from 'components/Prosopo' -import { Dialog, Transition } from '@headlessui/react' -import { ShowCaptchasState } from 'components/Prosopo/types' -import Button from 'components/Button' -import Modal, { ModalProps } from 'components/Modal' -import React, { Fragment, useCallback, useEffect, useState } from 'react' -import ReactCurrencyInput from 'react-currency-input-field' -import demoApi, { formatPrice } from 'api/demoApi' -import toast from 'react-hot-toast' - -type Props = Omit & { - id: string - price: string - title: string - successCallback?: () => {} -} - -function formatGas(value: string) { - if (!value) { - return '0' - } - - const decimals = value.split('.')[1]?.length || 0 - return value.replaceAll(/[.|,]/g, '') + '0'.repeat(9 - decimals) -} - -function CheckoutModal(props: Props) { - return {(contextProps) => } -} - -function CheckoutModalInternal({ - id, - price, - title, - clientInterface, - captchasVisible, - successCallback, - showCaptchas, - showFaucetModal, - captchaReloadKey, - ...props -}: Props & Partial): JSX.Element { - const [balance, setBalance] = useState(new BN(0)) - const [gas, setGas] = useState('') - const [estimatedGas, setEstimatedGas] = useState('0') - const [loading, setLoading] = useState(false) - - const onSubmit = useCallback( - async (approved = true) => { - const isHuman = await demoApi.isHuman() - - if (!isHuman) { - toast.error('Captcha threshold not met. Please solve more captchas.') - } - - if (!isHuman || !approved) { - setLoading(false) - return - } - const signer = clientInterface.getExtension()?.getExtension?.()?.signer - await demoApi - .buy(signer, id, formatGas(gas)) - .then((x) => { - if (x == null) { - toast.error('Something went wrong! Please try again.') - return - } - console.log({ success: x }) - toast.success('Purchase successful!') - successCallback?.() - }) - .catch((error) => { - if (error.method && error.method == 'ContractReverted') { - toast.error('Something went wrong! Please try again.') - } else if (error.docs) { - toast.error(error.docs.join(' ')) - } else { - toast.error(error.message) - } - console.log({ error }) - }) - setLoading(false) - props.onClose() - }, - [id, clientInterface, gas] - ) - - const info = [ - { key: 'Balance', value: formatPrice(balance.toString()) }, - { key: 'Item Price', value: formatPrice(price) }, - { key: 'Estimated Gas', value: estimatedGas }, - ] - - const onBuy = async () => { - setLoading(true) - const isHuman = await demoApi.isHuman() - - if (isHuman) { - onSubmit() - } else { - showCaptchas(onSubmit) - } - } - - useEffect(() => { - if (props.isOpen) { - const account = clientInterface.getExtension()?.getAccount?.() - demoApi.setAccount(account).then(() => { - demoApi - .getBalance() - .then((x) => setBalance(new BN((x?.toHuman().free || '0').replaceAll(',', '')))) - .catch(console.log) - demoApi - .estimateBuyGasFees(id) - .then((gas) => { - if (gas == null) { - toast.error('Something went wrong! Please try again.') - return - } - const formatted = formatPrice(gas) - const doubledFormatted = formatPrice(new BN(gas).muln(2).toString()) - setEstimatedGas(formatted) - setGas(doubledFormatted.split(' ')[0]) - }) - .catch(console.log) - }) - } - }, [props.isOpen]) - - const insufficient = new BN(price).cmp(balance) > 0 - - return ( - { - e.preventDefault() - e.stopPropagation() - }} - > - -
- {insufficient &&
Insufficiently Funds
} - {info.map(({ key, value }) => ( -
- {key}
{value}
-
- ))} -
-
Gas Limit
- setGas(value)} - value={gas} - /> -
milli Unit
-
-

- If required to complete a captcha, you will need to pay a small fee which will be refunded upon - successfully completing it. -

-
-
- {insufficient ? ( - <> -
Balance too low
-
- - - - - - - -
-
- ) -} - -export default CheckoutModal diff --git a/demos/demo-nft-marketplace/src/components/Modal/FaucetModal.tsx b/demos/demo-nft-marketplace/src/components/Modal/FaucetModal.tsx deleted file mode 100644 index 10e4d0f1df..0000000000 --- a/demos/demo-nft-marketplace/src/components/Modal/FaucetModal.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { BN } from '@polkadot/util' -import { ProsopoConsumer } from 'components/Prosopo' -import { ShowCaptchasState } from 'components/Prosopo/types' -import Button, { ButtonType } from 'components/Button' -import Modal, { ModalProps } from './Modal' -import React, { useEffect, useState } from 'react' -import axios from 'axios' -import demoApi, { MAX_BALANCE_FAUCET, MIN_BALANCE_FAUCET, formatPrice } from 'api/demoApi' -import toast from 'react-hot-toast' - -type Props = Omit - -async function requestFunds(account: string): Promise { - const res = await axios.post('/api/faucet', { account }) - - const amount = res.data.amount - - return formatPrice(amount) -} - -function FaucetModal(props: Props) { - return ( - - {({ clientInterface }) => } - - ) -} - -function FaucetModalInternal({ clientInterface, ...modalProps }: Props & Partial) { - const [balance, setBalance] = useState(new BN(0)) - const [amountAllowed, setAmountAllowed] = useState(new BN(0)) - const [message, setMessage] = useState('') - const [loading, setLoading] = useState(false) - - useEffect(() => { - if (!modalProps.isOpen) { - return - } - - const account = clientInterface.getExtension()?.getAccount?.() - demoApi.setAccount(account).then(() => { - demoApi - .getBalance() - .then((x) => { - const _balance = new BN((x?.toHuman().free || '0').replaceAll(',', '')) - setBalance(_balance) - setAmountAllowed(new BN(0)) - if (_balance.gt(new BN(MIN_BALANCE_FAUCET.toString()))) { - setMessage('You have enough funds for transactions.') - return - } - const _amountAllowed = new BN(MAX_BALANCE_FAUCET.toString()).sub(_balance) - if (_amountAllowed.gtn(0)) { - setAmountAllowed(_amountAllowed) - } else { - console.log('gt') - setMessage('You have enough funds for transactions.') - return - } - setMessage('') - }) - .catch(console.log) - }) - }, [modalProps.isOpen]) - - const info = [ - { key: 'Balance', value: formatPrice(balance.toString()) }, - { key: 'Allowed amount', value: formatPrice(amountAllowed.toString()) }, - ] - - const onSend = async () => { - setLoading(true) - try { - const { address } = await demoApi.getAccount() - const res = await requestFunds(address) - - modalProps.onClose() - toast.success(`${res} added.`) - } catch (err) { - toast.error('Something went wrong!') - console.error(err) - } - setLoading(false) - } - - return ( - -
- {info.map(({ key, value }) => ( -
- {key}
{value}
-
- ))} -
-

{message}

- {message ? ( - } - - )} - - )} - - setWalletModalOpen(false)} /> - setFaucetModalOpen(false)} /> - - - ) -} - -export const ProsopoConsumer: FC = ({ children }) => { - return ( - - {() => {(options) => children(options)}} - - ) -} diff --git a/demos/demo-nft-marketplace/src/components/Prosopo/index.ts b/demos/demo-nft-marketplace/src/components/Prosopo/index.ts deleted file mode 100644 index dd18f89b2d..0000000000 --- a/demos/demo-nft-marketplace/src/components/Prosopo/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ConsumerProps, ProviderProps } from './types' -import { ProsopoCaptchaClient, TExtensionAccount } from '@prosopo/procaptcha' -import dynamic from 'next/dynamic' - -const ProsopoProvider = dynamic( - () => import('components/Prosopo/ProsopoContext').then((mod) => mod.ProsopoProvider), - { - ssr: false, - } -) - -const ProsopoConsumer = dynamic( - () => import('components/Prosopo/ProsopoContext').then((mod) => mod.ProsopoConsumer), - { - ssr: false, - } -) - -const CaptchaComponent = dynamic<{ clientInterface: ProsopoCaptchaClient }>( - () => import('@prosopo/procaptcha-react').then((mod) => mod.CaptchaComponent), - { - ssr: false, - } -) - -const ExtensionAccountSelect = dynamic<{ - value?: TExtensionAccount - options: TExtensionAccount[] - onChange: (value: TExtensionAccount | null) => void -}>(() => import('@prosopo/procaptcha-react').then((mod) => mod.ExtensionAccountSelect), { - ssr: false, -}) - -export { ProsopoProvider, ProsopoConsumer, CaptchaComponent, ExtensionAccountSelect } diff --git a/demos/demo-nft-marketplace/src/components/Prosopo/types.ts b/demos/demo-nft-marketplace/src/components/Prosopo/types.ts deleted file mode 100644 index 29f7609ef9..0000000000 --- a/demos/demo-nft-marketplace/src/components/Prosopo/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { PropsWithChildren, ReactNode } from 'react' -import { ProsopoCaptchaClient } from '@prosopo/procaptcha' - -export type ShowCaptchasState = { - captchasVisible: boolean - showCaptchas: (callback?: (approved: boolean) => Promise) => void - clientInterface?: ProsopoCaptchaClient - showWalletModal: () => void - showFaucetModal: () => void - captchaReloadKey: number - loading: boolean -} - -export type ConsumerProps = { - children(options: ShowCaptchasState): ReactNode -} - -export type ProviderProps = PropsWithChildren<{}> diff --git a/demos/demo-nft-marketplace/src/config/index.ts b/demos/demo-nft-marketplace/src/config/index.ts deleted file mode 100644 index 540b75b7ac..0000000000 --- a/demos/demo-nft-marketplace/src/config/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -// ../.env | .env.local | .env.development >> -// REACT_APP_API_BASE_URL=http://localhost:8282 -// REACT_APP_API_PATH_PREFIX=/v1/prosopo -// REACT_APP_DAPP_CONTRACT_ADDRESS=5FzjruAqyhRGV81pMb4yznNS7t52hNB8u2VC2N1P22j5QLY9 -// https://create-react-app.dev/docs/adding-custom-environment-variables/ - -import { ProsopoCaptchaConfig } from '@prosopo/procaptcha' - -const config: ProsopoCaptchaConfig = { - 'providerApi.baseURL': process.env.NEXT_PUBLIC_API_BASE_URL || '', - 'providerApi.prefix': process.env.NEXT_PUBLIC_API_PATH_PREFIX || '', - dappAccount: process.env.NEXT_PUBLIC_CONTRACT_ADDRESS || '', - dappUrl: process.env.NEXT_PUBLIC_CONTRACT_URL || '', - solutionThreshold: 80, - web2: process.env.NEXT_PUBLIC_WEB2 === 'true', - prosopoContractAccount: process.env.NEXT_PUBLIC_PROSOPO_CONTRACT_ADDRESS || '', - accountCreator: { - area: { width: 300, height: 300 }, - offsetParameter: 2001000001, - multiplier: 15000, - fontSizeFactor: 1.5, - maxShadowBlur: 50, - numberOfRounds: 5, - seed: 42, - }, - dappName: process.env.NEXT_PUBLIC_DAPP_NAME || '', -} - -export default config diff --git a/demos/demo-nft-marketplace/src/hooks/useToggle.tsx b/demos/demo-nft-marketplace/src/hooks/useToggle.tsx deleted file mode 100644 index e7f12b8836..0000000000 --- a/demos/demo-nft-marketplace/src/hooks/useToggle.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -const toggleReducer = (state: boolean, nextValue?: unknown) => (typeof nextValue === 'boolean' ? nextValue : !state) - -function useToggle(initialValue: boolean): [boolean, (nextValue?: unknown) => void] { - return React.useReducer>(toggleReducer, initialValue) -} - -export { useToggle } diff --git a/demos/demo-nft-marketplace/src/pages/_app.tsx b/demos/demo-nft-marketplace/src/pages/_app.tsx deleted file mode 100644 index d91f58f319..0000000000 --- a/demos/demo-nft-marketplace/src/pages/_app.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import '../styles/global.css' -import { AppProps } from 'next/app' -import { ProsopoProvider } from 'components/Prosopo' -import { useRouter } from 'next/router' -import LoadingBar, { LoadingBarRef } from 'react-top-loading-bar' -import Navbar from 'components/Navbar/Navbar' -import React, { FC, useEffect, useRef } from 'react' - -const App: FC = ({ Component, pageProps }: AppProps) => { - const ref = useRef(null) - - const router = useRouter() - - useEffect(() => { - const startLoadingBar = () => ref.current.continuousStart(0, 0) - const completeLoadingBar = () => ref.current.complete() - - router.events.on('routeChangeStart', startLoadingBar) - router.events.on('routeChangeComplete', completeLoadingBar) - return () => { - router.events.off('routeChangeStart', startLoadingBar) - router.events.off('routeChangeComplete', completeLoadingBar) - } - }, [router.events]) - - return ( - - - - - - - - ) -} - -export default App diff --git a/demos/demo-nft-marketplace/src/pages/api/faucet.ts b/demos/demo-nft-marketplace/src/pages/api/faucet.ts deleted file mode 100644 index 50b1c21f60..0000000000 --- a/demos/demo-nft-marketplace/src/pages/api/faucet.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { HandlerOrMiddleware, RequestMethod, requestMethodMapper } from 'utils/apiUtils' -import demoApi from 'api/demoApi' - -type PostBody = { account: string } - -const handlePost: HandlerOrMiddleware = async (req, res) => { - const { account } = req.body - - try { - const amount = await demoApi.faucet(account) - res.status(200).send({ amount }) - } catch (err) { - res.status(400).send(err.message) - } -} - -const methodHandlers: RequestMethod[] = [{ method: 'POST', handler: handlePost }] - -export default async function handler(req, res) { - await requestMethodMapper(methodHandlers, req, res) -} diff --git a/demos/demo-nft-marketplace/src/pages/index.tsx b/demos/demo-nft-marketplace/src/pages/index.tsx deleted file mode 100644 index 6445574d8c..0000000000 --- a/demos/demo-nft-marketplace/src/pages/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { ProductList } from 'components/ProductCard' -import React, { useCallback, useState } from 'react' -import demoApi, { PAGE_SIZE, Token } from 'api/demoApi' - -export interface HomeProps { - tokens: Token[] -} - -const Home: React.FunctionComponent = ({ tokens: _tokens }) => { - const [tokens, setTokens] = useState(_tokens) - const [canLoadMore, setCanLoadMore] = useState(_tokens.length % PAGE_SIZE == 0) - const loadMore = useCallback(async () => { - const newTokens = await demoApi.getTokens(PAGE_SIZE, tokens.length).catch(() => []) - - if (newTokens.length == 0 || newTokens.length % PAGE_SIZE > 0) { - setCanLoadMore(false) - } - - setTokens([...tokens, ...newTokens]) - }, [tokens]) - - return ( -
- -
- ) -} -export async function getServerSideProps(): Promise<{ props: HomeProps }> { - const tokens = await demoApi.getTokens(PAGE_SIZE).catch(() => []) - - return { - props: { tokens }, - } -} - -export default Home diff --git a/demos/demo-nft-marketplace/src/pages/item/[nft-item-details].tsx b/demos/demo-nft-marketplace/src/pages/item/[nft-item-details].tsx deleted file mode 100644 index 22280e9019..0000000000 --- a/demos/demo-nft-marketplace/src/pages/item/[nft-item-details].tsx +++ /dev/null @@ -1,135 +0,0 @@ -import { CheckoutModal } from 'components/Modal' -import { NextPageContext } from 'next' -import { ProsopoConsumer } from 'components/Prosopo' -import { ShowCaptchasState } from 'components/Prosopo/types' -import { shortAddress } from 'utils/itemUtils' -import { useRouter } from 'next/router' -import { useToggle } from 'hooks/useToggle' -import Button from 'components/Button' -import HorizontalCard from 'components/HorizontalCard' -import Image from 'components/Image/Image' -import React, { useEffect, useState } from 'react' -import demoApi, { Token, formatPrice } from 'api/demoApi' -import makeBlockie from 'ethereum-blockies-base64' -import toast from 'react-hot-toast' - -type Props = { token: Token | null } - -function ItemDetailsPage(props: Props): JSX.Element { - return {(options) => } -} - -type ItemDetailsProps = Props & ShowCaptchasState - -function ItemDetails({ token: _token }: ItemDetailsProps) { - const [isCheckoutVisible, setCheckoutVisible] = useToggle(false) - const [creatorAvatar, setCreatorAvatar] = useState(null) - const [token, setToken] = useState(_token) - const router = useRouter() - - useEffect(() => { - if (!token) { - router.push('/') - return - } - setCreatorAvatar(makeBlockie(token.owner)) - }, []) - - const getToken = () => { - return demoApi - .getToken(_token?.id) - .then((t) => { - setToken(t) - setCreatorAvatar(makeBlockie(t.owner)) - }) - .catch((error) => { - if (error.docs) { - toast.error(error.docs.join(' ')) - } else { - toast.error(error.message) - } - console.log({ error }) - }) - } - - const price = formatPrice(token?.price) - - const renderButton = () => { - const onClick = () => { - // TODO: show captcha and add aditional checks if needed - setCheckoutVisible(true) - } - - return !token.onSale ? null : ( -