일반적으로 Flash Loan이나 Flash Swap의 매커니즘 자체는 복잡할 것이 없지만, 실제 코드를 구현하는 것은 또 다른 느낌이기에 해당 둘의 인터페이스를 간단하게 정리해보고자 한다.
얼핏 보기에는
fee를 붙여서 상환하면 될 것 같지만,
막상 구현하려니 fee를 어떻게 계산해야 하나 조금 헷갈렸었다.
/**
* @dev allows smartcontracts to access the liquidity of the pool within one transaction,
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
* that must be kept into consideration. For further details please visit https://developers.aave.com
* @param receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
* @param assets the address of the principal reserve
* @param amounts the amount requested for this flashloan
* @param modes the flashloan borrow modes
* @param params a bytes array to be sent to the flashloan executor
* @param referralCode the referral code of the caller
*
*/
function flashLoan(
address receiver,
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata modes,
address onBehalfOf,
bytes calldata params,
uint16 referralCode
) external;
이자율에 대해 mode를 설정해 주어야 한다.

Test를 위해서는 그냥 0만 넣어도 무방하다.
AAVE의 Flash Loan에서는 Receiver 함수를 호출해줄 때 이자를 같이 파라미터 값에 넣어서 알려준다.
interface IFlashLoanReceiver {
/**
* @notice Executes an operation after receiving the flash-borrowed assets
* @dev Ensure that the contract can return the debt + premium, e.g., has
* enough funds to repay and has approved the Pool to pull the total amount
* @param assets The addresses of the flash-borrowed assets
* @param amounts The amounts of the flash-borrowed assets
* @param premiums The fee of each flash-borrowed asset
* @param initiator The address of the flashloan initiator
* @param params The byte-encoded params passed when initiating the flashloan
* @return True if the execution of the operation succeeds, false otherwise
*/
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool);
function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
function POOL() external view returns (IPool);
}
executeOperation라는 이름으로 콜백 함수를 정의해두면 premiums [] 배열에다가 넣어준다.
function getFlashLoan(address token, uint256 amount) external {
address receiver = address(this);
address[] memory assets = new address[](1);
assets[0] = token;
uint256[] memory amounts = new uint[](1);
amounts[0] = amount;
uint256[] memory modes = new uint[](1);
modes[0] = 0;
address onBehalfOf = address(this);
bytes memory params = "";
uint16 referralCode = 0;
pool.flashLoan(receiver, assets, amounts, modes, onBehalfOf, params, referralCode);
}
function executeOperation(
address[] memory assets,
uint256[] memory amounts,
uint256[] memory premiums,
address initiator,
bytes memory params
) public returns (bool) {
for (uint256 i = 0; i < assets.length; i++) {
uint256 amountOwing = amounts[i] + premiums[i];
IERC20(assets[i]).approve(address(pool), amountOwing);
}
return true;
}
이런 식으로 구현하면 된다.
Uniswap V2의 Flash Swap에 대해서는 이 글에서 다루었다.
Token A ⎯ Token B pair가 있을 때
30.08 bp)30 bp)하는 2 가지의 방법이 있다.

0.3% (30 bp) 의 fee를 지불해야 한다.
뭐의 0.3%냐? 하는 부분이 살짝 헷갈릴 수 있는데, Swap이라고 생각하면 조금 단순하다.
x * y = k
의 CPMM 공식에서,
우리는 y를 빌리고 x로 갚는다고 생각해보자.
(x + 갚을X) * (y - 빌릴y) = k
이 공식을 만족하면 된다.
x는 원래 pool에 있던 x의 reserve,
y는 원래 pool에 있던 y의 reserve,
빌릴 y는 우리가 빌리고자 하는 y의 양이다.
k는 당연히 x * y이다.
모든 값은 고정되어 있고, 갚을 X의 양만 계산해내면 된다.
(이 때 여기서 fee는 포함되어있지 않다)
근데 이걸 그냥 Swap이라고 생각하면 이렇게도 볼 수 있다.
x를 주고 y를 받는 Swap이라고 생각해보자.
(x + 낼x) * (y - 받을y) = k
여기서 내가 줄 x의 양의 0.3%는 fee로 pool에 내야한다.
(x + 0.997*낼x) * (y - 받을y) = k
이걸 다시 Flash Swap의 관점에서 생각하면 이렇다.
(x + 0.997*갚을x) * (y - 빌릴y) = k
그냥 Swap인데, 내가 빌릴 돈을 먼저 받고, x를 갚는 식이다.
그럼 이제 이런 방식으로 구현된다.
y를 y_out만큼 빌린다.x를 갚는다.getAmountsIn 함수를 이용하여, y_out만큼 받으려면, 얼마의 x를 내야하는지를 계산하면 fee가 포함된 상환액을 계산 가능)y를 y_out만큼 받는 swap을 하려면, 얼마만큼의 x를 내야 하는지의 계산하는 것과 동일하다.사실 이렇게까지 설명할 필요 조차 없는 간단한 개념이지만, 막상 Flash Swap이라는 거창한 단어를 붙이고 구현하려니 Fee가 어떻게 계산이 되는지 조금 헷갈려서 정리를 해보았다.

이 경우에는 0.3008 % (30.09 bp)를 fee로 지불해야 한다.
왜 A를 빌려서 B로 갚는 경우와 수수료가 다른지에 대해서는 이 글에서 자세히 설명해 두었다.
(x - x_out + 0.997*x_in) * y = k
하나만 뺐다가 넣으면 이 공식이 성립해야 한다.
즉, x_in은 x_out / 0.997과 같다.
이는 즉,
x_out * (1000 / 997)
x_out * (1 + 3/997)
x_in = x_out(원금) + x_out * 3/997(fee)
가 된다.
그냥 돈을 뺐다가 넣는 트랜잭션을 발생시키기 위해서는
x_out * 3/997 (0.003009027081)
만큼을 프로토콜에 지불해야 한다.
https://solidity-by-example.org/defi/uniswap-v2-flash-swap/
이 링크에 자세히 나와있다.
uniswapV2Call 함수를 구현하고, swap함수를 호출하면 된다.