본문 바로가기
블록체인 backEnd

DeFi Contract 작업

by gun_poo 2023. 10. 3.

 

생각했던 것 보다 상당히 구조가 복잡하고 권한자 설정 때문에 애를 먹었다.

 

  • Pool
    • poolFactory
    • poolLogic
    • poolProxy
    • poolNft
  • priceOracle
    • priceOracleLogic
    • priceOracleProxy
  • Swap
    • Swap
  • bittoToken(보상)
    • erc20
  • Staking - 작업 전

큰 구조는 이러하다. 

세폴리아 테스트넷에서 진행하며 테스트를 진행하는 것이기 때문에 실제 토큰이 아니라

erc20토큰을 배포하고 priceOracle을 통해 가격을 페깅시켜서 사용한다.

 

 

흐름은 이러하다.

hardHat Scripts

  1. 비토 토큰 배포 (bitto-contarct)
  2. NFT_Deploy.js
  3. MutliDataDeploy.js (proxyaddres, logic Abi), (setPrice role => FactoryAddress) 프록시 배포시 주인 => admin
  4. poolLogic_Deploy.js 
  5. poolFactory_Deploy.js (createPool role => admin address)
  6. createPool

 

중요한 부분만 정리해보자

PriceOracle

이 컨트렉트는 체인링크의 AggregatorV3Interface 사용한다.

AggregatorV3Interface는 체인링크에서 특정 코인의 가격을 실시간으로 알려준다.

특정 코인의 주소값이 있다면 

 

function getLatestPrice(
address tokenAddress
) public view override returns (int) {
(
,
/* uint80 roundID */ int answer /*uint startedAt*/ /*uint timeStamp*/ /*uint80 answeredInRound*/,
,
,

) = priceFeeds[tokenAddress].latestRoundData();

return answer;
}

요로코롬 알 수있다.

 

Factory Contract를 배포할때 구조체에 주소값이 필요하므로 먼저 배포해주고 

팩토리 컨트렉트 내부에서 함수 호출을 해야하는데 이때 필요한 함수들의 권한을 권한자만 호출할수 있게 만들어놨다.

또한 팩토리 컨트렉트가 함수를 호출하기 때문에 팩토리 컨트렉트를 권한자에 추가해주고

아무나 함수를 호출하면 안되기 때문에 초기 설정에 

 

initialize(address _admin
_setupRole(FEED_SETTER_ROLE, _admin);

권한자를 추가해주고 

팩토리 컨트렉트가 배포 되고 나면 이 함수를 호출해 펙토리 컨트랙트에 권한을 부여한다.

function grantFeedSetterRole(
address _factory
) public onlyRole(FEED_SETTER_ROLE) {
_setupRole(FEED_SETTER_ROLE, _factory);
}

 

pool contract

 

는 중요한 부분이기 때문에 따로 정리를 하겠다.

 

poolFactory contract

pool factory에서 토큰 1, 토큰 2 에 대한 쌍을 만들고 프록시 컨트랙트를 통해  pool Contract로

해당 풀을 생성한다. 

function createPool(
address _tokenA,
address _tokenB,
address feedAddressA,
address feedAddressB
) external onlyRole(CREATEPOOL_ROLE) returns (address pool) {
require(pools[_tokenA][_tokenB] == address(0), "Pool already exists");

require(
feedAddressA != address(0) && feedAddressB != address(0),
"Feed addresses are not valid"
);
require(_tokenA != _tokenB, "Tokens A and B should not be the same.");

priceOracle.setPriceFeed(_tokenA, feedAddressA);
priceOracle.setPriceFeed(_tokenB, feedAddressB);

// Generate initialization data for the proxy contract.
bytes memory initData = abi.encodeWithSignature(
"initialize(address,address,address,address,address)",
_tokenA,
_tokenB,
address(liquidityNFT),
address(rewardToken),
address(priceOracle)
);

BittoSwapPoolProxy poolProxy = new BittoSwapPoolProxy(
bittoSwapPoolLogic,
msg.sender,
initData
);

pool = address(poolProxy);

pools[address(_tokenA)][address(_tokenB)] = pool;
pools[address(_tokenB)][address(_tokenA)] = pool;

emit PoolCreated(address(_tokenA), address(_tokenB), pool);

return pool;
}

여기서 실수한 부분이 있어 상당히 애를 먹었는데

배포를 할때 풀의 initialize 가 보니까 owner만 호출하게 되어있더라. 그 부분을 못찾고 어디가 문제인지 한참 해맸다..

 

풀의 initialize 를

external initializer

로 수정!

 

HardHat deploy & create Pool 

scripts/CreatePool.js

 

권한을 모두 체크를 하고 인자값을 잘 전달하고 함수 실행! 

컨트랙트를 통한 배포이기 때문에 백엔드에 바로 값들이 넘어오지 않는다.

이를 해결 하기 위해서

// Get a filter for the "PoolCreated" event.
let filter = poolFactoryInstance.filters.PoolCreated();

// Query the contract events with this filter.
let events = await poolFactoryInstance.queryFilter(filter, "latest");

if (events.length > 0) {
let event = events[0];

console.log("Event found: ", event);

let poolAddress;

if (event.args && event.args.pool) {
poolAddress = event.args.pool;
} else if (event.topics && event.topics[2]) {
poolAddress = ethers.utils.getAddress(event.topics[2]);
}

console.log("The address of the newly created pool is:", poolAddress);

return poolAddress;
} else {
throw new Error("'PoolCreated' event not found.");
}
  • poolFactoryInstance.filters.PoolCreated()를 사용하여 PoolCreated 이벤트에 대한 필터를 생성, 이 필터는 해당 이벤트를 모니터링하기 위해 사용된다
  • poolFactoryInstance.queryFilter(filter, "latest")를 호출하여 생성한 필터를 사용하여 스마트 컨트랙트에서 PoolCreated 이벤트를 검색, "latest"는 가장 최근 블록부터 이벤트를 검색하라는 의미
  • 이벤트가 발생한 경우(events.length > 0), 첫 번째 이벤트(events[0])를 가져온다
  • 이벤트에서 args 속성을 확인하여 pool 속성이 있는지 확인, event.args.pool은 새로 생성된 풀의 주소, 이 속성이 존재하지 않는 경우 이벤트의 topics를 확인하여 event.topics[2]로부터 풀의 주소를 얻는다.
  • 새로 생성된 풀의 주소(poolAddress) 출력한다.

이 값들이 왜 필요하냐면 백엔드에서 사용하여야 되기 때문에 값들을 받아와 데이터베이스에 저장을 해준다.

 

pool Create 성공 확인! 

 

이제 유동성 제공 테스트와 스왑 테스트를 해야한다.

 

하드햇에서 테스트 하는데 아쉬움이 있는게 하드햇 네트워크를 사용할수 없다는 점이다.

체인링크에서 제공하는 주소값들은 세폴리아를 지원해주기 때문에 관련 코드들은 모두 세폴리아에서 진행해야한다.

그래도 테스트넷에서 진행하는게 어디냐만은

또 transparentproxy를 사용하면 owner는 함수를 호출할수가 없다. 그래서 권한자가 꼭 필요하다. 

컨트렉트가 여기저기 얽혀있어 권한자 설정하는데 애좀 먹었다.

댓글