DEV Community

Cover image for How to Interact with a Pizza Charity ink! Smart Contract with PAPI and Reactive DOT
Aliyu Adeniji
Aliyu Adeniji

Posted on

How to Interact with a Pizza Charity ink! Smart Contract with PAPI and Reactive DOT

Introduction

Smart contracts on the blockchain networks are like the the powerhouse that need needed to be controlled and interacted with by workers.

To do this, PAPI(Polkadot-API) is the messenger that allows a seamless interaction with Polkadot smart contracts, it provides modular functions for building decentralized Applications.

In this article, I will show you how to effectively interact with an ink! smart contract using PAPI. We will also use ReactiveDOT to interact with the generated contract typescript bindings in our frontend.

  1. To get started with the project create a new inkathon project using npx create-inkathon-app@latest and follow the prompts.
  2. Change directory to the project you just created using cd pizza_charity and run bun run dev, this will install all dependencies to get ahead in the project.

For the smart contract part, generate and build your pizza charity smart contract in the following steps.

  1. cd contracts/src
  2. cargo contract new pizza_charity
  3. Add your new contract to the contracts/Cargo.toml file.
[workspace]
members = [
    "src/flipper",
    "src/pizza_charity",
]
Enter fullscreen mode Exit fullscreen mode
  1. After building your contract completely, build and generate typescript bindings using:

bun run build && bun run codegen

  1. Add the code snippet below to the contracts/scripts/deploy.ts before deployment to initialize the constructor function in your smart contract and supply the default variables.
const main = async () => {
  const initResult = await initApi()

  const deployResult = await deployContract(initResult, "pizza_limited", contracts.pizza_limited, "new", {
    max_order_per_user: 5,
  })

  await writeAddresses({ pizza_limited: deployResult })
}
Enter fullscreen mode Exit fullscreen mode
  1. Deploy your contract to any blockchain of your choice with: CHAIN=<chain-name> bun deploy Replace with the actual name of the chain you want to deploy to. After a successful deployment, you should get the deployment addresses in /contracts/deployments/pizza_charity/<chain>.ts. That is all for the smart contract, let's go ahead with interacting with the smart contract on the frontend.

The reamaining part of this tutorial will be focused on the frontend interaction with the smart contract you just deployed.

  1. The first step is to configure the frontend of the application to the chain where you deployed your contract, go to frontend/src/lib/reactive-dot/config.ts and export your chain configuration as seen below. This configuration sets passethub as the chain on which are interacting with the smart contract, and it sets the frontend ready for wallets interaction with the deployed smart contract.
  export const config = defineConfig({
  chains: {
    passethub: {
      descriptor: passethub,
      provider: getWsProvider("wss://testnet-passet-hub.polkadot.io"),
    },
    // Add more chains here
  },
  ssr: true,
  wallets: [new InjectedWalletProvider()],
})
Enter fullscreen mode Exit fullscreen mode
  1. Import the contract deployment into the frontend/src/lib/inkathon/deployments.ts file.
import { contracts } from "@polkadot-api/descriptors"
import * as pizzaPassethub from "contracts/deployments/pizza_limited/passethub"



export const pizza = {
  contract: contracts.pizza_limited,
  evmAddresses: {
    passethub: pizzaPassethub.evmAddress,
    // Add more deployments here
  },
  ss58Addresses: {
    passethub: pizzaPassethub.ss58Address,
    // Add more deployments here
  },
}

export const deployments = {
  pizza
  // Add more contracts here
}
Enter fullscreen mode Exit fullscreen mode

This will import the generated contract descriptors from polkadot-api and also import the chain we want to interact with.

The contract deployment will be linked with the passethub chain addresses here.

  1. The next step is the actual interaction with the contract. We want to do two types of interaction with the contract.
    1. Query the contract and get some information from the contract, for example, you can get the total daily supply of pizza and the remaining pizza for the day.
    2. write and sign a transaction to the contract, here, you can request for a certain amount of pizza and get it delivered to you.

To do that, go to the frontend/src/components/web3/contract-card.tsx file in the project's folder, and add the code below to query the contract and get the daily pizza supply and display it in your frontend.

 const queryContract = useCallback(async () => {
    setQueryIsLoading(true)
    try {
      if (!api || !chain) return

      const storageResult = await contract.getStorage().getRoot()

       const result = await contract.query("get_daily_supply", {
          origin: ALICE
        }); 
      const newDailySupplyState = result.success ? result.value.response : undefined
      setDailySupply(newDailySupplyState)
      console.log(dailySupply);

       const result1 = await contract.query("get_remaining_supply", {
          origin: ALICE
        }); 
      const newRemainingSupplyState = result1.success ? result1.value.response : undefined
      setRemainingSupply(newRemainingSupplyState)

      const newMaxOrderPerUserState = storageResult.success ? storageResult.value.max_order_per_user : undefined
      setMaxOrderPerUser(newMaxOrderPerUserState )

    } catch (error) {
      console.error(error)
    } finally {
      setQueryIsLoading(false)
    }
  }, [api, chain])

  useEffect(() => {
    queryContract()
  }, [queryContract])
Enter fullscreen mode Exit fullscreen mode

What exactly is happening in this code block?

  • We created connection with the Polkadot-SDK node using ReviveSdk and also a connected the pizza contract with the typedApi().
  • We also created an instance of the pizza contract on the passethub chain.
  • We are reading the root storage of the contract, this allows us to query every stored items in the contract.
  • We now used contract.query() to read the contract's functions and return the responses such as getting the total daily supply of pizza.

Let us write a signed transaction to the contract to request for some pizza.

   const orderPizza = useCallback(async () => {
    if (!api || !chain || !signer) return

    // Map account if not mapped
    const isMapped = await sdk.addressIsMapped(signerAddress)
    if (!isMapped) {
      toast.error("Account not mapped. Please map your account first.")
      return
    }

    // Send transaction
    const tx = contract
      .send("order_pizza",
        {
          data: {
            quantity_ordered: quantityOrdered
          },
          origin: signerAddress
        },

      )
      .signAndSubmit(signer)
      .then((tx) => {
        queryContract()
        if (!tx.ok) throw new Error("Failed to send transaction here, please check it", { cause: tx.dispatchError })
      })

    toast.promise(tx, {
      loading: "Sending transaction...",
      success: "Successfully Ordered, your Pizza is on its way",
      error: "Failed to send transaction here too",
    })
  }, [signer, api, chain])
Enter fullscreen mode Exit fullscreen mode

Here, the first thing we did was to check if the accounts interacting with the contract is supported by the substrate runtime, if not, the function can prompts to map the account's address to the Polkadot type.

We also wrote a contract.send() function that takes three arguments;

  • The actual function you want to call which is order_pizza function.
  • The data argument that supplies the contract with the quantity of pizza you want to request, and,
  • The origin of the transaction which is the user's signer address.

And lastly, we have a cron job that runs every 24 hours function that reset the daily pizza supply every 24 hours.

Conclusion.
In this project, we demonstrated the ease of deploying ink! smart contracts and interacted with it from the frontend using Polkadot-API.

With your knowledge of ink! smart contract develpment and frontend web development, polkadot-API offers you a level playground to make your apps available to users without any hassle.

Top comments (0)