創(chuàng)建一個(gè)ERC721標(biāo)準(zhǔn)的NFT

打開CHAINPIP社區(qū),進(jìn)入應(yīng)用,創(chuàng)建一個(gè)新應(yīng)用;



將模板代碼刪除,上傳本次測(cè)試的合約代碼。



這是一個(gè)符合ERC721標(biāo)準(zhǔn)的NFT合約代碼,其中包括了幾個(gè)常用的標(biāo)準(zhǔn)接口和合約,下面是個(gè)合約的具體信息:

IERC65.sol

pragma?solidity?^0.8.0;


/**

?* @dev ERC165標(biāo)準(zhǔn)接口, 詳見

?* https://eips.ethereum.org/EIPS/eip-165[EIP].

?*

?* 合約可以聲明支持的接口,供其他合約檢查

?*

?*/

interface IERC165 {

? ? /**

? ? ?* @dev 如果合約實(shí)現(xiàn)了查詢的`interfaceId`,則返回true

? ? ?* 規(guī)則詳見:https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]

? ? ?*

? ? ?*/

? ? function?supportsInterface(bytes4?interfaceId) external?view returns?(bool);

}


IERC721.sol

// SPDX-License-Identifier: MIT


pragma?solidity?^0.8.0;


import?"./IERC165.sol";


/**

?* @dev ERC721標(biāo)準(zhǔn)接口.

?*/

interface IERC721 is?IERC165 {

? ? event?Transfer(address?indexed from, address?indexed to, uint256?indexed tokenId);

? ? event?Approval(address?indexed owner, address?indexed approved, uint256?indexed tokenId);

? ? event?ApprovalForAll(address?indexed owner, address?indexed operator, bool?approved);


? ? function?balanceOf(address?owner) external?view returns?(uint256?balance);


? ? function?ownerOf(uint256?tokenId) external?view returns?(address?owner);


? ? function?safeTransferFrom(

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint256?tokenId,

? ? ? ? bytes?calldata data

? ? ) external;


? ? function?safeTransferFrom(

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint256?tokenId

? ? ) external;


? ? function?transferFrom(

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint256?tokenId

? ? ) external;


? ? function?approve(address?to, uint256?tokenId) external;


? ? function?setApprovalForAll(address?operator, bool?_approved) external;


? ? function?getApproved(uint256?tokenId) external?view returns?(address?operator);


? ? function?isApprovedForAll(address?owner, address?operator) external?view returns?(bool);

}

IERC721Receiver.sol

// SPDX-License-Identifier: MIT

pragma?solidity?^0.8.0;


// ERC721接收者接口:合約必須實(shí)現(xiàn)這個(gè)接口來通過安全轉(zhuǎn)賬接收ERC721

interface IERC721Receiver {

? ? function?onERC721Received(

? ? ? ? address?operator,

? ? ? ? address?from,

? ? ? ? uint?tokenId,

? ? ? ? bytes?calldata data

? ? ) external?returns?(bytes4);

}


IERC721Metadata.sol

// SPDX-License-Identifier: MIT

pragma?solidity?^0.8.0;


interface IERC721Metadata {

? ? function?name() external?view returns?(string?memory);


? ? function?symbol() external?view returns?(string?memory);


? ? function?tokenURI(uint256?tokenId) external?view returns?(string?memory);

}


Strings.sol

// SPDX-License-Identifier: MIT

// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)


pragma?solidity?^0.8.4;


/**

?* @dev String operations.

?*/

library?Strings {

? ? bytes16?private?constant?_HEX_SYMBOLS = "0123456789abcdef";

? ? uint8?private?constant?_ADDRESS_LENGTH = 20;


? ? /**

? ? ?* @dev Converts a `uint256` to its ASCII `string` decimal representation.

? ? ?*/

? ? function?toString(uint256?value) internal pure returns?(string?memory) {

? ? ? ? // Inspired by OraclizeAPI's implementation - MIT licence

? ? ? ? // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol


? ? ? ? if?(value == 0) {

? ? ? ? ? ? return?"0";

? ? ? ? }

? ? ? ? uint256?temp = value;

? ? ? ? uint256?digits;

? ? ? ? while?(temp != 0) {

? ? ? ? ? ? digits++;

? ? ? ? ? ? temp /= 10;

? ? ? ? }

? ? ? ? bytes?memory buffer = new?bytes(digits);

? ? ? ? while?(value != 0) {

? ? ? ? ? ? digits -= 1;

? ? ? ? ? ? buffer[digits] = bytes1(uint8(48?+ uint256(value % 10)));

? ? ? ? ? ? value /= 10;

? ? ? ? }

? ? ? ? return?string(buffer);

? ? }


? ? /**

? ? ?* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.

? ? ?*/

? ? function?toHexString(uint256?value) internal pure returns?(string?memory) {

? ? ? ? if?(value == 0) {

? ? ? ? ? ? return?"0x00";

? ? ? ? }

? ? ? ? uint256?temp = value;

? ? ? ? uint256?length = 0;

? ? ? ? while?(temp != 0) {

? ? ? ? ? ? length++;

? ? ? ? ? ? temp >>= 8;

? ? ? ? }

? ? ? ? return?toHexString(value, length);

? ? }


? ? /**

? ? ?* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.

? ? ?*/

? ? function?toHexString(uint256?value, uint256?length) internal pure returns?(string?memory) {

? ? ? ? bytes?memory buffer = new?bytes(2?* length + 2);

? ? ? ? buffer[0] = "0";

? ? ? ? buffer[1] = "x";

? ? ? ? for?(uint256?i = 2?* length + 1; i > 1; --i) {

? ? ? ? ? ? buffer[i] = _HEX_SYMBOLS[value & 0xf];

? ? ? ? ? ? value >>= 4;

? ? ? ? }

? ? ? ? require(value == 0, "Strings: hex length insufficient");

? ? ? ? return?string(buffer);

? ? }


? ? /**

? ? ?* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.

? ? ?*/

? ? function?toHexString(address?addr) internal pure returns?(string?memory) {

? ? ? ? return?toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);

? ? }

}


Address.sol

// SPDX-License-Identifier: MIT

pragma?solidity?^0.8.1;


// Address庫(kù)

library?Address {

? ? // 利用extcodesize判斷一個(gè)地址是否為合約地址

? ? function?isContract(address?account) internal view returns?(bool) {

? ? ? ? uint?size;

? ? ? ? assembly {

? ? ? ? ? ? size := extcodesize(account)

? ? ? ? }

? ? ? ? return?size > 0;

? ? }

}


ERC721.sol

// SPDX-License-Identifier: MIT

// by 0xAA

pragma?solidity?^0.8.4;


import?"./IERC165.sol";

import?"./IERC721.sol";

import?"./IERC721Receiver.sol";

import?"./IERC721Metadata.sol";

import?"./Address.sol";

import?"./String.sol";


contract?ERC721 is?IERC721, IERC721Metadata{

? ? using?Address for?address; // 使用Address庫(kù),用isContract來判斷地址是否為合約

? ? using?Strings for?uint256; // 使用String庫(kù),


? ? // Token名稱

? ? string?public?override name;

? ? // Token代號(hào)

? ? string?public?override symbol;

? ? // tokenId 到 owner address 的持有人映射

? ? mapping(uint?=> address) private?_owners;

? ? // address 到 持倉(cāng)數(shù)量 的持倉(cāng)量映射

? ? mapping(address?=> uint) private?_balances;

? ? // tokenID 到 授權(quán)地址 的授權(quán)映射

? ? mapping(uint?=> address) private?_tokenApprovals;

? ? // ?owner地址。到operator地址 的批量授權(quán)映射

? ? mapping(address?=> mapping(address?=> bool)) private?_operatorApprovals;


? ? /**

? ? ?* 構(gòu)造函數(shù),初始化`name` 和`symbol` .

? ? ?*/

? ? constructor(string?memory name_, string?memory symbol_) {

? ? ? ? name = name_;

? ? ? ? symbol = symbol_;

? ? }


? ? // 實(shí)現(xiàn)IERC165接口supportsInterface

? ? function?supportsInterface(bytes4?interfaceId)

? ? ? ? external

? ? ? ? pure

? ? ? ? override

? ? ? ? returns?(bool)

? ? {

? ? ? ? return

? ? ? ? ? ? interfaceId == type(IERC721).interfaceId ||

? ? ? ? ? ? interfaceId == type(IERC165).interfaceId ||

? ? ? ? ? ? interfaceId == type(IERC721Metadata).interfaceId;

? ? }


? ? // 實(shí)現(xiàn)IERC721的balanceOf,利用_balances變量查詢owner地址的balance。

? ? function?balanceOf(address?owner) external?view override returns?(uint) {

? ? ? ? require(owner != address(0), "owner = zero address");

? ? ? ? return?_balances[owner];

? ? }


? ? // 實(shí)現(xiàn)IERC721的ownerOf,利用_owners變量查詢tokenId的owner。

? ? function?ownerOf(uint?tokenId) public?view override returns?(address?owner) {

? ? ? ? owner = _owners[tokenId];

? ? ? ? require(owner != address(0), "token doesn't exist");

? ? }


? ? // 實(shí)現(xiàn)IERC721的isApprovedForAll,利用_operatorApprovals變量查詢owner地址是否將所持NFT批量授權(quán)給了operator地址。

? ? function?isApprovedForAll(address?owner, address?operator)

? ? ? ? external

? ? ? ? view

? ? ? ? override

? ? ? ? returns?(bool)

? ? {

? ? ? ? return?_operatorApprovals[owner][operator];

? ? }


? ? // 實(shí)現(xiàn)IERC721的setApprovalForAll,將持有代幣全部授權(quán)給operator地址。調(diào)用_setApprovalForAll函數(shù)。

? ? function?setApprovalForAll(address?operator, bool?approved) external?override {

? ? ? ? _operatorApprovals[msg.sender][operator] = approved;

? ? ? ? emit ApprovalForAll(msg.sender, operator, approved);

? ? }


? ? // 實(shí)現(xiàn)IERC721的getApproved,利用_tokenApprovals變量查詢tokenId的授權(quán)地址。

? ? function?getApproved(uint?tokenId) external?view override returns?(address) {

? ? ? ? require(_owners[tokenId] != address(0), "token doesn't exist");

? ? ? ? return?_tokenApprovals[tokenId];

? ? }


? ? // 授權(quán)函數(shù)。通過調(diào)整_tokenApprovals來,授權(quán) to 地址操作 tokenId,同時(shí)釋放Approval事件。

? ? function?_approve(

? ? ? ? address?owner,

? ? ? ? address?to,

? ? ? ? uint?tokenId

? ? ) private?{

? ? ? ? _tokenApprovals[tokenId] = to;

? ? ? ? emit Approval(owner, to, tokenId);

? ? }


? ? // 實(shí)現(xiàn)IERC721的approve,將tokenId授權(quán)給 to 地址。條件:to不是owner,且msg.sender是owner或授權(quán)地址。調(diào)用_approve函數(shù)。

? ? function?approve(address?to, uint?tokenId) external?override {

? ? ? ? address?owner = _owners[tokenId];

? ? ? ? require(

? ? ? ? ? ? msg.sender == owner || _operatorApprovals[owner][msg.sender],

? ? ? ? ? ? "not owner nor approved for all"

? ? ? ? );

? ? ? ? _approve(owner, to, tokenId);

? ? }


? ? // 查詢 spender地址是否被可以使用tokenId(他是owner或被授權(quán)地址)。

? ? function?_isApprovedOrOwner(

? ? ? ? address?owner,

? ? ? ? address?spender,

? ? ? ? uint?tokenId

? ? ) private?view returns?(bool) {

? ? ? ? return?(spender == owner ||

? ? ? ? ? ? _tokenApprovals[tokenId] == spender ||

? ? ? ? ? ? _operatorApprovals[owner][spender]);

? ? }


? ? /*

? ? ?* 轉(zhuǎn)賬函數(shù)。通過調(diào)整_balances和_owner變量將 tokenId 從 from 轉(zhuǎn)賬給 to,同時(shí)釋放Tranfer事件。

? ? ?* 條件:

? ? ?* 1. tokenId 被 from 擁有

? ? ?* 2. to 不是0地址

? ? ?*/

? ? function?_transfer(

? ? ? ? address?owner,

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint?tokenId

? ? ) private?{

? ? ? ? require(from == owner, "not owner");

? ? ? ? require(to != address(0), "transfer to the zero address");


? ? ? ? _approve(owner, address(0), tokenId);


? ? ? ? _balances[from] -= 1;

? ? ? ? _balances[to] += 1;

? ? ? ? _owners[tokenId] = to;


? ? ? ? emit Transfer(from, to, tokenId);

? ? }


? ? // 實(shí)現(xiàn)IERC721的transferFrom,非安全轉(zhuǎn)賬,不建議使用。調(diào)用_transfer函數(shù)

? ? function?transferFrom(

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint?tokenId

? ? ) external?override {

? ? ? ? address?owner = ownerOf(tokenId);

? ? ? ? require(

? ? ? ? ? ? _isApprovedOrOwner(owner, msg.sender, tokenId),

? ? ? ? ? ? "not owner nor approved"

? ? ? ? );

? ? ? ? _transfer(owner, from, to, tokenId);

? ? }


? ? /**

? ? ?* 安全轉(zhuǎn)賬,安全地將 tokenId 代幣從 from 轉(zhuǎn)移到 to,會(huì)檢查合約接收者是否了解 ERC721 協(xié)議,以防止代幣被永久鎖定。調(diào)用了_transfer函數(shù)和_checkOnERC721Received函數(shù)。條件:

? ? ?* from 不能是0地址.

? ? ?* to 不能是0地址.

? ? ?* tokenId 代幣必須存在,并且被 from擁有.

? ? ?* 如果 to 是智能合約, 他必須支持 IERC721Receiver-onERC721Received.

? ? ?*/

? ? function?_safeTransfer(

? ? ? ? address?owner,

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint?tokenId,

? ? ? ? bytes?memory _data

? ? ) private?{

? ? ? ? _transfer(owner, from, to, tokenId);

? ? ? ? require(_checkOnERC721Received(from, to, tokenId, _data), "not ERC721Receiver");

? ? }


? ? /**

? ? ?* 實(shí)現(xiàn)IERC721的safeTransferFrom,安全轉(zhuǎn)賬,調(diào)用了_safeTransfer函數(shù)。

? ? ?*/

? ? function?safeTransferFrom(

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint?tokenId,

? ? ? ? bytes?memory _data

? ? ) public?override {

? ? ? ? address?owner = ownerOf(tokenId);

? ? ? ? require(

? ? ? ? ? ? _isApprovedOrOwner(owner, msg.sender, tokenId),

? ? ? ? ? ? "not owner nor approved"

? ? ? ? );

? ? ? ? _safeTransfer(owner, from, to, tokenId, _data);

? ? }


? ? // safeTransferFrom重載函數(shù)

? ? function?safeTransferFrom(

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint?tokenId

? ? ) external?override {

? ? ? ? safeTransferFrom(from, to, tokenId, "");

? ? }


? ? /**

? ? ?* 鑄造函數(shù)。通過調(diào)整_balances和_owners變量來鑄造tokenId并轉(zhuǎn)賬給 to,同時(shí)釋放Tranfer事件。鑄造函數(shù)。通過調(diào)整_balances和_owners變量來鑄造tokenId并轉(zhuǎn)賬給 to,同時(shí)釋放Tranfer事件。

? ? ?* 這個(gè)mint函數(shù)所有人都能調(diào)用,實(shí)際使用需要開發(fā)人員重寫,加上一些條件。

? ? ?* 條件:

? ? ?* 1. tokenId尚不存在。

? ? ?* 2. to不是0地址.

? ? ?*/

? ? function?_mint(address?to, uint?tokenId) internal virtual {

? ? ? ? require(to != address(0), "mint to zero address");

? ? ? ? require(_owners[tokenId] == address(0), "token already minted");


? ? ? ? _balances[to] += 1;

? ? ? ? _owners[tokenId] = to;


? ? ? ? emit Transfer(address(0), to, tokenId);

? ? }


? ? // 銷毀函數(shù),通過調(diào)整_balances和_owners變量來銷毀tokenId,同時(shí)釋放Tranfer事件。條件:tokenId存在。

? ? function?_burn(uint?tokenId) internal virtual {

? ? ? ? address?owner = ownerOf(tokenId);

? ? ? ? require(msg.sender == owner, "not owner of token");


? ? ? ? _approve(owner, address(0), tokenId);


? ? ? ? _balances[owner] -= 1;

? ? ? ? delete _owners[tokenId];


? ? ? ? emit Transfer(owner, address(0), tokenId);

? ? }


? ? // _checkOnERC721Received:函數(shù),用于在 to 為合約的時(shí)候調(diào)用IERC721Receiver-onERC721Received, 以防 tokenId 被不小心轉(zhuǎn)入黑洞。

? ? function?_checkOnERC721Received(

? ? ? ? address?from,

? ? ? ? address?to,

? ? ? ? uint?tokenId,

? ? ? ? bytes?memory _data

? ? ) private?returns?(bool) {

? ? ? ? if?(to.isContract()) {

? ? ? ? ? ? return

? ? ? ? ? ? ? ? IERC721Receiver(to).onERC721Received(

? ? ? ? ? ? ? ? ? ? msg.sender,

? ? ? ? ? ? ? ? ? ? from,

? ? ? ? ? ? ? ? ? ? tokenId,

? ? ? ? ? ? ? ? ? ? _data

? ? ? ? ? ? ? ? ) == IERC721Receiver.onERC721Received.selector;

? ? ? ? } else?{

? ? ? ? ? ? return?true;

? ? ? ? }

? ? }


? ? /**

? ? ?* 實(shí)現(xiàn)IERC721Metadata的tokenURI函數(shù),查詢metadata。

? ? ?*/

? ? function?tokenURI(uint256?tokenId) public?view virtual override returns?(string?memory) {

? ? ? ? require(_owners[tokenId] != address(0), "Token Not Exist");


? ? ? ? string?memory baseURI = _baseURI();

? ? ? ? return?bytes(baseURI).length > 0?? string(abi.encodePacked(baseURI, tokenId.toString())) : "";

? ? }


? ? /**

? ? ?* 計(jì)算{tokenURI}的BaseURI,tokenURI就是把baseURI和tokenId拼接在一起,需要開發(fā)重寫。

? ? ?* BAYC的baseURI為ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/

? ? ?*/

? ? function?_baseURI() internal view virtual returns?(string?memory) {

? ? ? ? return?"";

? ? }

}


編譯、部署合約,設(shè)置NFT名稱為HHTest,符號(hào)為HH。



合約部署成功后,進(jìn)入ABI操作頁(yè)面,開始測(cè)試合約的mint函數(shù),測(cè)試鑄造一個(gè)NFT。



鑄造完成后使用合約地址在opensea的測(cè)試鏈版中查詢,可以發(fā)現(xiàn)我們剛剛鑄造的NFT可以在其中查詢出來,由于合約用的是無聊猿的tokenURI地址,所以顯示的是無聊猿的信息。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容