🙄
[Astar]コントラクト備忘録49(コントラクトの値を表示する簡単なdAppを作ってみよう!)
本日は、今までの知見を活かして、簡単なdAppを作ってみようと思います。
こちらが完成したものです。
では、作っていきましょう。
なお、コードは最後に貼っております。
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