وب۳
اولین قرارداد هوشمندت با Hardhat
اگر این مقاله را خواندی، قدم بعدی همین است.
بدون قیمت ارز و بدون شعار آینده اینترنت. فقط همان چیزی که لازم است تا یک رابط وب واقعی با بلاک چین حرف بزند و وقتی کاربر تراکنش را کنسل کرد، رابطت از هم نپاشد.
بیشتر آموزش های وب۳ فارسی از جای اشتباهی شروع می شوند: از قیمت و صرافی. برای یک برنامه نویس فرانت اند، وب۳ فقط یک چیز است: یک API عجیب که در مرورگر کاربر تزریق شده، امضای دیجیتال می خواهد، چند ثانیه تاخیر دارد و هر درخواستش می تواند پول خرج کند. همین. اگر این را بپذیری، بقیه ماجرا مهندسی معمولی است.
جاوااسکریپت مدرن، async و await، و یک کیف پول مرورگری نصب شده. سالیدیتی لازم نیست. در این مقاله قرارداد نمی نویسیم، فقط با آن حرف می زنیم.
هر اپلیکیشن غیرمتمرکز، هر قدر هم پیچیده، از همین سه لایه ساخته می شود:
کلید خصوصی هرگز از کیف پول بیرون نمی آید. کد تو فقط درخواست امضا می دهد و منتظر بله یا نه کاربر می ماند.قاعده اول امنیت در فرانت اند وب۳
هیچ وقت فرض نکن کیف پول نصب است. هیچ وقت هم بدون کلیک کاربر درخواست اتصال نفرست. کیف پول ها این رفتار را اسپم حساب می کنند.
import { BrowserProvider } from "ethers";
export async function connectWallet() {
if (!window.ethereum) {
throw new Error("کیف پولی پیدا نشد");
}
const provider = new BrowserProvider(window.ethereum);
const [account] = await provider.send("eth_requestAccounts", []);
const signer = await provider.getSigner();
const { chainId, name } = await provider.getNetwork();
return { provider, signer, account, chainId, network: name };
}
کاربر می تواند وسط کار حساب یا شبکه را عوض کند. اگر گوش ندهی، رابطت دیتای حساب دیگری را نشان می دهد.
window.ethereum?.on("accountsChanged", (accounts) => {
accounts.length ? setAccount(accounts[0]) : signOut();
});
window.ethereum?.on("chainChanged", () => location.reload());
آدرس ها را برای مقایسه با toLowerCase() یکدست کن.
کیف پول ها آدرس را با حروف بزرگ و کوچک ترکیبی برمی گردانند و
مساوی ساده بی رحمانه false می دهد.
خواندن با provider انجام می شود، نه signer. یعنی حتی بدون اتصال کیف پول هم می توانی داده نشان بدهی. برای صفحه اول سایت این خیلی مهم است.
import { Contract, formatUnits } from "ethers";
const ABI = [
"function balanceOf(address) view returns (uint256)",
"function decimals() view returns (uint8)",
"function symbol() view returns (string)",
];
export async function getBalance(provider, token, wallet) {
const contract = new Contract(token, ABI, provider);
const [raw, decimals, symbol] = await Promise.all([
contract.balanceOf(wallet),
contract.decimals(),
contract.symbol(),
]);
return `${formatUnits(raw, decimals)} ${symbol}`;
}
عددهای بلاک چین BigInt هستند، نه Number. اگر آن ها را داخل parseFloat بیندازی، روی مبالغ بزرگ دقت را از دست می دهی.
تراکنش سه حالت دارد: رد شدن توسط کاربر، شکست در شبکه، و موفقیت با تاخیر. هر سه را باید در رابطت نشان بدهی.
import { parseEther } from "ethers";
async function mint(contract) {
setState("waiting-signature");
try {
const tx = await contract.mint(1n, { value: parseEther("0.01") });
setState("pending", tx.hash);
const receipt = await tx.wait();
setState("done", receipt.transactionHash);
} catch (err) {
if (err.code === 4001) return setState("idle");
setState("error", err.shortMessage || "تراکنش شکست خورد");
}
}
لینک explorer را همان لحظه که tx.hash را گرفتی نشان بده. کاربر دوست ندارد روی یک دکمه بی جان خیره شود و نداند چه خبر است.
| وضعیت | چیزی که باید نشان بدهی |
|---|---|
| قبل از اتصال | یک دکمه واضح و توضیح کوتاه |
| در انتظار امضا | پیام واضح که پنجره کیف پول باز شده |
| در انتظار تایید شبکه | لینک تراکنش و وضعیت pending |
| خطا | پیام انسانی، نه آبجکت خام خطا |
| موفق | تایید نهایی و آپدیت داده ها |
همین پنج مورد اگر درست اجرا شوند، محصولت از نود درصد نمونه کارهای دم دستی وب۳ بهتر است.
چیزهایی که خودم موقع ساختن پروژه ها یاد می گیرم را همان جا می نویسم. نه برای این که کامل ترین مرجع باشم، برای این که موقع گیر کردن به درد بخورم.
وب۳
اگر این مقاله را خواندی، قدم بعدی همین است.
فرانت اند
مقایسه ای که وقت انتخاب کتابخانه نجاتت می دهد.
امنیت
امنیت را با مثال واقعی یاد بگیر، نه با تعریف.
خلاصه مقاله های هفته و یک باگ واقعی. بدون تبلیغ.