sui-move基础(七):letsmove-task6需求完成dapp-kit学习完成SuiSDK学习完成PTB的概念学习完成NaviProtocol学习在dapp-kit里面用chrome钱包签名提交完成必须在同一个PTB完成NaviSDK1.[存入N
在任务描述中,已经给了我们一些进行学习的相关资料。
因为task6涉及到的内容比较多,所以还是建议都大致浏览一下,其中的NAVI book和dapp-kit部分更是需要着重关注,仔细阅读一下。
其实,如果相关经验的话,例如将训练营task作为起点的程序员,想要完成本节任务是十分困难的。而在task之外,补充相关知识也是非常重要的。毕竟,学习才是主要的目的(在拥有足够赚取报酬的代码和知识水平之前),task只是一个引导我们学习的路径罢了。
在这里,我仍然将任务指南贴出来:
另外,尤其推荐观看uvd老师的相关视频教程,这里我贴出与本节最相关的一个教程:
在最开始,对于如何进行一个dapp的开发,我们肯定是十分迷茫的。
幸运的是,我们相对而言不用这么迷茫,sui已经有了dapp开发的脚手架,我们不用像最开始的冒险家那样面对一片陌生的新大陆。
在上述uvd老师的视频里,介绍了dapp开发的脚手架:dapp-kit , 我在这里对如何下载进行一个简要的介绍:
准备工作
安装必要的工具 确保你有以下开发环境:
Node.js (版本不宜过旧) npm 或 yarn
安装 dapp-kit 在项目目录中运行以下命令: npm i --save @mysten/dapp-kit @mysten/sui @tanstack/react-query
创建项目
在终端运行:
npm create @mysten/dapp
控制台会引导你创建一个新的 dApp 项目。
创建新的 dApp 时,你有以下模板可供选择:
react-client-dapp:一个基本的 React dApp,用于获取连接的钱包拥有的对象列表。
react-e2e-counter:一个端到端示例,其中包含 Move 代码和简单计数器应用程序的 UI。
就本task而言,选择react-client-dapp即可。
项目结构
创建项目后,你将看到:
my-first-sui-dapp$ tree
.
├── README.md
├── index.html
├── package.json
├── prettier.config.cjs
├── src
│ ├── App.tsx
│ ├── OwnedObjects.tsx
│ ├── WalletStatus.tsx
│ ├── main.tsx
│ ├── networkConfig.ts
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
1 directory, 13 files
这是为我们自动创建的模板,我们可以在它的基础上进行修改。
那么在正式开始编码之前,我们先了解一下Programmable Transaction Block (PTB)和Sui上的NAVI Protocol。
Programmable Transaction Block (PTB) 是 Sui 网络中的一种强大工具,允许开发者在单个交易中包含多个操作。PTB 通过将多个交易命令链接在一起,提供了一个灵活且高效的方式来生成交易。每个 PTB 可以执行多达 1024 个独特的操作,显著降低了 gas 费用并加快了执行速度。PTB 的设计保证了交易的原子性,如果其中一个命令失败,整个块将失败,且不会应用任何效果。
PTB 的主要特性
模块化设计:
PTB 是由一组操作和指令组成的事务单元,每个操作可以执行独立的功能,如调用 Move 合约、转账资产等。 通过组合多个操作,开发者可以实现复杂的逻辑,同时保持事务的原子性。
原子性:
PTB 中的所有操作要么全部成功执行,要么全部回滚。这种设计保证了状态的一致性,非常适合构建需要强一致性保证的应用。
灵活的组合性:
PTB 可以包含多个输入(如代币、对象)和多个输出(如新生成的对象或资产)。 支持跨模块调用,实现合约之间的互操作。
高性能:
PTB 的设计充分利用了 Sui 的并行执行引擎。通过对事务依赖的静态分析,PTB 能够最大化并行度,提升执行效率。
NAVI Protocol 是构建于 Sui 区块链之上的一站式去中心化流动性协议,为用户提供高效的资产借贷和流动性管理服务。NAVI 的设计目标是通过创新的去中心化金融(DeFi)基础设施,为用户提供无中介的资产操作,同时优化安全性和资本效率。
核心功能
借贷市场:
用户可以通过 NAVI 提供或借入多种数字资产,收益来源于利息。 提供流动性时,用户会获得“Navi 收据代币”(例如 nSUI),作为其在流动性池中的份额证明,这些代币可以用作抵押品。
自动杠杆金库(Automatic Leverage Vaults):
用户可以在 NAVI 中使用低利率资产借贷进行杠杆操作,优化资产收益,比如参与质押奖励或流动性挖矿。
隔离模式(Isolation Market):
NAVI 支持在特定的隔离市场中列出新资产,设定债务上限(Debt Ceiling)以限制波动性资产的借贷风险。这种机制增强了平台的安全性。
灵活的治理机制:
持有原生治理代币 NAVX 的用户可以参与投票,决定如利率设定、新资产支持和抵押率调整等关键事项。
那么如何完成要求的任务呢?
我们可以分两个模块来完成:
第一个模块用于构建PTB,我们将PTB进行的操作都在这个模块定义。
第二个模块用于调用执行PTB,在这个模块中,我们进行钱包连接和前端调用等操作。
而对于第一个模块,可以分为以下几个步骤:
定义 PTB
Step 1: 存入 1 SUI 到 Navi Protocol
Step 2: 借出 USDC
Step 3: 再次存入等量的 USDC
Step 4: 提交 PTB ,签名并执行交易
任务要求我们进行日期计算,我们可以将计算放在PTB中,但为了模块化考虑,我们在另一个模块中完成它。
对于这个模块,我们作此建构:
动态金额计算:
调用 calculateBorrowAmount 根据当前时间生成借贷金额,格式为 0.MMDDHH。
连接钱包:
使用 useWallets 获取支持的钱包列表。 用户选择钱包后,通过 connect 方法完成连接。 在连接成功后显示用户的钱包地址。
交易 UI:
展示用户需要完成的交易流程。 通过按钮点击触发 mainAction,完成 PTB 事务的签名与提交。
PTB 提交:
mainAction 是具体实现事务的核心逻辑,负责: 组合事务(如存入 SUI、借出 USDC 等)。 调用 signAndExecuteTransaction 提交事务。
因为之前我们使用dap-kit进行了初步的预构建, 这时我们只需要在其基础上进行修改即可。
我们新增一个文件,NAVI.ts:
import { Transaction } from '@mysten/sui/transactions';
import { depositCoin, borrowCoin, } from 'navi-sdk/dist/libs/PTB'
import { Sui, wUSDC } from "navi-sdk/dist/address";
import { Pool, PoolConfig } from "navi-sdk/dist/types";
import { pool } from 'navi-sdk/dist/address'
export const mainAction = async (account: any, { signAndExecute, usdcAmount, onSuccess = () => { } }: {
signAndExecute: any;
usdcAmount: number;
onSuccess?: (result: any) => void;
onError?: (result: any) => void;
}) => {
if (!account) {
postMessage("Please connect your wallet first");
return;
}
const tx = new Transaction();
tx.setSender(account.address);
// 获取池配置
const SuiPool: PoolConfig = pool[Sui.symbol as keyof Pool];
const wUSDCPool: PoolConfig = pool[wUSDC.symbol as keyof Pool];
if (!SuiPool || !wUSDCPool) {
throw new Error("Invalid pool configuration");
}
// 设置gas
tx.setGasBudget(1e9);
try {
// 分离出1sui
const [sui] = tx.splitCoins(tx.gas, [1e9]);
console.log("Checking object existence...");
const suiObject = await tx.object(sui);
if (!suiObject) {
throw new Error(`Object ${sui} does not exist`);
}
// 将其存入navi
await depositCoin(tx as any, SuiPool, sui, 1e9);
// 借出 usdc
const [usdc_coin] = await borrowCoin(tx as any, wUSDCPool, usdcAmount);
// 存入 usdc
await depositCoin(tx as any, wUSDCPool, usdc_coin, usdcAmount);
// 签名执行交易
await signAndExecute({
transaction: tx, chain: "sui:mainnet"
}, {
onSuccess,
onError: (error: any) => { // 为 error 参数指定类型
console.error("Transaction failed:", error);
postMessage(error.message || "Transaction failed");
},
});
} catch (error) {
console.error("Transaction failed:", error);
postMessage(error instanceof Error ? error.message : "An unknown error occurred");
}
}
简而言之,在这个 NAVI.ts
中,我们构建了一个PTB,并在其中
编写了所有我们需要进行的交易,并最终签名并执行。
前端调用模块我们可以直接改造App.tsx:
import { useCurrentAccount, useSignAndExecuteTransaction } from "@mysten/dapp-kit";
import { Container, Flex, Heading, Text, Button } from "@radix-ui/themes";
import { ConnectButton, useConnectWallet, useWallets } from '@mysten/dapp-kit';
import { wUSDC } from "navi-sdk/dist/address";
import { mainAction } from "./NAVI.ts";
const calculateBorrowAmount = (month: number, day: number, hour: number): number => {
const borrowAmountStr = `0.${month.toString().padStart(2, "0")}${day.toString().padStart(2, "0")}${hour.toString().padStart(2, "0")}`;
return parseFloat(borrowAmountStr) * 10 ** wUSDC.decimal;
};
const Execute = () => {
const wallets = useWallets();
const { mutate: connect } = useConnectWallet();
const account = useCurrentAccount();
const { mutate: signAndExecute } = useSignAndExecuteTransaction();
const date = new Date();
const usdcAmount = calculateBorrowAmount(date.getMonth() + 1, date.getDate(), date.getHours());
return (
<Container my="2">
<Heading mb="2">Wallet Status</Heading>
<div style={{ padding: 20 }}>
<ConnectButton />
<ul>
{wallets.map((wallet) => (
<li key={wallet.name}>
<button
onClick={() => {
connect(
{ wallet },
{
onSuccess: () => console.log('connected'),
},
);
}}
>
Connect to {wallet.name}
</button>
</li>
))}
</ul>
</div>
{account ? (
<Flex direction="column">
<Text>Wallet connected</Text>
<Text>Address: {account.address}</Text>
</Flex>
) : (
<Text>Wallet not connected</Text>
)}
<Heading mb="2">Transaction Action</Heading>
{account ? (
<Flex direction="column">
<Heading mb="2">执行交易</Heading>
<Flex direction="column">
<Text>1. [存入Navi Protocol 1 SUI] </Text>
<Text>{`2.[借出当前日期的USDC ${usdcAmount} 的 USDC ]`}</Text>
<Text>3.[然后在存入等额的USDC]</Text>
</Flex>
<Button
size="3"
onClick={() => {
mainAction(account, {
signAndExecute, usdcAmount, onSuccess(result) {
console.log(result, 'Transaction success!')
}, onError(error) {
console.log(error, 'Something went wrong!')
},
});
}}
style={{ cursor: 'pointer' }}
>
<Text>点击执行此交易</Text>
</Button>
</Flex>
) : null}
</Container>
);
}
export default Execute;
我们定义一个 calculateBorrowAmount
用于计算需要存入的金额,注意需要乘以wUSDC的精度 10 ** wUSDC.decimal
。
由于需要使用chrome钱包进行交易,我们编写一个连接钱包的逻辑:
<div style={{ padding: 20 }}>
<ConnectButton />
<ul>
{wallets.map((wallet) => (
<li key={wallet.name}>
<button
onClick={() => {
connect(
{ wallet },
{
onSuccess: () => console.log('connected'),
},
);
}}
>
Connect to {wallet.name}
</button>
</li>
))}
</ul>
</div>
这一部分的代码可以直接在官方文档中找到:https://sdk.mystenlabs.com/dapp-kit/wallet-hooks/useConnectWallet。
下面的Wallet Status代码可以直接从已经存在的WalletStatus.tsx中迁移过来。
最后就是打印一些log,并执行调用mainAction
的逻辑。
至此,task6的编写便告完成。
在相应路径下运行 npm run
,然后来到前端页面进行调用即可。
如果你发现调用失败的话,或许你需要注意main.tsx中的 defaultNetwork 定义。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!