开发第一个Sui 区块链应用

区块链本身可能和以往一样复杂,但围绕它们的工具已经大大成熟。让我向你展示在更新更快的区块链之一 Sui上构建你的第一个区块链应用程序是多么容易。

开发第一个Sui 区块链应用
一键发币: SUI | SOL | BNB | ETH | BASE | ARB | OP | POLYGON | AVAX | FTM | OK

啊,区块链。这个词让许多开发人员心生恐惧。

说实话,我们几乎没人真正知道它们到底是怎么工作的。加密这个,节点那个,哈希我的地址。验证器到底是什么?验证 deez 疯子!

好吧,你我都很幸运,现在已经不是 2017 年了。区块链本身可能和以往一样复杂,但围绕它们的工具已经大大成熟。让我向你展示在更新更快的区块链之一 Sui(“sweet”,t 不发音)上构建你的第一个区块链应用程序是多么容易。

我们的最终应用程序将显示任何给定钱包的硬币余额,如下所示:

1、环境搭建

计划很简单:使用 HTMX 和 Typescript 构建一个网站。没有 React。没有 Move。没有 Vite。呃,不寒而栗。不,不。我们喜欢简单的东西。而且可爱。

这就是为什么我们将使用 bunjs 作为我们的包管理器。它将帮助你安装其他东西。为什么是 bun?看看它!天哪,我只想捏捏它!

1.1 安装 bun(或者我称之为 bun-bun)

如果你使用的是 Windows 计算机,那么首先,对不起,其次,你可以安装 Node。只需将 bun create替换为 node create,将 bun add替换为 node i

打开你的终端并运行此命令(当然不要输入 $ 符号):

$ exec bash

以确保我们使用的是 bash shell(当然是所有 shell 中最好的),然后运行此命令:

$ curl -fsSL https://bun.sh/install | bash

安装完成后,我们可以使用 bun create命令来启动项目,但该命令缺乏……你说的是什么,“创造力”?是的!我非常同意。

在终端中运行此命令,这样我们就可以使用更多有趣的命令:

$ echo -e '\nalias bake="bun create"\nalias prep="bun init"\nalias bun-bun="bun add"' >> ~/.bashrc

这让我们可以输入 bake而不是 bun create。因为 buns.. bake。哈哈

好的,输入此命令以重新加载配置文件,以便你的终端知道新的别名:

$ source ~/.bashrc

2、创建项目

将终端指向将保存项目的父文件夹。如果你已经有一个,请使用 cd 命令导航到那里,否则通过依次运行这两个命令创建一个:

$ mkdir Sui
$ cd Sui

现在你已经创建了 Sui 目录并导航到它。终端窗口底部的行应该类似于此(除了你将使用 Sui 而不是 sui):

呃,“Fuck-Putin”只是我的计算机的名称。因此: [ComputerName]:[ActiveFolder] [username]$ 是你应该看到的。

现在让我们告诉 bun-bun 使用名为 elysia的模板为我们创建项目。Elysia 是我们将要使用的 Web 服务器,它有自己的同名模板。从模板创建完全是可选的;它只是为我们节省了一些步骤。

依次运行这 3 个命令(等待上一个命令完成):

$ bake elysia sui-first-bun
$ cd sui-first-bun
$ bun-bun @elysiajs/html @mysten/sui.js

如果您觉得无聊,请将 bake替换为 bun create,将 bun-bun替换为 bun add

哇哦!Bun 为我们烘焙了项目脚手架,并添加了所需的两个依赖项:Elysia 的 html 扩展和 Sui 的 JS 库。我们准备好享用 err 代码了!

3、基础

打开 Visual Studio Code(或你选择的编辑器),然后选择文件 -> 打开文件夹...,选择主用户目录中的 Sui(或您选择的任何名称)文件夹(在 Mac 上,它位于 Macintosh HD/Users/[用户名]),然后选择 sui-first-bun,然后单击打开。您应该看到以下内容:

让我们确保它运行。返回终端,确保你位于项目的文件夹(sui-first-bun)中,然后运行命令 bun dev。你应该看到以下输出:

现在在浏览器中导航到 localhost:3000,应该会看到“Hello Elysia” - 这是你现在在机器上运行的 Web 服务器的输出!

如果你想知道 bun dev 的作用,请返回 VS Code 并在项目的根文件夹中打开 package.json(CMD+B 或 CTRL+B 显示/隐藏主侧边栏,然后显示带有两个文件作为图标的选项卡)。你将在 scripts下看到一行 "dev": "bun run --watch src/index.ts"。如果愿意,你可以添加自己的行,例如 "debug": "bun --inspect --watch src/index.tsx"。这非常适合你想要逐行使用调试器逐步执行 TypeScript 文件的情况,并且如果你在项目的根文件夹中在终端中输入 bun debug,它将运行。但我离题了……

3.1 配置

最后几件事使我们能够混合 HTML 和 TypeScript 代码,而不会让编译器给我们带来问题。

将 index.ts 文件重命名为 index.tsx(在 VS Code 中,当焦点位于文件名上时,只需按 Enter)。在 package.json 文件的第 6 行也更新名称。

打开 tsconfig.json 并在 compilerOptions下添加以下3行,如下所示:

  "compilerOptions": {
      "jsx": "react",
      "jsxFactory": "Html.createElement",
      "jsxFragmentFactory": "Html.Fragment",

回到index.tsx,在顶部添加以下2行:

  import { html } from "@elysiajs/html";
  import * as Sui from "@mysten/sui.js/client";

最后,添加 .use(html()) 到 Elysia 服务器声明,并可能将其格式化为:

  const app = new Elysia()
    .use(html())
    .get("/", () => "Hello Elysia")
    .listen(3000);

如果你保存了所有文件,应该会在终端底部看到“🦊 Elysia 正在运行...”消息,否则请再次尝试 bun dev 命令。

3.2 向用户提供主页

是时候用一些真正的 HTML 替换“Hello Elysia”了。即,一个文本框,用户可以在其中输入钱包地址和一个启动按钮。

单击主侧边栏中的 index.tsx(CMD+B),然后单击侧边栏顶部的“新建文件...”图标,将此文件命名为 BaseHTML.tsx。在里面粘贴以下代码:

export default function BaseHTML() {
  return (
    <html lang="en">
      <head>
      <script
      src="https://unpkg.com/htmx.org@1.9.10"
      integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
      crossorigin="anonymous"></script>
      <script src="https://cdn.tailwindcss.com"></script>
      <title>My First Sui-t Bun</title>
      </head>
      <body>
        <div class="flex justify-center p-4">
          <form hx-post="/coins" hx-target="#results">
            <input name="address" type="text" class="border pl-1 pr-1"></input>
            <button type="submit" class="ml-4 border pl-2 pr-2">Check if Chose Rich</button>
          </form>
        </div>
        <div id="results" class="flex justify-center"></div>
      </body>
    </html>
  )
}

它有什么用?第一个 <script> 标签导入 HTMX javascript 库,第二个 - Tailwind CSS(我个人选择用于设置页面样式)。然后我们有一个简单的表单,其中包含一个文本字段和一个按钮。该按钮将文本字段的内容发送到服务器,然后获取服务器返回的任何 html 并将其插入到结果 div 中。当然,最重要的一行是 <title> 标签之间的文本,因为,让我们面对现实吧,这是一个非常棒的双关语😊。

如果你以前没有使用过 HTMX,那么这里披着斗篷的英雄是两个关键字 hx-post 和 hx-target。第一个将表单发送到 Web 服务器上的 /coins 地址,后者告诉 htmx 将从服务器返回的任何内容粘贴到 id 为 results的 div 中。非常简单,简单就是好。

为了向用户展示这个精彩的新 html 代码,在 index.tsx 中将 .get("/", () => "Hello Elysia") 行替换为:

.get("/", () => BaseHTML())

同时在顶部添加此导​​入行,以便 index.tsx 知道在哪里查找 BaseHTML 函数:

import BaseHTML from "./BaseHTML";

保存文件后,刷新浏览器页面,你应该会看到:

当然,如果你粘贴钱包地址并单击按钮,则不会发生任何事情,因为 Web 服务器尚未设置为在  /coins 路由接收任何内容,更不用说返回任何有用的东西了。但是,如果你检查开发人员工具 -> 控制台,会看到记录的错误,其中客户端在服务器上调用  /coins 并返回 404 错误(未找到),这是应该的。

现在给自己倒一杯,因为你已经完成了一半以上,我们终于准备好使用 Sui javascript 包装器从区块链中读取数据了,这次完全不是开玩笑。是时候了。

4、要点

首先,我们必须告诉 Elysia 当有人尝试访问 /coins 时她必须做什么。在  .get 和  .listen 行之间插入以下行:

.post("/coins", ({ body }) => balances(body))

现在我们的代码应如下所示:

const app = new Elysia()
  .use(html())
  .get("/", () => BaseHTML())
  .post("/coins", ({ body }) => balances(body))
  .listen(3000);

body 是 Elysia 用来表示从客户端收到的数据的特殊关键字。我们告诉 Web 服务器“请将 POST 的主体(内容)传递给我们的函数 balances。谢谢。” )

你会注意到 balances带有下划线,因为我们尚未定义该函数。让我们这样做。在 index.tsx 的底部,添加:

function balances(body: any) {
  const addy = body.address;

  if (!addy || addy === "") {
    return <div>Plz enter wallet addy above</div>;
  }

  return (
    <div class="grid grid-cols-[50px_60px_150px] text-middle">
      <div class="col-span-3">{addy}</div>
    </div>
  );
}

首先,我们期望 body 对象上有一个地址字段。如果没有这样的字段,我们将返回 HTML,告诉用户输入地址。否则,我们现在返回仅显示输入地址的 HTML,作为我们收到该地址的确认。

好的,现在开始真正的讨论:是时候实际使用 Sui JS 包装器来访问区块链了。

function balances(...) 行的正上方,插入以下行:

const client = new Sui.SuiClient({ url: Sui.getFullnodeUrl("mainnet") });

然后回到 balances() 函数内部,在检查是否提供了地址之后,让我们读取区块链!

const coins = await client.getAllBalances({ owner: addy });

你会看到 await现在带有下划线:

这是因为我们的 balances() 函数未标记为异步,并且所有 Sui 函数都定义为 Promises,即异步操作。 这意味着我们的闭包函数也必须是这样的。

通过在函数 balances 前面添加 async 来解决这个问题:

async function balances(body: any) {

现在让我们处理钱包没有硬币的情况。在 const coins = ... 之后添加:


  if (!coins || coins.length === 0) {
    return <div>No coins in this wallet. They didn't choose rich.</div>;
  }

如果我们确实取回了币余额,我们应该将它们包含在返回给客户端的 HTML 中。将 <div class="col-span-3">{addy}</div> 替换为:

{coins.map(c => <><div class="col-span-2">{c.coinType}</div><div>{c.totalBalance}</div></>)}

请注意空的 <></> 标签。这是我们使用的库的一个工件,它只允许将返回的 html 包装在一个父标签中。当我们不想使用其他标签(例如 div)时,会使用空标签。它允许代码工作,但实际上它只是被忽略,并且不会影响 Web 服务器返回给客户端的最终 html。

如果你正确遵循并刷新页面并提供钱包地址,可能会看到一些非常丑陋的东西:

这是因为我们被调用的 getAllBalances() 函数“弄得”很“粗糙”。它不会返回每个币的元数据,例如符号 ($SUI 等) 或币使用的小数位数。因此,对于 SUI,我们实际上看到的是 MIST 余额,而不是 SUI 余额。即,我的钱包(见此处)有 4.35 SUI,而不是 40 亿 SUI。不幸的是😅。  ::fud::FUD 前面的那个长数字?它基本上是部署币的合约的地址。

FUD 币的余额隐藏在地址后面,因为,听着,我不是 CSS 专家,好吧,我只是一个想写博客的邻家男孩。

丑陋与否,关键在于你刚刚从区块链中读取了数据!!!哇哦!喝上一两杯龙舌兰酒,既是为了庆祝,也是为了应对令人沮丧的认识:事实上,我确实在整整 15 分钟的事情上对你撒了谎,我可能不是你以为的那个朋友。

5、最终代码

由于 getAllBalances() 对我们很不利,我们需要对返回的每个硬币进行另一次调用,以获取该硬币的元数据 - 它的符号、图标/图像的 url(如果有)等。

有很多方法可以剥这只狐狸的皮;在这里,我选择了一种快速而肮脏的方法。

让我们首先定义一个封装我们想要发送回客户端的信息的类型。将此代码插入到函数 balances() 的主体之外,例如 const client = ... 行的正上方:

type coin_balance = {
  symbol: string,
  url?: string | null,
  balance: string
}

现在回到 balances() 函数内部,在最后的 return 语句上方,添加以下代码:

let coin_balances: coin_balance[] = []
for (let coin of coins) { 
  const meta = await client.getCoinMetadata({ coinType: coin.coinType });

  if (!meta) {     
    coin_balances.push({ symbol: coin.coinType, url: undefined, balance: coin.totalBalance });
  } else {
    const balance = Number(coin.totalBalance) / Math.pow(10, meta.decimals);
    coin_balances.push({ symbol: meta.symbol, url: meta.iconUrl, balance: String(balance)});
  }
}

对于 getAllBalances() 函数返回的每种硬币,我们尝试获取其元数据。如果我们没有得到它(即 !meta),那么我们将创建一个 coin_balance 对象,并用我们已有的丑陋数据填充。否则,我们将用从 getCoinMetadata() 函数获得的漂亮数据填充它。通过将 totalBalance 除以从元数据中获得的 10 的幂,我们得到了真实的硬币数量(据我所知)。也就是说,我的 40 亿 SUI 余额变成了更现实的 4.35 SUI。

现在我们需要显示这些数据。将 return 语句中的  {coins.map...} 行替换为:

{coin_balances.map(coin_row)}

最后,在 balances() 函数下方添加 coin_row() 函数:

function coin_row(cb: coin_balance) {
  const img = cb.url ? <img src={cb.url} class="ml-2 w-6 h-6"></img> : <></>;
  return <><div>{img}</div><div>{cb.symbol}:</div> <div>{cb.balance}</div></>;
}

最后一个函数只是采用 coin_balance 视图模型(此类对象的术语)并为包含 3 列的行创建 HTML:图像、符号和余额。如果提供的数据中没有 url,它会返回空标签代替图像,即我们之前讨论过的标签。

保存,返回浏览器并刷新,输入钱包地址,然后 vuala:

我今天在那只狗币上赚了 5%!也就是......支票纸币...... 0.08 美元。太棒了!😭

就这样结束了!你可以尝试前 100 名列表中的一些 Sui 钱包,看看它们包含哪些硬币。


原文链接:Build Your First Blockchain App in 15 min with Sui & Typescript

DefiPlot翻译整理,转载请标明出处

免责声明:本站资源仅用于学习目的,也不应被视为投资建议,读者在采取任何行动之前应自行研究并对自己的决定承担全部责任。
通过 NowPayments 打赏