>>> router = Contract.from_explorer('0x60aE616a2155Ee3d9A68541Ba4544862310933d4')
>>> mim = Contract.from_explorer('0x130966628846bfd36ff31a822705796e8cb8c18d')
>>> dai = Contract.from_explorer('0xd586e7f844cea2f87f50152665bcbc2c279d8d70')
>>> mim.balanceOf(user.address)
0
>>> dai.balanceOf(user.address)
0
각 토큰 컨트랙트 주소를 불러오고, user가 토큰을 얼마나 가지고 있는지 확인해본다.
>>> mim.allowance(user.address, router.address)
0
>>> dai.allowance(user.address, router.address)
0
이제 user가 router에 어느정도의 규모로 권한을 위임했는지 확인한다. 현재는 어떤 권한도 위임하지 않았기 때문에 기본값인 0
으로 나온다. Router를 통해 자동으로 스왑하려면 해당 토큰을 스왑할 수 있는 권한을 모두 Router에 넘겨야 한다. 유니스왑이 현재 다른 dApp에 비해 안전하다고는 하지만 언제 어떤 일이 터질지는 아무도 모른다. 아무리 신뢰하는 dApp이라도 권한을 위임할 때는 항상 주의할 필요가 있다.
유니스왑에 있는 swapETHForExactTokens()
함수는 이더리움을 다른 ERC-20 토큰으로 정확한 양에 맞게 바꿔주는 역할을 한다. 아발란체 라우터에도 이와 같은 역할을 하는 swapAVAXForExactTokens()
함수가 있다.
function swapAVAXForExactTokens(
uint256 amountOut,
address[] calldata path,
address to,
uint256 deadline
)
external
payable
virtual
override
ensure(deadline)
returns (uint256[] memory amounts)
{
require(path[0] == WAVAX, "JoeRouter: INVALID_PATH");
amounts = JoeLibrary.getAmountsIn(factory, amountOut, path);
require(amounts[0] <= msg.value, "JoeRouter: EXCESSIVE_INPUT_AMOUNT");
IWAVAX(WAVAX).deposit{value: amounts[0]}();
assert(
IWAVAX(WAVAX).transfer(
JoeLibrary.pairFor(factory, path[0], path[1]),
amounts[0]
)
);
_swap(amounts, path, to);
// refund dust eth, if any
if (msg.value > amounts[0])
TransferHelper.safeTransferAVAX(msg.sender, msg.value - amounts[0]);
}
인자로는 다음과 같다.
- 스왑을 통해 받고 싶은 토큰의 양
- 팔고싶은 토큰, 사고싶은 토큰으로 페어를 이룬 리스트
- 구매한 토큰을 어디로 전송할 지
- 스왑 마감기한
실제로 AVAX를 팔아서 5개의 MIM을 산다고 했을 때 코드는 아래처럼 하면 된다. 마감기한은 time.time()
으로 현재 시간을 가져오고 30초를 더해서 설정했는데, 이대로 하면 UNIX 상 밀리초로 인식돼서 1000을 곱해줘야 한다.(그 전에 int()
를 써서 정수로 만들어줘야 함) 만약 30초 동안 스왑이 진행되지 않는다면 해당 스왑은 라우터에 의해 revert 될 것이다.
>>> router.swapAVAXForExactTokens(5*(10**mim.decimals()), [wavax.address, mim.address], user.address, 1000*int(time.time()+30), {'from':user.address, 'value':0.3*10**18})
마지막에 인자에는 없는 from, value
값이 들어가는데, 어디서 토큰이 왔고 스왑할 토큰의 양은 어느정도인지 Brownie에 알려주는 용도라고 한다. 함수에 포함되지 않은 인자가 왜 필요한지 아직까지는 잘 모르겠다.. 현재 아발란체 시세가 1개에 $16.79이기 때문에 5개의 MIM을 구매하려면 0.3개 정도가 필요하다.
시험삼아 1달러로 바꿔봤는데 성공!!
이제 MIM을 DAI로 스왑하기 위해 approve를 해보자. 아까 추가로 더 스왑해서 5 MIM이 있는 상황이다. 시험삼아 그 반절인 2.5 MIM만 approve 해보자.
>>> mim.approve(router.address, 2.5*10**mim.decimals(), {'from':user.address})
얼마나 approve 했는지 나오진 않지만 성공!
이제 본격적으로 approve 한 만큼 스왑을 해보자. 우리가 사용할 함수는 swapExactTokensForTokens()
이다.
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
)
external
virtual
override
ensure(deadline)
returns (uint256[] memory amounts)
{
amounts = JoeLibrary.getAmountsOut(factory, amountIn, path);
require(
amounts[amounts.length - 1] >= amountOutMin,
"JoeRouter: INSUFFICIENT_OUTPUT_AMOUNT"
);
TransferHelper.safeTransferFrom(
path[0],
msg.sender,
JoeLibrary.pairFor(factory, path[0], path[1]),
amounts[0]
);
_swap(amounts, path, to);
}
콘솔에는 다음과 같이 입력하면 된다.
>>> router.swapExactTokensForTokens(2.5*10**mim.decimals(), 2.5*10**dai.decimals(), [mim.address, dai.address ], user.address, 1000*int(time.time()+30), {'from':user.address})
하지만 에러가 난다. 그 이유는 amountOutMin 값 즉, 최소로 받을 DAI 양을 2.5로 설정했기 때문. 이렇게 하면 2.5개의 MIM을 2.5개의 DAI로 바꿔줘라는 얘긴데 이건 MIM과 DAI의 비율이 정확히 1:1 혹은 그 이상이 되어야 한다.(1:1.1 등등) 따라서 현재 거래 비율이 1:1을 만족시키지 못해서 revert가 난 것.
>>>router.swapExactTokensForTokens(2.5*10**mim.decimals(), 0.95*2.5*10**dai.decimals(), [mim.address, dai.address], user.address, 1000*int(time.time()+30), {'from':user.address})
따라서 좀 더 여유있게 1:0.95비율로 스왑을 해보면
1:0.96의 비율로 스왑 한 것을 볼 수 있다.