با Next.js، Solidity و Axelar یک برنامه Interchain Full Stack بسازید

اکوسیستم بلاک چین امروزی با بسیاری از شبکه های بلاک چین که به طور مستقل فعالیت می کنند، مشخص می شود که هر کدام دارای ویژگی ها و عملکردهای منحصر به فردی هستند. با این حال، این پراکندگی چالش هایی را در رابطه با ارتباط و همکاری یکپارچه بین این شبکه ها ایجاد می کند. برای غلبه بر این موانع، یک عنصر حیاتی که پدید آمده است، یک لایه قابلیت همکاری بلاک چین قابل برنامه ریزی است.
لایه قابلیت همکاری بلاک چین قابل برنامه ریزی، شبکه های مختلف بلاک چین را پل می کند و تبادل اطلاعات و دارایی ها را به صورت ایمن و کارآمد تسهیل می کند. با ارائه یک چارچوب استاندارد شده برای ارتباطات متقابل زنجیره ای، تعامل صاف را امکان پذیر می کند و به شبکه های مختلف بلاک چین اجازه می دهد تا به طور یکپارچه با یکدیگر همکاری کنند.
در این آموزش یاد خواهید گرفت که چگونه یک برنامه غیرمتمرکز زنجیره ای کامل بسازید با پیام های Next.js، Solidity و Axelar General برای ارسال پیام از یک بلاکچین به بلاک چین دیگر.
برای شروع سریع، می توانید کد کامل این آموزش را در GitHub پیدا کنید. با این حال، در ویدیوی ارائه شده در زیر، می توانید برنامه تکمیل شده را مشاهده کنید که به کاربران این امکان را می دهد:
https://www.youtube.com/watch?v=o-OrYvyN38M
شروع با گذراندن پیام عمومی Axelar
ویژگی ارسال پیام عمومی (GMP) Axelar، توسعه دهندگان را قادر می سازد تا هر عملکردی را در زنجیره های به هم پیوسته به طور یکپارچه فراخوانی کنند.
با GMP، توسعه دهندگان این توانایی را به دست می آورند:
-
قراردادی را در زنجیره A صدا بزنید و با قراردادی در زنجیره B تعامل کنید.
-
تراکنشهای زنجیرهای متقابل را با فراخوانی قرارداد در زنجیره A و ارسال توکن به زنجیره B انجام دهید.
پيش نياز
قبل از شروع، به پیش نیازهای زیر نیاز دارید:
-
Node.js و مدیریت بسته NPM، نسخه 18*. تأیید کنید Node.js با اجرای دستور ترمینال زیر نصب شده است:
node -v && npm -v
-
درک اولیه جاوا اسکریپت، Solidity و React/Next.js
راه اندازی و نصب پروژه
برای شروع سریع راه اندازی و نصب پروژه، این را شبیه سازی کنید پروژه در GitHub. مطمئن شوید که در آن هستید starter
با استفاده از دستور زیر شاخه کنید:
git clone <https://github.com/axelarnetwork/fullstack-interchain-dapp>
در مرحله بعد، پروژه را پس از شبیه سازی با استفاده از دستور زیر در ترمینال خود، به صورت محلی نصب کنید.
در اینجا نحوه نصب پروژه با استفاده از آن آورده شده است npm
:
cd fullstack-interchain-dapp && npm i && npm run dev
Next.js یک محیط توسعه بارگیری مجدد را شروع می کند که به طور پیش فرض در http://localhost:3000 قابل دسترسی است.
ساخت یک قرارداد هوشمند با Hardhat و Axelar GMP
در این بخش، با استفاده از ویژگی Axelar GMP برای ارسال پیام از یک زنجیره به زنجیره دیگر، یک قرارداد هوشمند بین زنجیره ای ایجاد می کنید.
به پوشه ریشه پروژه که در مرحله قبل کلون کردید بروید و سپس دستورات زیر را برای ایجاد یک پروژه جدید Hardhat اجرا کنید.
mkdir hardhat
cd hardhat
npm install --save-dev hardhat
بیایید با اجرای دستور زیر یک پروژه نمونه دریافت کنیم:
npx hardhat
ما با گزینه های زیر پیش خواهیم رفت:
What do you want to do?
✔ Create A JavaScript Project
✔ Hardhat project root:
? Do you want to add a .gitignore? (Y/n) › y
? Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) › y
را @nomicfoundation/hardhat-toolbox
افزونه همه بستههای رایج مورد استفاده و پلاگینهای Hardhat را که برای شروع توسعه با Hardhat توصیه میشود، جمعبندی میکند.
فقط در صورتی که به صورت خودکار نصب نشد، این مورد دیگر را با دستور زیر نصب کنید:
npm i @nomicfoundation/hardhat-toolbox
بعد، نصب کنید @axelar-network/axelar-gmp-sdk-solidity
برای Axelar General Message Passing SDK در Solidity و dotenv
با دستور زیر:
npm i @axelar-network/axelar-gmp-sdk-solidity dotenv
برای اطمینان از کارکرد همه چیز، دستور زیر را در قسمت اجرا کنید hardhat
فهرست راهنما.
npx hardhat test
شما یک نتیجه آزمایشی را در کنسول ما خواهید دید.
حذف Lock.js
از پوشه تست و حذف کنید deploy.js
از scripts
فهرست راهنما. پس از آن، به قراردادها بروید و حذف کنید Lock.sol.
خود پوشه ها نباید پاک شوند!
ایجاد یک SendMessage.sol
فایل داخل contracts
دایرکتوری و با قطعه کد زیر آن را به روز کنید. هنگام استفاده از Hardhat، طرح بندی فایل بسیار مهم است، بنابراین توجه کنید!
// SPDX-License-Identifier: MIT
// SPDX license identifier specifies which open-source license is being used for the contract
pragma solidity 0.8.9;
// Importing external contracts for dependencies
import { AxelarExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol';
import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
contract SendMessage is AxelarExecutable {
string public value;
string public sourceChain;
string public sourceAddress;
IAxelarGasService public immutable gasService;
constructor(address gateway_, address gasReceiver_) AxelarExecutable(gateway_) {
// Sets the immutable state variable to the address of gasReceiver_
gasService = IAxelarGasService(gasReceiver_);
}
function sendMessage(
string calldata destinationChain,
string calldata destinationAddress,
string calldata value_
) external payable {
bytes memory payload = abi.encode(value_);
if (msg.value > 0) {
gasService.payNativeGasForContractCall{ value: msg.value }(
address(this),
destinationChain,
destinationAddress,
payload,
msg.sender
);
}
// Calls the Axelar gateway contract with the specified destination chain and address and sends the payload along with the call
gateway.callContract(destinationChain, destinationAddress, payload);
}
function _execute(
string calldata sourceChain_,
string calldata sourceAddress_,
bytes calldata payload_
) internal override {
// Decodes the payload bytes into the string value and sets the state variable for this contract
(value) = abi.decode(payload_, (string));
sourceChain = sourceChain_;
sourceAddress = sourceAddress_;
}
}
در قطعه کد بالا ما:
-
ایجاد یک
SendMessage
قراردادی که تمدید می کندAxelarExecutable
قرارداد -
وارد كردن
AxelarExecutable
،IAxelarGateway
،IAxelarGasService
از@axelar-network/axelar-gmp-sdk-solidity
کتابخانه -
تعريف كردن
four
متغیرهای حالت:value
،sourceChain
،sourceAddress
، وgasService
. راgasService
متغیر حالت تغییرناپذیر است و فقط در حین استقرار قرارداد قابل تنظیم است. -
مقداردهی اولیه کنید
gasService
متغیر با ارائه شدهgasReceiver_
نشانی. -
ایجاد یک
sendMessage
تابعی که سه پارامتر رشته را می گیرد:destinationChain
،destinationAddress
،value_
و استفاده می کندgasService.payNativeGasForContractCall
با گاز بومی (اتر). -
استفاده کرد
gateway
قراردادcallContract
عملکرد با مشخص شدهdestinationChain
،destinationAddress
، وpayload
مولفه های. -
را
_execute
تابع بایت های بارگذاری را در رمزگشایی می کندvalue
رشته و متغیرهای حالت را به روز می کندsourceChain
وsourceAddress
.
تنظیم اسکریپت استقرار
بعد، یک را ایجاد کنید deploy.js
فایل در scripts
پوشه و قطعه کد زیر را اضافه کنید:
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
// will compile your contracts, add the Hardhat Runtime Environment's members to the
// global scope, and execute the script.
const hre = require("hardhat");
async function main() {
const SendMessage = await hre.ethers.getContractFactory("SendMessage");
const sendMessage = await SendMessage.deploy(
"",
""
);
await sendMessage.deployed();
console.log(`SendMessage contract deployed to ${sendMessage.address}`);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
در قطعه کد بالا:
-
را
main
تابع داردSendMessage
قرارداد کارخانه به دست آمده با استفاده ازhre.ethers.getContractFactory
. -
را
sendMessage
قرارداد با استفاده ازSendMessage.deploy
متد با دو رشته به عنوان آرگومان. -
await sendMessage.deployed()
بیانیه تضمین می کند که استقرار قبل از حرکت به جلو تکمیل شده است. -
آدرس قرارداد مستقر در کنسول وارد شده است.
راه اندازی تماس رویه از راه دور (RPC) در Testnet
تماس رویه از راه دور (RPC) پروتکلی است که برای ارتباط بین سیستم های مشتری و سرور در یک محیط شبکه یا بلاک چین استفاده می شود. کلاینت ها را قادر می سازد تا رویه ها یا توابع را روی سرورهای راه دور اجرا کرده و نتایج را دریافت کنند. RPC جزئیات شبکه زیرین را خلاصه می کند و به کلاینت ها اجازه می دهد تا روش ها را بر روی سرورها به گونه ای که محلی هستند فراخوانی کنند.
قبل از شروع به راه اندازی RPC، a ایجاد کنید .env
فایل با استفاده از دستور زیر:
touch .env
قبل از اجرای دستور بالا، مطمئن شوید که در دایرکتوری hardhat هستید.
درون .env
فایلی که به تازگی ایجاد کرده اید، کلید زیر را اضافه کنید:
PRIVATE_KEY= // Add your account private key here
دریافت کلید حساب خصوصی شما آسان است. این رو چک کن پست.
در مرحله بعد، RPC را برای شبکه های Binance و Avalanche با به روز رسانی آن تنظیم کنید hardhat.config.js
فایل با قطعه کد زیر:
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config({ path: ".env" });
require("solidity-coverage");
const PRIVATE_KEY = process.env.PRIVATE_KEY;
// This is a sample Hardhat task. To learn how to create your own go to
// <https://hardhat.org/guides/create-task.html>
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to <https://hardhat.org/config/> to learn more
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.9",
networks: {
bsc: {
url: "<https://data-seed-prebsc-1-s1.binance.org:8545>",
chainId: 97,
accounts: [PRIVATE_KEY],
},
avalancheFujiTestnet: {
url: "<https://avalanche-fuji-c-chain.publicnode.com>",
chainId: 43113,
accounts: [PRIVATE_KEY],
},
},
mocha: {
timeout: 10000000000,
},
};
شما با موفقیت RPC را برای شبکه های آزمایشی Binance و Avalanche پیکربندی کرده اید، در مرحله زیر به استقرار قرارداد هوشمند در آن شبکه ها ادامه خواهید داد.
قرارداد هوشمند را برای شبکه بایننس و آوالانچ پیاده کنید
در این بخش، قرارداد هوشمند را در بایننس و آوالانچ تست نت مستقر خواهید کرد. با این حال، قبل از ادامه، باید Axelar را مشخص کنید Gateway Service
و Gas Service
قرارداد در SendMessage.deploy()
روش در deploy.js
فایلی که قبلا ایجاد کردید
میتوانید لیست قراردادهای خدمات گاز و گیتوی Axelar را برای تمام زنجیرههایی که Axelar در حال حاضر پشتیبانی میکند را در اینجا بیابید.
همچنین برای اطمینان از استقرار موفقیت آمیز قرارداد، به یک شیر آب برای حساب های Binance و Avalanche خود نیاز دارید. برای تهیه شیر آب بایننس به این لینک مراجعه کنید و برای شیر آب آلانچ از اینجا وارد شوید.
استقرار به بایننس تست نت
را به روز کنید deploy.js
فایل داخل scripts
پوشه ای برای استقرار در بایننس تست نت با قطعه کد زیر:
//...
async function main() {
//...
// Update arguments with the Axelar gateway and
// gas service on Binance testnet
const sendMessage = await SendMessage.deploy(
"0x4D147dCb984e6affEEC47e44293DA442580A3Ec0",
"0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6"
);
//...
}
//..
برای استقرار قرارداد در شبکه آزمایشی Binance، دستور زیر را اجرا کنید:
npx hardhat run scripts/deploy.js --network bsc
به عنوان مثال، آدرس قرارداد در کنسول شما نمایش داده می شود: 0xC1b8fC9208E51aC997895626b0f384153E94f2A7
.
مستقر در بهمن فوجی تست نت
را به روز کنید deploy.js
فایل داخل scripts
پوشه ای برای استقرار در نرم افزار Avalanche testnet با قطعه کد زیر:
//...
async function main() {
//...
// Update arguments with the Axelar gateway and
// gas service on Avalanche testnet
const sendMessage = await SendMessage.deploy(
"0xC249632c2D40b9001FE907806902f63038B737Ab",
"0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6"
);
//...
}
//..
برای استقرار قرارداد در شبکه آزمایشی Avalanche، دستور زیر را اجرا کنید:
npx hardhat run scripts/deploy.js --network avalancheFujiTestnet
آدرس قرارداد در کنسول شما نمایش داده می شود. مثلا، 0x2a03DCB9B24431d02839822209D58262f5e5df52
. مطمئن شوید که هر دو آدرس قرارداد مستقر شده را ذخیره کرده اید، زیرا برای ادغام front-end به آنها نیاز خواهید داشت.
ادغام یک برنامه Nextjs Frontend با قرارداد هوشمند
در مراحل قبلی، قرارداد هوشمند را با موفقیت ساخته و اجرا کردید. اکنون، زمان آن رسیده است که از قسمت جلویی با آن تعامل کنید، درست همانطور که معمولاً با برنامه های غیرمتمرکز در وب تعامل می کنید.
شما قبلاً پروژه Next.js frontend کلون شده و پیکربندی برای WAGMI
و Rainbowkit
راه اندازی شده است. این بدان معنی است که می توانید برنامه موجود را به روز کنید و قرارداد هوشمند خود را برای آزمایش وصل کنید.
اجرای عملکرد قرارداد هوشمند نوشتن
تعامل با قرارداد ما از طریق برنامه front-end بسیار ساده است، به لطف WAGMI
، RainbowKit
، و ethers
.
ایجاد یک .env.local
با استفاده از دستور زیر در دایرکتوری ریشه قرار دهید:
touch .env.local
قبل از اجرای دستور بالا مطمئن شوید که در دایرکتوری ریشه قرار دارید.
درون .env.local
فایلی که به تازگی ایجاد کرده اید، کلید زیر را اضافه کنید:
NEXT_PUBLIC_AVALANCHE_RPC_URL=https://avalanche-fuji-c-chain.publicnode.com
NEXT_PUBLIC_BSC_CONTRACT_ADDRESS=<BSC_CONTRACT_ADDRESS>
NEXT_PUBLIC_AVALANCHE_CONTRACT_ADDRESS=<AVALANCHE_CONTRACT_ADDRESS>
جایگزین کردن <BSC_CONTRACT_ADDRESS>
با آدرس قرارداد، در شبکه آزمایشی Binance مستقر شده و جایگزین کنید <AVALANCHE_CONTRACT_ADDRESS>
با آدرس قرارداد، قبلاً در این آموزش در بهمن فوجی تست نت مستقر شده اید.
سپس، عملکرد نوشتن را برای قرارداد هوشمند پیاده سازی کنید، قطعه کد زیر را به آن اضافه کنید index.js
فایل واقع در pages
فهرست راهنما.
//...
const BSC_CONTRACT_ADDRESS = process.env.NEXT_PUBLIC_BSC_CONTRACT_ADDRESS;
const AVALANCHE_CONTRACT_ADDRESS =
process.env.NEXT_PUBLIC_AVALANCHE_CONTRACT_ADDRESS;
const AVALANCHE_RPC_URL = process.env.NEXT_PUBLIC_AVALANCHE_RPC_URL;
export default function Home() {
//...
const [message, setMessage] = useState("");
const [sourceChain, setSourceChain] = useState("");
const { config } = usePrepareContractWrite({ // Calling a hook to prepare the contract write configuration
address: BSC_CONTRACT_ADDRESS, // Address of the BSC contract
abi: SendMessageContract.abi, // ABI (Application Binary Interface) of the contract
functionName: "sendMessage", // Name of the function to call on the contract
args: ["Avalanche", AVALANCHE_CONTRACT_ADDRESS, message], // Arguments to pass to the contract function
value: ethers.utils.parseEther("0.01"), // Value to send along with the contract call for gas fee
});
const { data: useContractWriteData, write } = useContractWrite(config);
const { data: useWaitForTransactionData, isSuccess } = useWaitForTransaction({
hash: useContractWriteData?.hash, // Hash of the transaction obtained from the contract write data
});
const handleSendMessage = () => {
write(); // Initiating the contract call
toast.info("Sending message...", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: false,
draggable: true,
});
};
useEffect(() => {
const body = document.querySelector("body");
darkMode ? body.classList.add("dark") : body.classList.remove("dark");
isSuccess
? toast.success("Message sent!", {
position: "top-right",
autoClose: 7000,
closeOnClick: true,
pauseOnHover: false,
draggable: true,
})
: useWaitForTransactionData?.error || useContractWriteData?.error
? toast.error("Error sending message")
: null;
}, [darkMode, useContractWriteData, useWaitForTransactionData]);
return (
//..
)
}
در قطعه کد بالا:
-
دو متغیر حالت،
message
وsourceChain
، با استفاده ازuseState
به ترتیب محتوای پیام و زنجیره منبع را قلاب کنید. -
را
usePrepareContractWrite
هوک برای آماده سازی پیکربندی برای عملیات نوشتن قرارداد فراخوانی می شود. چندین پارامتر مانند آدرس قرارداد BSC، ABI، نام تابع، آرگومان ها و مقدار را می طلبد. -
useContractWrite
hook داده های نوشتن قرارداد و تابع نوشتن را بر اساس پیکربندی به دست آمده از مرحله قبل بازیابی می کند. -
useWaitForTransaction
هوک فراخوانی می شود تا منتظر استخراج تراکنش باشد. هش تراکنش به دست آمده از داده های نوشتن قرارداد را می گیرد. -
handleSendMessage
تابع تعریف شده است که فراخوانی قرارداد را با فراخوانی آغاز می کندwrite
تابع. -
useEffect
هوک زمانی که وابستگیهای خاصی برای نمایش اعلانهای نان تست برای ارسال موفقیتآمیز یا ناموفق پیام تغییر میکند، اقداماتی را انجام میدهد.
را به روز کنید Send
دکمه و textarea
با قطعه کد زیر برای ارسال پیام و بازیابی داده های ارسالی.
//...
return (
//...
<div className="border border-gray-300 rounded-lg p-8 m-2 ">
<h2 className="text-2xl font-bold mb-4">Send Message 📓 </h2>
<textarea
//...
onChange={(e) => setMessage(e.target.value)}
/>
<button
//...
onClick={() => handleSendMessage()}
>
Send
</button>
</div>
//...
);
}
اجرای عملکرد قرارداد هوشمند خواندن
در مرحله قبل، قابلیت نوشتن قرارداد هوشمند را از قسمت جلو اجرا کردید. این بخش به شما آموزش می دهد که چگونه عملکرد خواندن داده ها از یک قرارداد هوشمند را پیاده سازی کنید.
را به روز کنید index.js
با قطعه کد زیر:
//...
export default function Home() {
//...
const [value, setValue] = useState("");
const provider = new ethers.providers.JsonRpcProvider(AVALANCHE_RPC_URL); // Create an instance of JsonRpcProvider with Avalanche RPC URL
const contract = new ethers.Contract(
AVALANCHE_CONTRACT_ADDRESS,
SendMessageContract.abi,
provider
);
async function readDestinationChainVariables() {
try {
const value = await contract.value();
const sourceChain = await contract.sourceChain();
setValue(value.toString()); // Convert the value to a string and store it
setSourceChain(sourceChain); // Store the source chain
} catch (error) {
toast.error("Error reading message"); // Display an error toast if reading fails
}
}
useEffect(() => {
readDestinationChainVariables(); // Call the function to read destination chain variables
//...
}, [darkMode, useContractWriteData, useWaitForTransactionData]);
return (
//...
{value ? ( // Add value here
<>
//...
</>
) : (
<span className="text-red-500 ">waiting for response...</span>
)}
);
}
در کد بالا،
-
نمونه ای از
JsonRpcProvider
کلاس از کتابخانه ethers.js با استفاده از URL RPC Avalanche (AVALANCHE_RPC_URL
) به عنوان پارامتر. -
نمونه ای از
Contract
کلاس از کتابخانه ethers.js ایجاد می شود که نشان دهنده یک قرارداد در شبکه Avalanche است. پارامترهایی مانند آدرس قرارداد (AVALANCHE_CONTRACT_ADDRESS
), ABI (SendMessageContract.abi
) و ارائه دهنده. -
یک تابع ناهمزمان به نام
readDestinationChainVariables
تعریف شده است. سعی میکند متغیرهای زنجیره ارزش و منبع قرارداد را با استفاده از آن بخواندvalue()
وsourceChain()
توابع، به ترتیب. -
اگر در حین خواندن خطایی رخ دهد، یک اعلان نان تست با پیام “خطا در خواندن پیام” نمایش داده می شود.
-
را
useEffect
قلاب تماس می گیردreadDestinationChainVariables
عملکرد زمانی که وابستگی های خاصی تغییر می کند.
در حال امتحان کردن برنامه
هورای 🥳، شما با موفقیت یک برنامه غیرمتمرکز زنجیره ای فول استک ساخته و اجرا کرده اید.
می توانید تراکنش GMP را در Axelarscan Testnet اینجا و کد کامل این پروژه را در GitHub بیابید.
بعدش چی؟
این پست استفاده از پیام عمومی Axelar را پوشش می دهد callContract
، اما این تمام کاری نیست که Passage General می تواند انجام دهد.
شما همیشه می توانید سایر عملکردهای مانند callContractWithToken
، SendToken
، Deposit addresses
، NFT Linker
، JavaScript SDK
، و غیره.
اگر تا اینجا پیش رفته اید، فوق العاده هستید! شما همچنین می توانید در مورد ساختن یا دنبال کردن تجربه خود به همراه این آموزش توییت کنید تا حمایت خود را از نویسنده و تگ کنید @axelarcore.
نتیجه
این آموزش به شما آموزش می دهد که چگونه با استفاده از Next.js، Solidity و Axelar’s General Message Passing یک برنامه غیرمتمرکز زنجیره ای کامل بسازید. شما یاد گرفتید که چگونه پیامهایی را از بایننس به شبکههای آزمایشی Avalanche ارسال کنید و از طریق یک برنامه Frontend Next.js با آنها تعامل کنید.
ارجاع