[멋쟁이 사자처럼 블록체인 스쿨 3기] 23-06-29

임형석·2023년 6월 29일
0

Uniswap


uniswap 이란?

Uniswap V3-Core/contract

유니스왑은 DEX(Decentralized EXchange) 의 하나로, 탈중앙화 거래소중 하나이다.

DEX 순위를 보면, 아래 사진과 같이 유니스왑이 대다수를 차지하고 있다.

점유율을 보면 사실상 1위.

유니스왑은 현재 V3 까지 출시되었으며, V4 의 코어 개발을 마쳤으며, Audit 만 남아있다고 한다.

유니스왑은 스마트 컨트랙트만으로 동작하는데, 유저들이 Pool 을 구성하고

구성된 Pool 에 유동성을 공급한 유저들에게는 지속적으로 거래 수수료를 지급.

동시에, uniswap 이라는 LP 토큰을 받아갈 수 있다.

따라서 예치한만큼의 금액이 묶이는 것이 아니고 LP 토큰을 거래하여 LP 토큰만큼의 자금을 그대로 운용할 수 있는 것이다.


Uniswap pool

유니스왑의 풀은 CA 이다. 그리고 CA 에는 두개의 토큰이 예치되어 스왑에 사용된다.

CA 에 예치된 두개 토큰의 비율이 가격을 의미한다.

예를 들어, 스테이블 코인과 이더리움으로 구성된 풀이 있다고 가정했을 때.

10000 스테이블 코인 / 50 이더리움 만큼으로 풀이 구성되어있다면,

1 이더리움은 2000 스테이블 코인만큼의 가치를 가진다.

1 스테이블 코인은 1달러의 가치를 유지하므로, 1 이더리움은 $2000 이다.

따라서 예치된 두개의 토큰량을 가져온다면, 각 토큰의 가격을 알 수 있다.


Solidity


uniswap pool CA 가져오기

먼저, uniswap pool CA 를 가져와야한다.

이미 이더스캔과 같은 사이트에는 각 pool CA 에 대한 정보가 등록되어 있다.

하지만 우리가 사용할 네트워크는 goerli 테스트넷이므로.. CA 부터 찾아본다.

아래의 컨트랙트를 이용하면, CA 값을 받아올 수 있다.

먼저, uniswap v3-core 의 IUniswapV3Factory.sol 을 import 한다.
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";

interface 를 선언하고 사용할 함수를 적는다. (getPool 함수)

interface UniswapV3Factory { function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool); }

컨트랙트를 선언하고 유니스왑v3 의 Factory contract address 를 선언해준다.

address public factoryAddress;  // Uniswap V3 Factory contract address

컨스트럭터를 선언하고, 팩토리 컨트랙트 주소를 input 으로 받는다.

팩토리 컨트랙트 주소는 Uniswap Contract Deployments 여기서 확인할 수 있다.

constructor(address _factoryAddress) {
        factoryAddress = _factoryAddress;
    }

마지막으로, 풀의 주소를 가져오는 함수를 선언해주면 된다.

이 함수에는 a토큰 CA, b토큰 CA, pool 의 스왑 수수료

3가지가 input 으로 들어간다.

function getPoolInfo(address tokenA, address tokenB, uint24 fee) external view returns (address) {
        IUniswapV3Factory factory = IUniswapV3Factory(factoryAddress);
        address pool = factory.getPool(tokenA, tokenB, fee);
        return pool;
    }

작성된 컨트랙트는 아래와 같다.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;


```import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";```


interface UniswapV3Factory {
    function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);
}

contract getPool_Address {
    address public factoryAddress;  // Uniswap V3 Factory contract address
    
    constructor(address _factoryAddress) {
        factoryAddress = _factoryAddress;
    }
    
    function getPoolInfo(address tokenA, address tokenB, uint24 fee) external view returns (address) {
        IUniswapV3Factory factory = IUniswapV3Factory(factoryAddress);
        address pool = factory.getPool(tokenA, tokenB, fee);
        return pool;
    }
}

컨트랙트를 테스트넷에 배포해서 확인해보았다.

먼저 컨스트럭터에 팩토리 컨트랙트 주소를 넣어야 하므로 위의 링크에 들어가 확인한다.

배포한 컨트랙트에서, 위에서 설명한 3개의 input 을 넣고 확인한다.

A 는 WETH. B 는 DAI. fee 는 0.3% 이다.

반환받은 주소 값을 사용해서 다음 단계로...


uniswap pool 토큰 예치량 가져오기

위에서 반환받은 풀의 주소를 이용해서, 풀에 예치된 두개 토큰의 수량을 확인해보았다.

먼저, ERC20, V3Pool 컨트랙트 파일을 import 한다.

import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

풀의 주소를 입력받을 상태변수를 선언하고 컨스트럭터로 배포 시에 주소 값을 받도록 한다.

contract getBalance {
    address public poolAddress;  // WETH/DAI 페어의 풀 컨트랙트 주소
    
constructor(address _poolAddress) {
        poolAddress = _poolAddress;
    }
    

입력한 풀 주소의 토큰 0, 1번의 예치량을 각각 가져와 지역변수에 담아준다.

그리고 지역변수를 return.

    function getPoolBalances() public view returns (uint256 DAI_Balance, uint256 WETH_Balance) {
        IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);
        
        address WETH_Token = pool.token0();
        IERC20 WETH = IERC20(WETH_Token);
        WETH_Balance = WETH.balanceOf(poolAddress);
        // WETH 예치량 가져오기
        
        
        address DAI_Token = pool.token1();
        IERC20 DAI = IERC20(DAI_Token);
        DAI_Balance = DAI.balanceOf(poolAddress);
        // DAI 예치량 가져오기
        
        return (WETH_Balance, DAI_Balance);
    }
}

여기서 address WETH_Token = pool.token0(); 코드를 보면,

풀 컨트랙트 내부에는 WETH 토큰이 0번 토큰으로 설정되있는 것을 알 수 있다.

그러므로, 위에서 풀의 주소 값을 가져올 때 처럼 WETH, DAI 토큰의 CA 를 입력할 필요가 없다.

아래는 전체 컨트랙트 코드이다.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";


contract getBalance {
    address public poolAddress;  // WETH/DAI 페어의 풀 컨트랙트 주소
    
    constructor(address _poolAddress) {
        poolAddress = _poolAddress;
    }
    
    function getPoolBalances() public view returns (uint256 DAI_Balance, uint256 WETH_Balance) {
        IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);
        
        // WETH 예치량 가져오기
        address WETH_Token = pool.token0();
        IERC20 WETH = IERC20(WETH_Token);
        WETH_Balance = WETH.balanceOf(poolAddress);
        
        // DAI 예치량 가져오기
        address DAI_Token = pool.token1();
        IERC20 DAI = IERC20(DAI_Token);
        DAI_Balance = DAI.balanceOf(poolAddress);
        
        return (WETH_Balance, DAI_Balance);
    }
}

위에서 반환받은 풀 CA 를 컨스트럭터로 입력하고 테스트넷에 배포한 후, 함수를 실행해본다.

풀 CA 안에 예치된 토큰의 양을 반환한다.

이더스캔에서 실제 CA 에 예치된 양이 맞는지 확인해본다.

1,377,272 DAI
4.585 ETH

반환받은 값이 맞는 걸 확인할 수 있다.

그렇다면, 이 풀에서 1 ETH 의 가격은 얼마일까?

DAI 예치량 / ETH 예치량 = 1 ETH 가격

대략 $ 300,386 이다...

실제 이더리움의 가격과는 상당히 괴리감이 있지만, Goerli 테스트넷의 이더리움은 24시간마다 0.02 개만 받아갈 수 있으므로 위의 가격이 형성되는 것으로 보인다.


실제 메인넷에 배포했다면, 더 많은 토큰의 풀에 접근할 수 있었을 것이다.

아쉽게도 테스트넷으로 프로젝트를 진행하므로, 다음번에...

대체재로 체인링크의 오라클을 이용해서 데이터를 가져온다면, 더 많은 데이터를 가져올 수 있을 것 같다.

다음은 체인링크로..

0개의 댓글