mint機能実装の下準備をしよう
📝 コントラクトの情報の更新+NFT の mint の下準備を実装しよう
前回まででコントラクトに必要なデータは宣言することができました。
ここからはデータの初期化、NFTのmintの下準備を実装していきます!
まずはlib.rsへ移動して下のように書き換えましょう。 [lib.rs]
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::{LazyOption, LookupMap, UnorderedMap, UnorderedSet};
use near_sdk::json_types::U128;
use near_sdk::serde::{Deserialize, Serialize};
use near_sdk::{env, near_bindgen, AccountId, Balance, CryptoHash, PanicOnDefault, Promise};
mod vote;
mod enumeration;
mod internal;
mod metadata;
mod mint;
mod nft_core;
pub use crate::enumeration::*;
use crate::internal::*;
pub use crate::metadata::*;
pub use crate::mint::*;
pub use crate::nft_core::*;
pub use vote::*;
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
pub struct Contract {
// contract state value
pub owner_id: AccountId,
pub tokens_per_owner: LookupMap<AccountId, UnorderedSet<TokenId>>,
pub tokens_per_kind: LookupMap<TokenKind, UnorderedSet<TokenId>>,
pub tokens_by_id: LookupMap<TokenId, TokenOwner>,
pub token_metadata_by_id: UnorderedMap<TokenId, TokenMetadata>,
pub metadata: LazyOption<NFTContractMetadata>,
pub token_id_counter: u128,
pub likes_per_candidate: LookupMap<TokenId, Likes>,
pub added_voter_list: LookupMap<ReceiverId, TokenId>,
pub voted_voter_list: LookupMap<ReceiverId, u128>,
pub is_election_closed: bool,
}
#[derive(BorshSerialize)]
pub enum StorageKey {
TokensPerOwner,
TokensPerKind,
TokensPerOwnerInner { account_id_hash: CryptoHash },
TokensPerKindInner { token_kind: TokenKind },
TokensById,
TokenMetadataById,
TokensPerTypeInner { token_type_hash: CryptoHash },
NFTContractMetadata,
LikesPerCandidate,
AddedVoterList,
VotedVoterList,
}
+ #[near_bindgen]
+ impl Contract {
+ // function for initialization(new_default_meta)
+ #[init]
+ pub fn new(owner_id: AccountId, metadata: NFTContractMetadata) -> Self {
+ let this = Self {
+ owner_id,
+ tokens_per_owner: LookupMap::new(StorageKey::TokensPerOwner.try_to_vec().unwrap()),
+ tokens_per_kind: LookupMap::new(StorageKey::TokensPerKind.try_to_vec().unwrap()),
+ tokens_by_id: LookupMap::new(StorageKey::TokensById.try_to_vec().unwrap()),
+ token_metadata_by_id: UnorderedMap::new(
+ StorageKey::TokenMetadataById.try_to_vec().unwrap(),
+ ),
+ metadata: LazyOption::new(
+ StorageKey::NFTContractMetadata.try_to_vec().unwrap(),
+ Some(&metadata),
+ ),
+ token_id_counter: 0,
+ likes_per_candidate: LookupMap::new(
+ StorageKey::LikesPerCandidate.try_to_vec().unwrap(),
+ ),
+ added_voter_list: LookupMap::new(StorageKey::AddedVoterList.try_to_vec().unwrap()),
+ voted_voter_list: LookupMap::new(StorageKey::VotedVoterList.try_to_vec().unwrap()),
+ is_election_closed: false,
+ };
+ this
+ }
+
+ // initialization function
+ #[init]
+ pub fn new_default_meta(owner_id: AccountId) -> Self {
+ Self::new(
+ owner_id,
+ NFTContractMetadata {
+ spec: "nft-1.0.0".to_string(),
+ name: "Near Vote Contract".to_string(),
+ reference: "This contract is design for fair election!".to_string(),
+ },
+ )
+ }
+ }
はじめの#[near_bindgen]
はnearのチェーンで有効な構造体、関数を宣言できるようにするための ものです。
次のimpl Contract
についてですが、これはContract
という構造体に{}
内のメソッドを持たせるということを意味しています。
#[near_bindgen]
impl Contract
その次にある#[init]
は初期化のための関数であることを示しています。lesson-1で説明したDefaultOnPanic
というトレイトを覚えているでしょうか? デプロイした後に初期化の関数をまず動かさないと、他の関数を走らせることはできないというものです。
このトレイトは#[init]
という印が付いている関数を初期化のための関数と認識しています。
ここで宣言しているnew関数
では、引数としてAccountId
型のowner_id
という変数を必要とすることがわかります。metadata
という変数についても同じことです。
そして->Self
というのはContract
という構造体自身を返すということです。これはコントラクト自体のインスタンスを関数内で生成して、それを返り値として返すということです。
#[init]
pub fn new(owner_id: AccountId, metadata: NFTContractMetadata) -> Self {
中身ではそれぞれの変数の初期化をしています。
例えばtokens_per_ownerを例にとると、LookupMap
という型が持つnew
というメソッドによって初期化されて新しいインスタンスが生み出されます。
その後try_to_vec()
によってResult型のベクター(他の言語では配列)が作られて、unwrap()
というメソッドによって
Result<Vec<u8>>->Vec<u8>
に変換されます。くわしくはResult の説明とunwrap の説明をご覧ください
UnorderedMap
とLookupMap
はどちらもMap形式の型なのですがUnorderedMap
はそれぞれのmapがインデックス化されておりベクター型のmapなのですが、LookupMap
はインデックス化されておらず、mapだけが存在しているものです。
最後のthisというのはここで宣言したthisという変数を返り値とすることを示しています。rustではreturn
を明示的に使わなくても、最後に返したい値を;
なしで記述すれば暗黙的に返り値なんだと解釈してくれます。
{
let this = Self {
owner_id,
tokens_per_owner: LookupMap::new(StorageKey::TokensPerOwner.try_to_vec().unwrap()),
tokens_per_kind: LookupMap::new(StorageKey::TokensPerKind.try_to_vec().unwrap()),
tokens_by_id: LookupMap::new(StorageKey::TokensById.try_to_vec().unwrap()),
token_metadata_by_id: UnorderedMap::new(
StorageKey::TokenMetadataById.try_to_vec().unwrap(),
),
metadata: LazyOption::new(
StorageKey::NFTContractMetadata.try_to_vec().unwrap(),
Some(&metadata),
),
token_id_counter: 0,
likes_per_candidate: LookupMap::new(
StorageKey::LikesPerCandidate.try_to_vec().unwrap(),
),
added_voter_list: LookupMap::new(StorageKey::AddedVoterList.try_to_vec().unwrap()),
voted_voter_list: LookupMap::new(StorageKey::VotedVoterList.try_to_vec().unwrap()),
is_election_closed: false,
};
this
}
次のnew_default_meta関数
はコントラクトのオーナーのWallet Idを引数として受け取ります。Self::new
というのはSelf
つまりこのコントラクト自体がもつnew
という関数を呼び出していることを表しています。
これはnew_default_meta関数
を呼び出すことでnew関数
とnew_default_meta関数
の両方を読んでいることになるので、一度で初期化が完了していることを意味しています。なのでnew関数を読んでないけど初期化できているの? と疑問に思う時がきてもこのような理由で完了していることを理解しておいてください。
中身ではspec,name,reference
の3つのコントラクトに関する情報とowner_id
を更新しています。
ここでowner_id
に何も値が入っていないと疑問に思った方もいるかもしれませんが大丈夫です。rustでは引数と同じものであれば省略できるというルールがあるからです。なのでここでは引数として入れられた値がそのままowner_id
に入っていることになります。
それ以外の変数は好きなように変えていただいて構いません。
ただ、ここでどの変数にもto_string()
というメソッドが適用されていることに疑問を抱いた方が多いと思います。その解説についてはこちらをご覧ください。
#[init]
pub fn new_default_meta(owner_id: AccountId) -> Self {
Self::new(
owner_id,
NFTContractMetadata {
spec: "nft-1.0.0".to_string(),
name: "Near Vote Contract".to_string(),
reference: "This contract is design for fair election!".to_string(),
},
)
}
これでコントラクトの初期化機能の実装は完了しました。次のレッスンではNFTをmint,transfer機能を実装しましょう。