문제 상황
Balancer는 분산화된 자동화 마켓 메이커(AMM; Automated Martket Maker)로, 여러 종류의 pool을 통합한 확장 가능한 프로토콜입니다. Balancer에서 사용할 수 있는 Pool의 종류는 Weighted Pools, Composable Stable Pools, Boosted Pools 등 여러 종류가 있는데요. 저는 스마트 컨트랙트 상에서 wstETH와 WETH를 사용하는 Composable Stable Pool에 유동성을 추가하는 joinPool 함수를 호출하다가 <BAL#103> 에러를 마주쳤습니다.
Error: VM Exception while processing transaction: reverted with reason string 'BAL#103'
at <UnrecognizedContract>.<unknown> (0x65fe9314be50890fb01457be076fafd05ff32b9a)
at <UnrecognizedContract>.<unknown> (0xba12222222228d8ba445958a75a0704d566bf2c8)
at WethStrategy.deposit (contracts/WethStrategy.sol:69)
에러 분석
먼저, Balancer에서 <BAL#103> 에러 코드는 INPUT_LENGTH_MISMATCH 에러를 의미하며, 여러 개 주어진 array의 길이가 서로 동일한지 확인하는 데 사용되며, 만약 의도한 것과 다른 array 길이를 입력한 경우에 위 에러가 발생합니다.
Pool에 유동성을 공급하기 위해서 joinPool 함수를 호출했는데요. 이때 매개변수로 넘겨주는 값들에는 여러 array가 들어갑니다. joinPool의 내용을 살펴보면, 다음과 같습니다. 참고로, joinPool 함수는 Pool 레벨의 컨트랙트가 아니라 Vault 레벨의 컨트랙트에 구현되어 있으며, 유저들을 pool과 직접적으로 상호작용하기보다는 vault를 통해 간접적으로 상호작용하게 됩니다.
function joinPool(
bytes32 poolId,
address sender,
address recipient,
JoinPoolRequest request
)
struct JoinPoolRequest {
address[] assets,
uint256[] maxAmountsIn,
bytes userData,
bool fromInternalBalance
}
각각의 매개 변수들은 아래와 같은 변수들입니다.
- poolId : 상호작용 하려는 pool의 id
- sender / recipient : token을 보내고 받을 주소
- request : joinPool을 위한 상세 request 구조체
- request.assets : pool에서 다루고 있는 모든 토큰들의 sorted list
- request.maxAmountsIn : 유동성을 공급하려고 하는 토큰 수량 list (assets list와 길이와 순서가 동일해야 함)
- request.userData : join type에 따라서 달라지는 cutom bytes field
- request.fromInternalBalance : internal token balance를 사용하는지 여부 (True : 사용함, False : 사용 안함)
이때, userData에서 말하는 join type은 여러 종류가 있는데, 저는 이 중 Exact Tokens Join(EXACT_TOKENS_IN_BPT_OUT) type을 선택하였습니다. 해당 type을 선택한 경우에는 userData를 아래와 같은 내용으로 encoding 해 생성합니다.
- userData ABI : ['uint256', 'uint256 []', 'uint256']
- userData : [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]
해당 join type은 내가 amountsIn에 입력한 토큰의 수량만큼 pool에 예치하고, 이에 비례한 (알 수 없는) BPT 수량을 받겠다는 의미이므로, amountsIn에도 토큰 수량 list를 넘겨주었습니다.
해결 방안
위에서 joinPool을 자세히 살펴본 것 대로, 해당 함수에는 총 3가지의 array가 들어갑니다.
1. request.assets
2. request.maxAmountsIn
3. request.userData.amountsIn
이때, request.assets와 request.maxAmountsIn은 pool에서 관리하고 있는 토큰들의 순서와 길이 그대로 넘겨주어야 합니다. 위에서 설명했다시피 저는 composable stable pool과 상호작용하고 있었는데, 이때 pool에서 관리하고 있는 토큰들에는 BPT(Balancer Pool Token. Balancer pool에 유동성을 공급했다는 증명 토큰)가 포함되어 있습니다.
따라서 1, 2, 3 모두에게 BPT 토큰이 포함되어 있는 list를 넘겨주고 있었는데, 배열 길이와 관련된 에러가 발생해 찾아보니 3번 list에는 BPT를 제외한 list를 넘겨주었어야 했습니다. 그래서 예상한 배열의 길이와 제가 넘겨준 parameter의 배열이 차이가 나니 #103 에러가 발생했던 것이었고, 3번에 넘겨지는 배열을 수정하니 성공적으로 유동성을 공급할 수 있었습니다.
해결 방안은 Balancer의 Discord를 검색하여 확인하였습니다.
https://docs.balancer.fi/reference/joins-and-exits/pool-joins.html
https://polygonscan.com/address/0xBA12222222228d8Ba445958a75a0704d566BF2C8#code
https://polygonscan.com/address/0x65Fe9314bE50890Fb01457be076fAFD05Ff32B9A#code
https://docs.balancer.fi/reference/contracts/error-codes.html#input
https://discord.com/channels/638460494168064021/638465986839707660/1089906328991977482
'Blockchain' 카테고리의 다른 글
Babylon Finance (0) | 2023.04.27 |
---|---|
Proxy Pattern & Upgradeable Contract (0) | 2023.04.23 |
Hardhat Test | 테스트 중간에 Hardhat network를 reset 하기 (0) | 2023.04.13 |
Smart contract의 code size를 줄이는 방법 (0) | 2023.04.10 |
Hardhat을 통해 Polygon Test network에서 swap test 진행하기 (0) | 2023.04.04 |
댓글