本节作者:@愚指导
这一讲会完成前端和链交互相关的获取流动性列表、添加流动性、移除流动性和提取流动性的逻辑。
我们在之前的《PositionManager 合约开发》中已经实现了 getAllPositions
方法,和上一讲类似,我们直接通过 useReadPositionManagerGetAllPositions
Hook 来获取所有的流动性列表。
+ import { useReadPositionManagerGetAllPositions } from "@/utils/contracts";
const PoolListTable: React.FC = () => {
+ const { data = [], refetch } = useReadPositionManagerGetAllPositions({
+ address: getContractAddress("PositionManager"),
+ });
return (
<>
<Table
rowKey="id"
scroll={{ x: "max-content" }}
title={() => (
// ...
)}
columns={columns}
+ dataSource={data}
/>
</>
);
};
相比添加交易池来说,添加流动性更麻烦一些,因为添加流动性涉及到 Token 的转移,需要 LP 授权。也就是在添加流动性之前需要先调用 ERC20
合约的 approve
方法,授权给 PositionManager
合约来管理用户的 Token。
我们可以使用 useWriteErc20Approve
Hook 来授权,使用 useWritePositionManagerMint
Hook 来添加流动性。
关键代码如下:
```diff
import {
useReadPositionManagerGetAllPositions,
+ useWriteErc20Approve
+ useWritePositionManagerMint,
} from "@/utils/contracts";
const PoolListTable: React.FC = () => {
+ const [loading, setLoading] = React.useState(false);
const [openAddPositionModal, setOpenAddPositionModal] = React.useState(false);
const { account } = useAccount();
const { data = [], refetch } = useReadPositionManagerGetAllPositions({
address: getContractAddress("PositionManager"),
});
const { writeContractAsync } = useWritePositionManagerMint();
const { writeContractAsync: writeErc20Approve } = useWriteErc20Approve();
return (
<>
<Table
rowKey="id"
scroll={{ x: "max-content" }}
title={() => (
<Flex justify="space-between">
<div>Positions</div>
<Space>
<Button
type="primary"
+ loading={loading}
onClick={() => {
setOpenAddPositionModal(true);
}}
>
Add
</Button>
</Space>
</Flex>
)}
columns={columns}
dataSource={data}
/>
<AddPositionModal
open={openAddPositionModal}
onCancel={() => {
setOpenAddPositionModal(false);
}}
onCreatePosition={async (createParams) => {
+ console.log("get createParams", createParams);
+ if (account?.address === undefined) {
+ message.error("Please connect wallet first");
+ return;
+ }
+ setOpenAddPositionModal(false);
+ setLoading(true);
+ try {
+ await writeErc20Approve({
+ address: createParams.token0,
+ args: [
+ getContractAddress("PositionManager"),
+ createParams.amount0Desired,
+ ],
+ });
+ await writeErc20Approve({
+ address: createParams.token1,
+ args: [
+ getContractAddress("PositionManager"),
+ createParams.amount1Desired,
+ ],
+ });
+ await writeContractAsync({
+ address: getContractAddress("PositionManager"),
+ args: [
+ {
+ token0: createParams.token0,
+ token1: createParams.token1,
+ index: createParams.index,
+ amount0Desired: createParams.amount0Desired,
+ amount1Desired: createParams.amount1Desired,
+ recipient: account?.address as `0x${string}`,
+ deadline: createParams.deadline,
+ },
+ ],
+ });
+ refetch();
+ } catch (error: any) {
+ message.error(error.message);
+ } finally {
+ setLoading(false);
+ }
}}
/>
</>
);
};
核心逻辑是完善了 onCreatePosition
方法,它会调用授权和 Mint 的 writeContractAsync
方法,一共会三次唤起钱包签名,注入流动性。
我们在每一列的最后添加两个操作按钮 Remove
和 Coolect
,分别调用合约的 burn
和 collect
方法,提供给 LP 移除流动性并收回自己的 Token。
const { writeContractAsync: writePositionManagerBurn } =
useWritePositionManagerBurn();
const { writeContractAsync: writePositionManagerCollect } =
useWritePositionManagerCollect();
const columns: TableProps["columns"] = [
// ...
{
title: "Actions",
key: "actions",
fixed: "right",
render: (_, item) => {
if (item.owner !== account?.address) {
return "-";
}
return (
<Space className={styles.actions}>
{item.liquidity > 0 && (
<a
onClick={async () => {
try {
await writePositionManagerBurn({
address: getContractAddress("PositionManager"),
args: [item.id],
});
refetch();
} catch (error: any) {
message.error(error.message);
}
}}
>
Remove
</a>
)}
{(item.tokensOwed0 > 0 || item.tokensOwed1 > 0) && (
<a
onClick={async () => {
try {
await writePositionManagerCollect({
address: getContractAddress("PositionManager"),
args: [item.id, account?.address as `0x${string}`],
});
refetch();
} catch (error: any) {
message.error(error.message);
}
}}
>
Collect
</a>
)}
</Space>
);
},
},
];
上面是核心逻辑的代码,需要注意的是,因为我们在 Actions
这一列的操作中需要调用 Hooks,所以我们需要把 columns
整体移动到组件内部。
另外我们还需要做一个简单的判断,只允许当前用户操作自己的流动性,以及只能针对 liquidity
大于 0 的头寸做移除,针对 tokensOwed0
或者 tokensOwed1
的头寸做提取。
完成的代码你可以在 demo/pages/wtfswap/positions.tsx 中查看。
最后的效果如下: