Skip to content

Latest commit

 

History

History
215 lines (189 loc) · 6.76 KB

readme.md

File metadata and controls

215 lines (189 loc) · 6.76 KB

本节作者:@愚指导

这一讲会完成前端和链交互相关的获取流动性列表、添加流动性、移除流动性和提取流动性的逻辑。


获取流动性列表

我们在之前的《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 方法,一共会三次唤起钱包签名,注入流动性。

移除和提取流动性

我们在每一列的最后添加两个操作按钮 RemoveCoolect,分别调用合约的 burncollect 方法,提供给 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 中查看。

最后的效果如下:

positions