🙄

[Astar]コントラクト備忘録49(コントラクトの値を表示する簡単なdAppを作ってみよう!)

2023/03/19に公開

本日は、今までの知見を活かして、簡単なdAppを作ってみようと思います。

こちらが完成したものです。

https://wasm-test10.vercel.app/

では、作っていきましょう。

なお、コードは最後に貼っております。

1 コントラクトについて

今回はpsp22Metadata、psp22Mitableを元にしたものを作っています。

下のように実装しているのが確認できます。

また、画像データも格納したいので、データ構造に新しく「image」を追加しています。


下のように、初期設定で、URLを格納しています。

また、それを取得するための「token_image」という関数も作っています。

2 next.jsについて

では、next.js側も見てみましょう。

まずは、「getContract」という関数を作成し、コントラクトを取得しています。

コントラクトの取得のためには、「provider」から「api」を作り、「contractPromise」を使って、コントラクトを作成しています。

コントラクトの作成ができたので、次は値を取得しています。

「contract.query」で値を取得することができました。

また、gasLimitは「WeightV2」型であることも留意する必要がありました。

今回実装した、imageも同様にして取得することができます。

最後に、「onClick」を用いて、関数を実行しています。

ショートサーキット評価も使っていますね。

今回は以上です。

index.tsx
import Head from 'next/head'
import Image from 'next/image'
import { Inter } from 'next/font/google'
import styles from '@/styles/Home.module.css'
import { ApiPromise, WsProvider } from '@polkadot/api';
import { ContractPromise } from '@polkadot/api-contract';
import { BN, BN_ONE } from "@polkadot/util";
import type { WeightV2 } from '@polkadot/types/interfaces'
import '@polkadot/api-augment'
import { useState, useEffect } from 'react'

import metadata from "./metadata.json";

const inter = Inter({ subsets: ['latin'] })
const ALICE = ""

const MAX_CALL_WEIGHT = new BN(5_000_000_000_000).isub(BN_ONE);
const PROOFSIZE = new BN(1_000_000);
const storageDepositLimit = null;


// main().then(() => console.log('completed'))

export default function Home() {
  const [api, setApi] = useState<ApiPromise | null>(null);
  const [contract, setContract] = useState<ContractPromise | null>(null);
  const [name, setName] = useState('');
  const [symbol, setSymbol] = useState('');
  const [image, setImage] = useState('');
  const [contractAddress, setContractAddress] = useState('');
  const [getContractResult, setGetContractResult] = useState("");

  async function getContract () {
    try{
      // Initialise the provider to connect to the local node
      const provider = new WsProvider('wss://rpc.shibuya.astar.network');
        
      const api = await ApiPromise.create({ provider});
      setApi(api);

      const contract = new ContractPromise(api, metadata, contractAddress)
      setContract(contract)
      console.log("contract", contract)
      setGetContractResult("OK");
    }catch (error) {
      console.error(error);
      // If there's an error, set getContractResult to the error message
      setGetContractResult("NG");
    }
    
  }
  async function getName () {
    if (contract !== null) {
      const { output }  = await contract.query['psp22Metadata::tokenName'](ALICE,
        {
          gasLimit: api?.registry.createType('WeightV2', {
            refTime: MAX_CALL_WEIGHT,
            proofSize: PROOFSIZE,
          }) as WeightV2,
          storageDepositLimit,
        },)
    
      console.log("output", output?.toHuman()?.toString())
      setName(output?.toHuman()?.toString()|| '')
    }
  }
  async function getSymbol () {
    if (contract !== null) {
      const { output }  = await contract.query['psp22Metadata::tokenSymbol'](ALICE,
        {
          gasLimit: api?.registry.createType('WeightV2', {
            refTime: MAX_CALL_WEIGHT,
            proofSize: PROOFSIZE,
          }) as WeightV2,
          storageDepositLimit,
        },)
    
      console.log("output", output?.toHuman()?.toString())
      setSymbol(output?.toHuman()?.toString()|| '')
    }
  }

  async function getImage () {
    if (contract !== null) {
      const { output }  = await contract.query.tokenImage(ALICE,
        {
          gasLimit: api?.registry.createType('WeightV2', {
            refTime: MAX_CALL_WEIGHT,
            proofSize: PROOFSIZE,
          }) as WeightV2,
          storageDepositLimit,
        },)
    
      console.log("output", output?.toHuman()?.toString())
      setImage(output?.toHuman()?.toString()|| '')
    }
  }
  async function getAll () {
    getName ()
    getSymbol ()
    getImage()
  }
  return (
    <>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className={styles.main}>
        <h1 style={{marginBottom: "80px"}}>Get Your Contract Information(psp22Metadata)</h1>
        <div className={styles.description}>
          <div>
            contractAddress:<input  style={{width: "400px"}} type="text" value={contractAddress} onChange={(e) => setContractAddress(e.target.value)} />
            <button className={styles.rotatebutton} onClick={getContract}>getContract</button>

            {getContractResult && <p>Get Contract result: {getContractResult}</p>}
            <button className={styles.rotatebutton} onClick={getAll}>get contract information</button>
            {name && <p style={{marginBottom: "20px"}}>Name: {name}</p>}
            {symbol && <p style={{marginBottom: "20px"}}>Symbol: {symbol}</p>}
            {image && (
            <>
              <h4 style={{marginBottom: "10px"}}>Image</h4>
              <img src={image} alt="Image" />
            </>
          )}
          </div>
        </div>
      </main>
    </>
  )
}
lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
#![feature(min_specialization)]

#[openbrush::contract]
pub mod my_psp22 {
    use ink_storage::traits::SpreadAllocate;
    use openbrush::{
        contracts::psp22::extensions::metadata::*,
        contracts::psp22::extensions::mintable::*,
        traits::{
            Storage,
            String,
        },
    };

    #[ink(storage)]
    #[derive(Default, SpreadAllocate, Storage)]
    pub struct Contract {
        #[storage_field]
        psp22: psp22::Data,
        #[storage_field]
        metadata: metadata::Data,

        image: Option<String>,
    }

    impl PSP22 for Contract {}

    impl PSP22Metadata for Contract {}

    impl PSP22Mintable for Contract {}

    impl Contract {
        #[ink(constructor)]
        pub fn new(name: Option<String>, symbol: Option<String>, decimal: u8, image: Option<String>) -> Self {
            ink_lang::codegen::initialize_contract(|instance: &mut Self| {
                instance.metadata.name = name;
                instance.metadata.symbol = symbol;
                instance.metadata.decimals = decimal;
                instance.image = image;
            })
        }
        #[ink(message)]
        pub fn token_image(&self) -> Option<String> {
            self.image.clone()
        }
    }
}

Discussion