Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

prosopo/demo-nft-marketplace

Repository files navigation

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. The PSP34 contracts have also been modified so that they are protected by Prosopo's human verification system.

Demo

Checkout the demo app!

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.

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

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 , subwallet or polkadotjs. 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 and select the development endpoint (ws://localhost:9944).

Select endpoint

Send some funds from one of the test accounts (Alice etc.) to your test account.

Send funds

4. Start the app

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

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. 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.

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;

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.

pub const HUMAN_THRESHOLD: u8 = 80;
pub const RECENCY: u32 = 5 * 60 * 1000; // 5 mins

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),
};

/// Calls the `Prosopo` contract to check if `user` is human
#[ink(message)]
pub fn is_human(&self, user: AccountId) -> Result<bool, PSP34Error> {
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());
}

Flow

The entire process flow can be visualised as follows.

Main CAPTCHA flow