Cover photo

Alchemy Road to web3 week9 คู่มือภาษาไทย

เมื่อไม่นานมานี้โปรเจค 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 ส่วนตัวผมมองว่าน่าจะได้เยอะอย่างแน่นอน.

เริ่มบทช่วยสอนในสัปดาห์ที่ 9 แบบลงมือปฏิบัติ: วิธีสร้าง Token Swap Dapp โดยใช้ 0x API

Step1: คัดลอกโค้ดอย่างเป็นทางการ

1. ป้อนโค้ดต่อไปนี้ในคอนโซล.

git clone https://github.com/0xProject/swap-demo-tutorial.git
post image

2. เปิดโฟลเดอร์ที่เราพื่งจะโคลนมา.

post image
post image

Step2 ติดตั้งเซิร์ฟเวอร์

1. ด้านช่ายมือเลือก extensions จากนั้นพิมพ์ live server เพื่อค้นหา และติดตั้งส่วนขยายนี้.

post image

Step3 แก้ไขโค้ด

1. วางโค้ดด้านล่างแทนโค้ดเก่าในไฟล์ index.js ใต้โฟลเดอร์ swap-demo-tutorial-part-9. (ไฟล์ index.js นะครับไม่ใช่ .html ในรูปแคปผิด).

const qs = require('qs');
const Web3 = require('web3');
const { default: BigNumber } = require('bignumber.js');

let currentTrade = {};
let currentSelectSide;
let tokens;

async function init() {
    await listAvailableTokens();
}

async function listAvailableTokens() {
    console.log("initializing");
    // let response = await fetch('https://tokens.coingecko.com/uniswap/all.json');
    // let tokenListJSON = await response.json();
    let response='{"name":"CoinGecko","logoURI":"https://www.coingecko.com/assets/thumbnail-007177f3eca19695592f0b8b0eabbdae282b54154e1be912285c9034ea6cbaf2.png","keywords":["defi"],"timestamp":"2022-08-17T04:08:12.925+00:00","tokens":[{"chainId":56,"address":"0x55d398326f99059fF775485246999027B3197955","name":"busd","symbol":"busd","decimals":18,"logoURI":"https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734"},{"chainId":56,"address":"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c","name":"bnb","symbol":"bnb","decimals":18,"logoURI":"https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734"}],"version":{"major":975,"minor":1,"patch":0}}';
    let tokenListJSON = JSON.parse(response);
    console.log("Listing available tokens: ", tokenListJSON);
    tokens = tokenListJSON.tokens;
    console.log("tokens: ", tokens);

    let parent = document.getElementById("token_list");
    for(const i in tokens) {
        let div = document.createElement("div");
        div.className = "token_row";

        let html = 
            `<img class="token_list_img" src="${tokens[i].logoURI}">
                <span class="token_list_text">${tokens[i].symbol}</span>`;
        div.innerHTML = html;
        div.onclick = () => {
            selectToken(tokens[i]);
        }
        parent.appendChild(div);       
    }
}

function selectToken(token) {
    closeModal();
    currentTrade[currentSelectSide] = token;
    console.log("currentTrade: ", currentTrade);
    renderInterface();
}

function renderInterface() {
    if(currentTrade.from) {
        document.getElementById("from_token_img").src = currentTrade.from.logoURI;
        document.getElementById("from_token_text").innerHTML = currentTrade.from.symbol;
    }
    if(currentTrade.to) {
        document.getElementById("to_token_img").src = currentTrade.to.logoURI;
        document.getElementById("to_token_text").innerHTML = currentTrade.to.symbol;
    }
}

async function connect() {
    if (typeof window.ethereum !== "undefined") {
        try {
            console.log("Connecting");
            await ethereum.request({ method: "eth_requestAccounts" });
        } catch (error) {
            console.log(error);
        }
        document.getElementById("login_button").innerHTML = "Connected";
        document.getElementById("swap_button").disabled = false;
    } else {
        document.getElementById("login_button").innerHTML = 
            "Please install Metamask";
    }
}

async function getPrice() {
    console.log("Getting Price");

    if(!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return;
    let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals);

    const params = {
        sellToken: currentTrade.from.address,
        buyToken: currentTrade.to.address,
        sellAmount: amount,
    }

    // Fetch the swap price
    const response = await fetch(`https://bsc.api.0x.org/swap/v1/price?${qs.stringify(params)}`);

    swapPriceJSON = await response.json();
    console.log("Price: ", swapPriceJSON);

    document.getElementById("to_amount").value = swapPriceJSON.buyAmount / (10 ** currentTrade.to.decimals);
    document.getElementById("gas_estimate").innerHTML = swapPriceJSON.estimatedGas;
}

async function getQuote(account) {
    console.log("Getting Quote");

    if(!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return;
    let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals);

    const params = {
        sellToken: currentTrade.from.symbol,
        buyToken: currentTrade.to.symbol,
        sellAmount: amount,
        takerAddress: account,
        slippagePercentage: 0.05
    }; 

    // Fetch the swap price
    const response = await fetch(`https://bsc.api.0x.org/swap/v1/quote?${qs.stringify(params)}`);

    swapQuoteJSON = await response.json();
    console.log("Quote: ", swapQuoteJSON);

    // document.getElementById("to_amount").value = swapQuoteJSON.price;
    document.getElementById("gas_estimate").innerHTML = swapQuoteJSON.estimatedGas;

    return swapQuoteJSON;
}

async function trySwap() {

    let accounts = await ethereum.request({ method: "eth_accounts" });
    let takerAddress = accounts[0];

    console.log("takerAddress:", takerAddress);

    const swapQuoteJSON = await getQuote(takerAddress);

    // Set Token Allowance
    // Interact with ERC20TokenContract
    const web3 = new Web3(Web3.givenProvider);
    const fromTokenAddress = currentTrade.from.address;
    const erc20abi = [{ "inputs": [ { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "string", "name": "symbol", "type": "string" }, { "internalType": "uint256", "name": "max_supply", "type": "uint256" } ], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }, { "inputs": [ { "internalType": "address", "name": "owner", "type": "address" }, { "internalType": "address", "name": "spender", "type": "address" } ], "name": "allowance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "approve", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "account", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "burn", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "account", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "burnFrom", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "decimals", "outputs": [ { "internalType": "uint8", "name": "", "type": "uint8" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "subtractedValue", "type": "uint256" } ], "name": "decreaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "addedValue", "type": "uint256" } ], "name": "increaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "name", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalSupply", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }]
    console.log("trying swap"); 

    const ERC20TokenContract = new web3.eth.Contract(erc20abi, fromTokenAddress);
    console.log("setup ERC20TokenContract: ", ERC20TokenContract);

    const maxApproval = new BigNumber(2).pow(256).minus(1);
    console.log("approval amount: ", maxApproval);

    const tx = await ERC20TokenContract.methods
        .approve(swapQuoteJSON.allowanceTarget, maxApproval)
        .send({ from: takerAddress })
        .then((tx) => {
            console.log("tx: ", tx)
        });

    const receipt = await web3.eth.sendTransaction(swapQuoteJSON);
    console.log("receipt: ", receipt);
}

init();

function openModal(side) {
    currentSelectSide = side;
    document.getElementById("token_modal").style.display = "block";
}

function closeModal() {
    document.getElementById("token_modal").style.display = "none";
}

document.getElementById("login_button").onclick = connect;
document.getElementById("from_token_select").onclick = () => {
     openModal("from");
};
document.getElementById("to_token_select").onclick = () => {
    openModal("to");
};
document.getElementById("modal_close").onclick = closeModal;
document.getElementById("from_amount").onblur = getPrice;
document.getElementById("swap_button").onclick = trySwap;
post image

Step4 ติดตั้ง modules

1. ป้อน code ต่อไปนี้ในคอนโซลเพื่อติดตั้งโมดูลที่เกี่ยวข้อง.

cd swap-demo-tutorial
cd swap-demo-tutorial-part-9
npm i qs
npm i bignumber -s
npm i web3
npm install -g browserify
browserify index.js --standalone bundle -o bundle.js
post image
post image

Step5 ทดสอบฟังก์ชันโต้ตอบ

1. กลับไปที่ vscode เลือก index.html และคลิกขวา open with live server.

post image

2. อินเทอร์เฟซนี้จะปรากฏขึ้น.

post image

แน่นอน คุณยังสามารถเพิ่มคำว่า "select currency" ในบรรทัดที่ 31 และ 40 ของ index.html ได้อีกด้วย. หลังจากบันทึกมันจะเป็นแบบนี้เมื่อรัน, แต่สิ่งนี้ไม่สำคัญ, จะไม่มีผลใดๆเลย.

post image
post image

4. จะเห็นว่ามี 2 เหรียญ Busd และ Bnb ที่สามารถ Swap ได้ (จริงๆสามารถเพิ่มได้เยอะกว่านี้อีกแต่ใน index.js ที่ผมให้วางข้างบนมีแค่ 2 เหรียญนี้ที่ดืงมา), จากนั้นให้ลอง Swap ดู.

post image

5. จะเห็น Transaction ที่เรา Swap มา.

post image
post image

Step6 อัปโหลด code ไปที่ github

1. เข้าสู่ระบบ GitHub และคลิกใหม่

post image

2. เลือกตามภาพ แล้วกด Create repository.

post image

3. คลิก code และกด coppy url.

post image

4. ขั้นตอนต่อไปคือการดำเนินการภายในเครื่อง, ขั้นแรกตรวจสอบให้แน่ใจว่าคุณได้ติดตั้งซอฟต์แวร์ Git สำเร็จแล้ว. ค้นหาโฟลเดอร์โครงการที่คุณต้องการอัปโหลดไปยัง Github บนคอมพิวเตอร์ของคุณ, เข้าสู่โฟลเดอร์โครงการ, คลิกขวา และเลือก Git Bash . ดังแสดงในภาพด้านล่าง.

post image

5. จากนั้นป้อนโค้ดต่อไปนี้ (ขั้นตอนสำคัญ) เพื่อโคลนที่เก็บบน github ไปยัง local.

git clone https://github.com/JQVector/week9-alchemy.git
https://github.com/JQVector/week9-alchemy.git 
(ตรงนี้ให้แทนที่ด้วย url github ที่คุณสร้างขื้น)
post image

6. หลังจากขั้นตอนข้างบนจะมีโฟลเดอร์เพิ่มเติมภายใต้โฟลเดอร์โปรเจคที่คุณสร้างขื้น. ก็ให้คัดลอกไฟล์ และโฟลเดอร์ทั้งหมดยกเว้นโฟลเดอร์ที่เราพื่งสร้างขื้น (อย่างของผมก็ week9-alchemy) ยัดลงไปในโฟลเดอร์ที่เราพื่งสร้างนั้น.

post image

7. จากนั้นให้ป้อนคำสั่ง cd week9-alchemy เพื่อที่จะเข้าไปที่โฟลเดอร์ week9-alchemy (week9-alchemy คือโฟลเดอร์ที่ผมสร้าง, ตรงนี้สามารถเปลี่ยนเป็นชื่อที่คุณอยากจะตั้งเองได้).

post image

8. จากนั้นป้อนโค้ดต่อไปนี้เพื่อดำเนินการอื่นที่เหลือให้เสร็จสิ้น:

git add . (หมายเหตุ: อย่าลืม . ข้างหลัง, การดำเนินการนี้คือการเพิ่มไฟล์ทั้งหมดภายใต้โฟลเดอร์ Test)
git commit -m "commit information" (หมายเหตุ: "commit information" สามารถแทนที่ด้วยคำที่คุณต้องการ หรือคุณสามารถปล่อยทิ้งไว้. 
git config --global user.email "you@example.com" <"ใส่เมลของตัวเองที่สมัคร github เข้าไป">
git config --global user.name "Your Name" <"ใส่ชื่อ github">
git push -u origin main (คำสั่งนี้คือการ push local folder ไปยัง github ถ้าคุณพื่งจะลองทำครั้งแรกจะกำหนดให้คุณต้องป้อนชื่อบัญชี และรหัสผ่านด้วย)
post image

Step7 ส่งงาน

https://docs.google.com/forms/d/e/1FAIpQLSdNNLXMYZmIhjcWoT-UedS3AoGpRiPDRaNARUPGXLbX1TVvSg/viewform

ส่งที่อยู่รหัส GitHub และที่อยู่สัญญาของการทำธุรกรรม

Step8 รับ NFT

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

post image

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

Twitter Telegram Mirror