일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- ethers typescript
- git rebase
- ambiguous function description
- 스마트컨트렉트 예약어 함수이름 중복
- ethers websocket
- 오블완
- 컨트렉트 배포 자동화
- 티스토리챌린지
- 계정추상화
- SBT표준
- erc4337 contract
- Vue.js
- 스마트컨트렉트테스트
- 컨트렉트 동일한 함수이름 호출
- rust 기초
- Vue
- erc4337
- 러스트 기초
- 머신러닝기초
- 러스트 기초 학습
- 체인의정석
- ethers v6
- ethers
- chainlink 설명
- 스마트컨트렉트 함수이름 중복 호출
- 러스트기초
- multicall
- ethers type
- vue기초
- 스마트 컨트렉트 함수이름 중복
- Today
- Total
체인의정석
DeFI 소스코드 분석 - DyDX flashloan 본문
github.com/Devilla/eth-arbitrage/blob/master/contracts/Arbitrage.sol
위 소스코드를 분석하였다.
일단 주소값을 보면 kovan test network와 연결된 소스임을 확인 할 수 있었다.
pool에는 DyDx주소를 두어서 인식을 시켜주고, 각각 등록된 화폐를 넣어준다.
여기서 tokenToMarketId는 등록된 토큰에 해당된다. 만약 새로운 종류의 토큰을 등록하고 싶다면 위에서 정의 한 후 생성자에서 등록하면 될 것 같다. 하지만, 실제 환경과 같게 하기 위해서 WETH를 사용할 예정이다.
그 다음 라인까지 가면 드디어 flashloan이 나오게 된다. 받는 값은 token주소, amount양, data가 있다. data의 경우에 처음에는 withdraw가 들어간 구조체를 넣어주고, 나중에 다시 돌려줄 땐 deposit을 넣어서 실행해주면 된다. 차익거래의 핵심은 한 명령어 안에 실행하는 것이므로, 이전 포스팅에서 설명한 총 3단계에 걸친 차익거래 로직이 실행되게 된다.
contract DyDxFlashLoan is Structs {
DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public SAI = 0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359;
address public USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address public DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
mapping(address => uint256) public currencies;
constructor() public {
currencies[WETH] = 1;
currencies[SAI] = 2;
currencies[USDC] = 3;
currencies[DAI] = 4;
}
modifier onlyPool() {
require(
msg.sender == address(pool),
"FlashLoan: could be called by DyDx pool only"
);
_;
}
function tokenToMarketId(address token) public view returns (uint256) {
uint256 marketId = currencies[token];
require(marketId != 0, "FlashLoan: Unsupported token");
return marketId - 1;
}
// the DyDx will call `callFunction(address sender, Info memory accountInfo, bytes memory data) public` after during `operate` call
function flashloan(address token, uint256 amount, bytes memory data)
internal
{
IERC20(token).approve(address(pool), amount + 1);
Info[] memory infos = new Info[](1);
ActionArgs[] memory args = new ActionArgs[](3);
infos[0] = Info(address(this), 0);
AssetAmount memory wamt = AssetAmount(
false,
AssetDenomination.Wei,
AssetReference.Delta,
amount
);
ActionArgs memory withdraw;
withdraw.actionType = ActionType.Withdraw;
withdraw.accountId = 0;
withdraw.amount = wamt;
withdraw.primaryMarketId = tokenToMarketId(token);
withdraw.otherAddress = address(this);
args[0] = withdraw;
ActionArgs memory call;
call.actionType = ActionType.Call;
call.accountId = 0;
call.otherAddress = address(this);
call.data = data;
args[1] = call;
ActionArgs memory deposit;
AssetAmount memory damt = AssetAmount(
true,
AssetDenomination.Wei,
AssetReference.Delta,
amount + 1
);
deposit.actionType = ActionType.Deposit;
deposit.accountId = 0;
deposit.amount = damt;
deposit.primaryMarketId = tokenToMarketId(token);
deposit.otherAddress = address(this);
args[2] = deposit;
pool.operate(infos, args);
}
}
여기서 call.data가 무엇을 의미하는지 모르겠어서 소스코드를 찾아 보았다.
* Arguments that are passed to Solo in an ordered list as part of a single operation.
* Each ActionArgs has an actionType which specifies which action struct that this data will be
* parsed into before being processed.
*/
struct ActionArgs {
ActionType actionType;
uint256 accountId;
Types.AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
아무래도 딱히 역할은 무엇인지 모르겠지만 call을 할때 사용한는것을 볼 수 있었다.
다른 사람은 어떻게 할지 한참을 찾아보다가 깃허브에서 한 소스를 발견하였다.
github.com/fifikobayashi/Aggregated-Flashloan/blob/master/contracts/AggregatedFlashloans.sol
pragma solidity >= 0.5.0 < 0.7.0;
import "@kollateral/contracts/invoke/IInvoker.sol";
import "@kollateral/contracts/invoke/KollateralInvokable.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
contract AggregatedFlashloans is KollateralInvokable {
address owner; // contract owner
address internal INVOKER_ROPSTEN;
// initialize deployment parameters
constructor() public{
owner = msg.sender;
INVOKER_ROPSTEN = address(0x234A76352e816c48098F20F830A21c820085b902); //Kollateral inovker on Ropsten
}
// only the contract owner can call fund
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
// initializes the flash loan aggregation parameters
function invoke() external payable {
address to = address(this);
bytes memory data = "";
address tokenAddress = address(0x0000000000000000000000000000000000000001); //Ropsten ETH
uint256 tokenAmount = 1e18; // i.e. 1 ETH worth
IInvoker(INVOKER_ROPSTEN).invoke(to, data, tokenAddress, tokenAmount);
}
/**
Mid-flash loan aggregation logic i.e. what you do with
the temporarily acquired flash liquidity from DyDx and Aave
*/
function execute(bytes calldata data) external payable {
// ***Step 1: Arbitrage or liquidation or whatever logic goes here
repay(); // ***Step 2: Repay the aggregated flash loan + above fees
/* Remember to factor in:
+ 6bps in Kollateral aggregation fee
+ 9bps in native Aave flash loan fee
+ 2 Wei in native DyDx floan loan fee
*/
}
/**
***Step 3: sweep entire balance on the contract back to contract owner
*/
function WithdrawBalance() public payable onlyOwner {
msg.sender.transfer(address(this).balance); // withdraw all ETH
}
}
여기서 보면 data는 공백으로 두었다. 공백으로 두어도 작동한다면, 공백으로 두고 돌려보기로 하였다. 나머지 입력값은 콜레터럴을 쓰던, AAVE를 쓰던 DyDX를 쓰던 똑같은것 같다.
function getFlashloan(address flashToken, uint256 flashAmount, address arbToken, bytes calldata zrxData, uint256 oneSplitMinReturn, uint256[] calldata oneSplitDistribution) external payable onlyOwner {
uint256 balanceBefore = IERC20(flashToken).balanceOf(address(this));
bytes memory data = abi.encode(flashToken, flashAmount, balanceBefore, arbToken, zrxData, oneSplitMinReturn, oneSplitDistribution);
flashloan(flashToken, flashAmount, data); // execution goes to `callFunction`
// and this point we have succefully paid the dept
}
github.com/cryptopixelfrog/DyDxFlashloanArb/blob/master/contracts/dydx/DyDxFlashLoan.sol
다음 소스코드를 본 결과
data안에 들어간 변수는 차익거래를 실행할 때 사용되는 변수임을 알 수 있었다. 차익거래에 필요한 입력값을 한번에 받아와서 넘기면 해당 입력값을 통해서 차익거래를 실행하게 된다. 따라서 넘기는 변수로는 차익거래 시에 필요한 변수를 상황에 맞게 넣어주면 된다.
이에 관련하여 더 찾아보니 eip 3156이 있었다.
eips.ethereum.org/EIPS/eip-3156
'블록체인 > 디파이' 카테고리의 다른 글
UniswapV2 정리 2 - Uniswap V2 Factory (0) | 2022.10.26 |
---|---|
UniswapV2 정리 - UniswapV2Pair & UniswapV2ERC20 컨트렉트 (0) | 2022.10.24 |
Uniswap V2 정리 - 기본 컨셉과 참고링크 (0) | 2022.10.24 |
DeFI - AAVE 분석 (0) | 2021.02.05 |
DeFI 코드 참고 소스 (0) | 2021.02.03 |