Add Pool page UI development

Author of this section: @Fish

in this talk, we will implement the UI of Wtfswap adding transaction pop-up window.


Add pop-up window

we refer to the following design draft:

the design draft is a design draft that adds fluidity. This is only for reference. Because it is a course, we will not fully realize the effect in the design draft, but only realize the necessary functions through the necessary code.

We will mainly use Ant Design Modal component and Form component to implement a pop-up window and a form in the pop-up window. We first create a component WtfAddPoolModal defining its component properties. This component collects information about the trading pool entered by LP through a pop-up window form and passes the field that creates the trading pool to the page through the callback function provided in the parameter.

import { Modal } from "antd";

interface CreatePoolParams {
  token0: string;
  token1: string;
  fee: number;
  tickLower: number;
  tickUpper: number;
  sqrtPriceX96: BigInt;
}

interface AddPoolModalProps {
  open: boolean;
  onCancel: () => void;
  onCreatePool: (params: CreatePoolParams) => void;
}

export default function AddPoolModal(props: AddPoolModalProps) {
  const { open, onCancel, onCreatePool } = props;
  const [form] = Form.useForm();

  return (
    <Modal
      title="Add Pool"
      open={open}
      onCancel={onCancel}
      okText="Create"
      onOk={() => {
        // TODO
      }}
    ></Modal>
  );
}

Then we introduce the list of trading pools implemented in the previous talk UI. AddPoolModal :

import React from "react";
import { Flex, Table, Space, Typography, Button } from "antd";
import type { TableProps } from "antd";
import WtfLayout from "@/components/WtfLayout";
+ import AddPoolModal from "@/components/AddPoolModal";
import Link from "next/link";
import styles from "./pool.module.css";

//...

const PoolListTable: React.FC = () => {
+  const [openAddPoolModal, setOpenAddPoolModal] = React.useState(false);
  const data = [
    {
      token0: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
      token1: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
      index: 0,
      fee: 3000,
      tickLower: -100000,
      tickUpper: 100000,
      tick: 1000,
      sqrtPriceX96: BigInt("7922737261735934252089901697281"),
    },
  ];
  return (
    <>
      <Table
        rowKey="token0"
        title={() => (
          <Flex justify="space-between">
            <div>Pool List</div>
            <Space>
              <Link href="/wtfswap/positions">
                <Button>My Positions</Button>
              </Link>
              <Button
                type="primary"
                onClick={() => {
+                  setOpenAddPoolModal(true);
                }}
              >
                Add Pool
              </Button>
            </Space>
          </Flex>
        )}
        columns={columns}
        dataSource={data}
      />
+      <AddPoolModal
+        open={openAddPoolModal}
+        onCancel={() => {
+          setOpenAddPoolModal(false);
+        }}
+        onCreatePool={(createPram) => {
+          console.log("get createPram", createPram);
+          setOpenAddPoolModal(false);
+        }}
+      />
    </>
  );
};

we pass the state in the component openAddPoolModal to control the opening and closing of the pop-up window, in addition onCreatePool the creation of transaction pools is handled in, and the logic of the creation will be supplemented in subsequent and chain interaction courses.

Add Form

we continue in CreatePoolModal add relevant code to the component, mainly using Ant Design's Form components and Input , InputNumber components.

The relevant new component code is as follows:

<Form layout="vertical">
  <Form.Item required label="Token 0" name="token0">
    <Input />
  </Form.Item>
  <Form.Item required label="Token 1" name="token1">
    <Input />
  </Form.Item>
  <Form.Item required label="Fee" name="fee">
    <Select>
      <Select.Option value={3000}>0.3%</Select.Option>
      <Select.Option value={500}>0.05%</Select.Option>
      <Select.Option value={10000}>1%</Select.Option>
    </Select>
  </Form.Item>
  <Form.Item required label="Tick Lower" name="tickLower">
    <InputNumber />
  </Form.Item>
  <Form.Item required label="Tick Upper" name="tickUpper">
    <InputNumber />
  </Form.Item>
  <Form.Item required label="Init Price(token1/token0)" name="price">
    <InputNumber min={0.000001} max={1000000} />
  </Form.Item>
</Form>

among them required means that the form item is required, label is the name of the form item displayed on the UI, and, name is the value of the field. For specific usage, please refer to Ant Design's documentation these are properties defined by Ant Design.

Finally, we need to click on the pop-up window Create called when the button onCreatePool function, before calling, you also need to process the next price, will. price convert sqrtPriceX96. In addition, we will enter the user's price limiting to a range is easy to calculate (avoiding exceeding the Number size range of JavaScript), you should be careful to handle this yourself in actual business requirements.

Price conversion we need to introduce @uniswap/sdk this npm package, first you need to pass npm I @uniswap/sdk install it, and then we create a new file. utils/common.ts , create a tool method parsePriceToSqrtPriceX96 :

import { encodeSqrtRatioX96 } from "@uniswap/v3-sdk";

export const parsePriceToSqrtPriceX96 = (price: number): BigInt => {
  return BigInt(encodeSqrtRatioX96(price * 1000000, 1000000).toString());
};

then in AddPoolModal add related logic to the component

import { Modal, Form, Input, InputNumber, Select } from "antd";
+ import { parsePriceToSqrtPriceX96 } from "@/utils/common";

interface CreatePoolParams {
  token0: string;
  token1: string;
  fee: number;
  tickLower: number;
  tickUpper: number;
  sqrtPriceX96: BigInt;
}

interface AddPoolModalProps {
  open: boolean;
  onCancel: () => void;
  onCreatePool: (params: CreatePoolParams) => void;
}

export default function AddPoolModal(props: AddPoolModalProps) {
  const { open, onCancel, onCreatePool } = props;
+  const [form] = Form.useForm();

  return (
    <Modal
      title="Add Pool"
      open={open}
      onCancel={onCancel}
      okText="Create"
      onOk={() => {
+        form.validateFields().then((values) => {
+          onCreatePool({
+            ...values,
+            sqrtPriceX96: parsePriceToSqrtPriceX96(values.price),
+          });
+        });
      }}
    >
+      <Form layout="vertical" form={form}>
-      <Form layout="vertical">
        <Form.Item required label="Token 0" name="token0">
          <Input />
        </Form.Item>
        <Form.Item required label="Token 1" name="token1">
          <Input />
        </Form.Item>
        <Form.Item required label="Fee" name="fee">
          <Select>
            <Select.Option value={3000}>0.3%</Select.Option>
            <Select.Option value={500}>0.05%</Select.Option>
            <Select.Option value={10000}>1%</Select.Option>
          </Select>
        </Form.Item>
        <Form.Item required label="Tick Lower" name="tickLower">
          <InputNumber />
        </Form.Item>
        <Form.Item required label="Tick Upper" name="tickUpper">
          <InputNumber />
        </Form.Item>
        <Form.Item required label="Init Price(token1/token0)" name="price">
          <InputNumber min={0.000001} max={1000000} />
        </Form.Item>
      </Form>
    </Modal>
  );
}

in this way, our UI part of creating the trading pool is completed, and the final effect is:

please refer to the complete code: AddPoolModal/index.tsx

we'll refine the code in a later lesson and add logic for calling the contract interface to create a trading pool.