판게아 풀 생성하기

현재 판게아에 배포되어 있는 모든 풀의 정보를 가져오는 예제입니다. 판게아의 풀은 생성과 예치가 나뉘어져 있습니다. 풀을 먼저 생성한 후 풀에 자산을 예치해야 스왑이 정상적으로 진행됩니다.

주의 생성된 풀은 삭제할 수 없고, 한번 생성한 파라미터(token0/token1/swapFee/tickSpacing)로 재생성은 불가능합니다. Test Network나 local network에서 충분히 테스트하시길 바랍니다.

/// MasterDeployer__factory는 판게아 프로토콜의 types/ 내에 있는 typechain 객체입니다. 
/// typechain와 ethers.js를 통해 JS 환경에서 간편하게 interaction할 수 있습니다.
import {ConcentratedLiquidityPoolFactory__factory, MasterDeployer__factory} from "./types";
import {BigNumberish, ethers} from "ethers";
import {BigNumber} from "@ethersproject/bignumber";

/// 주소는 네트워크 별로 달라야 합니다
const masterDeployerAddress = "0xEB4B1CE03bb947Ce23ABd1403dF7C9B86004178d";
const poolFactoryAddress = "0x3d94b5E3b83CbD52B9616930D33515613ADfAd67";

/// web3 Provider (metamask provider / jsonRPC provider 등)
const provider = ethers.getDefaultProvider();

async function createPool(
    token0Address:string, /// 풀을 구성할 토큰 0의 주소
    token1Address:string, /// 풀을 구성할 토큰 1의 주소
    swapFee:number,       /// 스왑 수수료 (단위 : 1000 = 0.1%)
    tickSpacing:number,   /// 틱의 간격
                          /// 현재 가능한 스왑 수수료와 틱 간격의 조합은 (10_000, 100) / (2_000, 20) / (600, 6) / (100, 1)
    amount0:BigNumberish, /// 토큰 가격 산정을 위해, 상대 갯수 지정
    amount1:BigNumberish  /// amount0 : amount1 = 1 : 10 이면, price = (√10 * 2^96)
) {
  const masterDeployer = await MasterDeployer__factory.connect(
      masterDeployerAddress, provider
  );
  const factory = await ConcentratedLiquidityPoolFactory__factory.connect(
      poolFactoryAddress, provider
  );

  // validation rule [1]
  // > 풀 생성 시 토큰 주소는 token0Address < token1Address를 만족해야 합니다.
  if(token0Address.toLowerCase() > token1Address.toLowerCase()){
    throw new Error("invalid token order");
  }

  // validation rule [2]
  // > 현재 가능한 스왑 수수료와 틱 간격의 조합은 아래와 같습니다.
  // (1% , 100) (0.2%, 20) (0.06%, 6) (0.01%, 1)
  // 틱 간격에 대한 자세한 설명은 https://docs.pangeaswap.com/developers/concept-overview/price-tick
  // 에서 참고하시길 바랍니다.
  if (! (await factory.availableFeeAndTickSpacing(swapFee, tickSpacing))) {
    throw Error("Not ABLE FEE & TICK SPACING");
  }

  // [1] Price 계산하기
  // 풀 형성 시 두 토큰의 상대 가격을 지정해야 합니다.
  // 풀에서의 price는 `√(token1/token0*2^192)`의 형태로 관리됩니다.
  const price = sqrtValue(
      BigNumber.from(2).pow(192).mul(amount1).div(amount0)
  );

  /// [2] 판게아 풀 생성하기
  /// 동일한 페어에 대해, 동일한 swapFee & tickSpacing이 존재하는 경우,
  /// 풀 생성은 실패합니다.
  const tx = await masterDeployer.deployPool(poolFactoryAddress,
      ethers.utils.defaultAbiCoder.encode(
          ["address", "address", "uint24", "uint160", "uint24"],
          [token0Address, token1Address, swapFee, price, tickSpacing]
      );
  await tx.wait();

  /// [3] 생성한 풀의 주소 가져오기
  /// (token0Address, token1Address, swapFee, tickSpacing) 별로 unique 합니다.
  const poolFactory = await ConcentratedLiquidityPoolFactory__factory.connect(
      poolFactoryAddress, provider
  );
  const poolAddress = poolFactory.configAddress(
      ethers.utils.defaultAbiCoder.encode(
          ["address", "address", "uint24", "uint24"],
          [token0Address, token1Address, swapFee, tickSpacing]
      );
  console.log(`생성한 풀의 주소 : ${poolAddress}`);
}

/**
 * 제곱근 계산
 */
function sqrtValue(value) {
  const ONE = BigNumber.from(1);
  const TWO = BigNumber.from(2);

  let x = BigNumber.from(value);
  let z = x.add(ONE).div(TWO);
  let y = x;
  while (z.sub(y).isNegative()) {
    y = z;
    z = x.div(z).add(z).div(TWO);
  }
  return y;
}

Last updated