Alchemy Road to web3 week5 คู่มือภาษาไทย
August 19th, 2022

เมื่อไม่นานมานี้โปรเจค Alchemy ได้เข้าสู่ web3 ด้วยมูลค่า 10.2 พันล้านดอลลาร์ และได้รับการระดมทุน 545 ล้านดอลลาร์.

Alchemy คือโปรเจคอะไร?

Alchemy คือแพลตฟอร์มสำหรับนักพัฒนา dApp บน web3. เป็นรากฐานที่อยู่เบื้องหลังตลาด NFT ยอดนิยมอย่าง OpenSea, Nifty และคอลเล็กชั่นระดับโลกที่สำคัญมากมาย.

12/2019, Alchemy เสร็จสิ้นการจัดหาเงินทุน Series A มูลค่า 15 ล้านดอลลาร์จาก Pantera Capital, Stanford University, Coinbase, Samsung,…

4/2021, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน Series B มูลค่า 80 ล้านดอลลาร์ โดยมีมูลค่า 505 ล้านดอลลาร์ นำโดย Coatue and Addition โดยมีส่วนร่วมจาก DFJ Growth, K5 Global, Chainsmokers, นักแสดง Jared Leto และครอบครัว Glazer.

10/2021, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน Series C มูลค่า 250 ล้านดอลลาร์ โดยมีมูลค่า 3.5 พันล้านดอลลาร์ นำโดย a16z.

2/2022, Alchemy ได้เสร็จสิ้นการจัดหาเงินทุน 200 ล้านดอลลาร์โดยมีมูลค่า 10.2 พันล้านดอลลาร์ซึ่งนำโดย Lightspeed และ Silver Lake.

Alchemy เป็นทีมที่มีพื้นฐานที่แข็งแกร่ง, เงินทุนที่เพียงพอ, ทำงานจริง, และยังไม่ได้ออก token.

และ Alchemy วางแผนที่จะใช้เงินทุนใหม่นี้เพื่อกระตุ้นการใช้งาน Web3, บางส่วนรวมถึงการเปิดตัว Web3 University, ซึ่งปัจจุบันเป็นงาน Road to Web3 เป็นเวลา 10 สัปดาห์ด้วยหนึ่ง NFT ต่อสัปดาห์. ผมเห็นว่าจำนวน nfts ที่มิ้นออกมานั้นน้อยมาก คาดว่าเนื่องจากความยากของงาน คนจำนวนมากจึงยอมแพ้ที่จะเข้าร่วม, หากโปรเจคนี้มี Airdrop ส่วนตัวผมมองว่าน่าจะได้เยอะอย่างแน่นอน.

Dynamic NFT คืออะไร?

บทเรียนนี้ค่อนข้างสนุก, จะเปลี่ยนไปดังภาพด้านล่าง, มาเริ่มกันเลย!

1. ปรับกระเป๋าเงิน metamask ให้เป็นเชน rinkeby จากนั้นไปที่ faucets.chain.link และรับเหรียญทดสอบ. (ตอนนี้ rinkeby ใช้ไม่ได้แล้วต้องเปลี่ยนมาใช้ Goerli แทน พอเปลี่ยนแล้วสามารถเริ่มเรียนตามบทช่วยสอนด้านล่างได้เลย).

Step2 ทำสัญญา

1.เข้าสู่ remix.ethereum.org folder ใหม่ดังแสดงในรูป และตั้งชื่อตามต้องการ.

2.คลิกขวาเพื่อลบทั้งสามไฟล์ภายใต้สัญญา.

3.สร้างไฟล์หม่ชื่อ Bull&Bear.sol ภายใต้สัญญา.

4.คัดลอกและวางโค้ดด้านล่าง (แก้ไขโค้ดเพื่อให้เข้ากับเชนใหม่ให้เรียบร้อยแล้วสามารถใช้ได้เลย).

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

// Chainlink Imports
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
// This import includes functions from both ./KeeperBase.sol and
// ./interfaces/KeeperCompatibleInterface.sol
import "@chainlink/contracts/src/v0.8/KeeperCompatible.sol";

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

// Dev imports. This only works on a local dev network
// and will not work on any test or main livenets.
import "hardhat/console.sol";

contract BullBear is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable, VRFConsumerBaseV2, KeeperCompatibleInterface  {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;
    uint public interval;
    uint public lastTimeStamp;

    AggregatorV3Interface public priceFeed;
    int256 public currentPrice;

    // IPFS URIs for the dynamic nft graphics/metadata.
    // NOTE: These connect to my IPFS Companion node.
    // You should upload the contents of the /ipfs folder to your own node for development.
    string[] bullUrisIpfs = [
        "https://ipfs.io/ipfs/QmRXyfi3oNZCubDxiVFre3kLZ8XeGt6pQsnAQRZ7akhSNs?filename=gamer_bull.json",
        "https://ipfs.io/ipfs/QmRJVFeMrtYS2CUVUM2cHJpBV5aX2xurpnsfZxLTTQbiD3?filename=party_bull.json",
        "https://ipfs.io/ipfs/QmdcURmN1kEEtKgnbkVJJ8hrmsSWHpZvLkRgsKKoiWvW9g?filename=simple_bull.json"
    ];
    string[] bearUrisIpfs = [
        "https://ipfs.io/ipfs/Qmdx9Hx7FCDZGExyjLR6vYcnutUR8KhBZBnZfAPHiUommN?filename=beanie_bear.json",
        "https://ipfs.io/ipfs/QmTVLyTSuiKGUEmb88BgXG3qNC8YgpHZiFbjHrXKH3QHEu?filename=coolio_bear.json",
        "https://ipfs.io/ipfs/QmbKhBXVWmwrYsTPFYfroR2N7NAekAMxHUVg2CWks7i9qj?filename=simple_bear.json"
    ];


    // random
    VRFCoordinatorV2Interface COORDINATOR;

    // Your subscription ID.
    uint64 s_subscriptionId;

    // Goerli coordinator. For other networks,
    // see https://docs.chain.link/docs/vrf-contracts/#configurations
    address vrfCoordinator = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;

    // The gas lane to use, which specifies the maximum gas price to bump to.
    // For a list of available gas lanes on each network,
    // see https://docs.chain.link/docs/vrf-contracts/#configurations
    bytes32 keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;

    // Depends on the number of requested values that you want sent to the
    // fulfillRandomWords() function. Storing each word costs about 20,000 gas,
    // so 100,000 is a safe default for this example contract. Test and adjust
    // this limit based on the network that you select, the size of the request,
    // and the processing of the callback request in the fulfillRandomWords()
    // function.
    uint32 callbackGasLimit = 100000;

    // The default is 3, but you can set this higher.
    uint16 requestConfirmations = 3;

    // For this example, retrieve 2 random values in one request.
    // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
    uint32 numWords =  2;
    uint256[] public s_randomWords;
    uint256 public s_requestId;



    event TokensUpdated(string marketTrend);

    constructor(uint updateInterval, address _priceFeed, uint64 subscriptionId) ERC721("Bull&Bear", "BBTK") VRFConsumerBaseV2(vrfCoordinator) {
        interval = updateInterval;
        lastTimeStamp = block.timestamp;

        // https://rinkeby.etherscan.io/address/0xECe365B379E1dD183B20fc5f022230C044d51404
        priceFeed = AggregatorV3Interface(_priceFeed);
        currentPrice = getLatestPrice();

        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        s_subscriptionId = subscriptionId;
    }

    function safeMint(address to) public {
        // Current counter value will be the minted token's token ID.
        uint256 tokenId = _tokenIdCounter.current();

        // Increment it so next time it's correct when we call .current()
        _tokenIdCounter.increment();

        // Mint the token
        _safeMint(to, tokenId);

        // Default to a bull NFT
        string memory defaultUri = bullUrisIpfs[s_randomWords[0]%3];
        _setTokenURI(tokenId, defaultUri);

        console.log(
            "DONE!!! minted token ",
            tokenId,
            " and assigned token url: ",
            defaultUri
        );
    }

    function checkUpkeep(bytes calldata) external view override returns (bool upkeepNeeded, bytes memory /*performData*/){
        upkeepNeeded = (block.timestamp - lastTimeStamp) > interval;
    }

    function performUpkeep(bytes calldata) external override{
        if((block.timestamp - lastTimeStamp) > interval){
            lastTimeStamp = block.timestamp;
            int latestPrice = getLatestPrice();

            if(latestPrice == currentPrice){
                return;
            }else if(latestPrice < currentPrice){
                updateAllTokenUris("bears");
            }else{
                updateAllTokenUris("bull");
            }

            currentPrice = latestPrice;
        }
    }

    function getLatestPrice() public view returns(int256){
        (,
        int price,
        ,
        ,) = priceFeed.latestRoundData();
        return price;
    }

    function updateAllTokenUris(string memory trend) internal{
        if(compareStrings("bears", trend)){
            for(uint i=0; i< _tokenIdCounter.current(); i++){
                _setTokenURI(i,bearUrisIpfs[s_randomWords[0]%3]);
            }
        }else {
            for(uint i=0; i< _tokenIdCounter.current(); i++){
                _setTokenURI(i,bullUrisIpfs[s_randomWords[0]%3]);
            }
        }

        emit TokensUpdated(trend);
    }

    function setInterval(uint256 newInterval) public onlyOwner{
        interval = newInterval;
    }

    function setPriceFeed(address newFeed) public onlyOwner{
        priceFeed = AggregatorV3Interface(newFeed);
    }

    function compareStrings(string memory a, string memory b) internal pure returns (bool){
        return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
    }

    // The following functions are overrides required by Solidity.
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId)
        internal
        override(ERC721, ERC721URIStorage)
    {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

        // Assumes the subscription is funded sufficiently.
    function requestRandomWords() external onlyOwner {
        // Will revert if subscription is not set and funded.
        s_requestId = COORDINATOR.requestRandomWords(
        keyHash,
        s_subscriptionId,
        requestConfirmations,
        callbackGasLimit,
        numWords
        );
    }

    function fulfillRandomWords(
        uint256, /* requestId */
        uint256[] memory randomWords
    ) internal override {
        s_randomWords = randomWords;
    }
}

5.คลิกเมนู compiler ทางด้านซ้าย เลือก 0.8.4 สำหรับเวอร์ชัน, แล้วคลิก compile.

6.คลิกเมนู "Deploy and run transaction" จากนั้นตรวจสอบให้แน่ใจว่ากระเป๋าเราเป็นเชน rinkeby และตัวเลือกอื่นๆจะแสดงในช่องสีแดงตามในภาพด้านล่าง.

Step3 รับหมายเลขการสมัคร oracle

1.ไปที่ vrf.chain.link และเชื่อมต่อกระเป๋า Metamask.

2.กดเข้าไปที่ Create Subscription.

3.ทีนี้จะเห็นเลขกระเป๋าเราขื้นมา, ให้กด Create subscription อีกทีพร้อมยืนยันธุกรรมในกระเป๋า Metamask จากนั้นกด add funds.

4. ป้อนตามรูปแล้วคลิก Add Funds อีกที และยืนยันธุกรรมในกระเป๋า Metamask..

5.รอประมาณสิบวินาทีแล้วคลิก Add consumer.

6.ในหน้านี้, ให้เว้นว่างไว้ก่อน, จากนั้นคลิก I'll do it later.

7.ดังที่แสดงในรูป, ตัวเลขที่ผมได้คือ 16659 (เลขของแต่ละคนจะไม่เหมือนกัน).

Step4 ปรับใช้สัญญา

1. กลับไปที่ Remix ตรง Deploy:

ป้อน 10 ในบรรทัดแรก.

ป้อน 0xA39434A63A52E749F02807ae27335515BA4b07F7 ในบรรทัดที่สอง (ให้ใช้ที่อยู่นี้ต่างจากในรูปเพราะมีการอัปเดตเชนใหม่).

ป้อนหมายเลขที่คุณเพิ่งได้รับในบรรทัดที่สาม, ของผมคือ 16659.

2.กด transact แล้วยืนยันธุรกรรมให้เรียบร้อย.

3.ถ้าขื้นแบบนี้แปลว่าถูกต้อง.

4. กลับไปที่อินเทอร์เฟซ oracle ของ step3 แล้วคลิก Add consumer.

5. คัดลอกที่อยู่สัญญาที่เพิ่งสร้างขึ้น วางลงในอินเทอร์เฟซของ oracle คลิก Add consumer แล้วยืนยันยืนยันธุรกรรมใน Metamask.

6. คลิก close เพื่อปิด.

Step5 mint NFT

1.คลิกลูกศรลงเพื่อแสดงรายละเอียดสัญญา, คลิก requestRandomWords กระเป๋าเงิน Metamask จะปรากฏขึ้น, แล้วคลิกเพื่อยืนยัน.

ขื้นแบบนี้แปลว่าทำถูกต้องแล้ว.

2.ป้อน 0 ใน s_randomWords แล้วกด call.

3. เลื่อนขื้นไปข้างบน, จากนั้นกรอกเลขกระเป๋าของคุณลงใน safemint คลิกทำธุรกรรม และกดยืนยันธุรกรรม.

ก็สำเร็จตามภาพ.

4.เข้าไปที่ OpenseaTestnet เพื่อดู nft ของเราที่ Mint มา.

Step6 ส่งงาน

Step7 รับ NFT

เราสามารถเช็ด nft ที่เรายังไม่ได้เคลมได้นะครับโดยเข้าไปที่ mintkudos จากนั้นต่อกระเป๋ามุมขวามือลองกดเช็ดดูถ้ามีปลุ่ม claim แปลว่าเรามี nft ที่เราสามารถเคลมได้แต่เรายังไม่ได้เคลม.

เท่านี้ก็จบกันไปแล้วนะครับกับ Alchemy Road to web3 week5 ใครมีคำถาม หรือสงสัยยังไงสามารถถามได้. ส่วนใครที่อยากจะสนับสนุนค่ากาแฟให้ผู้เขียนสามารถกดปุ่ม Collect Entry ได้ที่ด้านล่างนี้จากนั้นเราจะได้ nft บทความของ mirror.xyz chain optimism และสามารถเช็ด nft ที่เรา collect มาได้ที่ QxProfile.

Twitter Telegram Mirror

Subscribe to JQ
Receive the latest updates directly to your inbox.
Nft graphic
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from JQ

Skeleton

Skeleton

Skeleton